gbdt.cpp 34.8 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
#include <algorithm>
16
17
#include <chrono>
#include <ctime>
18
#include <memory>
19
#include <queue>
20
#include <sstream>
21
22
23
24
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
25

Guolin Ke's avatar
Guolin Ke committed
26
27
namespace LightGBM {

28
29
Common::Timer global_timer;

30
31
32
int LGBM_config_::current_device = lgbm_device_cpu;
int LGBM_config_::current_learner = use_cpu_learner;

33
34
35
GBDT::GBDT()
    : iter_(0),
      train_data_(nullptr),
36
      config_(nullptr),
37
38
      objective_function_(nullptr),
      early_stopping_round_(0),
39
      early_stopping_min_delta_(0.0),
40
41
42
43
44
45
      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),
46
      num_init_iteration_(0) {
Guolin Ke's avatar
Guolin Ke committed
47
  average_output_ = false;
Guolin Ke's avatar
Guolin Ke committed
48
  tree_learner_ = nullptr;
49
  linear_tree_ = false;
50
  data_sample_strategy_.reset(nullptr);
shiyu1994's avatar
shiyu1994 committed
51
52
53
  gradients_pointer_ = nullptr;
  hessians_pointer_ = nullptr;
  boosting_on_gpu_ = false;
Guolin Ke's avatar
Guolin Ke committed
54
55
56
57
58
}

GBDT::~GBDT() {
}

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

79
  if (config_->device_type == std::string("cuda")) {
80
    LGBM_config_::current_learner = use_cuda_learner;
81
82
    #ifdef USE_CUDA
    if (config_->device_type == std::string("cuda")) {
83
84
85
      const int gpu_device_id = config_->gpu_device_id >= 0 ? config_->gpu_device_id : 0;
      CUDASUCCESS_OR_FATAL(cudaSetDevice(gpu_device_id));
    }
86
    #endif  // USE_CUDA
87
88
  }

89
  // load forced_splits file
90
91
92
93
94
  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
95
    forced_splits_json_ = Json::parse(buffer.str(), &err);
96
97
  }

98
99
100
  objective_function_ = objective_function;
  num_tree_per_iteration_ = num_class_;
  if (objective_function_ != nullptr) {
Guolin Ke's avatar
Guolin Ke committed
101
    num_tree_per_iteration_ = objective_function_->NumModelPerIteration();
102
103
104
    if (objective_function_->IsRenewTreeOutput() && !config->monotone_constraints.empty()) {
      Log::Fatal("Cannot use ``monotone_constraints`` in %s objective, please disable it.", objective_function_->GetName());
    }
105
106
  }

107
  data_sample_strategy_.reset(SampleStrategy::CreateSampleStrategy(config_.get(), train_data_, objective_function_, num_tree_per_iteration_));
108
109
  is_constant_hessian_ = GetIsConstHessian(objective_function);

110
111
112
  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

113
  tree_learner_ = std::unique_ptr<TreeLearner>(TreeLearner::CreateTreeLearner(config_->tree_learner, config_->device_type,
shiyu1994's avatar
shiyu1994 committed
114
                                                                              config_.get(), boosting_on_gpu_));
115
116
117

  // init tree learner
  tree_learner_->Init(train_data_, is_constant_hessian_);
118
  tree_learner_->SetForcedSplit(&forced_splits_json_);
119
120
121
122
123
124
125
126

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

127
128
  #ifdef USE_CUDA
  if (config_->device_type == std::string("cuda")) {
shiyu1994's avatar
shiyu1994 committed
129
    train_score_updater_.reset(new CUDAScoreUpdater(train_data_, num_tree_per_iteration_, boosting_on_gpu_));
130
  } else {
131
  #endif  // USE_CUDA
132
    train_score_updater_.reset(new ScoreUpdater(train_data_, num_tree_per_iteration_));
133
  #ifdef USE_CUDA
134
  }
135
  #endif  // USE_CUDA
136
137

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

139
140
141
142
143
144
145
  // 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();
146
  monotone_constraints_ = config->monotone_constraints;
147
148
  // get parser config file content
  parser_config_str_ = train_data_->parser_config_str();
149

150
151
152
  // check that forced splits does not use feature indices larger than dataset size
  CheckForcedSplitFeatures();

153
  // if need bagging, create buffer
154
155
  data_sample_strategy_->ResetSampleConfig(config_.get(), true);
  ResetGradientBuffers();
156
157
158

  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
