serial_tree_learner.cpp 24.8 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);
22
23
  #pragma omp parallel
  #pragma omp master
Guolin Ke's avatar
Guolin Ke committed
24
25
26
  {
    num_threads_ = omp_get_num_threads();
  }
Guolin Ke's avatar
Guolin Ke committed
27
28
29
}

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

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

Guolin Ke's avatar
Guolin Ke committed
60
  histogram_pool_.DynamicChangeSize(train_data_, tree_config_, max_cache_size, tree_config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
61
  // push split information for all leaves
Guolin Ke's avatar
Guolin Ke committed
62
  best_split_per_leaf_.resize(tree_config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
63

Guolin Ke's avatar
Guolin Ke committed
64
  // get ordered bin
Guolin Ke's avatar
Guolin Ke committed
65
  train_data_->CreateOrderedBins(&ordered_bins_);
Guolin Ke's avatar
Guolin Ke committed
66
67

  // check existing for ordered bin
Guolin Ke's avatar
Guolin Ke committed
68
  for (int i = 0; i < static_cast<int>(ordered_bins_.size()); ++i) {
Guolin Ke's avatar
Guolin Ke committed
69
70
71
72
73
    if (ordered_bins_[i] != nullptr) {
      has_ordered_bin_ = true;
      break;
    }
  }
wxchan's avatar
wxchan committed
74
  // initialize splits for leaf
Guolin Ke's avatar
Guolin Ke committed
75
76
  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
77
78

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

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

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

Guolin Ke's avatar
Guolin Ke committed
107
108
109
110
111
112
113
114
115
116
117
118
119
  // 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_);
120
    std::fill(is_data_in_leaf_.begin(), is_data_in_leaf_.end(), static_cast<char>(0));
Guolin Ke's avatar
Guolin Ke committed
121
122
  }
}
Guolin Ke's avatar
Guolin Ke committed
123

Guolin Ke's avatar
Guolin Ke committed
124
125
126
127
128
129
130
131
132
133
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
134
        total_histogram_size += sizeof(HistogramBinEntry) * train_data_->FeatureNumBin(i);
Guolin Ke's avatar
Guolin Ke committed
135
136
137
138
139
140
      }
      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
141
    histogram_pool_.DynamicChangeSize(train_data_, tree_config_, max_cache_size, tree_config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
142
143
144

    // push split information for all leaves
    best_split_per_leaf_.resize(tree_config_->num_leaves);
145
    data_partition_->ResetLeaves(tree_config_->num_leaves);
Guolin Ke's avatar
Guolin Ke committed
146
147
148
149
  } else {
    tree_config_ = tree_config;
  }

Guolin Ke's avatar
Guolin Ke committed
150
  histogram_pool_.ResetConfig(tree_config_);
Guolin Ke's avatar
Guolin Ke committed
151
152
}

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

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

Guolin Ke's avatar
Guolin Ke committed
167
  auto tree = std::unique_ptr<Tree>(new Tree(tree_config_->num_leaves));
Guolin Ke's avatar
Guolin Ke committed
168
169
  // root leaf
  int left_leaf = 0;
170
  int cur_depth = 1;
Guolin Ke's avatar
Guolin Ke committed
171
172
  // only root leaf can be splitted on first time
  int right_leaf = -1;
Guolin Ke's avatar
Guolin Ke committed
173
  for (int split = 0; split < tree_config_->num_leaves - 1; ++split) {
174
    #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
175
    start_time = std::chrono::steady_clock::now();
176
    #endif
Guolin Ke's avatar
Guolin Ke committed
177
    // some initial works before finding best split
Guolin Ke's avatar
Guolin Ke committed
178
    if (BeforeFindBestSplit(tree.get(), left_leaf, right_leaf)) {
179
      #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
180
      init_split_time += std::chrono::steady_clock::now() - start_time;
181
      #endif
Guolin Ke's avatar
Guolin Ke committed
182
      // find best threshold for every feature
Guolin Ke's avatar
Guolin Ke committed
183
      FindBestSplits();
Guolin Ke's avatar
Guolin Ke committed
184
185
186
187
188
189
190
    }
    // 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
191
      Log::Warning("No further splits with positive gain, best gain: %f", best_leaf_SplitInfo.gain);
Guolin Ke's avatar
Guolin Ke committed
192
193
      break;
    }
