gbdt.cpp 34.3 KB
Newer Older
1
2
3
4
/*!
 * Copyright (c) 2016 Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See LICENSE file in the project root for license information.
 */
Guolin Ke's avatar
Guolin Ke committed
5
6
7
#include "gbdt.h"

#include <LightGBM/metric.h>
Guolin Ke's avatar
Guolin Ke committed
8
#include <LightGBM/network.h>
9
10
11
12
#include <LightGBM/objective_function.h>
#include <LightGBM/prediction_early_stop.h>
#include <LightGBM/utils/common.h>
#include <LightGBM/utils/openmp_wrapper.h>
13
#include <LightGBM/sample_strategy.h>
Guolin Ke's avatar
Guolin Ke committed
14

15
16
#include <chrono>
#include <ctime>
17
#include <queue>
18
19
#include <sstream>

Guolin Ke's avatar
Guolin Ke committed
20
21
namespace LightGBM {

22
23
Common::Timer global_timer;

24
25
26
int LGBM_config_::current_device = lgbm_device_cpu;
int LGBM_config_::current_learner = use_cpu_learner;

27
28
29
GBDT::GBDT()
    : iter_(0),
      train_data_(nullptr),
30
      config_(nullptr),
31
32
      objective_function_(nullptr),
      early_stopping_round_(0),
33
      early_stopping_min_delta_(0.0),
34
35
36
37
38
39
      es_first_metric_only_(false),
      max_feature_idx_(0),
      num_tree_per_iteration_(1),
      num_class_(1),
      num_iteration_for_pred_(0),
      shrinkage_rate_(0.1f),
40
      num_init_iteration_(0) {
Guolin Ke's avatar
Guolin Ke committed
41
  average_output_ = false;
Guolin Ke's avatar
Guolin Ke committed
42
  tree_learner_ = nullptr;
43
  linear_tree_ = false;
44
  data_sample_strategy_.reset(nullptr);
shiyu1994's avatar
shiyu1994 committed
45
46
47
  gradients_pointer_ = nullptr;
  hessians_pointer_ = nullptr;
  boosting_on_gpu_ = false;
Guolin Ke's avatar
Guolin Ke committed
48
49
50
51
52
}

GBDT::~GBDT() {
}

Guolin Ke's avatar
Guolin Ke committed
53
void GBDT::Init(const Config* config, const Dataset* train_data, const ObjectiveFunction* objective_function,
54
                const std::vector<const Metric*>& training_metrics) {
Nikita Titov's avatar
Nikita Titov committed
55
  CHECK_NOTNULL(train_data);
56
  train_data_ = train_data;
57
  if (!config->monotone_constraints.empty()) {
Nikita Titov's avatar
Nikita Titov committed
58
    CHECK_EQ(static_cast<size_t>(train_data_->num_total_features()), config->monotone_constraints.size());
Nikita Titov's avatar
Nikita Titov committed
59
  }
60
  if (!config->feature_contri.empty()) {
Nikita Titov's avatar
Nikita Titov committed
61
    CHECK_EQ(static_cast<size_t>(train_data_->num_total_features()), config->feature_contri.size());
62
  }
63
  iter_ = 0;
wxchan's avatar
wxchan committed
64
  num_iteration_for_pred_ = 0;
65
  max_feature_idx_ = 0;
wxchan's avatar
wxchan committed
66
  num_class_ = config->num_class;
Guolin Ke's avatar
Guolin Ke committed
67
68
  config_ = std::unique_ptr<Config>(new Config(*config));
  early_stopping_round_ = config_->early_stopping_round;
69
  early_stopping_min_delta_ = config->early_stopping_min_delta;
70
  es_first_metric_only_ = config_->first_metric_only;
Guolin Ke's avatar
Guolin Ke committed
71
  shrinkage_rate_ = config_->learning_rate;
72

73
  if (config_->device_type == std::string("cuda")) {
74
    LGBM_config_::current_learner = use_cuda_learner;
75
76
    #ifdef USE_CUDA
    if (config_->device_type == std::string("cuda")) {
77
78
79
      const int gpu_device_id = config_->gpu_device_id >= 0 ? config_->gpu_device_id : 0;
      CUDASUCCESS_OR_FATAL(cudaSetDevice(gpu_device_id));
    }
80
    #endif  // USE_CUDA
81
82
  }

83
  // load forced_splits file
84
85
86
87
88
  if (!config->forcedsplits_filename.empty()) {
    std::ifstream forced_splits_file(config->forcedsplits_filename.c_str());
    std::stringstream buffer;
    buffer << forced_splits_file.rdbuf();
    std::string err;
Guolin Ke's avatar
Guolin Ke committed
89
    forced_splits_json_ = Json::parse(buffer.str(), &err);
90
91
  }

92
93
94
  objective_function_ = objective_function;
  num_tree_per_iteration_ = num_class_;
  if (objective_function_ != nullptr) {
Guolin Ke's avatar
Guolin Ke committed
95
    num_tree_per_iteration_ = objective_function_->NumModelPerIteration();
96
97
98
    if (objective_function_->IsRenewTreeOutput() && !config->monotone_constraints.empty()) {
      Log::Fatal("Cannot use ``monotone_constraints`` in %s objective, please disable it.", objective_function_->GetName());
    }
99
100
  }

101
  data_sample_strategy_.reset(SampleStrategy::CreateSampleStrategy(config_.get(), train_data_, objective_function_, num_tree_per_iteration_));
102
103
  is_constant_hessian_ = GetIsConstHessian(objective_function);

104
105
106
  boosting_on_gpu_ = objective_function_ != nullptr && objective_function_->IsCUDAObjective() &&
                     !data_sample_strategy_->IsHessianChange();  // for sample strategy with Hessian change, fall back to boosting on CPU

107
  tree_learner_ = std::unique_ptr<TreeLearner>(TreeLearner::CreateTreeLearner(config_->tree_learner, config_->device_type,
shiyu1994's avatar
shiyu1994 committed
108
                                                                              config_.get(), boosting_on_gpu_));
109
110
111

  // init tree learner
  tree_learner_->Init(train_data_, is_constant_hessian_);
112
  tree_learner_->SetForcedSplit(&forced_splits_json_);
113
114
115
116
117
118
119
120

  // push training metrics
  training_metrics_.clear();
  for (const auto& metric : training_metrics) {
    training_metrics_.push_back(metric);
  }
  training_metrics_.shrink_to_fit();

121
122
  #ifdef USE_CUDA
  if (config_->device_type == std::string("cuda")) {
shiyu1994's avatar
shiyu1994 committed
123
    train_score_updater_.reset(new CUDAScoreUpdater(train_data_, num_tree_per_iteration_, boosting_on_gpu_));
124
  } else {
125
  #endif  // USE_CUDA
126
    train_score_updater_.reset(new ScoreUpdater(train_data_, num_tree_per_iteration_));
127
  #ifdef USE_CUDA
128
  }
129
  #endif  // USE_CUDA
130
131

  num_data_ = train_data_->num_data();
shiyu1994's avatar
shiyu1994 committed
132

133
134
135
136
137
138
139
  // get max feature index
  max_feature_idx_ = train_data_->num_total_features() - 1;
  // get label index
  label_idx_ = train_data_->label_idx();
  // get feature names
  feature_names_ = train_data_->feature_names();
  feature_infos_ = train_data_->feature_infos();
140
  monotone_constraints_ = config->monotone_constraints;
141
142
  // get parser config file content
  parser_config_str_ = train_data_->parser_config_str();
143

144
145
146
  // check that forced splits does not use feature indices larger than dataset size
  CheckForcedSplitFeatures();

147
  // if need bagging, create buffer
148
149
  data_sample_strategy_->ResetSampleConfig(config_.get(), true);
  ResetGradientBuffers();
150
151
152

  class_need_train_ = std::vector<bool>(num_tree_per_iteration_, true);
  if (objective_function_ != nullptr && objective_function_->SkipEmptyClass()) {
Nikita Titov's avatar
Nikita Titov committed
153
    CHECK_EQ(num_tree_per_iteration_, num_class_);
154
155
    for (int i = 0; i < num_class_; ++i) {
      class_need_train_[i] = objective_function_->ClassNeedTrain(i);
156
157
    }
  }
158
159
160
161

  if (config_->linear_tree) {
    linear_tree_ = true;
  }
wxchan's avatar
wxchan committed
162
163
}

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
void GBDT::CheckForcedSplitFeatures() {
  std::queue<Json> forced_split_nodes;
  forced_split_nodes.push(forced_splits_json_);
  while (!forced_split_nodes.empty()) {
    Json node = forced_split_nodes.front();
    forced_split_nodes.pop();
    const int feature_index = node["feature"].int_value();
    if (feature_index > max_feature_idx_) {
      Log::Fatal("Forced splits file includes feature index %d, but maximum feature index in dataset is %d",
        feature_index, max_feature_idx_);
    }
    if (node.object_items().count("left") > 0) {
      forced_split_nodes.push(node["left"]);
    }
    if (node.object_items().count("right") > 0) {
      forced_split_nodes.push(node["right"]);
    }
  }
}

wxchan's avatar
wxchan committed
184
void GBDT::AddValidDataset(const Dataset* valid_data,
185
                           const std::vector<const Metric*>& valid_metrics) {
wxchan's avatar
wxchan committed
186
  if (!train_data_->CheckAlign(*valid_data)) {
187
    Log::Fatal("Cannot add validation data, since it has different bin mappers with training data");
188
  }
Guolin Ke's avatar
Guolin Ke committed
189
  // for a validation dataset, we need its score and metric
190
  auto new_score_updater =
191
192
    #ifdef USE_CUDA
    config_->device_type == std::string("cuda") ?
193
194
    std::unique_ptr<CUDAScoreUpdater>(new CUDAScoreUpdater(valid_data, num_tree_per_iteration_,
      objective_function_ != nullptr && objective_function_->IsCUDAObjective())) :
195
    #endif  // USE_CUDA
196
    std::unique_ptr<ScoreUpdater>(new ScoreUpdater(valid_data, num_tree_per_iteration_));
wxchan's avatar
wxchan committed
197
198
  // update score
  for (int i = 0; i < iter_; ++i) {
199
200
201
    for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
      auto curr_tree = (i + num_init_iteration_) * num_tree_per_iteration_ + cur_tree_id;
      new_score_updater->AddScore(models_[curr_tree].get(), cur_tree_id);
wxchan's avatar
wxchan committed
202
203
    }
  }
Guolin Ke's avatar
Guolin Ke committed
204
  valid_score_updater_.push_back(std::move(new_score_updater));
Guolin Ke's avatar
Guolin Ke committed
205
206
207
208
  valid_metrics_.emplace_back();
  for (const auto& metric : valid_metrics) {
    valid_metrics_.back().push_back(metric);
  }
Guolin Ke's avatar
Guolin Ke committed
209
  valid_metrics_.back().shrink_to_fit();
210

211
212
213
214
215
216
217
  if (early_stopping_round_ > 0) {
    auto num_metrics = valid_metrics.size();
    if (es_first_metric_only_) { num_metrics = 1; }
    best_iter_.emplace_back(num_metrics, 0);
    best_score_.emplace_back(num_metrics, kMinScore);
    best_msg_.emplace_back(num_metrics);
  }
Guolin Ke's avatar
Guolin Ke committed
218
219
}