159
    CHECK_EQ(num_tree_per_iteration_, num_class_);
160
161
    for (int i = 0; i < num_class_; ++i) {
      class_need_train_[i] = objective_function_->ClassNeedTrain(i);
162
163
    }
  }
164
165
166
167

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

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
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
190
void GBDT::AddValidDataset(const Dataset* valid_data,
191
                           const std::vector<const Metric*>& valid_metrics) {
wxchan's avatar
wxchan committed
192
  if (!train_data_->CheckAlign(*valid_data)) {
193
    Log::Fatal("Cannot add validation data, since it has different bin mappers with training data");
194
  }
Guolin Ke's avatar
Guolin Ke committed
195
  // for a validation dataset, we need its score and metric
196
  auto new_score_updater =
197
198
    #ifdef USE_CUDA
    config_->device_type == std::string("cuda") ?
199
200
    std::unique_ptr<CUDAScoreUpdater>(new CUDAScoreUpdater(valid_data, num_tree_per_iteration_,
      objective_function_ != nullptr && objective_function_->IsCUDAObjective())) :
201
    #endif  // USE_CUDA
202
    std::unique_ptr<ScoreUpdater>(new ScoreUpdater(valid_data, num_tree_per_iteration_));
wxchan's avatar
wxchan committed
203
204
  // update score
  for (int i = 0; i < iter_; ++i) {
205
206
207
    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
208
209
    }
  }
Guolin Ke's avatar
Guolin Ke committed
210
  valid_score_updater_.push_back(std::move(new_score_updater));
Guolin Ke's avatar
Guolin Ke committed
211
212
213
214
  valid_metrics_.emplace_back();
  for (const auto& metric : valid_metrics) {
    valid_metrics_.back().push_back(metric);
  }
Guolin Ke's avatar
Guolin Ke committed
215
  valid_metrics_.back().shrink_to_fit();
216

217
218
  if (early_stopping_round_ > 0) {
    auto num_metrics = valid_metrics.size();
219
220
221
    if (es_first_metric_only_) {
      num_metrics = 1;
    }
222
223
224
225
    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
226
227
}

Guolin Ke's avatar
Guolin Ke committed
228
void GBDT::Boosting() {
229
  Common::FunctionTimer fun_timer("GBDT::Boosting", global_timer);
Guolin Ke's avatar
Guolin Ke committed
230
  if (objective_function_ == nullptr) {
231
    Log::Fatal("No objective function provided");
Guolin Ke's avatar
Guolin Ke committed
232
233
234
  }
  // objective function will calculate gradients and hessians
  int64_t num_score = 0;
235
236
237
238
239
240
241
242
  if (config_->bagging_by_query) {
    data_sample_strategy_->Bagging(iter_, tree_learner_.get(), gradients_.data(), hessians_.data());
    objective_function_->
      GetGradients(GetTrainingScore(&num_score), data_sample_strategy_->num_sampled_queries(), data_sample_strategy_->sampled_query_indices(), gradients_pointer_, hessians_pointer_);
  } else {
    objective_function_->
      GetGradients(GetTrainingScore(&num_score), gradients_pointer_, hessians_pointer_);
  }
Guolin Ke's avatar
Guolin Ke committed
243
244
}

Guolin Ke's avatar
Guolin Ke committed
245
void GBDT::Train(int snapshot_freq, const std::string& model_output_path) {
246
  Common::FunctionTimer fun_timer("GBDT::Train", global_timer);
Guolin Ke's avatar
Guolin Ke committed
247
248
  bool is_finished = false;
  auto start_time = std::chrono::steady_clock::now();
Guolin Ke's avatar
Guolin Ke committed
249
  for (int iter = 0; iter < config_->num_iterations && !is_finished; ++iter) {
Guolin Ke's avatar
Guolin Ke committed
250
251
252
253
    is_finished = TrainOneIter(nullptr, nullptr);
    if (!is_finished) {
      is_finished = EvalAndCheckEarlyStopping();
    }
Guolin Ke's avatar
Guolin Ke committed
254
255
256
257
258
259
260
    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);
261
      SaveModelToFile(0, -1, config_->saved_feature_importance_type, snapshot_out.c_str());
Guolin Ke's avatar
Guolin Ke committed
262
263
264
265
    }
  }
}