194
    #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
195
    start_time = std::chrono::steady_clock::now();
196
    #endif
Guolin Ke's avatar
Guolin Ke committed
197
    // split tree with best leaf
Guolin Ke's avatar
Guolin Ke committed
198
    Split(tree.get(), best_leaf, &left_leaf, &right_leaf);
199
    #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
200
    split_time += std::chrono::steady_clock::now() - start_time;
201
    #endif
202
    cur_depth = std::max(cur_depth, tree->leaf_depth(left_leaf));
Guolin Ke's avatar
Guolin Ke committed
203
  }
Guolin Ke's avatar
Guolin Ke committed
204
  Log::Debug("Trained a tree with leaves=%d and max_depth=%d", tree->num_leaves(), cur_depth);
Guolin Ke's avatar
Guolin Ke committed
205
  return tree.release();
Guolin Ke's avatar
Guolin Ke committed
206
207
}

208
Tree* SerialTreeLearner::FitByExistingTree(const Tree* old_tree, const score_t* gradients, const score_t *hessians) const {
Guolin Ke's avatar
Guolin Ke committed
209
210
  auto tree = std::unique_ptr<Tree>(new Tree(*old_tree));
  CHECK(data_partition_->num_leaves() >= tree->num_leaves());
211
  OMP_INIT_EX();
Guolin Ke's avatar
Guolin Ke committed
212
  #pragma omp parallel for schedule(static)
213
  for (int i = 0; i < tree->num_leaves(); ++i) {
214
    OMP_LOOP_EX_BEGIN();
Guolin Ke's avatar
Guolin Ke committed
215
216
217
    data_size_t cnt_leaf_data = 0;
    auto tmp_idx = data_partition_->GetIndexOnLeaf(i, &cnt_leaf_data);
    double sum_grad = 0.0f;
218
    double sum_hess = kEpsilon;
Guolin Ke's avatar
Guolin Ke committed
219
220
221
222
223
224
225
    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,
                                                                  tree_config_->lambda_l1, tree_config_->lambda_l2);
226
    tree->SetLeafOutput(i, output* tree->shrinkage());
227
    OMP_LOOP_EX_END();
Guolin Ke's avatar
Guolin Ke committed
228
  }
229
  OMP_THROW_EX();
Guolin Ke's avatar
Guolin Ke committed
230
231
232
  return tree.release();
}

