gbdt.cpp 40.2 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>
Guolin Ke's avatar
Guolin Ke committed
13

14
15
16
17
#include <chrono>
#include <ctime>
#include <sstream>

Guolin Ke's avatar
Guolin Ke committed
18
19
namespace LightGBM {

20
21
Common::Timer global_timer;

22
23
24
int LGBM_config_::current_device = lgbm_device_cpu;
int LGBM_config_::current_learner = use_cpu_learner;

25
26
27
GBDT::GBDT()
    : iter_(0),
      train_data_(nullptr),
28
      config_(nullptr),
29
30
31
32
33
34
35
36
37
38
39
40
      objective_function_(nullptr),
      early_stopping_round_(0),
      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),
      num_init_iteration_(0),
      need_re_bagging_(false),
      balanced_bagging_(false),
      bagging_runner_(0, bagging_rand_block_) {
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;
shiyu1994's avatar
shiyu1994 committed
44
45
46
  gradients_pointer_ = nullptr;
  hessians_pointer_ = nullptr;
  boosting_on_gpu_ = false;
Guolin Ke's avatar
Guolin Ke committed
47
48
49
50
51
}

GBDT::~GBDT() {
}

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

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

81
  // load forced_splits file
82
83
84
85
86
  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
87
    forced_splits_json_ = Json::parse(buffer.str(), &err);
88
89
  }

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

99
100
  is_constant_hessian_ = GetIsConstHessian(objective_function);

shiyu1994's avatar
shiyu1994 committed
101
  boosting_on_gpu_ = objective_function_ != nullptr && objective_function_->IsCUDAObjective();
102
  tree_learner_ = std::unique_ptr<TreeLearner>(TreeLearner::CreateTreeLearner(config_->tree_learner, config_->device_type,
shiyu1994's avatar
shiyu1994 committed
103
                                                                              config_.get(), boosting_on_gpu_));
104
105
106

  // init tree learner
  tree_learner_->Init(train_data_, is_constant_hessian_);
107
  tree_learner_->SetForcedSplit(&forced_splits_json_);
108
109
110
111
112
113
114
115

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

116
117
  #ifdef USE_CUDA_EXP
  if (config_->device_type == std::string("cuda_exp")) {
shiyu1994's avatar
shiyu1994 committed
118
    train_score_updater_.reset(new CUDAScoreUpdater(train_data_, num_tree_per_iteration_, boosting_on_gpu_));
119
120
121
122
123
124
  } else {
  #endif  // USE_CUDA_EXP
    train_score_updater_.reset(new ScoreUpdater(train_data_, num_tree_per_iteration_));
  #ifdef USE_CUDA_EXP
  }
  #endif  // USE_CUDA_EXP
125
126

  num_data_ = train_data_->num_data();
Andrew Ziem's avatar
Andrew Ziem committed
127
  // create buffer for gradients and Hessians
128
  if (objective_function_ != nullptr) {
shiyu1994's avatar
shiyu1994 committed
129
    const size_t total_size = static_cast<size_t>(num_data_) * num_tree_per_iteration_;
130
    #ifdef USE_CUDA_EXP
shiyu1994's avatar
shiyu1994 committed
131
132
133
134
135
136
    if (config_->device_type == std::string("cuda_exp") && boosting_on_gpu_) {
      if (gradients_pointer_ != nullptr) {
        CHECK_NOTNULL(hessians_pointer_);
        DeallocateCUDAMemory<score_t>(&gradients_pointer_, __FILE__, __LINE__);
        DeallocateCUDAMemory<score_t>(&hessians_pointer_, __FILE__, __LINE__);
      }
137
138
139
140
141
142
143
144
145
146
147
      AllocateCUDAMemory<score_t>(&gradients_pointer_, total_size, __FILE__, __LINE__);
      AllocateCUDAMemory<score_t>(&hessians_pointer_, total_size, __FILE__, __LINE__);
    } else {
    #endif  // USE_CUDA_EXP
      gradients_.resize(total_size);
      hessians_.resize(total_size);
      gradients_pointer_ = gradients_.data();
      hessians_pointer_ = hessians_.data();
    #ifdef USE_CUDA_EXP
    }
    #endif  // USE_CUDA_EXP
shiyu1994's avatar
shiyu1994 committed
148
149
150
151
152
153
  } else if (config_->boosting == std::string("goss")) {
    const size_t total_size = static_cast<size_t>(num_data_) * num_tree_per_iteration_;
    gradients_.resize(total_size);
    hessians_.resize(total_size);
    gradients_pointer_ = gradients_.data();
    hessians_pointer_ = hessians_.data();
154
  }
shiyu1994's avatar
shiyu1994 committed
155

156
157
158
159
160
161
162
  // 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();
163
  monotone_constraints_ = config->monotone_constraints;
164
165
  // get parser config file content
  parser_config_str_ = train_data_->parser_config_str();
166
167

  // if need bagging, create buffer
Guolin Ke's avatar
Guolin Ke committed
168
  ResetBaggingConfig(config_.get(), true);
169
170
171

  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
172
    CHECK_EQ(num_tree_per_iteration_, num_class_);
173
174
    for (int i = 0; i < num_class_; ++i) {
      class_need_train_[i] = objective_function_->ClassNeedTrain(i);
175
176
    }
  }
177
178
179
180

  if (config_->linear_tree) {
    linear_tree_ = true;
  }
wxchan's avatar
wxchan committed
181
182
183
}

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

210
211
212
213
214
215
216
  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
217
218
}

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