266
267
268
269
270
void GBDT::RefitTree(const int* tree_leaf_prediction, const size_t nrow, const size_t ncol) {
  CHECK_GT(nrow * ncol, 0);
  CHECK_EQ(static_cast<size_t>(num_data_), nrow);
  CHECK_EQ(models_.size(), ncol);

271
272
  int num_iterations = static_cast<int>(models_.size() / num_tree_per_iteration_);
  std::vector<int> leaf_pred(num_data_);
273
274
  if (linear_tree_) {
    std::vector<int> max_leaves_by_thread = std::vector<int>(OMP_NUM_THREADS(), 0);
275
    #pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
276
    for (int i = 0; i < static_cast<int>(nrow); ++i) {
277
      int tid = omp_get_thread_num();
278
279
      for (size_t j = 0; j < ncol; ++j) {
        max_leaves_by_thread[tid] = std::max(max_leaves_by_thread[tid], tree_leaf_prediction[i * ncol + j]);
280
281
282
283
284
285
      }
    }
    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);
  }
286

287
288
289
290
  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;
291
      #pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
292
      for (int i = 0; i < num_data_; ++i) {
293
        leaf_pred[i] = tree_leaf_prediction[i * ncol + model_index];
Nikita Titov's avatar
Nikita Titov committed
294
        CHECK_LT(leaf_pred[i], models_[model_index]->num_leaves());
295
      }
296
      size_t offset = static_cast<size_t>(tree_id) * num_data_;
297
298
      auto grad = gradients_pointer_ + offset;
      auto hess = hessians_pointer_ + offset;
299
300
301
302
303
304
305
      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