Guolin Ke's avatar
Guolin Ke committed
220
void GBDT::Boosting() {
221
  Common::FunctionTimer fun_timer("GBDT::Boosting", global_timer);
Guolin Ke's avatar
Guolin Ke committed
222
  if (objective_function_ == nullptr) {
223
    Log::Fatal("No objective function provided");
Guolin Ke's avatar
Guolin Ke committed
224
225
226
227
  }
  // objective function will calculate gradients and hessians
  int64_t num_score = 0;
  objective_function_->
228
    GetGradients(GetTrainingScore(&num_score), gradients_pointer_, hessians_pointer_);
Guolin Ke's avatar
Guolin Ke committed
229
230
}

Guolin Ke's avatar
Guolin Ke committed
231
void GBDT::Train(int snapshot_freq, const std::string& model_output_path) {
232
  Common::FunctionTimer fun_timer("GBDT::Train", global_timer);
Guolin Ke's avatar
Guolin Ke committed
233
234
  bool is_finished = false;
  auto start_time = std::chrono::steady_clock::now();
Guolin Ke's avatar
Guolin Ke committed
235
  for (int iter = 0; iter < config_->num_iterations && !is_finished; ++iter) {
Guolin Ke's avatar
Guolin Ke committed
236
237
238
239
    is_finished = TrainOneIter(nullptr, nullptr);
    if (!is_finished) {
      is_finished = EvalAndCheckEarlyStopping();
    }
Guolin Ke's avatar
Guolin Ke committed
240
241
242
243
244
245
246
    auto end_time = std::chrono::steady_clock::now();
    // output used time per iteration
    Log::Info("%f seconds elapsed, finished iteration %d", std::chrono::duration<double,
              std::milli>(end_time - start_time) * 1e-3, iter + 1);
    if (snapshot_freq > 0
        && (iter + 1) % snapshot_freq == 0) {
      std::string snapshot_out = model_output_path + ".snapshot_iter_" + std::to_string(iter + 1);
247
      SaveModelToFile(0, -1, config_->saved_feature_importance_type, snapshot_out.c_str());
Guolin Ke's avatar
Guolin Ke committed
248
249
250
251
    }
  }
}

