serial_tree_learner.cpp 19.6 KB
Newer Older
Guolin Ke's avatar
Guolin Ke committed
1
2
3
4
5
6
7
8
9
#include "serial_tree_learner.h"

#include <LightGBM/utils/array_args.h>

#include <algorithm>
#include <vector>

namespace LightGBM {

Guolin Ke's avatar
Guolin Ke committed
10
11
12
13
14
15
16
17
18
#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
19
SerialTreeLearner::SerialTreeLearner(const TreeConfig* tree_config)
Guolin Ke's avatar
Guolin Ke committed
20
  :tree_config_(tree_config){
Guolin Ke's avatar
Guolin Ke committed
21
  random_ = Random(tree_config_->feature_fraction_seed);
Guolin Ke's avatar
Guolin Ke committed
22
23
24
25
26
#pragma omp parallel
#pragma omp master
  {
    num_threads_ = omp_get_num_threads();
  }
Guolin Ke's avatar
Guolin Ke committed
27
28
29
}

SerialTreeLearner::~SerialTreeLearner() {
Guolin Ke's avatar
Guolin Ke committed
30
31
32
33
34
35
36
37
#ifdef TIMETAG
  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);
#endif
Guolin Ke's avatar
Guolin Ke committed
38
39
40
41
42
43
}

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

Guolin Ke's avatar
Guolin Ke committed
59
  histogram_pool_.DynamicChangeSize(train_data_, tree_config_, max_cache_size, tree_config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
60
  // push split information for all leaves
Guolin Ke's avatar
Guolin Ke committed
61
  best_split_per_leaf_.resize(tree_config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
62
  
Guolin Ke's avatar
Guolin Ke committed
63
  // get ordered bin
Guolin Ke's avatar
Guolin Ke committed
64
  train_data_->CreateOrderedBins(&ordered_bins_);
Guolin Ke's avatar
Guolin Ke committed
65
66

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

  // initialize data partition
Guolin Ke's avatar
Guolin Ke committed
78
  data_partition_.reset(new DataPartition(num_data_, tree_config_->num_leaves));
Guolin Ke's avatar
Guolin Ke committed
79
  is_feature_used_.resize(num_features_);
Guolin Ke's avatar
Guolin Ke committed
80
  // initialize ordered gradients and hessians
Guolin Ke's avatar
Guolin Ke committed
81
82
83
  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
84
  if (has_ordered_bin_) {
Guolin Ke's avatar
Guolin Ke committed
85
    is_data_in_leaf_.resize(num_data_);
Guolin Ke's avatar
Guolin Ke committed
86
    std::fill(is_data_in_leaf_.begin(), is_data_in_leaf_.end(), 0);
Guolin Ke's avatar
Guolin Ke committed
87
88
89
90
91
92
    order_bin_indices_.clear();
    for (int i = 0; i < static_cast<int>(ordered_bins_.size()); i++) {
      if (ordered_bins_[i] != nullptr) {
        order_bin_indices_.push_back(i);
      }
    }
Guolin Ke's avatar
Guolin Ke committed
93
  }
Guolin Ke's avatar
Guolin Ke committed
94
  Log::Info("Number of data: %d, number of used features: %d", num_data_, num_features_);
Guolin Ke's avatar
Guolin Ke committed
95
96
}

Guolin Ke's avatar
Guolin Ke committed
97
98
99
100
101
102
void SerialTreeLearner::ResetTrainingData(const Dataset* train_data) {
  train_data_ = train_data;
  num_data_ = train_data_->num_data();
  num_features_ = train_data_->num_features();

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

Guolin Ke's avatar
Guolin Ke committed
105
106
  has_ordered_bin_ = false;
  // check existing for ordered bin
Guolin Ke's avatar
Guolin Ke committed
107
  for (int i = 0; i < static_cast<int>(ordered_bins_.size()); ++i) {
Guolin Ke's avatar
Guolin Ke committed
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
    if (ordered_bins_[i] != nullptr) {
      has_ordered_bin_ = true;
      break;
    }
  }
  // initialize splits for leaf
  smaller_leaf_splits_->ResetNumData(num_data_);
  larger_leaf_splits_->ResetNumData(num_data_);

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

  is_feature_used_.resize(num_features_);

  // 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_);
Guolin Ke's avatar
Guolin Ke committed
128
    std::fill(is_data_in_leaf_.begin(), is_data_in_leaf_.end(), 0);
Guolin Ke's avatar
Guolin Ke committed
129
130
131
132
133
134
    order_bin_indices_.clear();
    for (int i = 0; i < static_cast<int>(ordered_bins_.size()); i++) {
      if (ordered_bins_[i] != nullptr) {
        order_bin_indices_.push_back(i);
      }
    }
Guolin Ke's avatar
Guolin Ke committed
135
136
137
  }

}
Guolin Ke's avatar
Guolin Ke committed
138