306
/* If the custom "average" is implemented it will be used in place of the label average (if enabled)
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
*
* 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
327
double GBDT::BoostFromAverage(int class_id, bool update_scorer) {
328
  Common::FunctionTimer fun_timer("GBDT::BoostFromAverage", global_timer);
329
  // boosting from average label; or customized "average" if implemented for the current objective
330
331
332
  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);
333
      if (std::fabs(init_score) > kEpsilon) {
Guolin Ke's avatar
Guolin Ke committed
334
335
336
337
338
        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);
          }
339
340
341
        }
        Log::Info("Start training from score %lf", init_score);
        return init_score;
Guolin Ke's avatar
Guolin Ke committed
342
      }
343
344
345
    } 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")) {
346
      Log::Warning("Disabling boost_from_average in %s may cause the slow convergence", objective_function_->GetName());
347
    }
348
  }
Guolin Ke's avatar
Guolin Ke committed
349
350
  return 0.0f;
}
Guolin Ke's avatar
Guolin Ke committed
351

Guolin Ke's avatar
Guolin Ke committed
352
bool GBDT::TrainOneIter(const score_t* gradients, const score_t* hessians) {
353
  Common::FunctionTimer fun_timer("GBDT::TrainOneIter", global_timer);
354
  std::vector<double> init_scores(num_tree_per_iteration_, 0.0);
Guolin Ke's avatar
Guolin Ke committed
355
  // boosting first
Guolin Ke's avatar
Guolin Ke committed
356
  if (gradients == nullptr || hessians == nullptr) {
357
    for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
Guolin Ke's avatar
Guolin Ke committed
358
      init_scores[cur_tree_id] = BoostFromAverage(cur_tree_id, true);
359
    }
Guolin Ke's avatar
Guolin Ke committed
360
    Boosting();
361
362
363
    gradients = gradients_pointer_;
    hessians = hessians_pointer_;
  } else {
shiyu1994's avatar
shiyu1994 committed
364
    // use customized objective function
365
    // the check below fails unless objective=custom is provided in the parameters on Booster creation
shiyu1994's avatar
shiyu1994 committed
366
    CHECK(objective_function_ == nullptr);
367
    if (data_sample_strategy_->IsHessianChange()) {
shiyu1994's avatar
shiyu1994 committed
368
369
      // need to copy customized gradients when using GOSS
      int64_t total_size = static_cast<int64_t>(num_data_) * num_tree_per_iteration_;
370
      #pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
shiyu1994's avatar
shiyu1994 committed
371
372
373
374
375
376
      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());
377
378
379
380
381
      gradients = gradients_pointer_;
      hessians = hessians_pointer_;
    }
  }

382
  // bagging logic
383
384
385
  if (!config_->bagging_by_query) {
    data_sample_strategy_->Bagging(iter_, tree_learner_.get(), gradients_.data(), hessians_.data());
  }
386
387
388
  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
389

390
391
  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
392
393
  }

Guolin Ke's avatar
Guolin Ke committed
394
  bool should_continue = false;
395
  for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
396
    const size_t offset = static_cast<size_t>(cur_tree_id) * num_data_;
397
    std::unique_ptr<Tree> new_tree(new Tree(2, false, false));
398
    if (class_need_train_[cur_tree_id] && train_data_->num_features() > 0) {
399
400
      auto grad = gradients + offset;
      auto hess = hessians + offset;
Guolin Ke's avatar
Guolin Ke committed
401
      // need to copy gradients for bagging subset.
402
403
404
405
      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
406
        }
407
408
        grad = gradients_pointer_ + offset;
        hess = hessians_pointer_ + offset;
Guolin Ke's avatar
Guolin Ke committed
409
      }
410
411
      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));
412
    }
Guolin Ke's avatar
Guolin Ke committed
413

Guolin Ke's avatar
Guolin Ke committed
414
    if (new_tree->num_leaves() > 1) {
Guolin Ke's avatar
Guolin Ke committed
415
      should_continue = true;
416
      auto score_ptr = train_score_updater_->score() + offset;
417
418
      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,
419
                                     num_data_, bag_data_indices.data(), bag_data_cnt, train_score_updater_->score());
Guolin Ke's avatar
Guolin Ke committed
420
421
422
      // shrinkage by learning rate
      new_tree->Shrinkage(shrinkage_rate_);
      // update score
423
      UpdateScore(new_tree.get(), cur_tree_id);
424
425
      if (std::fabs(init_scores[cur_tree_id]) > kEpsilon) {
        new_tree->AddBias(init_scores[cur_tree_id]);
Guolin Ke's avatar
Guolin Ke committed
426
      }
427
428
    } else {
      // only add default score one-time
429
      if (models_.size() < static_cast<size_t>(num_tree_per_iteration_)) {
430
431
432
433
434
435
        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);
436
          }
437
        }
438
439
440
441
        new_tree->AsConstantTree(init_scores[cur_tree_id], num_data_);
      } else {
        // extend init_scores with zeros
        new_tree->AsConstantTree(0, num_data_);
442
443
      }
    }
Guolin Ke's avatar
Guolin Ke committed
444
445
446
    // add model
    models_.push_back(std::move(new_tree));
  }
Guolin Ke's avatar
Guolin Ke committed
447

Guolin Ke's avatar
Guolin Ke committed
448
  if (!should_continue) {
449
    Log::Warning("Stopped training because there are no more leaves that meet the split requirements");
450
451
452
453
    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
454
455
456
    }
    return true;
  }
457

Guolin Ke's avatar
Guolin Ke committed
458
459
  ++iter_;
  return false;
Guolin Ke's avatar
Guolin Ke committed
460
}
461

wxchan's avatar
wxchan committed
462
void GBDT::RollbackOneIter() {
463
464
465
  if (iter_ <= 0) {
    return;
  }
wxchan's avatar
wxchan committed
466
  // reset score
467
  for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
Guolin Ke's avatar
Guolin Ke committed
468
    auto curr_tree = models_.size() - num_tree_per_iteration_ + cur_tree_id;
wxchan's avatar
wxchan committed
469
    models_[curr_tree]->Shrinkage(-1.0);
470
    train_score_updater_->AddScore(models_[curr_tree].get(), cur_tree_id);
wxchan's avatar
wxchan committed
471
    for (auto& score_updater : valid_score_updater_) {
472
      score_updater->AddScore(models_[curr_tree].get(), cur_tree_id);
wxchan's avatar
wxchan committed
473
474
475
    }
  }
  // remove model
476
  for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
wxchan's avatar
wxchan committed
477
478
479
480
481
    models_.pop_back();
  }
  --iter_;
}

Guolin Ke's avatar
Guolin Ke committed
482
bool GBDT::EvalAndCheckEarlyStopping() {
483
484
  bool is_met_early_stopping = false;
  // print message for metric
Guolin Ke's avatar
Guolin Ke committed
485
  auto best_msg = OutputMetric(iter_);
Guolin Ke's avatar
Guolin Ke committed
486
487


Guolin Ke's avatar
Guolin Ke committed
488
  is_met_early_stopping = !best_msg.empty();
489
490
  if (is_met_early_stopping) {
    Log::Info("Early stopping at iteration %d, the best iteration round is %d",
491
              iter_, iter_ - early_stopping_round_);
Guolin Ke's avatar
Guolin Ke committed
492
    Log::Info("Output of best iteration round:\n%s", best_msg.c_str());
493
    // pop last early_stopping_round_ models
494
    for (int i = 0; i < early_stopping_round_ * num_tree_per_iteration_; ++i) {
495
496
497
498
      models_.pop_back();
    }
  }
  return is_met_early_stopping;
Guolin Ke's avatar
Guolin Ke committed
499
500
}

501
void GBDT::UpdateScore(const Tree* tree, const int cur_tree_id) {
502
  Common::FunctionTimer fun_timer("GBDT::UpdateScore", global_timer);
Guolin Ke's avatar
Guolin Ke committed
503
  // update training score
504
  if (!data_sample_strategy_->is_use_subset()) {
505
    train_score_updater_->AddScore(tree_learner_.get(), tree, cur_tree_id);
Guolin Ke's avatar
Guolin Ke committed
506

507
    const data_size_t bag_data_cnt = data_sample_strategy_->bag_data_cnt();
Guolin Ke's avatar
Guolin Ke committed
508
    // we need to predict out-of-bag scores of data for boosting
509
    if (num_data_ - bag_data_cnt > 0) {
510
511
      #ifdef USE_CUDA
      if (config_->device_type == std::string("cuda")) {
512
        train_score_updater_->AddScore(tree, data_sample_strategy_->cuda_bag_data_indices().RawData() + bag_data_cnt, num_data_ - bag_data_cnt, cur_tree_id);
513
      } else {
514
      #endif  // USE_CUDA
515
        train_score_updater_->AddScore(tree, data_sample_strategy_->bag_data_indices().data() + bag_data_cnt, num_data_ - bag_data_cnt, cur_tree_id);
516
      #ifdef USE_CUDA
517
      }
518
      #endif  // USE_CUDA
Guolin Ke's avatar
Guolin Ke committed
519
520
    }

Guolin Ke's avatar
Guolin Ke committed
521
  } else {
522
    train_score_updater_->AddScore(tree, cur_tree_id);
Guolin Ke's avatar
Guolin Ke committed
523
  }
Guolin Ke's avatar
Guolin Ke committed
524
525


Guolin Ke's avatar
Guolin Ke committed
526
  // update validation score
Guolin Ke's avatar
Guolin Ke committed
527
  for (auto& score_updater : valid_score_updater_) {
528
    score_updater->AddScore(tree, cur_tree_id);
Guolin Ke's avatar
Guolin Ke committed
529
530
531
  }
}

532
#ifdef USE_CUDA
533
534
535
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 {
536
537
#endif  // USE_CUDA
  #ifdef USE_CUDA
538
  const bool evaluation_on_cuda = metric->IsCUDAMetric();
shiyu1994's avatar
shiyu1994 committed
539
  if ((boosting_on_gpu_ && evaluation_on_cuda) || (!boosting_on_gpu_ && !evaluation_on_cuda)) {
540
  #endif  // USE_CUDA
541
    return metric->Eval(score, objective_function_);
542
  #ifdef USE_CUDA
shiyu1994's avatar
shiyu1994 committed
543
  } else if (boosting_on_gpu_ && !evaluation_on_cuda) {
544
    const size_t total_size = static_cast<size_t>(num_data) * static_cast<size_t>(num_tree_per_iteration_);
545
546
547
548
549
550
    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 {
551
    const size_t total_size = static_cast<size_t>(num_data) * static_cast<size_t>(num_tree_per_iteration_);
552
553
554
555
556
557
    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_);
  }
558
  #endif  // USE_CUDA
Guolin Ke's avatar
Guolin Ke committed
559
560
}

Guolin Ke's avatar
Guolin Ke committed
561
std::string GBDT::OutputMetric(int iter) {
Guolin Ke's avatar
Guolin Ke committed
562
  bool need_output = (iter % config_->metric_freq) == 0;
Guolin Ke's avatar
Guolin Ke committed
563
564
  std::string ret = "";
  std::stringstream msg_buf;
565
  std::vector<std::pair<size_t, size_t>> meet_early_stopping_pairs;
Guolin Ke's avatar
Guolin Ke committed
566
  // print training metric
Guolin Ke's avatar
Guolin Ke committed
567
  if (need_output) {
568
569
    for (auto& sub_metric : training_metrics_) {
      auto name = sub_metric->GetName();
570
      auto scores = EvalOneMetric(sub_metric, train_score_updater_->score(), train_score_updater_->num_data());
Guolin Ke's avatar
Guolin Ke committed
571
      for (size_t k = 0; k < name.size(); ++k) {
Guolin Ke's avatar
Guolin Ke committed
572
573
574
575
576
577
        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) {
578
          msg_buf << tmp_buf.str() << '\n';
Guolin Ke's avatar
Guolin Ke committed
579
        }
580
      }
581
    }
Guolin Ke's avatar
Guolin Ke committed
582
583
  }
  // print validation metric
Guolin Ke's avatar
Guolin Ke committed
584
  if (need_output || early_stopping_round_ > 0) {
585
586
    for (size_t i = 0; i < valid_metrics_.size(); ++i) {
      for (size_t j = 0; j < valid_metrics_[i].size(); ++j) {
587
        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
588
589
590
591
592
593
594
595
596
597
        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) {
598
            msg_buf << tmp_buf.str() << '\n';
599
          }
wxchan's avatar
wxchan committed
600
        }
601
602
603
        if (es_first_metric_only_ && j > 0) {
          continue;
        }
Guolin Ke's avatar
Guolin Ke committed
604
        if (ret.empty() && early_stopping_round_ > 0) {
605
          auto cur_score = valid_metrics_[i][j]->factor_to_bigger_better() * test_scores.back();
606
          if (cur_score - best_score_[i][j] > early_stopping_min_delta_) {
607
            best_score_[i][j] = cur_score;
608
            best_iter_[i][j] = iter;
Guolin Ke's avatar
Guolin Ke committed
609
            meet_early_stopping_pairs.emplace_back(i, j);
610
          } else {
611
612
613
            if (iter - best_iter_[i][j] >= early_stopping_round_) {
              ret = best_msg_[i][j];
            }
614
          }
wxchan's avatar
wxchan committed
615
616
        }
      }
Guolin Ke's avatar
Guolin Ke committed
617
618
    }
  }
Guolin Ke's avatar
Guolin Ke committed
619
620
621
  for (auto& pair : meet_early_stopping_pairs) {
    best_msg_[pair.first][pair.second] = msg_buf.str();
  }
wxchan's avatar
wxchan committed
622
  return ret;
Guolin Ke's avatar
Guolin Ke committed
623
624
}

625
/*! \brief Get eval result */
626
std::vector<double> GBDT::GetEvalAt(int data_idx) const {
Guolin Ke's avatar
Guolin Ke committed
627
  CHECK(data_idx >= 0 && data_idx <= static_cast<int>(valid_score_updater_.size()));
628
629
  std::vector<double> ret;
  if (data_idx == 0) {
630
    for (auto& sub_metric : training_metrics_) {
631
      auto scores = EvalOneMetric(sub_metric, train_score_updater_->score(), train_score_updater_->num_data());
632
633
634
      for (auto score : scores) {
        ret.push_back(score);
      }
635
    }
636
  } else {
637
638
    auto used_idx = data_idx - 1;
    for (size_t j = 0; j < valid_metrics_[used_idx].size(); ++j) {
639
      auto test_scores = EvalOneMetric(valid_metrics_[used_idx][j], valid_score_updater_[used_idx]->score(), valid_score_updater_[used_idx]->num_data());
640
641
642
      for (auto score : test_scores) {
        ret.push_back(score);
      }
643
644
645
646
647
    }
  }
  return ret;
}