252
void GBDT::RefitTree(const std::vector<std::vector<int>>& tree_leaf_prediction) {
253
254
255
  CHECK_GT(tree_leaf_prediction.size(), 0);
  CHECK_EQ(static_cast<size_t>(num_data_), tree_leaf_prediction.size());
  CHECK_EQ(static_cast<size_t>(models_.size()), tree_leaf_prediction[0].size());
256
257
  int num_iterations = static_cast<int>(models_.size() / num_tree_per_iteration_);
  std::vector<int> leaf_pred(num_data_);
258
259
  if (linear_tree_) {
    std::vector<int> max_leaves_by_thread = std::vector<int>(OMP_NUM_THREADS(), 0);
260
    #pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
261
262
263
264
265
266
267
268
269
270
    for (int i = 0; i < static_cast<int>(tree_leaf_prediction.size()); ++i) {
      int tid = omp_get_thread_num();
      for (size_t j = 0; j < tree_leaf_prediction[i].size(); ++j) {
        max_leaves_by_thread[tid] = std::max(max_leaves_by_thread[tid], tree_leaf_prediction[i][j]);
      }
    }
    int max_leaves = *std::max_element(max_leaves_by_thread.begin(), max_leaves_by_thread.end());
    max_leaves += 1;
    tree_learner_->InitLinear(train_data_, max_leaves);
  }
271
272
273
274
  for (int iter = 0; iter < num_iterations; ++iter) {
    Boosting();
    for (int tree_id = 0; tree_id < num_tree_per_iteration_; ++tree_id) {
      int model_index = iter * num_tree_per_iteration_ + tree_id;
275
      #pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
276
277
      for (int i = 0; i < num_data_; ++i) {
        leaf_pred[i] = tree_leaf_prediction[i][model_index];
Nikita Titov's avatar
Nikita Titov committed
278
        CHECK_LT(leaf_pred[i], models_[model_index]->num_leaves());
279
      }
280
      size_t offset = static_cast<size_t>(tree_id) * num_data_;
281
282
      auto grad = gradients_pointer_ + offset;
      auto hess = hessians_pointer_ + offset;
283
284
285
286
287
288
289
      auto new_tree = tree_learner_->FitByExistingTree(models_[model_index].get(), leaf_pred, grad, hess);
      train_score_updater_->AddScore(tree_learner_.get(), new_tree, tree_id);
      models_[model_index].reset(new_tree);
    }
  }
}

Andrew Ziem's avatar
Andrew Ziem committed
290
/* If the custom "average" is implemented it will be used in place of the label average (if enabled)
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
*
* An improvement to this is to have options to explicitly choose
* (i) standard average
* (ii) custom average if available
* (iii) any user defined scalar bias (e.g. using a new option "init_score" that overrides (i) and (ii) )
*
* (i) and (ii) could be selected as say "auto_init_score" = 0 or 1 etc..
*
*/
double ObtainAutomaticInitialScore(const ObjectiveFunction* fobj, int class_id) {
  double init_score = 0.0;
  if (fobj != nullptr) {
    init_score = fobj->BoostFromScore(class_id);
  }
  if (Network::num_machines() > 1) {
    init_score = Network::GlobalSyncUpByMean(init_score);
  }
  return init_score;
}

Guolin Ke's avatar
Guolin Ke committed
311
double GBDT::BoostFromAverage(int class_id, bool update_scorer) {
312
  Common::FunctionTimer fun_timer("GBDT::BoostFromAverage", global_timer);
313
  // boosting from average label; or customized "average" if implemented for the current objective
314
315
316
  if (models_.empty() && !train_score_updater_->has_init_score() && objective_function_ != nullptr) {
    if (config_->boost_from_average || (train_data_ != nullptr && train_data_->num_features() == 0)) {
      double init_score = ObtainAutomaticInitialScore(objective_function_, class_id);
317
      if (std::fabs(init_score) > kEpsilon) {
Guolin Ke's avatar
Guolin Ke committed
318
319
320
321
322
        if (update_scorer) {
          train_score_updater_->AddScore(init_score, class_id);
          for (auto& score_updater : valid_score_updater_) {
            score_updater->AddScore(init_score, class_id);
          }
323
324
325
        }
        Log::Info("Start training from score %lf", init_score);
        return init_score;
Guolin Ke's avatar
Guolin Ke committed
326
      }
327
328
329
    } else if (std::string(objective_function_->GetName()) == std::string("regression_l1")
               || std::string(objective_function_->GetName()) == std::string("quantile")
               || std::string(objective_function_->GetName()) == std::string("mape")) {
330
      Log::Warning("Disabling boost_from_average in %s may cause the slow convergence", objective_function_->GetName());
331
    }
332
  }
Guolin Ke's avatar
Guolin Ke committed
333
334
  return 0.0f;
}
Guolin Ke's avatar
Guolin Ke committed
335