230
data_size_t GBDT::BaggingHelper(data_size_t start, data_size_t cnt, data_size_t* buffer) {
231
232
233
  if (cnt <= 0) {
    return 0;
  }
234
  data_size_t cur_left_cnt = 0;
235
  data_size_t cur_right_pos = cnt;
236
237
  // random bagging, minimal unit is one record
  for (data_size_t i = 0; i < cnt; ++i) {
238
239
240
    auto cur_idx = start + i;
    if (bagging_rands_[cur_idx / bagging_rand_block_].NextFloat() < config_->bagging_fraction) {
      buffer[cur_left_cnt++] = cur_idx;
241
    } else {
242
      buffer[--cur_right_pos] = cur_idx;
243
244
245
246
    }
  }
  return cur_left_cnt;
}
Guolin Ke's avatar
Guolin Ke committed
247

248
249
data_size_t GBDT::BalancedBaggingHelper(data_size_t start, data_size_t cnt,
                                        data_size_t* buffer) {
Guolin Ke's avatar
Guolin Ke committed
250
251
252
253
254
  if (cnt <= 0) {
    return 0;
  }
  auto label_ptr = train_data_->metadata().label();
  data_size_t cur_left_cnt = 0;
255
  data_size_t cur_right_pos = cnt;
Guolin Ke's avatar
Guolin Ke committed
256
257
  // random bagging, minimal unit is one record
  for (data_size_t i = 0; i < cnt; ++i) {
258
    auto cur_idx = start + i;
Guolin Ke's avatar
Guolin Ke committed
259
260
261
    bool is_pos = label_ptr[start + i] > 0;
    bool is_in_bag = false;
    if (is_pos) {
262
263
      is_in_bag = bagging_rands_[cur_idx / bagging_rand_block_].NextFloat() <
                  config_->pos_bagging_fraction;
Guolin Ke's avatar
Guolin Ke committed
264
    } else {
265
266
      is_in_bag = bagging_rands_[cur_idx / bagging_rand_block_].NextFloat() <
                  config_->neg_bagging_fraction;
Guolin Ke's avatar
Guolin Ke committed
267
268
    }
    if (is_in_bag) {
269
      buffer[cur_left_cnt++] = cur_idx;
Guolin Ke's avatar
Guolin Ke committed
270
    } else {
271
      buffer[--cur_right_pos] = cur_idx;
Guolin Ke's avatar
Guolin Ke committed
272
273
274
275
276
    }
  }
  return cur_left_cnt;
}

277
void GBDT::Bagging(int iter) {
278
  Common::FunctionTimer fun_timer("GBDT::Bagging", global_timer);
Guolin Ke's avatar
Guolin Ke committed
279
  // if need bagging
Guolin Ke's avatar
Guolin Ke committed
280
281
  if ((bag_data_cnt_ < num_data_ && iter % config_->bagging_freq == 0) ||
      need_re_bagging_) {
Guolin Ke's avatar
Guolin Ke committed
282
    need_re_bagging_ = false;
283
284
285
286
287
288
289
290
    auto left_cnt = bagging_runner_.Run<true>(
        num_data_,
        [=](int, data_size_t cur_start, data_size_t cur_cnt, data_size_t* left,
            data_size_t*) {
          data_size_t cur_left_count = 0;
          if (balanced_bagging_) {
            cur_left_count =
                BalancedBaggingHelper(cur_start, cur_cnt, left);
Guolin Ke's avatar
Guolin Ke committed
291
          } else {
292
            cur_left_count = BaggingHelper(cur_start, cur_cnt, left);
Guolin Ke's avatar
Guolin Ke committed
293
          }
294
295
296
          return cur_left_count;
        },
        bag_data_indices_.data());
Guolin Ke's avatar
Guolin Ke committed
297
    bag_data_cnt_ = left_cnt;
Guolin Ke's avatar
Guolin Ke committed
298
    Log::Debug("Re-bagging, using %d data to train", bag_data_cnt_);
Guolin Ke's avatar
Guolin Ke committed
299
    // set bagging data to tree learner
Guolin Ke's avatar
Guolin Ke committed
300
    if (!is_use_subset_) {
301
302
303
304
305
306
307
308
309
310
      #ifdef USE_CUDA_EXP
      if (config_->device_type == std::string("cuda_exp")) {
        CopyFromHostToCUDADevice<data_size_t>(cuda_bag_data_indices_.RawData(), bag_data_indices_.data(), static_cast<size_t>(num_data_), __FILE__, __LINE__);
        tree_learner_->SetBaggingData(nullptr, cuda_bag_data_indices_.RawData(), bag_data_cnt_);
      } else {
      #endif  // USE_CUDA_EXP
        tree_learner_->SetBaggingData(nullptr, bag_data_indices_.data(), bag_data_cnt_);
      #ifdef USE_CUDA_EXP
      }
      #endif  // USE_CUDA_EXP
Guolin Ke's avatar
Guolin Ke committed
311
312
    } else {
      // get subset
Guolin Ke's avatar
Guolin Ke committed
313
      tmp_subset_->ReSize(bag_data_cnt_);
314
      tmp_subset_->CopySubrow(train_data_, bag_data_indices_.data(),
Guolin Ke's avatar
Guolin Ke committed
315
                              bag_data_cnt_, false);
316
317
318
319
320
321
322
323
324
325
326
327
      #ifdef USE_CUDA_EXP
      if (config_->device_type == std::string("cuda_exp")) {
        CopyFromHostToCUDADevice<data_size_t>(cuda_bag_data_indices_.RawData(), bag_data_indices_.data(), static_cast<size_t>(num_data_), __FILE__, __LINE__);
        tree_learner_->SetBaggingData(tmp_subset_.get(), cuda_bag_data_indices_.RawData(),
                                      bag_data_cnt_);
      } else {
      #endif  // USE_CUDA_EXP
        tree_learner_->SetBaggingData(tmp_subset_.get(), bag_data_indices_.data(),
                                      bag_data_cnt_);
      #ifdef USE_CUDA_EXP
      }
      #endif  // USE_CUDA_EXP
Guolin Ke's avatar
Guolin Ke committed
328
    }
Guolin Ke's avatar
Guolin Ke committed
329
330
331
  }
}