Guolin Ke's avatar
Guolin Ke committed
139
140
141
142
143
144
145
146
147
148
void SerialTreeLearner::ResetConfig(const TreeConfig* tree_config) {
  if (tree_config_->num_leaves != tree_config->num_leaves) {
    tree_config_ = tree_config;
    int max_cache_size = 0;
    // Get the max size of pool
    if (tree_config->histogram_pool_size <= 0) {
      max_cache_size = tree_config_->num_leaves;
    } else {
      size_t total_histogram_size = 0;
      for (int i = 0; i < train_data_->num_features(); ++i) {
Guolin Ke's avatar
Guolin Ke committed
149
        total_histogram_size += sizeof(HistogramBinEntry) * train_data_->FeatureNumBin(i);
Guolin Ke's avatar
Guolin Ke committed
150
151
152
153
154
155
      }
      max_cache_size = static_cast<int>(tree_config_->histogram_pool_size * 1024 * 1024 / total_histogram_size);
    }
    // at least need 2 leaves
    max_cache_size = std::max(2, max_cache_size);
    max_cache_size = std::min(max_cache_size, tree_config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
156
    histogram_pool_.DynamicChangeSize(train_data_, tree_config_, max_cache_size, tree_config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
157
158
159

    // push split information for all leaves
    best_split_per_leaf_.resize(tree_config_->num_leaves);
160
    data_partition_->ResetLeaves(tree_config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
161
162
163
164
  } else {
    tree_config_ = tree_config;
  }

Guolin Ke's avatar
Guolin Ke committed
165
  histogram_pool_.ResetConfig(tree_config_);
Guolin Ke's avatar
Guolin Ke committed
166
167
}

Guolin Ke's avatar
Guolin Ke committed
168
169
170
Tree* SerialTreeLearner::Train(const score_t* gradients, const score_t *hessians) {
  gradients_ = gradients;
  hessians_ = hessians;
Guolin Ke's avatar
Guolin Ke committed
171
172
173
174

#ifdef TIMETAG
  auto start_time = std::chrono::steady_clock::now();
#endif
Guolin Ke's avatar
Guolin Ke committed
175
176
  // some initial works before training
  BeforeTrain();
Guolin Ke's avatar
Guolin Ke committed
177
178
179
180
181

#ifdef TIMETAG
  init_train_time += std::chrono::steady_clock::now() - start_time;
#endif

Guolin Ke's avatar
Guolin Ke committed
182
  auto tree = std::unique_ptr<Tree>(new Tree(tree_config_->num_leaves));
Guolin Ke's avatar
Guolin Ke committed
183
  // save pointer to last trained tree
Guolin Ke's avatar
Guolin Ke committed
184
  last_trained_tree_ = tree.get();
Guolin Ke's avatar
Guolin Ke committed
185
186
  // root leaf
  int left_leaf = 0;
187
  int cur_depth = 1;
Guolin Ke's avatar
Guolin Ke committed
188
189
  // only root leaf can be splitted on first time
  int right_leaf = -1;
Guolin Ke's avatar
Guolin Ke committed
190
  for (int split = 0; split < tree_config_->num_leaves - 1; ++split) {
Guolin Ke's avatar
Guolin Ke committed
191
192
193
#ifdef TIMETAG
    start_time = std::chrono::steady_clock::now();
#endif
Guolin Ke's avatar
Guolin Ke committed
194
195
    // some initial works before finding best split
    if (BeforeFindBestSplit(left_leaf, right_leaf)) {
Guolin Ke's avatar
Guolin Ke committed
196
197
198
#ifdef TIMETAG
      init_split_time += std::chrono::steady_clock::now() - start_time;
#endif
Guolin Ke's avatar
Guolin Ke committed
199
200
201
202
203
204
205
206
207
208
209
      // find best threshold for every feature
      FindBestThresholds();
      // find best split from all features
      FindBestSplitsForLeaves();
    }
    // 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) {
210
      Log::Info("No further splits with positive gain, best gain: %f", best_leaf_SplitInfo.gain);
Guolin Ke's avatar
Guolin Ke committed
211
212
      break;
    }
Guolin Ke's avatar
Guolin Ke committed
213
214
215
#ifdef TIMETAG
    start_time = std::chrono::steady_clock::now();
#endif
Guolin Ke's avatar
Guolin Ke committed
216
    // split tree with best leaf
Guolin Ke's avatar
Guolin Ke committed
217
    Split(tree.get(), best_leaf, &left_leaf, &right_leaf);
Guolin Ke's avatar
Guolin Ke committed
218
219
220
#ifdef TIMETAG
    split_time += std::chrono::steady_clock::now() - start_time;
#endif
221
    cur_depth = std::max(cur_depth, tree->leaf_depth(left_leaf));
Guolin Ke's avatar
Guolin Ke committed
222
  }
223
  Log::Info("Trained a tree with leaves=%d and max_depth=%d", tree->num_leaves(), cur_depth);
Guolin Ke's avatar
Guolin Ke committed
224
  return tree.release();
Guolin Ke's avatar
Guolin Ke committed
225
226
227
}

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

229
230
  // reset histogram pool
  histogram_pool_.ResetMap();
Guolin Ke's avatar
Guolin Ke committed
231
  int used_feature_cnt = static_cast<int>(num_features_*tree_config_->feature_fraction);
Guolin Ke's avatar
Guolin Ke committed
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

  if (used_feature_cnt < num_features_) {
    // initialize used features
    std::memset(is_feature_used_.data(), 0, sizeof(int8_t) * num_features_);
    // Get used feature at current tree
    auto used_feature_indices = random_.Sample(num_features_, used_feature_cnt);
    #pragma omp parallel for schedule(static)
    for (int i = 0; i < static_cast<int>(used_feature_indices.size()); ++i) {
      is_feature_used_[used_feature_indices[i]] = 1;
    }
  } else {
    #pragma omp parallel for schedule(static)
    for (int i = 0; i < num_features_; ++i) {
      is_feature_used_[i] = 1;
    }
Guolin Ke's avatar
Guolin Ke committed
247
  }
248

Guolin Ke's avatar
Guolin Ke committed
249
250
251
252
  // initialize data partition
  data_partition_->Init();

  // reset the splits for leaves
Guolin Ke's avatar
Guolin Ke committed
253
  for (int i = 0; i < tree_config_->num_leaves; ++i) {
Guolin Ke's avatar
Guolin Ke committed
254
255
256
257
258
259
260
    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
261

Guolin Ke's avatar
Guolin Ke committed
262
263
  } else {
    // use bagging, only use part of data
Guolin Ke's avatar
Guolin Ke committed
264
    smaller_leaf_splits_->Init(0, data_partition_.get(), gradients_, hessians_);
Guolin Ke's avatar
Guolin Ke committed
265
266
267
268
269
270
  }

  larger_leaf_splits_->Init();

  // if has ordered bin, need to initialize the ordered bin
  if (has_ordered_bin_) {
Guolin Ke's avatar
Guolin Ke committed
271
272
273
#ifdef TIMETAG
    auto start_time = std::chrono::steady_clock::now();
#endif
Guolin Ke's avatar
Guolin Ke committed
274
275
    if (data_partition_->leaf_count(0) == num_data_) {
      // use all data, pass nullptr
Guolin Ke's avatar
Guolin Ke committed
276
277
278
      #pragma omp parallel for schedule(static)
      for (int i = 0; i < static_cast<int>(order_bin_indices_.size()); ++i) {
        ordered_bins_[order_bin_indices_[i]]->Init(nullptr, tree_config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
279
280
281
282
283
284
285
286
287
288
289
290
291
      }
    } 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);
      #pragma omp parallel for schedule(static)
      for (data_size_t i = begin; i < end; ++i) {
        is_data_in_leaf_[indices[i]] = 1;
      }
      // initialize ordered bin
Guolin Ke's avatar
Guolin Ke committed
292
293
294
      #pragma omp parallel for schedule(static)
      for (int i = 0; i < static_cast<int>(order_bin_indices_.size()); ++i) {
        ordered_bins_[order_bin_indices_[i]]->Init(is_data_in_leaf_.data(), tree_config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
295
      }
Guolin Ke's avatar
Guolin Ke committed
296
297
298
299
#pragma omp parallel for schedule(static)
      for (data_size_t i = begin; i < end; ++i) {
        is_data_in_leaf_[indices[i]] = 0;
      }
Guolin Ke's avatar
Guolin Ke committed
300
    }
Guolin Ke's avatar
Guolin Ke committed
301
302
303
#ifdef TIMETAG
    ordered_bin_time += std::chrono::steady_clock::now() - start_time;
#endif
Guolin Ke's avatar
Guolin Ke committed
304
305
306
307
  }
}

bool SerialTreeLearner::BeforeFindBestSplit(int left_leaf, int right_leaf) {
Guolin Ke's avatar
Guolin Ke committed
308
  // check depth of current leaf
Guolin Ke's avatar
Guolin Ke committed
309
  if (tree_config_->max_depth > 0) {
Guolin Ke's avatar
Guolin Ke committed
310
    // only need to check left leaf, since right leaf is in same level of left leaf
Guolin Ke's avatar
Guolin Ke committed
311
    if (last_trained_tree_->leaf_depth(left_leaf) >= tree_config_->max_depth) {
Guolin Ke's avatar
Guolin Ke committed
312
313
314
315
316
317
318
      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
319
320
321
  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
322
323
  if (num_data_in_right_child < static_cast<data_size_t>(tree_config_->min_data_in_leaf * 2)
    && num_data_in_left_child < static_cast<data_size_t>(tree_config_->min_data_in_leaf * 2)) {
Guolin Ke's avatar
Guolin Ke committed
324
325
326
327
328
329
    best_split_per_leaf_[left_leaf].gain = kMinScore;
    if (right_leaf >= 0) {
      best_split_per_leaf_[right_leaf].gain = kMinScore;
    }
    return false;
  }
330
  parent_leaf_histogram_array_ = nullptr;
Guolin Ke's avatar
Guolin Ke committed
331
332
  // only have root
  if (right_leaf < 0) {
333
    histogram_pool_.Get(left_leaf, &smaller_leaf_histogram_array_);
Guolin Ke's avatar
Guolin Ke committed
334
335
    larger_leaf_histogram_array_ = nullptr;
  } else if (num_data_in_left_child < num_data_in_right_child) {
Hui Xue's avatar
Hui Xue committed
336
    // put parent(left) leaf's histograms into larger leaf's histograms
337
338
339
    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
340
  } else {
Hui Xue's avatar
Hui Xue committed
341
    // put parent(left) leaf's histograms to larger leaf's histograms
342
343
    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
344
345
346
  }
  // split for the ordered bin
  if (has_ordered_bin_ && right_leaf >= 0) {
Guolin Ke's avatar
Guolin Ke committed
347
348
349
#ifdef TIMETAG
    auto start_time = std::chrono::steady_clock::now();
#endif
Guolin Ke's avatar
Guolin Ke committed
350
351
    // mark data that at left-leaf
    const data_size_t* indices = data_partition_->indices();
Guolin Ke's avatar
Guolin Ke committed
352
353
354
    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
355
    data_size_t begin = data_partition_->leaf_begin(left_leaf);
Guolin Ke's avatar
Guolin Ke committed
356
357
358
359
360
361
    data_size_t end = begin + left_cnt;
    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
362
363
364
365
366
    #pragma omp parallel for schedule(static)
    for (data_size_t i = begin; i < end; ++i) {
      is_data_in_leaf_[indices[i]] = 1;
    }
    // split the ordered bin
Guolin Ke's avatar
Guolin Ke committed
367
    #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
368
369
    for (int i = 0; i < static_cast<int>(order_bin_indices_.size()); ++i) {
      ordered_bins_[order_bin_indices_[i]]->Split(left_leaf, right_leaf, is_data_in_leaf_.data(), mark);
Guolin Ke's avatar
Guolin Ke committed
370
    }
Guolin Ke's avatar
Guolin Ke committed
371
372
373
374
#pragma omp parallel for schedule(static)
    for (data_size_t i = begin; i < end; ++i) {
      is_data_in_leaf_[indices[i]] = 0;
    }
Guolin Ke's avatar
Guolin Ke committed
375
376
377
#ifdef TIMETAG
    ordered_bin_time += std::chrono::steady_clock::now() - start_time;
#endif
Guolin Ke's avatar
Guolin Ke committed
378
379
380
381
382
  }
  return true;
}

void SerialTreeLearner::FindBestThresholds() {
Guolin Ke's avatar
Guolin Ke committed
383
384
385
#ifdef TIMETAG
  auto start_time = std::chrono::steady_clock::now();
#endif
Guolin Ke's avatar
Guolin Ke committed
386
  std::vector<int8_t> is_feature_used(num_features_, 0);
Guolin Ke's avatar
Guolin Ke committed
387
#pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
388
389
390
391
  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()) {
Guolin Ke's avatar
Guolin Ke committed
392
393
394
      smaller_leaf_histogram_array_[feature_index].set_is_splittable(false);
      continue;
    }
Guolin Ke's avatar
Guolin Ke committed
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
    is_feature_used[feature_index] = 1;
  }
  bool use_subtract = true;
  if (parent_leaf_histogram_array_ == nullptr) {
    use_subtract = false;
  }
  // construct smaller leaf
  HistogramBinEntry* ptr_smaller_leaf_hist_data = smaller_leaf_histogram_array_[0].RawData() - 1;
  train_data_->ConstructHistograms(is_feature_used,
    smaller_leaf_splits_->data_indices(), smaller_leaf_splits_->num_data_in_leaf(),
    smaller_leaf_splits_->LeafIndex(),
    ordered_bins_, gradients_, hessians_,
    ordered_gradients_.data(), ordered_hessians_.data(),
    ptr_smaller_leaf_hist_data);

  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,
      larger_leaf_splits_->data_indices(), larger_leaf_splits_->num_data_in_leaf(),
      larger_leaf_splits_->LeafIndex(),
      ordered_bins_, gradients_, hessians_,
      ordered_gradients_.data(), ordered_hessians_.data(),
      ptr_larger_leaf_hist_data);
  }