Guolin Ke's avatar
Guolin Ke committed
648
/*! \brief Get training scores result */
649
const double* GBDT::GetTrainingScore(int64_t* out_len) {
650
  *out_len = static_cast<int64_t>(train_score_updater_->num_data()) * num_class_;
Guolin Ke's avatar
Guolin Ke committed
651
  return train_score_updater_->score();
652
653
}

654
void GBDT::PredictContrib(const double* features, double* output) const {
655
  // set zero
Guolin Ke's avatar
Guolin Ke committed
656
657
  const int num_features = max_feature_idx_ + 1;
  std::memset(output, 0, sizeof(double) * num_tree_per_iteration_ * (num_features + 1));
658
659
  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) {
660
661
    // predict all the trees for one iteration
    for (int k = 0; k < num_tree_per_iteration_; ++k) {
Guolin Ke's avatar
Guolin Ke committed
662
      models_[i * num_tree_per_iteration_ + k]->PredictContrib(features, num_features, output + k*(num_features + 1));
663
    }
664
665
666
667
668
669
  }
}

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;
670
671
  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) {
672
673
674
    // 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]));
675
676
677
678
    }
  }
}

Guolin Ke's avatar
Guolin Ke committed
679
680
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
681

682
  const double* raw_scores = nullptr;