Guolin Ke's avatar
Guolin Ke committed
332
void GBDT::Train(int snapshot_freq, const std::string& model_output_path) {
333
  Common::FunctionTimer fun_timer("GBDT::Train", global_timer);
Guolin Ke's avatar
Guolin Ke committed
334
335
  bool is_finished = false;
  auto start_time = std::chrono::steady_clock::now();
Guolin Ke's avatar
Guolin Ke committed
336
  for (int iter = 0; iter < config_->num_iterations && !is_finished; ++iter) {
Guolin Ke's avatar
Guolin Ke committed
337
338
339
340
    is_finished = TrainOneIter(nullptr, nullptr);
    if (!is_finished) {
      is_finished = EvalAndCheckEarlyStopping();
    }
Guolin Ke's avatar
Guolin Ke committed
341
342
343
344
345
346
347
    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);
348
      SaveModelToFile(0, -1, config_->saved_feature_importance_type, snapshot_out.c_str());
Guolin Ke's avatar
Guolin Ke committed
349
350
351
352
    }
  }
}

353
void GBDT::RefitTree(const std::vector<std::vector<int>>& tree_leaf_prediction) {
354
355
356
  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());
357
358
  int num_iterations = static_cast<int>(models_.size() / num_tree_per_iteration_);
  std::vector<int> leaf_pred(num_data_);
359
360
361
362
363
364
365
366
367
368
369
370
371
  if (linear_tree_) {
    std::vector<int> max_leaves_by_thread = std::vector<int>(OMP_NUM_THREADS(), 0);
    #pragma omp parallel for schedule(static)
    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);
  }
372
373
374
375
376
377
378
  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;
      #pragma omp parallel for schedule(static)
      for (int i = 0; i < num_data_; ++i) {
        leaf_pred[i] = tree_leaf_prediction[i][model_index];
Nikita Titov's avatar
Nikita Titov committed
379
        CHECK_LT(leaf_pred[i], models_[model_index]->num_leaves());
380
      }
381
      size_t offset = static_cast<size_t>(tree_id) * num_data_;
382
383
      auto grad = gradients_pointer_ + offset;
      auto hess = hessians_pointer_ + offset;
384
385
386
387
388
389
390
      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