Guolin Ke's avatar
Guolin Ke committed
420
421
422
423
424
425
#ifdef TIMETAG
  hist_time += std::chrono::steady_clock::now() - start_time;
#endif
#ifdef TIMETAG
  start_time = std::chrono::steady_clock::now();
#endif
Guolin Ke's avatar
Guolin Ke committed
426
427
428
  std::vector<SplitInfo> smaller_best(num_threads_);
  std::vector<SplitInfo> larger_best(num_threads_);
  // find splits
Guolin Ke's avatar
Guolin Ke committed
429
  #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
430
431
432
433
434
435
436
437
  for (int feature_index = 0; feature_index < num_features_; ++feature_index) {
    if (!is_feature_used[feature_index]) { continue; }
    const int tid = omp_get_thread_num();
    SplitInfo smaller_split;
    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());
Guolin Ke's avatar
Guolin Ke committed
438

Guolin Ke's avatar
Guolin Ke committed
439
440
441
442
    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
443
444
445
446
      &smaller_split);
    if (smaller_split.gain > smaller_best[tid].gain) {
      smaller_best[tid] = smaller_split;
    }
Guolin Ke's avatar
Guolin Ke committed
447
    // only has root leaf
Guolin Ke's avatar
Guolin Ke committed
448
    if (larger_leaf_splits_ == nullptr || larger_leaf_splits_->LeafIndex() < 0) { continue; }