Guolin Ke's avatar
Guolin Ke committed
683
684
  data_size_t num_data = 0;
  if (data_idx == 0) {
wxchan's avatar
wxchan committed
685
    raw_scores = GetTrainingScore(out_len);
Guolin Ke's avatar
Guolin Ke committed
686
687
688
689
690
    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();
691
    *out_len = static_cast<int64_t>(num_data) * num_class_;
Guolin Ke's avatar
Guolin Ke committed
692
  }
693
  #ifdef USE_CUDA
694
695
696
697
698
699
  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();
  }
700
  #endif  // USE_CUDA
Guolin Ke's avatar
Guolin Ke committed
701
  if (objective_function_ != nullptr) {
702
    #pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
Guolin Ke's avatar
Guolin Ke committed
703
    for (data_size_t i = 0; i < num_data; ++i) {
Guolin Ke's avatar
Guolin Ke committed
704
      std::vector<double> tree_pred(num_tree_per_iteration_);
705
      for (int j = 0; j < num_tree_per_iteration_; ++j) {
Guolin Ke's avatar
Guolin Ke committed
706
        tree_pred[j] = raw_scores[j * num_data + i];
707
      }
Guolin Ke's avatar
Guolin Ke committed
708
709
      std::vector<double> tmp_result(num_class_);
      objective_function_->ConvertOutput(tree_pred.data(), tmp_result.data());
Guolin Ke's avatar
Guolin Ke committed
710
      for (int j = 0; j < num_class_; ++j) {
711
        out_result[j * num_data + i] = static_cast<double>(tmp_result[j]);
Guolin Ke's avatar
Guolin Ke committed
712
713
      }
    }
714
  } else {
715
    #pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
Guolin Ke's avatar
Guolin Ke committed
716
    for (data_size_t i = 0; i < num_data; ++i) {
717
      for (int j = 0; j < num_tree_per_iteration_; ++j) {
Guolin Ke's avatar
Guolin Ke committed
718
        out_result[j * num_data + i] = static_cast<double>(raw_scores[j * num_data + i]);
Guolin Ke's avatar
Guolin Ke committed
719
720
721
722
723
      }
    }
  }
}