Guolin Ke's avatar
Guolin Ke committed
336
bool GBDT::TrainOneIter(const score_t* gradients, const score_t* hessians) {
337
  Common::FunctionTimer fun_timer("GBDT::TrainOneIter", global_timer);
338
  std::vector<double> init_scores(num_tree_per_iteration_, 0.0);
Guolin Ke's avatar
Guolin Ke committed
339
  // boosting first
Guolin Ke's avatar
Guolin Ke committed
340
  if (gradients == nullptr || hessians == nullptr) {
341
    for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
Guolin Ke's avatar
Guolin Ke committed
342
      init_scores[cur_tree_id] = BoostFromAverage(cur_tree_id, true);
343
    }
Guolin Ke's avatar
Guolin Ke committed
344
    Boosting();
345
346
347
    gradients = gradients_pointer_;
    hessians = hessians_pointer_;
  } else {
shiyu1994's avatar
shiyu1994 committed
348
    // use customized objective function
349
    // the check below fails unless objective=custom is provided in the parameters on Booster creation
shiyu1994's avatar
shiyu1994 committed
350
    CHECK(objective_function_ == nullptr);
351
    if (data_sample_strategy_->IsHessianChange()) {
shiyu1994's avatar
shiyu1994 committed
352
353
      // need to copy customized gradients when using GOSS
      int64_t total_size = static_cast<int64_t>(num_data_) * num_tree_per_iteration_;
354
      #pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
shiyu1994's avatar
shiyu1994 committed
355
356
357
358
359
360
      for (int64_t i = 0; i < total_size; ++i) {
        gradients_[i] = gradients[i];
        hessians_[i] = hessians[i];
      }
      CHECK_EQ(gradients_pointer_, gradients_.data());
      CHECK_EQ(hessians_pointer_, hessians_.data());
361
362
363
364
365
      gradients = gradients_pointer_;
      hessians = hessians_pointer_;
    }
  }

366
  // bagging logic
367
368
369
370
  data_sample_strategy_->Bagging(iter_, tree_learner_.get(), gradients_.data(), hessians_.data());
  const bool is_use_subset = data_sample_strategy_->is_use_subset();
  const data_size_t bag_data_cnt = data_sample_strategy_->bag_data_cnt();
  const std::vector<data_size_t, Common::AlignmentAllocator<data_size_t, kAlignedSize>>& bag_data_indices = data_sample_strategy_->bag_data_indices();
Guolin Ke's avatar
Guolin Ke committed
371

372
373
  if (objective_function_ == nullptr && is_use_subset && bag_data_cnt < num_data_ && !boosting_on_gpu_ && !data_sample_strategy_->IsHessianChange()) {
    ResetGradientBuffers();
shiyu1994's avatar
shiyu1994 committed
374
375
  }

Guolin Ke's avatar
Guolin Ke committed
376
  bool should_continue = false;
377
  for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
378
    const size_t offset = static_cast<size_t>(cur_tree_id) * num_data_;
379
    std::unique_ptr<Tree> new_tree(new Tree(2, false, false));
380
    if (class_need_train_[cur_tree_id] && train_data_->num_features() > 0) {
381
382
      auto grad = gradients + offset;
      auto hess = hessians + offset;
Guolin Ke's avatar
Guolin Ke committed
383
      // need to copy gradients for bagging subset.
384
385
386
387
      if (is_use_subset && bag_data_cnt < num_data_ && !boosting_on_gpu_) {
        for (int i = 0; i < bag_data_cnt; ++i) {
          gradients_pointer_[offset + i] = grad[bag_data_indices[i]];
          hessians_pointer_[offset + i] = hess[bag_data_indices[i]];
Guolin Ke's avatar
Guolin Ke committed
388
        }
389
390
        grad = gradients_pointer_ + offset;
        hess = hessians_pointer_ + offset;
Guolin Ke's avatar
Guolin Ke committed
391
      }
392
393
      bool is_first_tree = models_.size() < static_cast<size_t>(num_tree_per_iteration_);
      new_tree.reset(tree_learner_->Train(grad, hess, is_first_tree));
394
    }
Guolin Ke's avatar
Guolin Ke committed
395

Guolin Ke's avatar
Guolin Ke committed
396
    if (new_tree->num_leaves() > 1) {
Guolin Ke's avatar
Guolin Ke committed
397
      should_continue = true;
398
      auto score_ptr = train_score_updater_->score() + offset;
399
400
      auto residual_getter = [score_ptr](const label_t* label, int i) {return static_cast<double>(label[i]) - score_ptr[i]; };
      tree_learner_->RenewTreeOutput(new_tree.get(), objective_function_, residual_getter,
401
                                     num_data_, bag_data_indices.data(), bag_data_cnt, train_score_updater_->score());
Guolin Ke's avatar
Guolin Ke committed
402
403
404
      // shrinkage by learning rate
      new_tree->Shrinkage(shrinkage_rate_);
      // update score
405
      UpdateScore(new_tree.get(), cur_tree_id);
406
407
      if (std::fabs(init_scores[cur_tree_id]) > kEpsilon) {
        new_tree->AddBias(init_scores[cur_tree_id]);
Guolin Ke's avatar
Guolin Ke committed
408
      }
409
410
    } else {
      // only add default score one-time
411
      if (models_.size() < static_cast<size_t>(num_tree_per_iteration_)) {
412
413
414
415
416
417
        if (objective_function_ != nullptr && !config_->boost_from_average && !train_score_updater_->has_init_score()) {
          init_scores[cur_tree_id] = ObtainAutomaticInitialScore(objective_function_, cur_tree_id);
          // updates scores
          train_score_updater_->AddScore(init_scores[cur_tree_id], cur_tree_id);
          for (auto& score_updater : valid_score_updater_) {
            score_updater->AddScore(init_scores[cur_tree_id], cur_tree_id);
418
          }
419
        }
420
        new_tree->AsConstantTree(init_scores[cur_tree_id]);
421
422
      }
    }
Guolin Ke's avatar
Guolin Ke committed
423
424
425
    // add model
    models_.push_back(std::move(new_tree));
  }
Guolin Ke's avatar
Guolin Ke committed
426

Guolin Ke's avatar
Guolin Ke committed
427
  if (!should_continue) {
428
    Log::Warning("Stopped training because there are no more leaves that meet the split requirements");
429
430
431
432
    if (models_.size() > static_cast<size_t>(num_tree_per_iteration_)) {
      for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
        models_.pop_back();
      }
Guolin Ke's avatar
Guolin Ke committed
433
434
435
    }
    return true;
  }
436

Guolin Ke's avatar
Guolin Ke committed
437
438
  ++iter_;
  return false;
Guolin Ke's avatar
Guolin Ke committed
439
}
440

