serial_tree_learner.cpp 35.4 KB
Newer Older
Guolin Ke's avatar
Guolin Ke committed
1
2
3
#include "serial_tree_learner.h"

#include <LightGBM/utils/array_args.h>
4
5
#include <LightGBM/network.h>
#include <LightGBM/objective_function.h>
Guolin Ke's avatar
Guolin Ke committed
6
7
8

#include <algorithm>
#include <vector>
9
#include <queue>
Guolin Ke's avatar
Guolin Ke committed
10
11
12

namespace LightGBM {

Guolin Ke's avatar
Guolin Ke committed
13
14
15
16
17
18
19
20
21
#ifdef TIMETAG
std::chrono::duration<double, std::milli> init_train_time;
std::chrono::duration<double, std::milli> init_split_time;
std::chrono::duration<double, std::milli> hist_time;
std::chrono::duration<double, std::milli> find_split_time;
std::chrono::duration<double, std::milli> split_time;
std::chrono::duration<double, std::milli> ordered_bin_time;
#endif // TIMETAG

Guolin Ke's avatar
Guolin Ke committed
22
23
24
SerialTreeLearner::SerialTreeLearner(const Config* config)
  :config_(config) {
  random_ = Random(config_->feature_fraction_seed);
25
26
  #pragma omp parallel
  #pragma omp master
Guolin Ke's avatar
Guolin Ke committed
27
28
29
  {
    num_threads_ = omp_get_num_threads();
  }
Guolin Ke's avatar
Guolin Ke committed
30
31
32
}

SerialTreeLearner::~SerialTreeLearner() {
33
  #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
34
35
36
37
38
39
  Log::Info("SerialTreeLearner::init_train costs %f", init_train_time * 1e-3);
  Log::Info("SerialTreeLearner::init_split costs %f", init_split_time * 1e-3);
  Log::Info("SerialTreeLearner::hist_build costs %f", hist_time * 1e-3);
  Log::Info("SerialTreeLearner::find_split costs %f", find_split_time * 1e-3);
  Log::Info("SerialTreeLearner::split costs %f", split_time * 1e-3);
  Log::Info("SerialTreeLearner::ordered_bin costs %f", ordered_bin_time * 1e-3);
40
  #endif
Guolin Ke's avatar
Guolin Ke committed
41
42
}

43
void SerialTreeLearner::Init(const Dataset* train_data, bool is_constant_hessian) {
Guolin Ke's avatar
Guolin Ke committed
44
45
46
  train_data_ = train_data;
  num_data_ = train_data_->num_data();
  num_features_ = train_data_->num_features();
47
  is_constant_hessian_ = is_constant_hessian;
48
49
  int max_cache_size = 0;
  // Get the max size of pool
Guolin Ke's avatar
Guolin Ke committed
50
51
  if (config_->histogram_pool_size <= 0) {
    max_cache_size = config_->num_leaves;
52
53
54
  } else {
    size_t total_histogram_size = 0;
    for (int i = 0; i < train_data_->num_features(); ++i) {
Guolin Ke's avatar
Guolin Ke committed
55
      total_histogram_size += sizeof(HistogramBinEntry) * train_data_->FeatureNumBin(i);
56
    }
Guolin Ke's avatar
Guolin Ke committed
57
    max_cache_size = static_cast<int>(config_->histogram_pool_size * 1024 * 1024 / total_histogram_size);
58
59
  }
  // at least need 2 leaves
Guolin Ke's avatar
Guolin Ke committed
60
  max_cache_size = std::max(2, max_cache_size);
Guolin Ke's avatar
Guolin Ke committed
61
  max_cache_size = std::min(max_cache_size, config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
62

Guolin Ke's avatar
Guolin Ke committed
63
  histogram_pool_.DynamicChangeSize(train_data_, config_, max_cache_size, config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
64
  // push split information for all leaves
Guolin Ke's avatar
Guolin Ke committed
65
  best_split_per_leaf_.resize(config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
66

Guolin Ke's avatar
Guolin Ke committed
67
  // get ordered bin
Guolin Ke's avatar
Guolin Ke committed
68
  train_data_->CreateOrderedBins(&ordered_bins_);
Guolin Ke's avatar
Guolin Ke committed
69
70

  // check existing for ordered bin
Guolin Ke's avatar
Guolin Ke committed
71
  for (int i = 0; i < static_cast<int>(ordered_bins_.size()); ++i) {
Guolin Ke's avatar
Guolin Ke committed
72
73
74
75
76
    if (ordered_bins_[i] != nullptr) {
      has_ordered_bin_ = true;
      break;
    }
  }
wxchan's avatar
wxchan committed
77
  // initialize splits for leaf
Guolin Ke's avatar
Guolin Ke committed
78
79
  smaller_leaf_splits_.reset(new LeafSplits(train_data_->num_data()));
  larger_leaf_splits_.reset(new LeafSplits(train_data_->num_data()));
Guolin Ke's avatar
Guolin Ke committed
80
81

  // initialize data partition
Guolin Ke's avatar
Guolin Ke committed
82
  data_partition_.reset(new DataPartition(num_data_, config_->num_leaves));
Guolin Ke's avatar
Guolin Ke committed
83
  is_feature_used_.resize(num_features_);
84
  valid_feature_indices_ = train_data_->ValidFeatureIndices();
Guolin Ke's avatar
Guolin Ke committed
85
  // initialize ordered gradients and hessians
Guolin Ke's avatar
Guolin Ke committed
86
87
88
  ordered_gradients_.resize(num_data_);
  ordered_hessians_.resize(num_data_);
  // if has ordered bin, need to allocate a buffer to fast split
Guolin Ke's avatar
Guolin Ke committed
89
  if (has_ordered_bin_) {
Guolin Ke's avatar
Guolin Ke committed
90
    is_data_in_leaf_.resize(num_data_);
91
    std::fill(is_data_in_leaf_.begin(), is_data_in_leaf_.end(), static_cast<char>(0));
Guolin Ke's avatar
Guolin Ke committed
92
    ordered_bin_indices_.clear();
Guolin Ke's avatar
Guolin Ke committed
93
94
    for (int i = 0; i < static_cast<int>(ordered_bins_.size()); i++) {
      if (ordered_bins_[i] != nullptr) {
Guolin Ke's avatar
Guolin Ke committed
95
        ordered_bin_indices_.push_back(i);
Guolin Ke's avatar
Guolin Ke committed
96
97
      }
    }
Guolin Ke's avatar
Guolin Ke committed
98
  }
Guolin Ke's avatar
Guolin Ke committed
99
  Log::Info("Number of data: %d, number of used features: %d", num_data_, num_features_);
Guolin Ke's avatar
Guolin Ke committed
100
101
}

Guolin Ke's avatar
Guolin Ke committed
102
103
104
void SerialTreeLearner::ResetTrainingData(const Dataset* train_data) {
  train_data_ = train_data;
  num_data_ = train_data_->num_data();
Guolin Ke's avatar
Guolin Ke committed
105
  CHECK(num_features_ == train_data_->num_features());
Guolin Ke's avatar
Guolin Ke committed
106
107

  // get ordered bin
Guolin Ke's avatar
Guolin Ke committed
108
109
  train_data_->CreateOrderedBins(&ordered_bins_);

Guolin Ke's avatar
Guolin Ke committed
110
111
112
113
114
115
116
117
118
119
120
121
122
  // initialize splits for leaf
  smaller_leaf_splits_->ResetNumData(num_data_);
  larger_leaf_splits_->ResetNumData(num_data_);

  // initialize data partition
  data_partition_->ResetNumData(num_data_);

  // initialize ordered gradients and hessians
  ordered_gradients_.resize(num_data_);
  ordered_hessians_.resize(num_data_);
  // if has ordered bin, need to allocate a buffer to fast split
  if (has_ordered_bin_) {
    is_data_in_leaf_.resize(num_data_);
123
    std::fill(is_data_in_leaf_.begin(), is_data_in_leaf_.end(), static_cast<char>(0));
Guolin Ke's avatar
Guolin Ke committed
124
125
  }
}
Guolin Ke's avatar
Guolin Ke committed
126

Guolin Ke's avatar
Guolin Ke committed
127
128
129
void SerialTreeLearner::ResetConfig(const Config* config) {
  if (config_->num_leaves != config->num_leaves) {
    config_ = config;
Guolin Ke's avatar
Guolin Ke committed
130
131
    int max_cache_size = 0;
    // Get the max size of pool
Guolin Ke's avatar
Guolin Ke committed
132
133
    if (config->histogram_pool_size <= 0) {
      max_cache_size = config_->num_leaves;
Guolin Ke's avatar
Guolin Ke committed
134
135
136
    } else {
      size_t total_histogram_size = 0;
      for (int i = 0; i < train_data_->num_features(); ++i) {
Guolin Ke's avatar
Guolin Ke committed
137
        total_histogram_size += sizeof(HistogramBinEntry) * train_data_->FeatureNumBin(i);
Guolin Ke's avatar
Guolin Ke committed
138
      }
Guolin Ke's avatar
Guolin Ke committed
139
      max_cache_size = static_cast<int>(config_->histogram_pool_size * 1024 * 1024 / total_histogram_size);
Guolin Ke's avatar
Guolin Ke committed
140
141
142
    }
    // at least need 2 leaves
    max_cache_size = std::max(2, max_cache_size);
Guolin Ke's avatar
Guolin Ke committed
143
144
    max_cache_size = std::min(max_cache_size, config_->num_leaves);
    histogram_pool_.DynamicChangeSize(train_data_, config_, max_cache_size, config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
145
146

    // push split information for all leaves
Guolin Ke's avatar
Guolin Ke committed
147
148
    best_split_per_leaf_.resize(config_->num_leaves);
    data_partition_->ResetLeaves(config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
149
  } else {
Guolin Ke's avatar
Guolin Ke committed
150
    config_ = config;
Guolin Ke's avatar
Guolin Ke committed
151
152
  }

Guolin Ke's avatar
Guolin Ke committed
153
  histogram_pool_.ResetConfig(config_);
Guolin Ke's avatar
Guolin Ke committed
154
155
}

156
Tree* SerialTreeLearner::Train(const score_t* gradients, const score_t *hessians, bool is_constant_hessian, Json& forced_split_json) {
Guolin Ke's avatar
Guolin Ke committed
157
158
  gradients_ = gradients;
  hessians_ = hessians;
159
  is_constant_hessian_ = is_constant_hessian;
160
  #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
161
  auto start_time = std::chrono::steady_clock::now();
162
  #endif
Guolin Ke's avatar
Guolin Ke committed
163
164
  // some initial works before training
  BeforeTrain();
Guolin Ke's avatar
Guolin Ke committed
165

166
  #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
167
  init_train_time += std::chrono::steady_clock::now() - start_time;
168
  #endif
Guolin Ke's avatar
Guolin Ke committed
169

Guolin Ke's avatar
Guolin Ke committed
170
  auto tree = std::unique_ptr<Tree>(new Tree(config_->num_leaves));
Guolin Ke's avatar
Guolin Ke committed
171
172
  // root leaf
  int left_leaf = 0;
173
  int cur_depth = 1;
Guolin Ke's avatar
Guolin Ke committed
174
175
  // only root leaf can be splitted on first time
  int right_leaf = -1;
176
177
178
179
180
181
182
183

  int init_splits = 0;
  bool aborted_last_force_split = false;
  if (!forced_split_json.is_null()) {
    init_splits = ForceSplits(tree.get(), forced_split_json, &left_leaf,
                              &right_leaf, &cur_depth, &aborted_last_force_split);
  }

Guolin Ke's avatar
Guolin Ke committed
184
  for (int split = init_splits; split < config_->num_leaves - 1; ++split) {
185
    #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
186
    start_time = std::chrono::steady_clock::now();
187
    #endif
Guolin Ke's avatar
Guolin Ke committed
188
    // some initial works before finding best split
189
    if (!aborted_last_force_split && BeforeFindBestSplit(tree.get(), left_leaf, right_leaf)) {
190
      #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
191
      init_split_time += std::chrono::steady_clock::now() - start_time;
192
      #endif
Guolin Ke's avatar
Guolin Ke committed
193
      // find best threshold for every feature
Guolin Ke's avatar
Guolin Ke committed
194
      FindBestSplits();
195
196
    } else if (aborted_last_force_split) {
      aborted_last_force_split = false;
Guolin Ke's avatar
Guolin Ke committed
197
    }
198

Guolin Ke's avatar
Guolin Ke committed
199
200
201
202
203
204
    // Get a leaf with max split gain
    int best_leaf = static_cast<int>(ArrayArgs<SplitInfo>::ArgMax(best_split_per_leaf_));
    // Get split information for best leaf
    const SplitInfo& best_leaf_SplitInfo = best_split_per_leaf_[best_leaf];
    // cannot split, quit
    if (best_leaf_SplitInfo.gain <= 0.0) {
Guolin Ke's avatar
Guolin Ke committed
205
      Log::Warning("No further splits with positive gain, best gain: %f", best_leaf_SplitInfo.gain);
Guolin Ke's avatar
Guolin Ke committed
206
207
      break;
    }
208
    #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
209
    start_time = std::chrono::steady_clock::now();
210
    #endif
Guolin Ke's avatar
Guolin Ke committed
211
    // split tree with best leaf
Guolin Ke's avatar
Guolin Ke committed
212
    Split(tree.get(), best_leaf, &left_leaf, &right_leaf);
213
    #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
214
    split_time += std::chrono::steady_clock::now() - start_time;
215
    #endif
216
    cur_depth = std::max(cur_depth, tree->leaf_depth(left_leaf));
Guolin Ke's avatar
Guolin Ke committed
217
  }
218
  Log::Debug("Trained a tree with leaves = %d and max_depth = %d", tree->num_leaves(), cur_depth);
Guolin Ke's avatar
Guolin Ke committed
219
  return tree.release();
Guolin Ke's avatar
Guolin Ke committed
220
221
}

222
Tree* SerialTreeLearner::FitByExistingTree(const Tree* old_tree, const score_t* gradients, const score_t *hessians) const {
Guolin Ke's avatar
Guolin Ke committed
223
224
  auto tree = std::unique_ptr<Tree>(new Tree(*old_tree));
  CHECK(data_partition_->num_leaves() >= tree->num_leaves());
225
  OMP_INIT_EX();
Guolin Ke's avatar
Guolin Ke committed
226
  #pragma omp parallel for schedule(static)
227
  for (int i = 0; i < tree->num_leaves(); ++i) {
228
    OMP_LOOP_EX_BEGIN();
Guolin Ke's avatar
Guolin Ke committed
229
230
231
    data_size_t cnt_leaf_data = 0;
    auto tmp_idx = data_partition_->GetIndexOnLeaf(i, &cnt_leaf_data);
    double sum_grad = 0.0f;
232
    double sum_hess = kEpsilon;
Guolin Ke's avatar
Guolin Ke committed
233
234
235
236
237
238
    for (data_size_t j = 0; j < cnt_leaf_data; ++j) {
      auto idx = tmp_idx[j];
      sum_grad += gradients[idx];
      sum_hess += hessians[idx];
    }
    double output = FeatureHistogram::CalculateSplittedLeafOutput(sum_grad, sum_hess,
Guolin Ke's avatar
Guolin Ke committed
239
                                                                  config_->lambda_l1, config_->lambda_l2, config_->max_delta_step);
240
    tree->SetLeafOutput(i, output* tree->shrinkage());
241
    OMP_LOOP_EX_END();
Guolin Ke's avatar
Guolin Ke committed
242
  }
243
  OMP_THROW_EX();
Guolin Ke's avatar
Guolin Ke committed
244
245
246
  return tree.release();
}

247
248
249
250
251
Tree* SerialTreeLearner::FitByExistingTree(const Tree* old_tree, const std::vector<int>& leaf_pred, const score_t* gradients, const score_t *hessians) {
  data_partition_->ResetByLeafPred(leaf_pred, old_tree->num_leaves());
  return FitByExistingTree(old_tree, gradients, hessians);
}

Guolin Ke's avatar
Guolin Ke committed
252
void SerialTreeLearner::BeforeTrain() {
Guolin Ke's avatar
Guolin Ke committed
253

254
255
  // reset histogram pool
  histogram_pool_.ResetMap();
Guolin Ke's avatar
Guolin Ke committed
256

Guolin Ke's avatar
Guolin Ke committed
257
258
  if (config_->feature_fraction < 1) {
    int used_feature_cnt = static_cast<int>(valid_feature_indices_.size()*config_->feature_fraction);
259
260
    // at least use one feature
    used_feature_cnt = std::max(used_feature_cnt, 1);
Guolin Ke's avatar
Guolin Ke committed
261
262
263
    // initialize used features
    std::memset(is_feature_used_.data(), 0, sizeof(int8_t) * num_features_);
    // Get used feature at current tree
Guolin Ke's avatar
Guolin Ke committed
264
    auto sampled_indices = random_.Sample(static_cast<int>(valid_feature_indices_.size()), used_feature_cnt);
265
    int omp_loop_size = static_cast<int>(sampled_indices.size());
Guolin Ke's avatar
Guolin Ke committed
266
267
    #pragma omp parallel for schedule(static, 512) if (omp_loop_size >= 1024)
    for (int i = 0; i < omp_loop_size; ++i) {
268
269
270
      int used_feature = valid_feature_indices_[sampled_indices[i]];
      int inner_feature_index = train_data_->InnerFeatureIndex(used_feature);
      CHECK(inner_feature_index >= 0);
Guolin Ke's avatar
Guolin Ke committed
271
      is_feature_used_[inner_feature_index] = 1;
Guolin Ke's avatar
Guolin Ke committed
272
273
    }
  } else {
Guolin Ke's avatar
Guolin Ke committed
274
    #pragma omp parallel for schedule(static, 512) if (num_features_ >= 1024)
Guolin Ke's avatar
Guolin Ke committed
275
276
277
    for (int i = 0; i < num_features_; ++i) {
      is_feature_used_[i] = 1;
    }
Guolin Ke's avatar
Guolin Ke committed
278
  }
279

Guolin Ke's avatar
Guolin Ke committed
280
281
282
283
  // initialize data partition
  data_partition_->Init();

  // reset the splits for leaves
Guolin Ke's avatar
Guolin Ke committed
284
  for (int i = 0; i < config_->num_leaves; ++i) {
Guolin Ke's avatar
Guolin Ke committed
285
286
287
288
289
290
291
    best_split_per_leaf_[i].Reset();
  }

  // Sumup for root
  if (data_partition_->leaf_count(0) == num_data_) {
    // use all data
    smaller_leaf_splits_->Init(gradients_, hessians_);
Guolin Ke's avatar
Guolin Ke committed
292

Guolin Ke's avatar
Guolin Ke committed
293
294
  } else {
    // use bagging, only use part of data
Guolin Ke's avatar
Guolin Ke committed
295
    smaller_leaf_splits_->Init(0, data_partition_.get(), gradients_, hessians_);
Guolin Ke's avatar
Guolin Ke committed
296
297
298
299
300
301
  }

  larger_leaf_splits_->Init();

  // if has ordered bin, need to initialize the ordered bin
  if (has_ordered_bin_) {
302
    #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
303
    auto start_time = std::chrono::steady_clock::now();
304
    #endif
Guolin Ke's avatar
Guolin Ke committed
305
306
    if (data_partition_->leaf_count(0) == num_data_) {
      // use all data, pass nullptr
307
308
      OMP_INIT_EX();
      #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
309
      for (int i = 0; i < static_cast<int>(ordered_bin_indices_.size()); ++i) {
310
        OMP_LOOP_EX_BEGIN();
Guolin Ke's avatar
Guolin Ke committed
311
        ordered_bins_[ordered_bin_indices_[i]]->Init(nullptr, config_->num_leaves);
312
        OMP_LOOP_EX_END();
Guolin Ke's avatar
Guolin Ke committed
313
      }
314
      OMP_THROW_EX();
Guolin Ke's avatar
Guolin Ke committed
315
316
317
318
319
320
321
    } else {
      // bagging, only use part of data

      // mark used data
      const data_size_t* indices = data_partition_->indices();
      data_size_t begin = data_partition_->leaf_begin(0);
      data_size_t end = begin + data_partition_->leaf_count(0);
Guolin Ke's avatar
Guolin Ke committed
322
323
      data_size_t loop_size = end - begin;
      #pragma omp parallel for schedule(static, 512) if(loop_size >= 1024)
Guolin Ke's avatar
Guolin Ke committed
324
325
326
      for (data_size_t i = begin; i < end; ++i) {
        is_data_in_leaf_[indices[i]] = 1;
      }
327
      OMP_INIT_EX();
Guolin Ke's avatar
Guolin Ke committed
328
      // initialize ordered bin
329
      #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
330
      for (int i = 0; i < static_cast<int>(ordered_bin_indices_.size()); ++i) {
331
        OMP_LOOP_EX_BEGIN();
Guolin Ke's avatar
Guolin Ke committed
332
        ordered_bins_[ordered_bin_indices_[i]]->Init(is_data_in_leaf_.data(), config_->num_leaves);
333
        OMP_LOOP_EX_END();
Guolin Ke's avatar
Guolin Ke committed
334
      }
335
      OMP_THROW_EX();
Guolin Ke's avatar
Guolin Ke committed
336
      #pragma omp parallel for schedule(static, 512) if(loop_size >= 1024)
Guolin Ke's avatar
Guolin Ke committed
337
338
339
      for (data_size_t i = begin; i < end; ++i) {
        is_data_in_leaf_[indices[i]] = 0;
      }
Guolin Ke's avatar
Guolin Ke committed
340
    }
341
    #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
342
    ordered_bin_time += std::chrono::steady_clock::now() - start_time;
343
    #endif
Guolin Ke's avatar
Guolin Ke committed
344
345
346
  }
}

Guolin Ke's avatar
Guolin Ke committed
347
bool SerialTreeLearner::BeforeFindBestSplit(const Tree* tree, int left_leaf, int right_leaf) {
Guolin Ke's avatar
Guolin Ke committed
348
  // check depth of current leaf
Guolin Ke's avatar
Guolin Ke committed
349
  if (config_->max_depth > 0) {
Guolin Ke's avatar
Guolin Ke committed
350
    // only need to check left leaf, since right leaf is in same level of left leaf
Guolin Ke's avatar
Guolin Ke committed
351
    if (tree->leaf_depth(left_leaf) >= config_->max_depth) {
Guolin Ke's avatar
Guolin Ke committed
352
353
354
355
356
357
358
      best_split_per_leaf_[left_leaf].gain = kMinScore;
      if (right_leaf >= 0) {
        best_split_per_leaf_[right_leaf].gain = kMinScore;
      }
      return false;
    }
  }
Guolin Ke's avatar
Guolin Ke committed
359
360
361
  data_size_t num_data_in_left_child = GetGlobalDataCountInLeaf(left_leaf);
  data_size_t num_data_in_right_child = GetGlobalDataCountInLeaf(right_leaf);
  // no enough data to continue
Guolin Ke's avatar
Guolin Ke committed
362
363
  if (num_data_in_right_child < static_cast<data_size_t>(config_->min_data_in_leaf * 2)
      && num_data_in_left_child < static_cast<data_size_t>(config_->min_data_in_leaf * 2)) {
Guolin Ke's avatar
Guolin Ke committed
364
365
366
367
368
369
    best_split_per_leaf_[left_leaf].gain = kMinScore;
    if (right_leaf >= 0) {
      best_split_per_leaf_[right_leaf].gain = kMinScore;
    }
    return false;
  }
370
  parent_leaf_histogram_array_ = nullptr;
Guolin Ke's avatar
Guolin Ke committed
371
372
  // only have root
  if (right_leaf < 0) {
373
    histogram_pool_.Get(left_leaf, &smaller_leaf_histogram_array_);
Guolin Ke's avatar
Guolin Ke committed
374
375
    larger_leaf_histogram_array_ = nullptr;
  } else if (num_data_in_left_child < num_data_in_right_child) {
Hui Xue's avatar
Hui Xue committed
376
    // put parent(left) leaf's histograms into larger leaf's histograms
377
378
379
    if (histogram_pool_.Get(left_leaf, &larger_leaf_histogram_array_)) { parent_leaf_histogram_array_ = larger_leaf_histogram_array_; }
    histogram_pool_.Move(left_leaf, right_leaf);
    histogram_pool_.Get(left_leaf, &smaller_leaf_histogram_array_);
Guolin Ke's avatar
Guolin Ke committed
380
  } else {
Hui Xue's avatar
Hui Xue committed
381
    // put parent(left) leaf's histograms to larger leaf's histograms
382
383
    if (histogram_pool_.Get(left_leaf, &larger_leaf_histogram_array_)) { parent_leaf_histogram_array_ = larger_leaf_histogram_array_; }
    histogram_pool_.Get(right_leaf, &smaller_leaf_histogram_array_);
Guolin Ke's avatar
Guolin Ke committed
384
385
386
  }
  // split for the ordered bin
  if (has_ordered_bin_ && right_leaf >= 0) {
387
    #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
388
    auto start_time = std::chrono::steady_clock::now();
389
    #endif
Guolin Ke's avatar
Guolin Ke committed
390
391
    // mark data that at left-leaf
    const data_size_t* indices = data_partition_->indices();
Guolin Ke's avatar
Guolin Ke committed
392
393
394
    const auto left_cnt = data_partition_->leaf_count(left_leaf);
    const auto right_cnt = data_partition_->leaf_count(right_leaf);
    char mark = 1;
Guolin Ke's avatar
Guolin Ke committed
395
    data_size_t begin = data_partition_->leaf_begin(left_leaf);
Guolin Ke's avatar
Guolin Ke committed
396
    data_size_t end = begin + left_cnt;
Guolin Ke's avatar
Guolin Ke committed
397
    data_size_t loop_size = end - begin;
Guolin Ke's avatar
Guolin Ke committed
398
399
400
401
402
    if (left_cnt > right_cnt) {
      begin = data_partition_->leaf_begin(right_leaf);
      end = begin + right_cnt;
      mark = 0;
    }
Guolin Ke's avatar
Guolin Ke committed
403
    #pragma omp parallel for schedule(static, 512) if(loop_size >= 1024)
Guolin Ke's avatar
Guolin Ke committed
404
405
406
    for (data_size_t i = begin; i < end; ++i) {
      is_data_in_leaf_[indices[i]] = 1;
    }
407
    OMP_INIT_EX();
Guolin Ke's avatar
Guolin Ke committed
408
    // split the ordered bin
409
    #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
410
    for (int i = 0; i < static_cast<int>(ordered_bin_indices_.size()); ++i) {
411
      OMP_LOOP_EX_BEGIN();
Guolin Ke's avatar
Guolin Ke committed
412
      ordered_bins_[ordered_bin_indices_[i]]->Split(left_leaf, right_leaf, is_data_in_leaf_.data(), mark);
413
      OMP_LOOP_EX_END();
Guolin Ke's avatar
Guolin Ke committed
414
    }
415
    OMP_THROW_EX();
Guolin Ke's avatar
Guolin Ke committed
416
    #pragma omp parallel for schedule(static, 512) if(loop_size >= 1024)
Guolin Ke's avatar
Guolin Ke committed
417
418
419
    for (data_size_t i = begin; i < end; ++i) {
      is_data_in_leaf_[indices[i]] = 0;
    }
420
    #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
421
    ordered_bin_time += std::chrono::steady_clock::now() - start_time;
422
    #endif
Guolin Ke's avatar
Guolin Ke committed
423
424
425
426
  }
  return true;
}

Guolin Ke's avatar
Guolin Ke committed
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
void SerialTreeLearner::FindBestSplits() {
  std::vector<int8_t> is_feature_used(num_features_, 0);
  #pragma omp parallel for schedule(static,1024) if (num_features_ >= 2048)
  for (int feature_index = 0; feature_index < num_features_; ++feature_index) {
    if (!is_feature_used_[feature_index]) continue;
    if (parent_leaf_histogram_array_ != nullptr
        && !parent_leaf_histogram_array_[feature_index].is_splittable()) {
      smaller_leaf_histogram_array_[feature_index].set_is_splittable(false);
      continue;
    }
    is_feature_used[feature_index] = 1;
  }
  bool use_subtract = parent_leaf_histogram_array_ != nullptr;
  ConstructHistograms(is_feature_used, use_subtract);
  FindBestSplitsFromHistograms(is_feature_used, use_subtract);
}

444
void SerialTreeLearner::ConstructHistograms(const std::vector<int8_t>& is_feature_used, bool use_subtract) {
Guolin Ke's avatar
Guolin Ke committed
445
  #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
446
  auto start_time = std::chrono::steady_clock::now();
Guolin Ke's avatar
Guolin Ke committed
447
  #endif
Guolin Ke's avatar
Guolin Ke committed
448
449
450
  // construct smaller leaf
  HistogramBinEntry* ptr_smaller_leaf_hist_data = smaller_leaf_histogram_array_[0].RawData() - 1;
  train_data_->ConstructHistograms(is_feature_used,
Guolin Ke's avatar
Guolin Ke committed
451
452
453
                                   smaller_leaf_splits_->data_indices(), smaller_leaf_splits_->num_data_in_leaf(),
                                   smaller_leaf_splits_->LeafIndex(),
                                   ordered_bins_, gradients_, hessians_,
454
                                   ordered_gradients_.data(), ordered_hessians_.data(), is_constant_hessian_,
Guolin Ke's avatar
Guolin Ke committed
455
                                   ptr_smaller_leaf_hist_data);
Guolin Ke's avatar
Guolin Ke committed
456
457
458
459
460

  if (larger_leaf_histogram_array_ != nullptr && !use_subtract) {
    // construct larger leaf
    HistogramBinEntry* ptr_larger_leaf_hist_data = larger_leaf_histogram_array_[0].RawData() - 1;
    train_data_->ConstructHistograms(is_feature_used,
Guolin Ke's avatar
Guolin Ke committed
461
462
463
                                     larger_leaf_splits_->data_indices(), larger_leaf_splits_->num_data_in_leaf(),
                                     larger_leaf_splits_->LeafIndex(),
                                     ordered_bins_, gradients_, hessians_,
464
                                     ordered_gradients_.data(), ordered_hessians_.data(), is_constant_hessian_,
Guolin Ke's avatar
Guolin Ke committed
465
                                     ptr_larger_leaf_hist_data);
Guolin Ke's avatar
Guolin Ke committed
466
  }
Guolin Ke's avatar
Guolin Ke committed
467
  #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
468
  hist_time += std::chrono::steady_clock::now() - start_time;
Guolin Ke's avatar
Guolin Ke committed
469
  #endif
470
471
}

Guolin Ke's avatar
Guolin Ke committed
472
void SerialTreeLearner::FindBestSplitsFromHistograms(const std::vector<int8_t>& is_feature_used, bool use_subtract) {
473
  #ifdef TIMETAG
474
  auto start_time = std::chrono::steady_clock::now();
475
  #endif
Guolin Ke's avatar
Guolin Ke committed
476
477
  std::vector<SplitInfo> smaller_best(num_threads_);
  std::vector<SplitInfo> larger_best(num_threads_);
478
  OMP_INIT_EX();
Guolin Ke's avatar
Guolin Ke committed
479
  // find splits
480
  #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
481
  for (int feature_index = 0; feature_index < num_features_; ++feature_index) {
482
    OMP_LOOP_EX_BEGIN();
Guolin Ke's avatar
Guolin Ke committed
483
484
485
    if (!is_feature_used[feature_index]) { continue; }
    const int tid = omp_get_thread_num();
    SplitInfo smaller_split;
Guolin Ke's avatar
Guolin Ke committed
486
487
488
489
    train_data_->FixHistogram(feature_index,
                              smaller_leaf_splits_->sum_gradients(), smaller_leaf_splits_->sum_hessians(),
                              smaller_leaf_splits_->num_data_in_leaf(),
                              smaller_leaf_histogram_array_[feature_index].RawData());
490
    int real_fidx = train_data_->RealFeatureIndex(feature_index);
Guolin Ke's avatar
Guolin Ke committed
491
492
493
494
    smaller_leaf_histogram_array_[feature_index].FindBestThreshold(
      smaller_leaf_splits_->sum_gradients(),
      smaller_leaf_splits_->sum_hessians(),
      smaller_leaf_splits_->num_data_in_leaf(),
Guolin Ke's avatar
Guolin Ke committed
495
496
      smaller_leaf_splits_->min_constraint(),
      smaller_leaf_splits_->max_constraint(),
Guolin Ke's avatar
Guolin Ke committed
497
      &smaller_split);
498
499
    smaller_split.feature = real_fidx;
    if (smaller_split > smaller_best[tid]) {
Guolin Ke's avatar
Guolin Ke committed
500
501
      smaller_best[tid] = smaller_split;
    }
Guolin Ke's avatar
Guolin Ke committed
502
    // only has root leaf
Guolin Ke's avatar
Guolin Ke committed
503
    if (larger_leaf_splits_ == nullptr || larger_leaf_splits_->LeafIndex() < 0) { continue; }
Guolin Ke's avatar
Guolin Ke committed
504

Guolin Ke's avatar
Guolin Ke committed
505
    if (use_subtract) {
506
507
      larger_leaf_histogram_array_[feature_index].Subtract(smaller_leaf_histogram_array_[feature_index]);
    } else {
Guolin Ke's avatar
Guolin Ke committed
508
      train_data_->FixHistogram(feature_index, larger_leaf_splits_->sum_gradients(), larger_leaf_splits_->sum_hessians(),
Guolin Ke's avatar
Guolin Ke committed
509
510
                                larger_leaf_splits_->num_data_in_leaf(),
                                larger_leaf_histogram_array_[feature_index].RawData());
511
    }
Guolin Ke's avatar
Guolin Ke committed
512
    SplitInfo larger_split;
Guolin Ke's avatar
Guolin Ke committed
513
    // find best threshold for larger child
Guolin Ke's avatar
Guolin Ke committed
514
515
516
517
    larger_leaf_histogram_array_[feature_index].FindBestThreshold(
      larger_leaf_splits_->sum_gradients(),
      larger_leaf_splits_->sum_hessians(),
      larger_leaf_splits_->num_data_in_leaf(),
Guolin Ke's avatar
Guolin Ke committed
518
519
      larger_leaf_splits_->min_constraint(),
      larger_leaf_splits_->max_constraint(),
Guolin Ke's avatar
Guolin Ke committed
520
      &larger_split);
521
522
    larger_split.feature = real_fidx;
    if (larger_split > larger_best[tid]) {
Guolin Ke's avatar
Guolin Ke committed
523
      larger_best[tid] = larger_split;
Guolin Ke's avatar
Guolin Ke committed
524
    }
525
    OMP_LOOP_EX_END();
Guolin Ke's avatar
Guolin Ke committed
526
  }
527
  OMP_THROW_EX();
Guolin Ke's avatar
Guolin Ke committed
528
529
530
531
532
533
534
535
536
537

  auto smaller_best_idx = ArrayArgs<SplitInfo>::ArgMax(smaller_best);
  int leaf = smaller_leaf_splits_->LeafIndex();
  best_split_per_leaf_[leaf] = smaller_best[smaller_best_idx];

  if (larger_leaf_splits_ != nullptr && larger_leaf_splits_->LeafIndex() >= 0) {
    leaf = larger_leaf_splits_->LeafIndex();
    auto larger_best_idx = ArrayArgs<SplitInfo>::ArgMax(larger_best);
    best_split_per_leaf_[leaf] = larger_best[larger_best_idx];
  }
538
  #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
539
  find_split_time += std::chrono::steady_clock::now() - start_time;
540
  #endif
Guolin Ke's avatar
Guolin Ke committed
541
542
}

543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
int32_t SerialTreeLearner::ForceSplits(Tree* tree, Json& forced_split_json, int* left_leaf,
                                       int* right_leaf, int *cur_depth, 
                                       bool *aborted_last_force_split) {
  int32_t result_count = 0;
  // start at root leaf
  *left_leaf = 0;
  std::queue<std::pair<Json, int>> q;
  Json left = forced_split_json;
  Json right;
  bool left_smaller = true;
  std::unordered_map<int, SplitInfo> forceSplitMap;
  q.push(std::make_pair(forced_split_json, *left_leaf));
  while(!q.empty()) {

    // before processing next node from queue, store info for current left/right leaf
    // store "best split" for left and right, even if they might be overwritten by forced split
    if (BeforeFindBestSplit(tree, *left_leaf, *right_leaf)) {
      FindBestSplits();
    }
    // then, compute own splits
    SplitInfo left_split;
    SplitInfo right_split;

    if (!left.is_null()) {
      const int left_feature = left["feature"].int_value();
      const double left_threshold_double = left["threshold"].number_value();
      const int left_inner_feature_index = train_data_->InnerFeatureIndex(left_feature);
      const uint32_t left_threshold = train_data_->BinThreshold(
              left_inner_feature_index, left_threshold_double);
      auto leaf_histogram_array = (left_smaller) ? smaller_leaf_histogram_array_ : larger_leaf_histogram_array_;
      auto left_leaf_splits = (left_smaller) ? smaller_leaf_splits_.get() : larger_leaf_splits_.get();
      leaf_histogram_array[left_inner_feature_index].GatherInfoForThreshold(
              left_leaf_splits->sum_gradients(),
              left_leaf_splits->sum_hessians(),
              left_threshold,
              left_leaf_splits->num_data_in_leaf(),
              &left_split);
      left_split.feature = left_feature;
      forceSplitMap[*left_leaf] = left_split;
      if (left_split.gain < 0) {
        forceSplitMap.erase(*left_leaf);
      }
    }

    if (!right.is_null()) {
      const int right_feature = right["feature"].int_value();
      const double right_threshold_double = right["threshold"].number_value();
      const int right_inner_feature_index = train_data_->InnerFeatureIndex(right_feature);
      const uint32_t right_threshold = train_data_->BinThreshold(
              right_inner_feature_index, right_threshold_double);
      auto leaf_histogram_array = (left_smaller) ? larger_leaf_histogram_array_ : smaller_leaf_histogram_array_;
      auto right_leaf_splits = (left_smaller) ? larger_leaf_splits_.get() : smaller_leaf_splits_.get();
      leaf_histogram_array[right_inner_feature_index].GatherInfoForThreshold(
        right_leaf_splits->sum_gradients(),
        right_leaf_splits->sum_hessians(),
        right_threshold,
        right_leaf_splits->num_data_in_leaf(),
        &right_split);
      right_split.feature = right_feature;
      forceSplitMap[*right_leaf] = right_split;
      if (right_split.gain < 0) {
        forceSplitMap.erase(*right_leaf);
      }
    }

    std::pair<Json, int> pair = q.front();
    q.pop();
    int current_leaf = pair.second;
    // split info should exist because searching in bfs fashion - should have added from parent
    if (forceSplitMap.find(current_leaf) == forceSplitMap.end()) {
        *aborted_last_force_split = true;
        break;
    }
    SplitInfo current_split_info = forceSplitMap[current_leaf];
    const int inner_feature_index = train_data_->InnerFeatureIndex(
            current_split_info.feature);
    auto threshold_double = train_data_->RealThreshold(
            inner_feature_index, current_split_info.threshold);

    // split tree, will return right leaf
    *left_leaf = current_leaf;
    if (train_data_->FeatureBinMapper(inner_feature_index)->bin_type() == BinType::NumericalBin) {
      *right_leaf = tree->Split(current_leaf,
                                inner_feature_index,
                                current_split_info.feature,
                                current_split_info.threshold,
                                threshold_double,
                                static_cast<double>(current_split_info.left_output),
                                static_cast<double>(current_split_info.right_output),
                                static_cast<data_size_t>(current_split_info.left_count),
                                static_cast<data_size_t>(current_split_info.right_count),
                                static_cast<float>(current_split_info.gain),
                                train_data_->FeatureBinMapper(inner_feature_index)->missing_type(),
                                current_split_info.default_left);
      data_partition_->Split(current_leaf, train_data_, inner_feature_index,
                             &current_split_info.threshold, 1,
                             current_split_info.default_left, *right_leaf);
    } else {
      std::vector<uint32_t> cat_bitset_inner = Common::ConstructBitset(
              current_split_info.cat_threshold.data(), current_split_info.num_cat_threshold);
      std::vector<int> threshold_int(current_split_info.num_cat_threshold);
      for (int i = 0; i < current_split_info.num_cat_threshold; ++i) {
        threshold_int[i] = static_cast<int>(train_data_->RealThreshold(
                    inner_feature_index, current_split_info.cat_threshold[i]));
      }
      std::vector<uint32_t> cat_bitset = Common::ConstructBitset(
              threshold_int.data(), current_split_info.num_cat_threshold);
      *right_leaf = tree->SplitCategorical(current_leaf,
                                           inner_feature_index,
                                           current_split_info.feature,
                                           cat_bitset_inner.data(),
                                           static_cast<int>(cat_bitset_inner.size()),
                                           cat_bitset.data(),
                                           static_cast<int>(cat_bitset.size()),
                                           static_cast<double>(current_split_info.left_output),
                                           static_cast<double>(current_split_info.right_output),
                                           static_cast<data_size_t>(current_split_info.left_count),
                                           static_cast<data_size_t>(current_split_info.right_count),
                                           static_cast<float>(current_split_info.gain),
                                           train_data_->FeatureBinMapper(inner_feature_index)->missing_type());
      data_partition_->Split(current_leaf, train_data_, inner_feature_index,
                             cat_bitset_inner.data(), static_cast<int>(cat_bitset_inner.size()),
                             current_split_info.default_left, *right_leaf);
    }

    if (current_split_info.left_count < current_split_info.right_count) {
      left_smaller = true;
      smaller_leaf_splits_->Init(*left_leaf, data_partition_.get(),
                                 current_split_info.left_sum_gradient,
                                 current_split_info.left_sum_hessian);
      larger_leaf_splits_->Init(*right_leaf, data_partition_.get(),
                                current_split_info.right_sum_gradient,
                                current_split_info.right_sum_hessian);
    } else {
      left_smaller = false;
      smaller_leaf_splits_->Init(*right_leaf, data_partition_.get(),
                                 current_split_info.right_sum_gradient, current_split_info.right_sum_hessian);
      larger_leaf_splits_->Init(*left_leaf, data_partition_.get(),
                                current_split_info.left_sum_gradient, current_split_info.left_sum_hessian);
    }

    left = Json();
    right = Json();
    if ((pair.first).object_items().count("left") > 0) {
      left = (pair.first)["left"];
      q.push(std::make_pair(left, *left_leaf));
    }
    if ((pair.first).object_items().count("right") > 0) {
      right = (pair.first)["right"];
      q.push(std::make_pair(right, *right_leaf));
    }
    result_count++;
    *(cur_depth) = std::max(*(cur_depth), tree->leaf_depth(*left_leaf));
  }
  return result_count;
}
Guolin Ke's avatar
Guolin Ke committed
699

700
701
void SerialTreeLearner::Split(Tree* tree, int best_leaf, int* left_leaf, int* right_leaf) {
  const SplitInfo& best_split_info = best_split_per_leaf_[best_leaf];
Guolin Ke's avatar
Guolin Ke committed
702
  const int inner_feature_index = train_data_->InnerFeatureIndex(best_split_info.feature);
Guolin Ke's avatar
Guolin Ke committed
703
  // left = parent
704
  *left_leaf = best_leaf;
Guolin Ke's avatar
Guolin Ke committed
705
706
  bool is_numerical_split = train_data_->FeatureBinMapper(inner_feature_index)->bin_type() == BinType::NumericalBin;
  if (is_numerical_split) {
707
    auto threshold_double = train_data_->RealThreshold(inner_feature_index, best_split_info.threshold);
708
709
710
711
712
    // split tree, will return right leaf
    *right_leaf = tree->Split(best_leaf,
                              inner_feature_index,
                              best_split_info.feature,
                              best_split_info.threshold,
713
                              threshold_double,
714
715
716
717
                              static_cast<double>(best_split_info.left_output),
                              static_cast<double>(best_split_info.right_output),
                              static_cast<data_size_t>(best_split_info.left_count),
                              static_cast<data_size_t>(best_split_info.right_count),
718
                              static_cast<float>(best_split_info.gain),
719
720
                              train_data_->FeatureBinMapper(inner_feature_index)->missing_type(),
                              best_split_info.default_left);
721
722
    data_partition_->Split(best_leaf, train_data_, inner_feature_index,
                           &best_split_info.threshold, 1, best_split_info.default_left, *right_leaf);
723
  } else {
724
725
726
727
728
729
    std::vector<uint32_t> cat_bitset_inner = Common::ConstructBitset(best_split_info.cat_threshold.data(), best_split_info.num_cat_threshold);
    std::vector<int> threshold_int(best_split_info.num_cat_threshold);
    for (int i = 0; i < best_split_info.num_cat_threshold; ++i) {
      threshold_int[i] = static_cast<int>(train_data_->RealThreshold(inner_feature_index, best_split_info.cat_threshold[i]));
    }
    std::vector<uint32_t> cat_bitset = Common::ConstructBitset(threshold_int.data(), best_split_info.num_cat_threshold);
730
731
732
    *right_leaf = tree->SplitCategorical(best_leaf,
                                         inner_feature_index,
                                         best_split_info.feature,
733
734
735
736
                                         cat_bitset_inner.data(),
                                         static_cast<int>(cat_bitset_inner.size()),
                                         cat_bitset.data(),
                                         static_cast<int>(cat_bitset.size()),
737
738
739
740
                                         static_cast<double>(best_split_info.left_output),
                                         static_cast<double>(best_split_info.right_output),
                                         static_cast<data_size_t>(best_split_info.left_count),
                                         static_cast<data_size_t>(best_split_info.right_count),
741
                                         static_cast<float>(best_split_info.gain),
742
                                         train_data_->FeatureBinMapper(inner_feature_index)->missing_type());
743
744
    data_partition_->Split(best_leaf, train_data_, inner_feature_index,
                           cat_bitset_inner.data(), static_cast<int>(cat_bitset_inner.size()), best_split_info.default_left, *right_leaf);
745
  }
746
747
748
749

  #ifdef DEBUG
  CHECK(best_split_info.left_count == data_partition_->leaf_count(best_leaf));
  #endif
Guolin Ke's avatar
Guolin Ke committed
750
751
  auto p_left = smaller_leaf_splits_.get();
  auto p_right = larger_leaf_splits_.get();
Guolin Ke's avatar
Guolin Ke committed
752
753
  // init the leaves that used on next iteration
  if (best_split_info.left_count < best_split_info.right_count) {
Guolin Ke's avatar
Guolin Ke committed
754
755
    smaller_leaf_splits_->Init(*left_leaf, data_partition_.get(), best_split_info.left_sum_gradient, best_split_info.left_sum_hessian);
    larger_leaf_splits_->Init(*right_leaf, data_partition_.get(), best_split_info.right_sum_gradient, best_split_info.right_sum_hessian);
Guolin Ke's avatar
Guolin Ke committed
756
  } else {
Guolin Ke's avatar
Guolin Ke committed
757
758
    smaller_leaf_splits_->Init(*right_leaf, data_partition_.get(), best_split_info.right_sum_gradient, best_split_info.right_sum_hessian);
    larger_leaf_splits_->Init(*left_leaf, data_partition_.get(), best_split_info.left_sum_gradient, best_split_info.left_sum_hessian);
Guolin Ke's avatar
Guolin Ke committed
759
760
761
762
763
764
765
766
767
768
769
770
771
772
    p_right = smaller_leaf_splits_.get();
    p_left = larger_leaf_splits_.get();
  }
  p_left->SetValueConstraint(best_split_info.min_constraint, best_split_info.max_constraint);
  p_right->SetValueConstraint(best_split_info.min_constraint, best_split_info.max_constraint);
  if (is_numerical_split) {
    double mid = (best_split_info.left_output + best_split_info.right_output) / 2.0f;
    if (best_split_info.monotone_type < 0) {
      p_left->SetValueConstraint(mid, best_split_info.max_constraint);
      p_right->SetValueConstraint(best_split_info.min_constraint, mid);
    } else if (best_split_info.monotone_type > 0) {
      p_left->SetValueConstraint(best_split_info.min_constraint, mid);
      p_right->SetValueConstraint(mid, best_split_info.max_constraint);
    }
Guolin Ke's avatar
Guolin Ke committed
773
774
775
  }
}

776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
void SerialTreeLearner::RenewTreeOutput(Tree* tree, const ObjectiveFunction* obj, const double* prediction,
                                        data_size_t total_num_data, const data_size_t* bag_indices, data_size_t bag_cnt) const {
  if (obj != nullptr && obj->IsRenewTreeOutput()) {
    CHECK(tree->num_leaves() <= data_partition_->num_leaves());
    const data_size_t* bag_mapper = nullptr;
    if (total_num_data != num_data_) {
      CHECK(bag_cnt == num_data_);
      bag_mapper = bag_indices;
    }
    #pragma omp parallel for schedule(static)
    for (int i = 0; i < tree->num_leaves(); ++i) {
      const double output = static_cast<double>(tree->LeafOutput(i));
      data_size_t cnt_leaf_data = 0;
      auto index_mapper = data_partition_->GetIndexOnLeaf(i, &cnt_leaf_data);
      CHECK(cnt_leaf_data > 0);
      // bag_mapper[index_mapper[i]]
      const double new_output = obj->RenewTreeOutput(output, prediction, index_mapper, bag_mapper, cnt_leaf_data);
      tree->SetLeafOutput(i, new_output);
    }
    if (Network::num_machines() > 1) {
      std::vector<double> outputs(tree->num_leaves());
      for (int i = 0; i < tree->num_leaves(); ++i) {
        outputs[i] = static_cast<double>(tree->LeafOutput(i));
      }
      Network::GlobalSum(outputs);
      for (int i = 0; i < tree->num_leaves(); ++i) {
        tree->SetLeafOutput(i, outputs[i] / Network::num_machines());
      }
    } 
  }
}

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