gbdt.cpp 40.9 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_, train_score_updater_->score());
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
  }
}

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

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

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

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

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

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;
744
745
  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) {
746
747
748
    // 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]));
749
750
751
752
    }
  }
}

Guolin Ke's avatar
Guolin Ke committed
753
754
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
755

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

798
799
double GBDT::GetUpperBoundValue() const {
  double max_value = 0.0;
Nikita Titov's avatar
Nikita Titov committed
800
  for (const auto &tree : models_) {
801
802
803
804
805
806
807
    max_value += tree->GetUpperBoundValue();
  }
  return max_value;
}

double GBDT::GetLowerBoundValue() const {
  double min_value = 0.0;
Nikita Titov's avatar
Nikita Titov committed
808
  for (const auto &tree : models_) {
809
810
811
812
813
    min_value += tree->GetLowerBoundValue();
  }
  return min_value;
}

Guolin Ke's avatar
Guolin Ke committed
814
815
816
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)) {
817
    Log::Fatal("Cannot reset training data, since new training data has different bin mappers");
wxchan's avatar
wxchan committed
818
819
  }

Guolin Ke's avatar
Guolin Ke committed
820
821
  objective_function_ = objective_function;
  if (objective_function_ != nullptr) {
Nikita Titov's avatar
Nikita Titov committed
822
    CHECK_EQ(num_tree_per_iteration_, objective_function_->NumModelPerIteration());
823
824
825
    if (objective_function_->IsRenewTreeOutput() && !config_->monotone_constraints.empty()) {
      Log::Fatal("Cannot use ``monotone_constraints`` in %s objective, please disable it.", objective_function_->GetName());
    }
826
  }
827
  is_constant_hessian_ = GetIsConstHessian(objective_function);
828

Guolin Ke's avatar
Guolin Ke committed
829
830
831
832
  // push training metrics
  training_metrics_.clear();
  for (const auto& metric : training_metrics) {
    training_metrics_.push_back(metric);
833
  }
Guolin Ke's avatar
Guolin Ke committed
834
  training_metrics_.shrink_to_fit();
835

shiyu1994's avatar
shiyu1994 committed
836
837
  boosting_on_gpu_ = objective_function_ != nullptr && objective_function_->IsCUDAObjective();
  tree_learner_->ResetBoostingOnGPU(boosting_on_gpu_);
838

Guolin Ke's avatar
Guolin Ke committed
839
840
841
842
  if (train_data != train_data_) {
    train_data_ = train_data;
    // not same training data, need reset score and others
    // create score tracker
843
844
    #ifdef USE_CUDA_EXP
    if (config_->device_type == std::string("cuda_exp")) {
shiyu1994's avatar
shiyu1994 committed
845
      train_score_updater_.reset(new CUDAScoreUpdater(train_data_, num_tree_per_iteration_, boosting_on_gpu_));
846
847
848
849
850
851
    } 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
852

Guolin Ke's avatar
Guolin Ke committed
853
854
855
856
857
858
    // 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);
      }
859
860
    }

Guolin Ke's avatar
Guolin Ke committed
861
    num_data_ = train_data_->num_data();
862

Guolin Ke's avatar
Guolin Ke committed
863
864
    // create buffer for gradients and hessians
    if (objective_function_ != nullptr) {
shiyu1994's avatar
shiyu1994 committed
865
      const size_t total_size = static_cast<size_t>(num_data_) * num_tree_per_iteration_;
866
      #ifdef USE_CUDA_EXP
shiyu1994's avatar
shiyu1994 committed
867
868
869
870
871
872
      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__);
        }
873
874
875
876
877
878
879
880
881
882
883
        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
884
885
886
887
888
889
    } 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
890
    }
891

Guolin Ke's avatar
Guolin Ke committed
892
893
894
895
    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();
896
    parser_config_str_ = train_data_->parser_config_str();
897

898
    tree_learner_->ResetTrainingData(train_data, is_constant_hessian_);
Guolin Ke's avatar
Guolin Ke committed
899
    ResetBaggingConfig(config_.get(), true);
900
901
  } else {
    tree_learner_->ResetIsConstantHessian(is_constant_hessian_);
902
  }
903
904
}