wxchan's avatar
wxchan committed
441
void GBDT::RollbackOneIter() {
442
  if (iter_ <= 0) { return; }
wxchan's avatar
wxchan committed
443
  // reset score
444
  for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
Guolin Ke's avatar
Guolin Ke committed
445
    auto curr_tree = models_.size() - num_tree_per_iteration_ + cur_tree_id;
wxchan's avatar
wxchan committed
446
    models_[curr_tree]->Shrinkage(-1.0);
447
    train_score_updater_->AddScore(models_[curr_tree].get(), cur_tree_id);
wxchan's avatar
wxchan committed
448
    for (auto& score_updater : valid_score_updater_) {
449
      score_updater->AddScore(models_[curr_tree].get(), cur_tree_id);
wxchan's avatar
wxchan committed
450
451
452
    }
  }
  // remove model
453
  for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
wxchan's avatar
wxchan committed
454
455
456
457
458
    models_.pop_back();
  }
  --iter_;
}

Guolin Ke's avatar
Guolin Ke committed
459
bool GBDT::EvalAndCheckEarlyStopping() {
460
461
  bool is_met_early_stopping = false;
  // print message for metric
Guolin Ke's avatar
Guolin Ke committed
462
  auto best_msg = OutputMetric(iter_);
Guolin Ke's avatar
Guolin Ke committed
463
464


Guolin Ke's avatar
Guolin Ke committed
465
  is_met_early_stopping = !best_msg.empty();
466
467
  if (is_met_early_stopping) {
    Log::Info("Early stopping at iteration %d, the best iteration round is %d",
468
              iter_, iter_ - early_stopping_round_);
Guolin Ke's avatar
Guolin Ke committed
469
    Log::Info("Output of best iteration round:\n%s", best_msg.c_str());
470
    // pop last early_stopping_round_ models
471
    for (int i = 0; i < early_stopping_round_ * num_tree_per_iteration_; ++i) {
472
473
474
475
      models_.pop_back();
    }
  }
  return is_met_early_stopping;
Guolin Ke's avatar
Guolin Ke committed
476
477
}

478
void GBDT::UpdateScore(const Tree* tree, const int cur_tree_id) {
479
  Common::FunctionTimer fun_timer("GBDT::UpdateScore", global_timer);
Guolin Ke's avatar
Guolin Ke committed
480
  // update training score
481
  if (!data_sample_strategy_->is_use_subset()) {
482
    train_score_updater_->AddScore(tree_learner_.get(), tree, cur_tree_id);
Guolin Ke's avatar
Guolin Ke committed
483

484
    const data_size_t bag_data_cnt = data_sample_strategy_->bag_data_cnt();
Guolin Ke's avatar
Guolin Ke committed
485
    // we need to predict out-of-bag scores of data for boosting
486
    if (num_data_ - bag_data_cnt > 0) {
487
488
      #ifdef USE_CUDA
      if (config_->device_type == std::string("cuda")) {
489
        train_score_updater_->AddScore(tree, data_sample_strategy_->cuda_bag_data_indices().RawData() + bag_data_cnt, num_data_ - bag_data_cnt, cur_tree_id);
490
      } else {
491
      #endif  // USE_CUDA
492
        train_score_updater_->AddScore(tree, data_sample_strategy_->bag_data_indices().data() + bag_data_cnt, num_data_ - bag_data_cnt, cur_tree_id);
493
      #ifdef USE_CUDA
494
      }
495
      #endif  // USE_CUDA
Guolin Ke's avatar
Guolin Ke committed
496
497
    }

Guolin Ke's avatar
Guolin Ke committed
498
  } else {
499
    train_score_updater_->AddScore(tree, cur_tree_id);
Guolin Ke's avatar
Guolin Ke committed
500
  }
Guolin Ke's avatar
Guolin Ke committed
501
502


Guolin Ke's avatar
Guolin Ke committed
503
  // update validation score
Guolin Ke's avatar
Guolin Ke committed
504
  for (auto& score_updater : valid_score_updater_) {
505
    score_updater->AddScore(tree, cur_tree_id);
Guolin Ke's avatar
Guolin Ke committed
506
507
508
  }
}