Guolin Ke's avatar
Guolin Ke committed
449

Guolin Ke's avatar
Guolin Ke committed
450
    if (use_subtract) {
451
452
      larger_leaf_histogram_array_[feature_index].Subtract(smaller_leaf_histogram_array_[feature_index]);
    } else {
Guolin Ke's avatar
Guolin Ke committed
453
454
455
      train_data_->FixHistogram(feature_index, larger_leaf_splits_->sum_gradients(), larger_leaf_splits_->sum_hessians(),
        larger_leaf_splits_->num_data_in_leaf(),
        larger_leaf_histogram_array_[feature_index].RawData());
456
    }
Guolin Ke's avatar
Guolin Ke committed
457
    SplitInfo larger_split;
Guolin Ke's avatar
Guolin Ke committed
458
    // find best threshold for larger child
Guolin Ke's avatar
Guolin Ke committed
459
460
461
462
    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
463
464
465
466
      &larger_split);
    if (larger_split.gain > larger_best[tid].gain) {
      larger_best[tid] = larger_split;
    }
Guolin Ke's avatar
Guolin Ke committed
467
  }
Guolin Ke's avatar
Guolin Ke committed
468
469
470
471
472
473
474
475
476
477

  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];
  }
Guolin Ke's avatar
Guolin Ke committed
478
479
480
#ifdef TIMETAG
  find_split_time += std::chrono::steady_clock::now() - start_time;