391
/* If the custom "average" is implemented it will be used in place of the label average (if enabled)
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
*
* 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
412
double GBDT::BoostFromAverage(int class_id, bool update_scorer) {
413
  Common::FunctionTimer fun_timer("GBDT::BoostFromAverage", global_timer);
414
  // boosting from average label; or customized "average" if implemented for the current objective
415
416
417
  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);
418
      if (std::fabs(init_score) > kEpsilon) {
Guolin Ke's avatar
Guolin Ke committed
419
420
421
422
423
        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);
          }
424
425
426
        }
        Log::Info("Start training from score %lf", init_score);
        return init_score;
Guolin Ke's avatar
Guolin Ke committed
427
      }
428
429
430
    } 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")) {
431
      Log::Warning("Disabling boost_from_average in %s may cause the slow convergence", objective_function_->GetName());
432
    }
433
  }
Guolin Ke's avatar
Guolin Ke committed
434
435
  return 0.0f;
}
Guolin Ke's avatar
Guolin Ke committed
436

Guolin Ke's avatar
Guolin Ke committed
437
bool GBDT::TrainOneIter(const score_t* gradients, const score_t* hessians) {
438
  Common::FunctionTimer fun_timer("GBDT::TrainOneIter", global_timer);
439
  std::vector<double> init_scores(num_tree_per_iteration_, 0.0);
Guolin Ke's avatar
Guolin Ke committed
440
  // boosting first
Guolin Ke's avatar
Guolin Ke committed
441
  if (gradients == nullptr || hessians == nullptr) {
442
    for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
Guolin Ke's avatar
Guolin Ke committed
443
      init_scores[cur_tree_id] = BoostFromAverage(cur_tree_id, true);
444
    }
Guolin Ke's avatar
Guolin Ke committed
445
    Boosting();
446
447
448
    gradients = gradients_pointer_;
    hessians = hessians_pointer_;
  } else {
shiyu1994's avatar
shiyu1994 committed
449
450
451
452
453
454
455
456
457
458
459
460
    // use customized objective function
    CHECK(objective_function_ == nullptr);
    if (config_->boosting == std::string("goss")) {
      // need to copy customized gradients when using GOSS
      int64_t total_size = static_cast<int64_t>(num_data_) * num_tree_per_iteration_;
      #pragma omp parallel for schedule(static)
      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());
461
462
463
464
465
      gradients = gradients_pointer_;
      hessians = hessians_pointer_;
    }
  }

466
467
  // bagging logic
  Bagging(iter_);
Guolin Ke's avatar
Guolin Ke committed
468

shiyu1994's avatar
shiyu1994 committed
469
470
471
472
473
474
475
476
477
  if (gradients != nullptr && is_use_subset_ && bag_data_cnt_ < num_data_ && !boosting_on_gpu_ && config_->boosting != std::string("goss")) {
    // allocate gradients_ and hessians_ for copy gradients for using data subset
    int64_t total_size = static_cast<int64_t>(num_data_) * num_tree_per_iteration_;
    gradients_.resize(total_size);
    hessians_.resize(total_size);
    gradients_pointer_ = gradients_.data();
    hessians_pointer_ = hessians_.data();
  }

Guolin Ke's avatar
Guolin Ke committed
478
  bool should_continue = false;
479
  for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
480
    const size_t offset = static_cast<size_t>(cur_tree_id) * num_data_;
481
    std::unique_ptr<Tree> new_tree(new Tree(2, false, false));
482
    if (class_need_train_[cur_tree_id] && train_data_->num_features() > 0) {
483
484
      auto grad = gradients + offset;
      auto hess = hessians + offset;
Guolin Ke's avatar
Guolin Ke committed
485
      // need to copy gradients for bagging subset.
shiyu1994's avatar
shiyu1994 committed
486
      if (is_use_subset_ && bag_data_cnt_ < num_data_ && !boosting_on_gpu_) {
Guolin Ke's avatar
Guolin Ke committed
487
        for (int i = 0; i < bag_data_cnt_; ++i) {
488
489
          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
490
        }
491
492
        grad = gradients_pointer_ + offset;
        hess = hessians_pointer_ + offset;
Guolin Ke's avatar
Guolin Ke committed
493
      }
494
495
      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));
496
    }
Guolin Ke's avatar
Guolin Ke committed
497

Guolin Ke's avatar
Guolin Ke committed
498
    if (new_tree->num_leaves() > 1) {
Guolin Ke's avatar
Guolin Ke committed
499
      should_continue = true;
500
      auto score_ptr = train_score_updater_->score() + offset;
501
502
      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,
503
                                     num_data_, bag_data_indices_.data(), bag_data_cnt_);
Guolin Ke's avatar
Guolin Ke committed
504
505
506
      // shrinkage by learning rate
      new_tree->Shrinkage(shrinkage_rate_);
      // update score
507
      UpdateScore(new_tree.get(), cur_tree_id);
508
509
      if (std::fabs(init_scores[cur_tree_id]) > kEpsilon) {
        new_tree->AddBias(init_scores[cur_tree_id]);
Guolin Ke's avatar
Guolin Ke committed
510
      }
511
512
    } else {
      // only add default score one-time
513
      if (models_.size() < static_cast<size_t>(num_tree_per_iteration_)) {
514
515
516
517
518
519
        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);
520
          }
521
        }
522
        new_tree->AsConstantTree(init_scores[cur_tree_id]);
523
524
      }
    }
Guolin Ke's avatar
Guolin Ke committed
525
526
527
    // add model
    models_.push_back(std::move(new_tree));
  }
Guolin Ke's avatar
Guolin Ke committed
528

Guolin Ke's avatar
Guolin Ke committed
529
  if (!should_continue) {
530
    Log::Warning("Stopped training because there are no more leaves that meet the split requirements");
531
532
533
534
    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
535
536
537
    }
    return true;
  }
538

Guolin Ke's avatar
Guolin Ke committed
539
540
  ++iter_;
  return false;
Guolin Ke's avatar
Guolin Ke committed
541
}
542

wxchan's avatar
wxchan committed
543
void GBDT::RollbackOneIter() {
544
  if (iter_ <= 0) { return; }
wxchan's avatar
wxchan committed
545
  // reset score
546
  for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
Guolin Ke's avatar
Guolin Ke committed
547
    auto curr_tree = models_.size() - num_tree_per_iteration_ + cur_tree_id;
wxchan's avatar
wxchan committed
548
    models_[curr_tree]->Shrinkage(-1.0);
549
    train_score_updater_->AddScore(models_[curr_tree].get(), cur_tree_id);
wxchan's avatar
wxchan committed
550
    for (auto& score_updater : valid_score_updater_) {
551
      score_updater->AddScore(models_[curr_tree].get(), cur_tree_id);
wxchan's avatar
wxchan committed
552
553
554
    }
  }
  // remove model
555
  for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
wxchan's avatar
wxchan committed
556
557
558
559
560
    models_.pop_back();
  }
  --iter_;
}

Guolin Ke's avatar
Guolin Ke committed
561
bool GBDT::EvalAndCheckEarlyStopping() {
562
563
  bool is_met_early_stopping = false;
  // print message for metric
Guolin Ke's avatar
Guolin Ke committed
564
  auto best_msg = OutputMetric(iter_);
Guolin Ke's avatar
Guolin Ke committed
565
566


Guolin Ke's avatar
Guolin Ke committed
567
  is_met_early_stopping = !best_msg.empty();
568
569
  if (is_met_early_stopping) {
    Log::Info("Early stopping at iteration %d, the best iteration round is %d",
570
              iter_, iter_ - early_stopping_round_);
Guolin Ke's avatar
Guolin Ke committed
571
    Log::Info("Output of best iteration round:\n%s", best_msg.c_str());
572
    // pop last early_stopping_round_ models
573
    for (int i = 0; i < early_stopping_round_ * num_tree_per_iteration_; ++i) {
574
575
576
577
      models_.pop_back();
    }
  }
  return is_met_early_stopping;
Guolin Ke's avatar
Guolin Ke committed
578
579
}

580
void GBDT::UpdateScore(const Tree* tree, const int cur_tree_id) {
581
  Common::FunctionTimer fun_timer("GBDT::UpdateScore", global_timer);
Guolin Ke's avatar
Guolin Ke committed
582
  // update training score
Guolin Ke's avatar
Guolin Ke committed
583
  if (!is_use_subset_) {
584
    train_score_updater_->AddScore(tree_learner_.get(), tree, cur_tree_id);
Guolin Ke's avatar
Guolin Ke committed
585
586
587

    // we need to predict out-of-bag scores of data for boosting
    if (num_data_ - bag_data_cnt_ > 0) {
588
589
590
591
592
593
594
595
596
      #ifdef USE_CUDA_EXP
      if (config_->device_type == std::string("cuda_exp")) {
        train_score_updater_->AddScore(tree, cuda_bag_data_indices_.RawData() + bag_data_cnt_, num_data_ - bag_data_cnt_, cur_tree_id);
      } else {
      #endif  // USE_CUDA_EXP
        train_score_updater_->AddScore(tree, bag_data_indices_.data() + bag_data_cnt_, num_data_ - bag_data_cnt_, cur_tree_id);
      #ifdef USE_CUDA_EXP
      }
      #endif  // USE_CUDA_EXP
Guolin Ke's avatar
Guolin Ke committed
597
598
    }

Guolin Ke's avatar
Guolin Ke committed
599
  } else {
600
    train_score_updater_->AddScore(tree, cur_tree_id);
Guolin Ke's avatar
Guolin Ke committed
601
  }
Guolin Ke's avatar
Guolin Ke committed
602
603


Guolin Ke's avatar
Guolin Ke committed
604
  // update validation score
Guolin Ke's avatar
Guolin Ke committed
605
  for (auto& score_updater : valid_score_updater_) {
606
    score_updater->AddScore(tree, cur_tree_id);
Guolin Ke's avatar
Guolin Ke committed
607
608
609
  }
}

Guolin Ke's avatar
Guolin Ke committed
610
std::vector<double> GBDT::EvalOneMetric(const Metric* metric, const double* score) const {
611
612
  #ifdef USE_CUDA_EXP
  const bool evaluation_on_cuda = metric->IsCUDAMetric();
shiyu1994's avatar
shiyu1994 committed
613
  if ((boosting_on_gpu_ && evaluation_on_cuda) || (!boosting_on_gpu_ && !evaluation_on_cuda)) {
614
615
616
  #endif  // USE_CUDA_EXP
    return metric->Eval(score, objective_function_);
  #ifdef USE_CUDA_EXP
shiyu1994's avatar
shiyu1994 committed
617
  } else if (boosting_on_gpu_ && !evaluation_on_cuda) {
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
    const size_t total_size = static_cast<size_t>(num_data_) * static_cast<size_t>(num_tree_per_iteration_);
    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 {
    const size_t total_size = static_cast<size_t>(num_data_) * static_cast<size_t>(num_tree_per_iteration_);
    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_);
  }
  #endif  // USE_CUDA_EXP
Guolin Ke's avatar
Guolin Ke committed
633
634
}

Guolin Ke's avatar
Guolin Ke committed
635
std::string GBDT::OutputMetric(int iter) {
Guolin Ke's avatar
Guolin Ke committed
636
  bool need_output = (iter % config_->metric_freq) == 0;
Guolin Ke's avatar
Guolin Ke committed
637
638
  std::string ret = "";
  std::stringstream msg_buf;
639
  std::vector<std::pair<size_t, size_t>> meet_early_stopping_pairs;
Guolin Ke's avatar
Guolin Ke committed
640
  // print training metric
Guolin Ke's avatar
Guolin Ke committed
641
  if (need_output) {
642
643
    for (auto& sub_metric : training_metrics_) {
      auto name = sub_metric->GetName();
Guolin Ke's avatar
Guolin Ke committed
644
      auto scores = EvalOneMetric(sub_metric, train_score_updater_->score());
Guolin Ke's avatar
Guolin Ke committed
645
      for (size_t k = 0; k < name.size(); ++k) {
Guolin Ke's avatar
Guolin Ke committed
646
647
648
649
650
651
        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) {
652
          msg_buf << tmp_buf.str() << '\n';
Guolin Ke's avatar
Guolin Ke committed
653
        }
654
      }
655
    }
Guolin Ke's avatar
Guolin Ke committed
656
657
  }
  // print validation metric
Guolin Ke's avatar
Guolin Ke committed
658
  if (need_output || early_stopping_round_ > 0) {
659
660
    for (size_t i = 0; i < valid_metrics_.size(); ++i) {
      for (size_t j = 0; j < valid_metrics_[i].size(); ++j) {
Guolin Ke's avatar
Guolin Ke committed
661
        auto test_scores = EvalOneMetric(valid_metrics_[i][j], valid_score_updater_[i]->score());
Guolin Ke's avatar
Guolin Ke committed
662
663
664
665
666
667
668
669
670
671
        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) {
672
            msg_buf << tmp_buf.str() << '\n';
673
          }
wxchan's avatar
wxchan committed
674
        }
675
        if (es_first_metric_only_ && j > 0) { continue; }
Guolin Ke's avatar
Guolin Ke committed
676
        if (ret.empty() && early_stopping_round_ > 0) {
677
678
679
          auto cur_score = valid_metrics_[i][j]->factor_to_bigger_better() * test_scores.back();
          if (cur_score > best_score_[i][j]) {
            best_score_[i][j] = cur_score;
680
            best_iter_[i][j] = iter;
Guolin Ke's avatar
Guolin Ke committed
681
            meet_early_stopping_pairs.emplace_back(i, j);
682
          } else {
Guolin Ke's avatar
Guolin Ke committed
683
            if (iter - best_iter_[i][j] >= early_stopping_round_) { ret = best_msg_[i][j]; }
684
          }
wxchan's avatar
wxchan committed
685
686
        }
      }
Guolin Ke's avatar
Guolin Ke committed
687
688
    }
  }
Guolin Ke's avatar
Guolin Ke committed
689
690
691
  for (auto& pair : meet_early_stopping_pairs) {
    best_msg_[pair.first][pair.second] = msg_buf.str();
  }
wxchan's avatar
wxchan committed
692
  return ret;
Guolin Ke's avatar
Guolin Ke committed
693
694
}

695
/*! \brief Get eval result */
696
std::vector<double> GBDT::GetEvalAt(int data_idx) const {
Guolin Ke's avatar
Guolin Ke committed
697
  CHECK(data_idx >= 0 && data_idx <= static_cast<int>(valid_score_updater_.size()));
698
699
  std::vector<double> ret;
  if (data_idx == 0) {
700
    for (auto& sub_metric : training_metrics_) {
Guolin Ke's avatar
Guolin Ke committed
701
      auto scores = EvalOneMetric(sub_metric, train_score_updater_->score());
702
703
704
      for (auto score : scores) {
        ret.push_back(score);
      }
705
    }
706
  } else {
707
708
    auto used_idx = data_idx - 1;
    for (size_t j = 0; j < valid_metrics_[used_idx].size(); ++j) {
Guolin Ke's avatar
Guolin Ke committed
709
      auto test_scores = EvalOneMetric(valid_metrics_[used_idx][j], valid_score_updater_[used_idx]->score());
710
711
712
      for (auto score : test_scores) {
        ret.push_back(score);
      }
713
714
715
716
717
    }
  }
  return ret;
}