509
#ifdef USE_CUDA
510
511
512
std::vector<double> GBDT::EvalOneMetric(const Metric* metric, const double* score, const data_size_t num_data) const {
#else
std::vector<double> GBDT::EvalOneMetric(const Metric* metric, const double* score, const data_size_t /*num_data*/) const {
513
514
#endif  // USE_CUDA
  #ifdef USE_CUDA
515
  const bool evaluation_on_cuda = metric->IsCUDAMetric();
shiyu1994's avatar
shiyu1994 committed
516
  if ((boosting_on_gpu_ && evaluation_on_cuda) || (!boosting_on_gpu_ && !evaluation_on_cuda)) {
517
  #endif  // USE_CUDA
518
    return metric->Eval(score, objective_function_);
519
  #ifdef USE_CUDA
shiyu1994's avatar
shiyu1994 committed
520
  } else if (boosting_on_gpu_ && !evaluation_on_cuda) {
521
    const size_t total_size = static_cast<size_t>(num_data) * static_cast<size_t>(num_tree_per_iteration_);
522
523
524
525
526
527
    if (total_size > host_score_.size()) {
      host_score_.resize(total_size, 0.0f);
    }
    CopyFromCUDADeviceToHost<double>(host_score_.data(), score, total_size, __FILE__, __LINE__);
    return metric->Eval(host_score_.data(), objective_function_);
  } else {
528
    const size_t total_size = static_cast<size_t>(num_data) * static_cast<size_t>(num_tree_per_iteration_);
529
530
531
532
533
534
    if (total_size > cuda_score_.Size()) {
      cuda_score_.Resize(total_size);
    }
    CopyFromHostToCUDADevice<double>(cuda_score_.RawData(), score, total_size, __FILE__, __LINE__);
    return metric->Eval(cuda_score_.RawData(), objective_function_);
  }
535
  #endif  // USE_CUDA
Guolin Ke's avatar
Guolin Ke committed
536
537
}

Guolin Ke's avatar
Guolin Ke committed
538
std::string GBDT::OutputMetric(int iter) {
Guolin Ke's avatar
Guolin Ke committed
539
  bool need_output = (iter % config_->metric_freq) == 0;
Guolin Ke's avatar
Guolin Ke committed
540
541
  std::string ret = "";
  std::stringstream msg_buf;
542
  std::vector<std::pair<size_t, size_t>> meet_early_stopping_pairs;
Guolin Ke's avatar
Guolin Ke committed
543
  // print training metric
Guolin Ke's avatar
Guolin Ke committed
544
  if (need_output) {
545
546
    for (auto& sub_metric : training_metrics_) {
      auto name = sub_metric->GetName();
547
      auto scores = EvalOneMetric(sub_metric, train_score_updater_->score(), train_score_updater_->num_data());
Guolin Ke's avatar
Guolin Ke committed
548
      for (size_t k = 0; k < name.size(); ++k) {
Guolin Ke's avatar
Guolin Ke committed
549
550
551
552
553
554
        std::stringstream tmp_buf;
        tmp_buf << "Iteration:" << iter
          << ", training " << name[k]
          << " : " << scores[k];
        Log::Info(tmp_buf.str().c_str());
        if (early_stopping_round_ > 0) {
555
          msg_buf << tmp_buf.str() << '\n';
Guolin Ke's avatar
Guolin Ke committed
556
        }
557
      }
558
    }
Guolin Ke's avatar
Guolin Ke committed
559
560
  }
  // print validation metric
Guolin Ke's avatar
Guolin Ke committed
561
  if (need_output || early_stopping_round_ > 0) {
562
563
    for (size_t i = 0; i < valid_metrics_.size(); ++i) {
      for (size_t j = 0; j < valid_metrics_[i].size(); ++j) {
564
        auto test_scores = EvalOneMetric(valid_metrics_[i][j], valid_score_updater_[i]->score(), valid_score_updater_[i]->num_data());
Guolin Ke's avatar
Guolin Ke committed
565
566
567
568
569
570
571
572
573
574
        auto name = valid_metrics_[i][j]->GetName();
        for (size_t k = 0; k < name.size(); ++k) {
          std::stringstream tmp_buf;
          tmp_buf << "Iteration:" << iter
            << ", valid_" << i + 1 << " " << name[k]
            << " : " << test_scores[k];
          if (need_output) {
            Log::Info(tmp_buf.str().c_str());
          }
          if (early_stopping_round_ > 0) {
575
            msg_buf << tmp_buf.str() << '\n';
576
          }
wxchan's avatar
wxchan committed
577
        }
578
        if (es_first_metric_only_ && j > 0) { continue; }
Guolin Ke's avatar
Guolin Ke committed
579
        if (ret.empty() && early_stopping_round_ > 0) {
580
          auto cur_score = valid_metrics_[i][j]->factor_to_bigger_better() * test_scores.back();
581
          if (cur_score - best_score_[i][j] > early_stopping_min_delta_) {
582
            best_score_[i][j] = cur_score;
583
            best_iter_[i][j] = iter;
Guolin Ke's avatar
Guolin Ke committed
584
            meet_early_stopping_pairs.emplace_back(i, j);
585
          } else {
Guolin Ke's avatar
Guolin Ke committed
586
            if (iter - best_iter_[i][j] >= early_stopping_round_) { ret = best_msg_[i][j]; }
587
          }
wxchan's avatar
wxchan committed
588
589
        }
      }
Guolin Ke's avatar
Guolin Ke committed
590
591
    }
  }
Guolin Ke's avatar
Guolin Ke committed
592
593
594
  for (auto& pair : meet_early_stopping_pairs) {
    best_msg_[pair.first][pair.second] = msg_buf.str();
  }
wxchan's avatar
wxchan committed
595
  return ret;
Guolin Ke's avatar
Guolin Ke committed
596
597
}

598
/*! \brief Get eval result */
599
std::vector<double> GBDT::GetEvalAt(int data_idx) const {
Guolin Ke's avatar
Guolin Ke committed
600
  CHECK(data_idx >= 0 && data_idx <= static_cast<int>(valid_score_updater_.size()));
601
602
  std::vector<double> ret;
  if (data_idx == 0) {
603
    for (auto& sub_metric : training_metrics_) {
604
      auto scores = EvalOneMetric(sub_metric, train_score_updater_->score(), train_score_updater_->num_data());
605
606
607
      for (auto score : scores) {
        ret.push_back(score);
      }
608
    }
609
  } else {
610
611
    auto used_idx = data_idx - 1;
    for (size_t j = 0; j < valid_metrics_[used_idx].size(); ++j) {
612
      auto test_scores = EvalOneMetric(valid_metrics_[used_idx][j], valid_score_updater_[used_idx]->score(), valid_score_updater_[used_idx]->num_data());
613
614
615
      for (auto score : test_scores) {
        ret.push_back(score);
      }
616
617
618
619
620
    }
  }
  return ret;
}

Guolin Ke's avatar
Guolin Ke committed
621
/*! \brief Get training scores result */
622
const double* GBDT::GetTrainingScore(int64_t* out_len) {
623
  *out_len = static_cast<int64_t>(train_score_updater_->num_data()) * num_class_;
Guolin Ke's avatar
Guolin Ke committed
624
  return train_score_updater_->score();
625
626
}

627
void GBDT::PredictContrib(const double* features, double* output) const {
628
  // set zero
Guolin Ke's avatar
Guolin Ke committed
629
630
  const int num_features = max_feature_idx_ + 1;
  std::memset(output, 0, sizeof(double) * num_tree_per_iteration_ * (num_features + 1));
631
632
  const int end_iteration_for_pred = start_iteration_for_pred_ + num_iteration_for_pred_;
  for (int i = start_iteration_for_pred_; i < end_iteration_for_pred; ++i) {
633
634
    // predict all the trees for one iteration
    for (int k = 0; k < num_tree_per_iteration_; ++k) {
Guolin Ke's avatar
Guolin Ke committed
635
      models_[i * num_tree_per_iteration_ + k]->PredictContrib(features, num_features, output + k*(num_features + 1));
636
    }
637
638
639
640
641
642
  }
}

void GBDT::PredictContribByMap(const std::unordered_map<int, double>& features,
                               std::vector<std::unordered_map<int, double>>* output) const {
  const int num_features = max_feature_idx_ + 1;
643
644
  const int end_iteration_for_pred = start_iteration_for_pred_ + num_iteration_for_pred_;
  for (int i = start_iteration_for_pred_; i < end_iteration_for_pred; ++i) {
645
646
647
    // predict all the trees for one iteration
    for (int k = 0; k < num_tree_per_iteration_; ++k) {
      models_[i * num_tree_per_iteration_ + k]->PredictContribByMap(features, num_features, &((*output)[k]));
648
649
650
651
    }
  }
}

Guolin Ke's avatar
Guolin Ke committed
652
653
void GBDT::GetPredictAt(int data_idx, double* out_result, int64_t* out_len) {
  CHECK(data_idx >= 0 && data_idx <= static_cast<int>(valid_score_updater_.size()));
Guolin Ke's avatar
Guolin Ke committed
654

655
  const double* raw_scores = nullptr;
Guolin Ke's avatar
Guolin Ke committed
656
657
  data_size_t num_data = 0;
  if (data_idx == 0) {
wxchan's avatar
wxchan committed
658
    raw_scores = GetTrainingScore(out_len);
Guolin Ke's avatar
Guolin Ke committed
659
660
661
662
663
    num_data = train_score_updater_->num_data();
  } else {
    auto used_idx = data_idx - 1;
    raw_scores = valid_score_updater_[used_idx]->score();
    num_data = valid_score_updater_[used_idx]->num_data();
664
    *out_len = static_cast<int64_t>(num_data) * num_class_;
Guolin Ke's avatar
Guolin Ke committed
665
  }
666
  #ifdef USE_CUDA
667
668
669
670
671
672
  std::vector<double> host_raw_scores;
  if (boosting_on_gpu_) {
    host_raw_scores.resize(static_cast<size_t>(*out_len), 0.0);
    CopyFromCUDADeviceToHost<double>(host_raw_scores.data(), raw_scores, static_cast<size_t>(*out_len), __FILE__, __LINE__);
    raw_scores = host_raw_scores.data();
  }
673
  #endif  // USE_CUDA
Guolin Ke's avatar
Guolin Ke committed
674
  if (objective_function_ != nullptr) {
675
    #pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
Guolin Ke's avatar
Guolin Ke committed
676
    for (data_size_t i = 0; i < num_data; ++i) {
Guolin Ke's avatar
Guolin Ke committed
677
      std::vector<double> tree_pred(num_tree_per_iteration_);
678
      for (int j = 0; j < num_tree_per_iteration_; ++j) {
Guolin Ke's avatar
Guolin Ke committed
679
        tree_pred[j] = raw_scores[j * num_data + i];
680
      }
Guolin Ke's avatar
Guolin Ke committed
681
682
      std::vector<double> tmp_result(num_class_);
      objective_function_->ConvertOutput(tree_pred.data(), tmp_result.data());
Guolin Ke's avatar
Guolin Ke committed
683
      for (int j = 0; j < num_class_; ++j) {
684
        out_result[j * num_data + i] = static_cast<double>(tmp_result[j]);
Guolin Ke's avatar
Guolin Ke committed
685
686
      }
    }
687
  } else {
688
    #pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
Guolin Ke's avatar
Guolin Ke committed
689
    for (data_size_t i = 0; i < num_data; ++i) {
690
      for (int j = 0; j < num_tree_per_iteration_; ++j) {
Guolin Ke's avatar
Guolin Ke committed
691
        out_result[j * num_data + i] = static_cast<double>(raw_scores[j * num_data + i]);
Guolin Ke's avatar
Guolin Ke committed
692
693
694
695
696
      }
    }
  }
}

697
698
double GBDT::GetUpperBoundValue() const {
  double max_value = 0.0;
Nikita Titov's avatar
Nikita Titov committed
699
  for (const auto &tree : models_) {
700
701
702
703
704
705
706
    max_value += tree->GetUpperBoundValue();
  }
  return max_value;
}

double GBDT::GetLowerBoundValue() const {
  double min_value = 0.0;
Nikita Titov's avatar
Nikita Titov committed
707
  for (const auto &tree : models_) {
708
709
710
711
712
    min_value += tree->GetLowerBoundValue();
  }
  return min_value;
}

Guolin Ke's avatar
Guolin Ke committed
713
714
715
void GBDT::ResetTrainingData(const Dataset* train_data, const ObjectiveFunction* objective_function,
                             const std::vector<const Metric*>& training_metrics) {
  if (train_data != train_data_ && !train_data_->CheckAlign(*train_data)) {
716
    Log::Fatal("Cannot reset training data, since new training data has different bin mappers");
wxchan's avatar
wxchan committed
717
718
  }

Guolin Ke's avatar
Guolin Ke committed
719
  objective_function_ = objective_function;
720
  data_sample_strategy_->UpdateObjectiveFunction(objective_function);
Guolin Ke's avatar
Guolin Ke committed
721
  if (objective_function_ != nullptr) {
Nikita Titov's avatar
Nikita Titov committed
722
    CHECK_EQ(num_tree_per_iteration_, objective_function_->NumModelPerIteration());
723
724
725
    if (objective_function_->IsRenewTreeOutput() && !config_->monotone_constraints.empty()) {
      Log::Fatal("Cannot use ``monotone_constraints`` in %s objective, please disable it.", objective_function_->GetName());
    }
726
  }
727
  is_constant_hessian_ = GetIsConstHessian(objective_function);
728

Guolin Ke's avatar
Guolin Ke committed
729
730
731
732
  // push training metrics
  training_metrics_.clear();
  for (const auto& metric : training_metrics) {
    training_metrics_.push_back(metric);
733
  }
Guolin Ke's avatar
Guolin Ke committed
734
  training_metrics_.shrink_to_fit();
735

736
  #ifdef USE_CUDA
737
738
  boosting_on_gpu_ = objective_function_ != nullptr && objective_function_->IsCUDAObjective() &&
                    !data_sample_strategy_->IsHessianChange();  // for sample strategy with Hessian change, fall back to boosting on CPU
shiyu1994's avatar
shiyu1994 committed
739
  tree_learner_->ResetBoostingOnGPU(boosting_on_gpu_);
740
  #endif  // USE_CUDA
741

Guolin Ke's avatar
Guolin Ke committed
742
743
  if (train_data != train_data_) {
    train_data_ = train_data;
744
    data_sample_strategy_->UpdateTrainingData(train_data);
Guolin Ke's avatar
Guolin Ke committed
745
746
    // not same training data, need reset score and others
    // create score tracker
747
748
    #ifdef USE_CUDA
    if (config_->device_type == std::string("cuda")) {
shiyu1994's avatar
shiyu1994 committed
749
      train_score_updater_.reset(new CUDAScoreUpdater(train_data_, num_tree_per_iteration_, boosting_on_gpu_));
750
    } else {
751
    #endif  // USE_CUDA
752
      train_score_updater_.reset(new ScoreUpdater(train_data_, num_tree_per_iteration_));
753
    #ifdef USE_CUDA
754
    }
755
    #endif  // USE_CUDA
756

Guolin Ke's avatar
Guolin Ke committed
757
758
759
760
761
762
    // update score
    for (int i = 0; i < iter_; ++i) {
      for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
        auto curr_tree = (i + num_init_iteration_) * num_tree_per_iteration_ + cur_tree_id;
        train_score_updater_->AddScore(models_[curr_tree].get(), cur_tree_id);
      }
763
764
    }

Guolin Ke's avatar
Guolin Ke committed
765
    num_data_ = train_data_->num_data();
766

767
    ResetGradientBuffers();
768

Guolin Ke's avatar
Guolin Ke committed
769
770
771
772
    max_feature_idx_ = train_data_->num_total_features() - 1;
    label_idx_ = train_data_->label_idx();
    feature_names_ = train_data_->feature_names();
    feature_infos_ = train_data_->feature_infos();
773
    parser_config_str_ = train_data_->parser_config_str();
774

775
    tree_learner_->ResetTrainingData(train_data, is_constant_hessian_);
776
    data_sample_strategy_->ResetSampleConfig(config_.get(), true);
777
778
  } else {
    tree_learner_->ResetIsConstantHessian(is_constant_hessian_);
779
  }
780
781
}