#endif
Guolin Ke's avatar
Guolin Ke committed
481
482
483
484
}

void SerialTreeLearner::FindBestSplitsForLeaves() {

Guolin Ke's avatar
Guolin Ke committed
485
486
487
488
489
490
491
492
}


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];
  // left = parent
  *left_leaf = best_Leaf;
  // split tree, will return right leaf
493
494
  *right_leaf = tree->Split(best_Leaf, best_split_info.feature,
    train_data_->FeatureBinMapper(best_split_info.feature)->bin_type(),
Guolin Ke's avatar
Guolin Ke committed
495
    best_split_info.threshold,
Guolin Ke's avatar
Guolin Ke committed
496
497
    train_data_->RealFeatureIndex(best_split_info.feature),
    train_data_->RealThreshold(best_split_info.feature, best_split_info.threshold),
498
499
    static_cast<double>(best_split_info.left_output),
    static_cast<double>(best_split_info.right_output),
Guolin Ke's avatar
Guolin Ke committed
500
501
    static_cast<data_size_t>(best_split_info.left_count),
    static_cast<data_size_t>(best_split_info.right_count),
502
    static_cast<double>(best_split_info.gain));
Guolin Ke's avatar
Guolin Ke committed
503
  // split data partition
Guolin Ke's avatar
Guolin Ke committed
504
  data_partition_->Split(best_Leaf, train_data_, best_split_info.feature, 
Guolin Ke's avatar
Guolin Ke committed
505
506
507
508
                         best_split_info.threshold, *right_leaf);

  // 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
509
    smaller_leaf_splits_->Init(*left_leaf, data_partition_.get(),
Guolin Ke's avatar
Guolin Ke committed
510
511
                               best_split_info.left_sum_gradient,
                               best_split_info.left_sum_hessian);
Guolin Ke's avatar
Guolin Ke committed
512
    larger_leaf_splits_->Init(*right_leaf, data_partition_.get(),
Guolin Ke's avatar
Guolin Ke committed
513
514
515
                               best_split_info.right_sum_gradient,
                               best_split_info.right_sum_hessian);
  } else {
Guolin Ke's avatar
Guolin Ke committed
516
517
    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
518
519
520
  }
}

Guolin Ke's avatar
Guolin Ke committed
521

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