Guolin Ke's avatar
Guolin Ke committed
718
/*! \brief Get training scores result */
719
const double* GBDT::GetTrainingScore(int64_t* out_len) {
720
  *out_len = static_cast<int64_t>(train_score_updater_->num_data()) * num_class_;
Guolin Ke's avatar
Guolin Ke committed
721
  return train_score_updater_->score();
722
723
}

724
void GBDT::PredictContrib(const double* features, double* output) const {
725
  // set zero
Guolin Ke's avatar
Guolin Ke committed
726
727
  const int num_features = max_feature_idx_ + 1;
  std::memset(output, 0, sizeof(double) * num_tree_per_iteration_ * (num_features + 1));
728
729
  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) {
730
731
    // predict all the trees for one iteration
    for (int k = 0; k < num_tree_per_iteration_; ++k) {
Guolin Ke's avatar
Guolin Ke committed
732
      models_[i * num_tree_per_iteration_ + k]->PredictContrib(features, num_features, output + k*(num_features + 1));
733
    }
734
735
736
737
738
739
  }
}

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;
740
741
  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) {
742
743
744
    // 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]));
745
746
747
748
    }
  }
}

Guolin Ke's avatar
Guolin Ke committed
749
750
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
751

752
  const double* raw_scores = nullptr;