Guolin Ke's avatar
Guolin Ke committed
782
783
void GBDT::ResetConfig(const Config* config) {
  auto new_config = std::unique_ptr<Config>(new Config(*config));
784
  if (!config->monotone_constraints.empty()) {
Nikita Titov's avatar
Nikita Titov committed
785
    CHECK_EQ(static_cast<size_t>(train_data_->num_total_features()), config->monotone_constraints.size());
786
787
  }
  if (!config->feature_contri.empty()) {
Nikita Titov's avatar
Nikita Titov committed
788
    CHECK_EQ(static_cast<size_t>(train_data_->num_total_features()), config->feature_contri.size());
789
  }
790
791
792
  if (objective_function_ != nullptr && objective_function_->IsRenewTreeOutput() && !config->monotone_constraints.empty()) {
    Log::Fatal("Cannot use ``monotone_constraints`` in %s objective, please disable it.", objective_function_->GetName());
  }
Guolin Ke's avatar
Guolin Ke committed
793
794
795
  early_stopping_round_ = new_config->early_stopping_round;
  shrinkage_rate_ = new_config->learning_rate;
  if (tree_learner_ != nullptr) {
Guolin Ke's avatar
Guolin Ke committed
796
    tree_learner_->ResetConfig(new_config.get());
797
  }
shiyu1994's avatar
shiyu1994 committed
798

799
800
  boosting_on_gpu_ = objective_function_ != nullptr && objective_function_->IsCUDAObjective() &&
                    !data_sample_strategy_->IsHessianChange();  // for sample strategy with Hessian change, fall back to boosting on CPU
shiyu1994's avatar
shiyu1994 committed
801
802
  tree_learner_->ResetBoostingOnGPU(boosting_on_gpu_);

Guolin Ke's avatar
Guolin Ke committed
803
  if (train_data_ != nullptr) {
804
805
806
807
808
    data_sample_strategy_->ResetSampleConfig(new_config.get(), false);
    if (data_sample_strategy_->NeedResizeGradients()) {
      // resize gradient vectors to copy the customized gradients for goss or bagging with subset
      ResetGradientBuffers();
    }
809
  }
810
  if (config_.get() != nullptr && config_->forcedsplits_filename != new_config->forcedsplits_filename) {
811
812
813
814
815
816
817
    // load forced_splits file
    if (!new_config->forcedsplits_filename.empty()) {
      std::ifstream forced_splits_file(
          new_config->forcedsplits_filename.c_str());
      std::stringstream buffer;
      buffer << forced_splits_file.rdbuf();
      std::string err;
Guolin Ke's avatar
Guolin Ke committed
818
      forced_splits_json_ = Json::parse(buffer.str(), &err);
819
820
821
822
823
824
      tree_learner_->SetForcedSplit(&forced_splits_json_);
    } else {
      forced_splits_json_ = Json();
      tree_learner_->SetForcedSplit(nullptr);
    }
  }
Guolin Ke's avatar
Guolin Ke committed
825
  config_.reset(new_config.release());
Guolin Ke's avatar
Guolin Ke committed
826
827
}