Guolin Ke's avatar
Guolin Ke committed
905
906
void GBDT::ResetConfig(const Config* config) {
  auto new_config = std::unique_ptr<Config>(new Config(*config));
907
  if (!config->monotone_constraints.empty()) {
Nikita Titov's avatar
Nikita Titov committed
908
    CHECK_EQ(static_cast<size_t>(train_data_->num_total_features()), config->monotone_constraints.size());
909
910
  }
  if (!config->feature_contri.empty()) {
Nikita Titov's avatar
Nikita Titov committed
911
    CHECK_EQ(static_cast<size_t>(train_data_->num_total_features()), config->feature_contri.size());
912
  }
913
914
915
  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
916
917
918
  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
919
    tree_learner_->ResetConfig(new_config.get());
920
  }
shiyu1994's avatar
shiyu1994 committed
921
922
923
924

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

Guolin Ke's avatar
Guolin Ke committed
925
926
  if (train_data_ != nullptr) {
    ResetBaggingConfig(new_config.get(), false);
927
  }
928
  if (config_.get() != nullptr && config_->forcedsplits_filename != new_config->forcedsplits_filename) {
929
930
931
932
933
934
935
    // 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
936
      forced_splits_json_ = Json::parse(buffer.str(), &err);
937
938
939
940
941
942
      tree_learner_->SetForcedSplit(&forced_splits_json_);
    } else {
      forced_splits_json_ = Json();
      tree_learner_->SetForcedSplit(nullptr);
    }
  }
Guolin Ke's avatar
Guolin Ke committed
943
  config_.reset(new_config.release());
Guolin Ke's avatar
Guolin Ke committed
944
945
}

Guolin Ke's avatar
Guolin Ke committed
946
void GBDT::ResetBaggingConfig(const Config* config, bool is_change_dataset) {
Guolin Ke's avatar
Guolin Ke committed
947
  // if need bagging, create buffer
Guolin Ke's avatar
Guolin Ke committed
948
949
950
951
952
953
  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) {
954
955
    need_re_bagging_ = false;
    if (!is_change_dataset &&
Guolin Ke's avatar
Guolin Ke committed
956
957
      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) {
958
959
      return;
    }
Guolin Ke's avatar
Guolin Ke committed
960
961
    if (balance_bagging_cond) {
      balanced_bagging_ = true;
962
      bag_data_cnt_ = static_cast<data_size_t>(num_pos_data * config->pos_bagging_fraction)
Guolin Ke's avatar
Guolin Ke committed
963
964
965
966
                      + 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
967
    bag_data_indices_.resize(num_data_);
968
969
970
971
972
    #ifdef USE_CUDA_EXP
    if (config->device_type == std::string("cuda_exp")) {
      cuda_bag_data_indices_.Resize(num_data_);
    }
    #endif  // USE_CUDA_EXP
973
974
975
976
977
978
    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);
    }
979

980
981
    double average_bag_rate =
        (static_cast<double>(bag_data_cnt_) / num_data_) / config->bagging_freq;
Guolin Ke's avatar
Guolin Ke committed
982
    is_use_subset_ = false;
983
984
985
986
987
988
989
990
991
992
    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
993
      }
Guolin Ke's avatar
Guolin Ke committed
994
995
    }

996
    need_re_bagging_ = true;
997

Guolin Ke's avatar
Guolin Ke committed
998
    if (is_use_subset_ && bag_data_cnt_ < num_data_) {
shiyu1994's avatar
shiyu1994 committed
999
1000
1001
      // 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_;
1002
        #ifdef USE_CUDA_EXP
shiyu1994's avatar
shiyu1994 committed
1003
1004
1005
1006
1007
1008
        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__);
          }
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
          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
1020
1021
1022
1023
1024
1025
      } 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();
1026
      }
1027
    }
1028
  } else {
Guolin Ke's avatar
Guolin Ke committed
1029
1030
    bag_data_cnt_ = num_data_;
    bag_data_indices_.clear();
1031
1032
1033
    #ifdef USE_CUDA_EXP
    cuda_bag_data_indices_.Clear();
    #endif  // USE_CUDA_EXP
1034
    bagging_runner_.ReSize(0);
Guolin Ke's avatar
Guolin Ke committed
1035
    is_use_subset_ = false;
1036
  }
wxchan's avatar
wxchan committed
1037
1038
}

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