Guolin Ke's avatar
Guolin Ke committed
753
754
  data_size_t num_data = 0;
  if (data_idx == 0) {
wxchan's avatar
wxchan committed
755
    raw_scores = GetTrainingScore(out_len);
Guolin Ke's avatar
Guolin Ke committed
756
757
758
759
760
    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();
761
    *out_len = static_cast<int64_t>(num_data) * num_class_;
Guolin Ke's avatar
Guolin Ke committed
762
  }
Guolin Ke's avatar
Guolin Ke committed
763
  if (objective_function_ != nullptr) {
Guolin Ke's avatar
Guolin Ke committed
764
765
    #pragma omp parallel for schedule(static)
    for (data_size_t i = 0; i < num_data; ++i) {
Guolin Ke's avatar
Guolin Ke committed
766
      std::vector<double> tree_pred(num_tree_per_iteration_);
767
      for (int j = 0; j < num_tree_per_iteration_; ++j) {
Guolin Ke's avatar
Guolin Ke committed
768
        tree_pred[j] = raw_scores[j * num_data + i];
769
      }
Guolin Ke's avatar
Guolin Ke committed
770
771
      std::vector<double> tmp_result(num_class_);
      objective_function_->ConvertOutput(tree_pred.data(), tmp_result.data());
Guolin Ke's avatar
Guolin Ke committed
772
      for (int j = 0; j < num_class_; ++j) {
773
        out_result[j * num_data + i] = static_cast<double>(tmp_result[j]);
Guolin Ke's avatar
Guolin Ke committed
774
775
      }
    }
776
  } else {
Guolin Ke's avatar
Guolin Ke committed
777
    #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
778
    for (data_size_t i = 0; i < num_data; ++i) {
779
      for (int j = 0; j < num_tree_per_iteration_; ++j) {
Guolin Ke's avatar
Guolin Ke committed
780
        out_result[j * num_data + i] = static_cast<double>(raw_scores[j * num_data + i]);
Guolin Ke's avatar
Guolin Ke committed
781
782
783
784
785
      }
    }
  }
}

786
787
double GBDT::GetUpperBoundValue() const {
  double max_value = 0.0;
Nikita Titov's avatar
Nikita Titov committed
788
  for (const auto &tree : models_) {
789
790
791
792
793
794
795
    max_value += tree->GetUpperBoundValue();
  }
  return max_value;
}

double GBDT::GetLowerBoundValue() const {
  double min_value = 0.0;
Nikita Titov's avatar
Nikita Titov committed
796
  for (const auto &tree : models_) {
797
798
799
800
801
    min_value += tree->GetLowerBoundValue();
  }
  return min_value;
}

Guolin Ke's avatar
Guolin Ke committed
802
803
804
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)) {
805
    Log::Fatal("Cannot reset training data, since new training data has different bin mappers");
wxchan's avatar
wxchan committed
806
807
  }

Guolin Ke's avatar
Guolin Ke committed
808
809
  objective_function_ = objective_function;
  if (objective_function_ != nullptr) {
Nikita Titov's avatar
Nikita Titov committed
810
    CHECK_EQ(num_tree_per_iteration_, objective_function_->NumModelPerIteration());
811
812
813
    if (objective_function_->IsRenewTreeOutput() && !config_->monotone_constraints.empty()) {
      Log::Fatal("Cannot use ``monotone_constraints`` in %s objective, please disable it.", objective_function_->GetName());
    }
814
  }
815
  is_constant_hessian_ = GetIsConstHessian(objective_function);
816

Guolin Ke's avatar
Guolin Ke committed
817
818
819
820
  // push training metrics
  training_metrics_.clear();
  for (const auto& metric : training_metrics) {
    training_metrics_.push_back(metric);
821
  }