828
829
830
831
void GBDT::ResetGradientBuffers() {
  const size_t total_size = static_cast<size_t>(num_data_) * num_tree_per_iteration_;
  const bool is_use_subset = data_sample_strategy_->is_use_subset();
  const data_size_t bag_data_cnt = data_sample_strategy_->bag_data_cnt();
Guolin Ke's avatar
Guolin Ke committed
832
  if (objective_function_ != nullptr) {
833
834
    #ifdef USE_CUDA
    if (config_->device_type == std::string("cuda") && boosting_on_gpu_) {
835
836
837
      if (cuda_gradients_.Size() < total_size) {
        cuda_gradients_.Resize(total_size);
        cuda_hessians_.Resize(total_size);
Guolin Ke's avatar
Guolin Ke committed
838
      }
839
840
841
      gradients_pointer_ = cuda_gradients_.RawData();
      hessians_pointer_ = cuda_hessians_.RawData();
    } else {
842
    #endif  // USE_CUDA
843
      if (gradients_.size() < total_size) {
shiyu1994's avatar
shiyu1994 committed
844
845
        gradients_.resize(total_size);
        hessians_.resize(total_size);
846
      }
847
848
      gradients_pointer_ = gradients_.data();
      hessians_pointer_ = hessians_.data();
849
    #ifdef USE_CUDA
850
    }
851
    #endif  // USE_CUDA
852
853
854
855
856
857
858
  } else if (data_sample_strategy_->IsHessianChange() || (is_use_subset && bag_data_cnt < num_data_ && !boosting_on_gpu_)) {
    if (gradients_.size() < total_size) {
      gradients_.resize(total_size);
      hessians_.resize(total_size);
    }
    gradients_pointer_ = gradients_.data();
    hessians_pointer_ = hessians_.data();
859
  }
wxchan's avatar
wxchan committed
860
861
}

Guolin Ke's avatar
Guolin Ke committed
862
}  // namespace LightGBM