724
725
double GBDT::GetUpperBoundValue() const {
  double max_value = 0.0;
Nikita Titov's avatar
Nikita Titov committed
726
  for (const auto &tree : models_) {
727
728
729
730
731
732
733
    max_value += tree->GetUpperBoundValue();
  }
  return max_value;
}

double GBDT::GetLowerBoundValue() const {
  double min_value = 0.0;
Nikita Titov's avatar
Nikita Titov committed
734
  for (const auto &tree : models_) {
735
736
737
738
739
    min_value += tree->GetLowerBoundValue();
  }
  return min_value;
}

Guolin Ke's avatar
Guolin Ke committed
740
741
742
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)) {
743
    Log::Fatal("Cannot reset training data, since new training data has different bin mappers");
wxchan's avatar
wxchan committed
744
745
  }

Guolin Ke's avatar
Guolin Ke committed
746
  objective_function_ = objective_function;
747
  data_sample_strategy_->UpdateObjectiveFunction(objective_function);
Guolin Ke's avatar
Guolin Ke committed
748
  if (objective_function_ != nullptr) {
Nikita Titov's avatar
Nikita Titov committed
749
    CHECK_EQ(num_tree_per_iteration_, objective_function_->NumModelPerIteration());
750
751
752
    if (objective_function_->IsRenewTreeOutput() && !config_->monotone_constraints.empty()) {
      Log::Fatal("Cannot use ``monotone_constraints`` in %s objective, please disable it.", objective_function_->GetName());
    }
753
  }
754
  is_constant_hessian_ = GetIsConstHessian(objective_function);
755

Guolin Ke's avatar
Guolin Ke committed
756
757
758
759
  // push training metrics
  training_metrics_.clear();
  for (const auto& metric : training_metrics) {
    training_metrics_.push_back(metric);
760
  }
Guolin Ke's avatar
Guolin Ke committed
761
  training_metrics_.shrink_to_fit();
762

763
  #ifdef USE_CUDA
764
765
  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
766
  tree_learner_->ResetBoostingOnGPU(boosting_on_gpu_);
767
  #endif  // USE_CUDA
768