233
234
235
236
237
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
238
void SerialTreeLearner::BeforeTrain() {
Guolin Ke's avatar
Guolin Ke committed
239

240
241
  // reset histogram pool
  histogram_pool_.ResetMap();
Guolin Ke's avatar
Guolin Ke committed
242

Guolin Ke's avatar
Guolin Ke committed
243
  if (tree_config_->feature_fraction < 1) {
244
245
246
    int used_feature_cnt = static_cast<int>(valid_feature_indices_.size()*tree_config_->feature_fraction);
    // at least use one feature
    used_feature_cnt = std::max(used_feature_cnt, 1);
Guolin Ke's avatar
Guolin Ke committed
247
248
249
    // 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
250
    auto sampled_indices = random_.Sample(static_cast<int>(valid_feature_indices_.size()), used_feature_cnt);
251
    int omp_loop_size = static_cast<int>(sampled_indices.size());
Guolin Ke's avatar
Guolin Ke committed
252
253
    #pragma omp parallel for schedule(static, 512) if (omp_loop_size >= 1024)
    for (int i = 0; i < omp_loop_size; ++i) {
254
255
256
      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
257
      is_feature_used_[inner_feature_index] = 1;
Guolin Ke's avatar
Guolin Ke committed
258
259
    }
  } else {
Guolin Ke's avatar
Guolin Ke committed
260
    #pragma omp parallel for schedule(static, 512) if (num_features_ >= 1024)
Guolin Ke's avatar
Guolin Ke committed
261
262
263
    for (int i = 0; i < num_features_; ++i) {
      is_feature_used_[i] = 1;
    }
Guolin Ke's avatar
Guolin Ke committed
264
  }
265

Guolin Ke's avatar
Guolin Ke committed
266
267
268
269
  // initialize data partition
  data_partition_->Init();

  // reset the splits for leaves
Guolin Ke's avatar
Guolin Ke committed
270
  for (int i = 0; i < tree_config_->num_leaves; ++i) {
Guolin Ke's avatar
Guolin Ke committed
271
272
273
274
275
276
277
    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
278

Guolin Ke's avatar
Guolin Ke committed
279
280
  } else {
    // use bagging, only use part of data
Guolin Ke's avatar
Guolin Ke committed
281
    smaller_leaf_splits_->Init(0, data_partition_.get(), gradients_, hessians_);
Guolin Ke's avatar
Guolin Ke committed
282
283
284
285
286
287
  }

  larger_leaf_splits_->Init();

  // if has ordered bin, need to initialize the ordered bin
  if (has_ordered_bin_) {
288
    #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
289
    auto start_time = std::chrono::steady_clock::now();
290
    #endif
Guolin Ke's avatar
Guolin Ke committed
291
292
    if (data_partition_->leaf_count(0) == num_data_) {
      // use all data, pass nullptr
293
294
      OMP_INIT_EX();
      #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
295
      for (int i = 0; i < static_cast<int>(ordered_bin_indices_.size()); ++i) {
296
        OMP_LOOP_EX_BEGIN();
Guolin Ke's avatar
Guolin Ke committed
297
        ordered_bins_[ordered_bin_indices_[i]]->Init(nullptr, tree_config_->num_leaves);
298
        OMP_LOOP_EX_END();
Guolin Ke's avatar
Guolin Ke committed
299
      }
300
      OMP_THROW_EX();
Guolin Ke's avatar
Guolin Ke committed
301
302
303
304
305
306
307
    } 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
308
309
      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
310
311
312
      for (data_size_t i = begin; i < end; ++i) {
        is_data_in_leaf_[indices[i]] = 1;
      }
313
      OMP_INIT_EX();
Guolin Ke's avatar
Guolin Ke committed
314
      // initialize ordered bin
315
      #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
316
      for (int i = 0; i < static_cast<int>(ordered_bin_indices_.size()); ++i) {
317
        OMP_LOOP_EX_BEGIN();
Guolin Ke's avatar
Guolin Ke committed
318
        ordered_bins_[ordered_bin_indices_[i]]->Init(is_data_in_leaf_.data(), tree_config_->num_leaves);
319
        OMP_LOOP_EX_END();
Guolin Ke's avatar
Guolin Ke committed
320
      }
321
      OMP_THROW_EX();
Guolin Ke's avatar
Guolin Ke committed
322
      #pragma omp parallel for schedule(static, 512) if(loop_size >= 1024)
Guolin Ke's avatar
Guolin Ke committed
323
324
325
      for (data_size_t i = begin; i < end; ++i) {
        is_data_in_leaf_[indices[i]] = 0;
      }
Guolin Ke's avatar
Guolin Ke committed
326
    }
327
    #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
328
    ordered_bin_time += std::chrono::steady_clock::now() - start_time;
329
    #endif
Guolin Ke's avatar
Guolin Ke committed
330
331
332
  }
}

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

Guolin Ke's avatar
Guolin Ke committed
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
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);
}