Guolin Ke's avatar
Guolin Ke committed
822
  training_metrics_.shrink_to_fit();
823

shiyu1994's avatar
shiyu1994 committed
824
825
  boosting_on_gpu_ = objective_function_ != nullptr && objective_function_->IsCUDAObjective();
  tree_learner_->ResetBoostingOnGPU(boosting_on_gpu_);
826

Guolin Ke's avatar
Guolin Ke committed
827
828
829
830
  if (train_data != train_data_) {
    train_data_ = train_data;
    // not same training data, need reset score and others
    // create score tracker
831
832
    #ifdef USE_CUDA_EXP
    if (config_->device_type == std::string("cuda_exp")) {
shiyu1994's avatar
shiyu1994 committed
833
      train_score_updater_.reset(new CUDAScoreUpdater(train_data_, num_tree_per_iteration_, boosting_on_gpu_));
834
835
836
837
838
839
    } else {
    #endif  // USE_CUDA_EXP
      train_score_updater_.reset(new ScoreUpdater(train_data_, num_tree_per_iteration_));
    #ifdef USE_CUDA_EXP
    }
    #endif  // USE_CUDA_EXP
840

Guolin Ke's avatar
Guolin Ke committed
841
842
843
844
845
846
    // 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);
      }
847
848
    }

Guolin Ke's avatar
Guolin Ke committed
849
    num_data_ = train_data_->num_data();
850

Guolin Ke's avatar
Guolin Ke committed
851
852
    // create buffer for gradients and hessians
    if (objective_function_ != nullptr) {
shiyu1994's avatar
shiyu1994 committed
853
      const size_t total_size = static_cast<size_t>(num_data_) * num_tree_per_iteration_;
854
      #ifdef USE_CUDA_EXP
shiyu1994's avatar
shiyu1994 committed
855
856
857
858
859
860
      if (config_->device_type == std::string("cuda_exp") && boosting_on_gpu_) {
        if (gradients_pointer_ != nullptr) {
          CHECK_NOTNULL(hessians_pointer_);
          DeallocateCUDAMemory<score_t>(&gradients_pointer_, __FILE__, __LINE__);
          DeallocateCUDAMemory<score_t>(&hessians_pointer_, __FILE__, __LINE__);
        }
861
862
863
864
865
866
867
868
869
870
871
        AllocateCUDAMemory<score_t>(&gradients_pointer_, total_size, __FILE__, __LINE__);
        AllocateCUDAMemory<score_t>(&hessians_pointer_, total_size, __FILE__, __LINE__);
      } else {
      #endif  // USE_CUDA_EXP
        gradients_.resize(total_size);
        hessians_.resize(total_size);
        gradients_pointer_ = gradients_.data();
        hessians_pointer_ = hessians_.data();
      #ifdef USE_CUDA_EXP
      }
      #endif  // USE_CUDA_EXP
shiyu1994's avatar
shiyu1994 committed
872
873
874
875
876
877
    } else if (config_->boosting == std::string("goss")) {
      const size_t total_size = static_cast<size_t>(num_data_) * num_tree_per_iteration_;
      gradients_.resize(total_size);
      hessians_.resize(total_size);
      gradients_pointer_ = gradients_.data();
      hessians_pointer_ = hessians_.data();
Guolin Ke's avatar
Guolin Ke committed
878
    }
879

Guolin Ke's avatar
Guolin Ke committed
880
881
882
883
    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();
884
    parser_config_str_ = train_data_->parser_config_str();
885

886
    tree_learner_->ResetTrainingData(train_data, is_constant_hessian_);
Guolin Ke's avatar
Guolin Ke committed
887
    ResetBaggingConfig(config_.get(), true);
888
889
  } else {
    tree_learner_->ResetIsConstantHessian(is_constant_hessian_);
890
  }
891
892
}

Guolin Ke's avatar
Guolin Ke committed
893
894
void GBDT::ResetConfig(const Config* config) {
  auto new_config = std::unique_ptr<Config>(new Config(*config));
895
  if (!config->monotone_constraints.empty()) {
Nikita Titov's avatar
Nikita Titov committed
896
    CHECK_EQ(static_cast<size_t>(train_data_->num_total_features()), config->monotone_constraints.size());
897
898
  }
  if (!config->feature_contri.empty()) {
Nikita Titov's avatar
Nikita Titov committed
899
    CHECK_EQ(static_cast<size_t>(train_data_->num_total_features()), config->feature_contri.size());
900
  }
901
902
903
  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
904
905
906
  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
907
    tree_learner_->ResetConfig(new_config.get());
908
  }
shiyu1994's avatar
shiyu1994 committed
909
910
911
912

  boosting_on_gpu_ = objective_function_ != nullptr && objective_function_->IsCUDAObjective();
  tree_learner_->ResetBoostingOnGPU(boosting_on_gpu_);

Guolin Ke's avatar
Guolin Ke committed
913
914
  if (train_data_ != nullptr) {
    ResetBaggingConfig(new_config.get(), false);
915
  }
916
  if (config_.get() != nullptr && config_->forcedsplits_filename != new_config->forcedsplits_filename) {
917
918
919
920
921
922
923
    // 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
924
      forced_splits_json_ = Json::parse(buffer.str(), &err);
925
926
927
928
929
930
      tree_learner_->SetForcedSplit(&forced_splits_json_);
    } else {
      forced_splits_json_ = Json();
      tree_learner_->SetForcedSplit(nullptr);
    }
  }
Guolin Ke's avatar
Guolin Ke committed
931
  config_.reset(new_config.release());
Guolin Ke's avatar
Guolin Ke committed
932
933
}