Guolin Ke's avatar
Guolin Ke committed
769
770
  if (train_data != train_data_) {
    train_data_ = train_data;
771
    data_sample_strategy_->UpdateTrainingData(train_data);
Guolin Ke's avatar
Guolin Ke committed
772
773
    // not same training data, need reset score and others
    // create score tracker
774
775
    #ifdef USE_CUDA
    if (config_->device_type == std::string("cuda")) {
shiyu1994's avatar
shiyu1994 committed
776
      train_score_updater_.reset(new CUDAScoreUpdater(train_data_, num_tree_per_iteration_, boosting_on_gpu_));
777
    } else {
778
    #endif  // USE_CUDA
779
      train_score_updater_.reset(new ScoreUpdater(train_data_, num_tree_per_iteration_));
780
    #ifdef USE_CUDA
781
    }
782
    #endif  // USE_CUDA
783

Guolin Ke's avatar
Guolin Ke committed
784
785
786
787
788
789
    // 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);
      }
790
791
    }

Guolin Ke's avatar
Guolin Ke committed
792
    num_data_ = train_data_->num_data();
793

794
    ResetGradientBuffers();
795

Guolin Ke's avatar
Guolin Ke committed
796
797
798
799
    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();
800
    parser_config_str_ = train_data_->parser_config_str();
801

802
    tree_learner_->ResetTrainingData(train_data, is_constant_hessian_);
803
    data_sample_strategy_->ResetSampleConfig(config_.get(), true);
804
805
  } else {
    tree_learner_->ResetIsConstantHessian(is_constant_hessian_);
806
  }
807
808
}

Guolin Ke's avatar
Guolin Ke committed
809
810
void GBDT::ResetConfig(const Config* config) {
  auto new_config = std::unique_ptr<Config>(new Config(*config));
811
  if (!config->monotone_constraints.empty()) {
Nikita Titov's avatar
Nikita Titov committed
812
    CHECK_EQ(static_cast<size_t>(train_data_->num_total_features()), config->monotone_constraints.size());
813
814
  }
  if (!config->feature_contri.empty()) {
Nikita Titov's avatar
Nikita Titov committed
815
    CHECK_EQ(static_cast<size_t>(train_data_->num_total_features()), config->feature_contri.size());
816
  }
817
818
819
  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
820
821
822
  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
823
    tree_learner_->ResetConfig(new_config.get());
824
  }
shiyu1994's avatar
shiyu1994 committed
825

826
827
  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
828
829
  tree_learner_->ResetBoostingOnGPU(boosting_on_gpu_);

Guolin Ke's avatar
Guolin Ke committed
830
  if (train_data_ != nullptr) {
831
832
833
834
835
    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();
    }
836
  }
837
  if (config_.get() != nullptr && config_->forcedsplits_filename != new_config->forcedsplits_filename) {
838
839
840
841
842
843
844
    // 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
845
      forced_splits_json_ = Json::parse(buffer.str(), &err);
846
847
848
849
850
851
      tree_learner_->SetForcedSplit(&forced_splits_json_);
    } else {
      forced_splits_json_ = Json();
      tree_learner_->SetForcedSplit(nullptr);
    }
  }
Guolin Ke's avatar
Guolin Ke committed
852
  config_.reset(new_config.release());
Guolin Ke's avatar
Guolin Ke committed
853
854
}

855
856
857
858
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
859
  if (objective_function_ != nullptr) {
860
861
    #ifdef USE_CUDA
    if (config_->device_type == std::string("cuda") && boosting_on_gpu_) {
862
863
864
      if (cuda_gradients_.Size() < total_size) {
        cuda_gradients_.Resize(total_size);
        cuda_hessians_.Resize(total_size);
Guolin Ke's avatar
Guolin Ke committed
865
      }
866
867
868
      gradients_pointer_ = cuda_gradients_.RawData();
      hessians_pointer_ = cuda_hessians_.RawData();
    } else {
869
    #endif  // USE_CUDA
870
      if (gradients_.size() < total_size) {
shiyu1994's avatar
shiyu1994 committed
871
872
        gradients_.resize(total_size);
        hessians_.resize(total_size);
873
      }
874
875
      gradients_pointer_ = gradients_.data();
      hessians_pointer_ = hessians_.data();
876
    #ifdef USE_CUDA
877
    }
878
    #endif  // USE_CUDA
879
880
881
882
883
884
885
  } 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();
886
  }
wxchan's avatar
wxchan committed
887
888
}

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