430
void SerialTreeLearner::ConstructHistograms(const std::vector<int8_t>& is_feature_used, bool use_subtract) {
Guolin Ke's avatar
Guolin Ke committed
431
  #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
432
  auto start_time = std::chrono::steady_clock::now();
Guolin Ke's avatar
Guolin Ke committed
433
  #endif
Guolin Ke's avatar
Guolin Ke committed
434
435
436
  // 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
437
438
439
                                   smaller_leaf_splits_->data_indices(), smaller_leaf_splits_->num_data_in_leaf(),
                                   smaller_leaf_splits_->LeafIndex(),
                                   ordered_bins_, gradients_, hessians_,
440
                                   ordered_gradients_.data(), ordered_hessians_.data(), is_constant_hessian_,
Guolin Ke's avatar
Guolin Ke committed
441
                                   ptr_smaller_leaf_hist_data);
Guolin Ke's avatar
Guolin Ke committed
442
443
444
445
446

  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
447
448
449
                                     larger_leaf_splits_->data_indices(), larger_leaf_splits_->num_data_in_leaf(),
                                     larger_leaf_splits_->LeafIndex(),
                                     ordered_bins_, gradients_, hessians_,
450
                                     ordered_gradients_.data(), ordered_hessians_.data(), is_constant_hessian_,
Guolin Ke's avatar
Guolin Ke committed
451
                                     ptr_larger_leaf_hist_data);
Guolin Ke's avatar
Guolin Ke committed
452
  }
Guolin Ke's avatar
Guolin Ke committed
453
  #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
454
  hist_time += std::chrono::steady_clock::now() - start_time;
Guolin Ke's avatar
Guolin Ke committed
455
  #endif
456
457
}

Guolin Ke's avatar
Guolin Ke committed
458
void SerialTreeLearner::FindBestSplitsFromHistograms(const std::vector<int8_t>& is_feature_used, bool use_subtract) {
459
  #ifdef TIMETAG
460
  auto start_time = std::chrono::steady_clock::now();
461
  #endif
Guolin Ke's avatar
Guolin Ke committed
462
463
  std::vector<SplitInfo> smaller_best(num_threads_);
  std::vector<SplitInfo> larger_best(num_threads_);
464
  OMP_INIT_EX();
Guolin Ke's avatar
Guolin Ke committed
465
  // find splits
466
  #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
467
  for (int feature_index = 0; feature_index < num_features_; ++feature_index) {
468
    OMP_LOOP_EX_BEGIN();
Guolin Ke's avatar
Guolin Ke committed
469
470
471
    if (!is_feature_used[feature_index]) { continue; }
    const int tid = omp_get_thread_num();
    SplitInfo smaller_split;
Guolin Ke's avatar
Guolin Ke committed
472
473
474
475
    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());
476
    int real_fidx = train_data_->RealFeatureIndex(feature_index);
Guolin Ke's avatar
Guolin Ke committed
477
478
479
480
    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
481
      &smaller_split);
482
483
    smaller_split.feature = real_fidx;
    if (smaller_split > smaller_best[tid]) {
Guolin Ke's avatar
Guolin Ke committed
484
485
      smaller_best[tid] = smaller_split;
    }
Guolin Ke's avatar
Guolin Ke committed
486
    // only has root leaf
Guolin Ke's avatar
Guolin Ke committed
487
    if (larger_leaf_splits_ == nullptr || larger_leaf_splits_->LeafIndex() < 0) { continue; }
Guolin Ke's avatar
Guolin Ke committed
488

Guolin Ke's avatar
Guolin Ke committed
489
    if (use_subtract) {
490
491
      larger_leaf_histogram_array_[feature_index].Subtract(smaller_leaf_histogram_array_[feature_index]);
    } else {
Guolin Ke's avatar
Guolin Ke committed
492
      train_data_->FixHistogram(feature_index, larger_leaf_splits_->sum_gradients(), larger_leaf_splits_->sum_hessians(),
Guolin Ke's avatar
Guolin Ke committed
493
494
                                larger_leaf_splits_->num_data_in_leaf(),
                                larger_leaf_histogram_array_[feature_index].RawData());
495
    }