Guolin Ke's avatar
Guolin Ke committed
934
void GBDT::ResetBaggingConfig(const Config* config, bool is_change_dataset) {
Guolin Ke's avatar
Guolin Ke committed
935
  // if need bagging, create buffer
Guolin Ke's avatar
Guolin Ke committed
936
937
938
939
940
941
  data_size_t num_pos_data = 0;
  if (objective_function_ != nullptr) {
    num_pos_data = objective_function_->NumPositiveData();
  }
  bool balance_bagging_cond = (config->pos_bagging_fraction < 1.0 || config->neg_bagging_fraction < 1.0) && (num_pos_data > 0);
  if ((config->bagging_fraction < 1.0 || balance_bagging_cond) && config->bagging_freq > 0) {
942
943
    need_re_bagging_ = false;
    if (!is_change_dataset &&
Guolin Ke's avatar
Guolin Ke committed
944
945
      config_.get() != nullptr && config_->bagging_fraction == config->bagging_fraction && config_->bagging_freq == config->bagging_freq
      && config_->pos_bagging_fraction == config->pos_bagging_fraction && config_->neg_bagging_fraction == config->neg_bagging_fraction) {
946
947
      return;
    }
Guolin Ke's avatar
Guolin Ke committed
948
949
    if (balance_bagging_cond) {
      balanced_bagging_ = true;
950
      bag_data_cnt_ = static_cast<data_size_t>(num_pos_data * config->pos_bagging_fraction)
Guolin Ke's avatar
Guolin Ke committed
951
952
953
954
                      + static_cast<data_size_t>((num_data_ - num_pos_data) * config->neg_bagging_fraction);
    } else {
      bag_data_cnt_ = static_cast<data_size_t>(config->bagging_fraction * num_data_);
    }
Guolin Ke's avatar
Guolin Ke committed
955
    bag_data_indices_.resize(num_data_);
956
957
958
959
960
    #ifdef USE_CUDA_EXP
    if (config->device_type == std::string("cuda_exp")) {
      cuda_bag_data_indices_.Resize(num_data_);
    }
    #endif  // USE_CUDA_EXP
961
962
963
964
965
966
    bagging_runner_.ReSize(num_data_);
    bagging_rands_.clear();
    for (int i = 0;
         i < (num_data_ + bagging_rand_block_ - 1) / bagging_rand_block_; ++i) {
      bagging_rands_.emplace_back(config_->bagging_seed + i);
    }
967

968
969
    double average_bag_rate =
        (static_cast<double>(bag_data_cnt_) / num_data_) / config->bagging_freq;
Guolin Ke's avatar
Guolin Ke committed
970
    is_use_subset_ = false;
971
972
973
974
975
976
977
978
979
980
    if (config_->device_type != std::string("cuda_exp")) {
      const int group_threshold_usesubset = 100;
      if (average_bag_rate <= 0.5
          && (train_data_->num_feature_groups() < group_threshold_usesubset)) {
        if (tmp_subset_ == nullptr || is_change_dataset) {
          tmp_subset_.reset(new Dataset(bag_data_cnt_));
          tmp_subset_->CopyFeatureMapperFrom(train_data_);
        }
        is_use_subset_ = true;
        Log::Debug("Use subset for bagging");
Guolin Ke's avatar
Guolin Ke committed
981
      }
Guolin Ke's avatar
Guolin Ke committed
982
983
    }

984
    need_re_bagging_ = true;
985

Guolin Ke's avatar
Guolin Ke committed
986
    if (is_use_subset_ && bag_data_cnt_ < num_data_) {
shiyu1994's avatar
shiyu1994 committed
987
988
989
      // resize gradient vectors to copy the customized gradients for goss or bagging with subset
      if (objective_function_ != nullptr) {
        const size_t total_size = static_cast<size_t>(num_data_) * num_tree_per_iteration_;
990
        #ifdef USE_CUDA_EXP
shiyu1994's avatar
shiyu1994 committed
991
992
993
994
995
996
        if (config_->device_type == std::string("cuda_exp") && boosting_on_gpu_) {
          if (gradients_pointer_ != nullptr) {
            CHECK_NOTNULL(hessians_pointer_);
            DeallocateCUDAMemory<score_t>(&gradients_pointer_, __FILE__, __LINE__);
            DeallocateCUDAMemory<score_t>(&hessians_pointer_, __FILE__, __LINE__);
          }
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
          AllocateCUDAMemory<score_t>(&gradients_pointer_, total_size, __FILE__, __LINE__);
          AllocateCUDAMemory<score_t>(&hessians_pointer_, total_size, __FILE__, __LINE__);
        } else {
        #endif  // USE_CUDA_EXP
          gradients_.resize(total_size);
          hessians_.resize(total_size);
          gradients_pointer_ = gradients_.data();
          hessians_pointer_ = hessians_.data();
        #ifdef USE_CUDA_EXP
        }
        #endif  // USE_CUDA_EXP
shiyu1994's avatar
shiyu1994 committed
1008
1009
1010
1011
1012
1013
      } else if (config_->boosting == std::string("goss")) {
        const size_t total_size = static_cast<size_t>(num_data_) * num_tree_per_iteration_;
        gradients_.resize(total_size);
        hessians_.resize(total_size);
        gradients_pointer_ = gradients_.data();
        hessians_pointer_ = hessians_.data();
1014
      }
1015
    }
1016
  } else {
Guolin Ke's avatar
Guolin Ke committed
1017
1018
    bag_data_cnt_ = num_data_;
    bag_data_indices_.clear();
1019
1020
1021
    #ifdef USE_CUDA_EXP
    cuda_bag_data_indices_.Clear();
    #endif  // USE_CUDA_EXP
1022
    bagging_runner_.ReSize(0);
Guolin Ke's avatar
Guolin Ke committed
1023
    is_use_subset_ = false;
1024
  }
wxchan's avatar
wxchan committed
1025
1026
}

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