Guolin Ke's avatar
Guolin Ke committed
496
    SplitInfo larger_split;
Guolin Ke's avatar
Guolin Ke committed
497
    // find best threshold for larger child
Guolin Ke's avatar
Guolin Ke committed
498
499
500
501
    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
502
      &larger_split);
503
504
    larger_split.feature = real_fidx;
    if (larger_split > larger_best[tid]) {
Guolin Ke's avatar
Guolin Ke committed
505
      larger_best[tid] = larger_split;
Guolin Ke's avatar
Guolin Ke committed
506
    }
507
    OMP_LOOP_EX_END();
Guolin Ke's avatar
Guolin Ke committed
508
  }
509
  OMP_THROW_EX();
Guolin Ke's avatar
Guolin Ke committed
510
511
512
513
514
515
516
517
518
519

  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];
  }
520
  #ifdef TIMETAG
Guolin Ke's avatar
Guolin Ke committed
521
  find_split_time += std::chrono::steady_clock::now() - start_time;
522
  #endif
Guolin Ke's avatar
Guolin Ke committed
523
524
}

Guolin Ke's avatar
Guolin Ke committed
525

526
527
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
528
  const int inner_feature_index = train_data_->InnerFeatureIndex(best_split_info.feature);
Guolin Ke's avatar
Guolin Ke committed
529
  // left = parent
530
531
  *left_leaf = best_leaf;
  if (train_data_->FeatureBinMapper(inner_feature_index)->bin_type() == BinType::NumericalBin) {
532
    auto threshold_double = train_data_->RealThreshold(inner_feature_index, best_split_info.threshold);
533
534
535
536
537
    // split tree, will return right leaf
    *right_leaf = tree->Split(best_leaf,
                              inner_feature_index,
                              best_split_info.feature,
                              best_split_info.threshold,
538
                              threshold_double,
539
540
541
542
                              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),
543
                              static_cast<float>(best_split_info.gain),
544
545
                              train_data_->FeatureBinMapper(inner_feature_index)->missing_type(),
                              best_split_info.default_left);
546
547
    data_partition_->Split(best_leaf, train_data_, inner_feature_index,
                           &best_split_info.threshold, 1, best_split_info.default_left, *right_leaf);
548
  } else {
549
550
551
552
553
554
    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);
555
556
557
    *right_leaf = tree->SplitCategorical(best_leaf,
                                         inner_feature_index,
                                         best_split_info.feature,
558
559
560
561
                                         cat_bitset_inner.data(),
                                         static_cast<int>(cat_bitset_inner.size()),
                                         cat_bitset.data(),
                                         static_cast<int>(cat_bitset.size()),
562
563
564
565
                                         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),
566
                                         static_cast<float>(best_split_info.gain),
567
                                         train_data_->FeatureBinMapper(inner_feature_index)->missing_type());
568
569
    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);
570
  }
571
572
573
574

  #ifdef DEBUG
  CHECK(best_split_info.left_count == data_partition_->leaf_count(best_leaf));
  #endif
Guolin Ke's avatar
Guolin Ke committed
575
576
577

  // 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
578
    smaller_leaf_splits_->Init(*left_leaf, data_partition_.get(),
Guolin Ke's avatar
Guolin Ke committed
579
580
                               best_split_info.left_sum_gradient,
                               best_split_info.left_sum_hessian);
Guolin Ke's avatar
Guolin Ke committed
581
    larger_leaf_splits_->Init(*right_leaf, data_partition_.get(),
Guolin Ke's avatar
Guolin Ke committed
582
583
                              best_split_info.right_sum_gradient,
                              best_split_info.right_sum_hessian);
Guolin Ke's avatar
Guolin Ke committed
584
  } else {
Guolin Ke's avatar
Guolin Ke committed
585
586
    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
587
588
589
590
  }
}

}  // namespace LightGBM