serial_tree_learner.h 9.47 KB
Newer Older
1
2
3
4
/*!
 * Copyright (c) 2016 Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See LICENSE file in the project root for license information.
 */
Guolin Ke's avatar
Guolin Ke committed
5
6
7
#ifndef LIGHTGBM_TREELEARNER_SERIAL_TREE_LEARNER_H_
#define LIGHTGBM_TREELEARNER_SERIAL_TREE_LEARNER_H_

8
9
10
#include <LightGBM/dataset.h>
#include <LightGBM/tree.h>
#include <LightGBM/tree_learner.h>
11
#include <LightGBM/cuda/vector_cudahost.h>
12
13
14
15
#include <LightGBM/utils/array_args.h>
#include <LightGBM/utils/json11.h>
#include <LightGBM/utils/random.h>

16
17
18
19
20
21
#include <string>
#include <cmath>
#include <cstdio>
#include <memory>
#include <random>
#include <vector>
22
#include <set>
23

24
#include "col_sampler.hpp"
Guolin Ke's avatar
Guolin Ke committed
25
#include "data_partition.hpp"
26
#include "feature_histogram.hpp"
27
#include "gradient_discretizer.hpp"
Guolin Ke's avatar
Guolin Ke committed
28
#include "leaf_splits.hpp"
29
#include "monotone_constraints.hpp"
Nikita Titov's avatar
Nikita Titov committed
30
#include "split_info.hpp"
Guolin Ke's avatar
Guolin Ke committed
31

32
#ifdef USE_GPU
Andrew Ziem's avatar
Andrew Ziem committed
33
// Use 4KBytes aligned allocator for ordered gradients and ordered Hessians when GPU is enabled.
34
35
36
// This is necessary to pin the two arrays in memory and make transferring faster.
#include <boost/align/aligned_allocator.hpp>
#endif
Guolin Ke's avatar
Guolin Ke committed
37
38

namespace LightGBM {
39
40
41

using json11::Json;

42
43
/*! \brief forward declaration */
class CostEfficientGradientBoosting;
44

Guolin Ke's avatar
Guolin Ke committed
45
46
47
48
/*!
* \brief Used for learning a tree by single machine
*/
class SerialTreeLearner: public TreeLearner {
Nikita Titov's avatar
Nikita Titov committed
49
 public:
50
  friend CostEfficientGradientBoosting;
Guolin Ke's avatar
Guolin Ke committed
51
  explicit SerialTreeLearner(const Config* config);
Guolin Ke's avatar
Guolin Ke committed
52
53
54

  ~SerialTreeLearner();

55
  void Init(const Dataset* train_data, bool is_constant_hessian) override;
Guolin Ke's avatar
Guolin Ke committed
56

57
58
59
60
61
62
63
64
65
66
67
68
  void ResetTrainingData(const Dataset* train_data,
                         bool is_constant_hessian) override {
    ResetTrainingDataInner(train_data, is_constant_hessian, true);
  }

  void ResetIsConstantHessian(bool is_constant_hessian) override {
    share_state_->is_constant_hessian = is_constant_hessian;
  }

  virtual void ResetTrainingDataInner(const Dataset* train_data,
                                      bool is_constant_hessian,
                                      bool reset_multi_val_bin);
Guolin Ke's avatar
Guolin Ke committed
69

Guolin Ke's avatar
Guolin Ke committed
70
  void ResetConfig(const Config* config) override;
Guolin Ke's avatar
Guolin Ke committed
71

72
73
74
75
76
77
78
79
  inline void SetForcedSplit(const Json* forced_split_json) override {
    if (forced_split_json != nullptr && !forced_split_json->is_null()) {
      forced_split_json_ = forced_split_json;
    } else {
      forced_split_json_ = nullptr;
    }
  }

80
  Tree* Train(const score_t* gradients, const score_t *hessians, bool is_first_tree) override;
Guolin Ke's avatar
Guolin Ke committed
81

82
  Tree* FitByExistingTree(const Tree* old_tree, const score_t* gradients, const score_t* hessians) const override;
Guolin Ke's avatar
Guolin Ke committed
83

84
  Tree* FitByExistingTree(const Tree* old_tree, const std::vector<int>& leaf_pred,
85
                          const score_t* gradients, const score_t* hessians) const override;
86

87
88
89
  void SetBaggingData(const Dataset* subset, const data_size_t* used_indices, data_size_t num_data) override {
    if (subset == nullptr) {
      data_partition_->SetUsedDataIndices(used_indices, num_data);
90
      share_state_->SetUseSubrow(false);
91
92
    } else {
      ResetTrainingDataInner(subset, share_state_->is_constant_hessian, false);
93
94
      share_state_->SetUseSubrow(true);
      share_state_->SetSubrowCopied(false);
95
96
97
      share_state_->bagging_use_indices = used_indices;
      share_state_->bagging_indices_cnt = num_data;
    }
Guolin Ke's avatar
Guolin Ke committed
98
99
  }

Guolin Ke's avatar
Guolin Ke committed
100
101
  void AddPredictionToScore(const Tree* tree,
                            double* out_score) const override {
102
    CHECK_LE(tree->num_leaves(), data_partition_->num_leaves());
Guolin Ke's avatar
Guolin Ke committed
103
104
105
106
    if (tree->num_leaves() <= 1) {
      return;
    }
#pragma omp parallel for schedule(static, 1)
107
    for (int i = 0; i < tree->num_leaves(); ++i) {
Guolin Ke's avatar
Guolin Ke committed
108
      double output = static_cast<double>(tree->LeafOutput(i));
Guolin Ke's avatar
Guolin Ke committed
109
110
      data_size_t cnt_leaf_data = 0;
      auto tmp_idx = data_partition_->GetIndexOnLeaf(i, &cnt_leaf_data);
Guolin Ke's avatar
Guolin Ke committed
111
      for (data_size_t j = 0; j < cnt_leaf_data; ++j) {
112
        out_score[tmp_idx[j]] += output;
Guolin Ke's avatar
Guolin Ke committed
113
114
115
116
      }
    }
  }

117
  void RenewTreeOutput(Tree* tree, const ObjectiveFunction* obj, std::function<double(const label_t*, int)> residual_getter,
118
                       data_size_t total_num_data, const data_size_t* bag_indices, data_size_t bag_cnt, const double* train_score) const override;
Guolin Ke's avatar
Guolin Ke committed
119

120
121
122
  /*! \brief Get output of parent node, used for path smoothing */
  double GetParentOutput(const Tree* tree, const LeafSplits* leaf_splits) const;

Nikita Titov's avatar
Nikita Titov committed
123
 protected:
124
125
  void ComputeBestSplitForFeature(FeatureHistogram* histogram_array_,
                                  int feature_index, int real_fidx,
Guolin Ke's avatar
Guolin Ke committed
126
                                  int8_t is_feature_used, int num_data,
127
                                  const LeafSplits* leaf_splits,
128
129
                                  SplitInfo* best_split, double parent_output);

130

131
  void GetShareStates(const Dataset* dataset, bool is_constant_hessian, bool is_first_time);
132

133
  void RecomputeBestSplitForLeaf(Tree* tree, int leaf, SplitInfo* split);
134

Guolin Ke's avatar
Guolin Ke committed
135
136
137
138
139
140
141
142
  /*!
  * \brief Some initial works before training
  */
  virtual void BeforeTrain();

  /*!
  * \brief Some initial works before FindBestSplit
  */
Guolin Ke's avatar
Guolin Ke committed
143
  virtual bool BeforeFindBestSplit(const Tree* tree, int left_leaf, int right_leaf);
Guolin Ke's avatar
Guolin Ke committed
144

145
  virtual void FindBestSplits(const Tree* tree);
Guolin Ke's avatar
Guolin Ke committed
146

147
148
  virtual void FindBestSplits(const Tree* tree, const std::set<int>* force_features);

Guolin Ke's avatar
Guolin Ke committed
149
  virtual void ConstructHistograms(const std::vector<int8_t>& is_feature_used, bool use_subtract);
Guolin Ke's avatar
Guolin Ke committed
150

151
  virtual void FindBestSplitsFromHistograms(const std::vector<int8_t>& is_feature_used, bool use_subtract, const Tree*);
Guolin Ke's avatar
Guolin Ke committed
152
153
154
155
156
157
158
159

  /*!
  * \brief Partition tree and data according best split.
  * \param tree Current tree, will be splitted on this function.
  * \param best_leaf The index of leaf that will be splitted.
  * \param left_leaf The index of left leaf after splitted.
  * \param right_leaf The index of right leaf after splitted.
  */
160
161
162
163
164
  inline virtual void Split(Tree* tree, int best_leaf, int* left_leaf,
    int* right_leaf) {
    SplitInner(tree, best_leaf, left_leaf, right_leaf, true);
  }

165
166
  void SplitInner(Tree* tree, int best_leaf, int* left_leaf, int* right_leaf,
                  bool update_cnt);
Guolin Ke's avatar
Guolin Ke committed
167

168
  /* Force splits with forced_split_json dict and then return num splits forced.*/
169
170
  int32_t ForceSplits(Tree* tree, int* left_leaf, int* right_leaf,
                      int* cur_depth);
171

172
173
  std::set<int> FindAllForceFeatures(Json force_split_leaf_setting);

174
175
  void CheckSplit(const SplitInfo& best_split_info, const int left_leaf_index, const int right_leaf_index);

Guolin Ke's avatar
Guolin Ke committed
176
177
178
179
180
181
  /*!
  * \brief Get the number of data in a leaf
  * \param leaf_idx The index of leaf
  * \return The number of data in the leaf_idx leaf
  */
  inline virtual data_size_t GetGlobalDataCountInLeaf(int leaf_idx) const;
182

Guolin Ke's avatar
Guolin Ke committed
183
184
185
186
187
188
189
  /*! \brief number of data */
  data_size_t num_data_;
  /*! \brief number of features */
  int num_features_;
  /*! \brief training data */
  const Dataset* train_data_;
  /*! \brief gradients of current iteration */
190
  const score_t* gradients_;
Guolin Ke's avatar
Guolin Ke committed
191
  /*! \brief hessians of current iteration */
192
  const score_t* hessians_;
Guolin Ke's avatar
Guolin Ke committed
193
  /*! \brief training data partition on leaves */
Guolin Ke's avatar
Guolin Ke committed
194
  std::unique_ptr<DataPartition> data_partition_;
195
196
  /*! \brief pointer to histograms array of parent of current leaves */
  FeatureHistogram* parent_leaf_histogram_array_;
Guolin Ke's avatar
Guolin Ke committed
197
198
199
200
201
202
  /*! \brief pointer to histograms array of smaller leaf */
  FeatureHistogram* smaller_leaf_histogram_array_;
  /*! \brief pointer to histograms array of larger leaf */
  FeatureHistogram* larger_leaf_histogram_array_;
  /*! \brief store best split points for all leaves */
  std::vector<SplitInfo> best_split_per_leaf_;
203
204
  /*! \brief store best split per feature for all leaves */
  std::vector<SplitInfo> splits_per_leaf_;
Nikita Titov's avatar
Nikita Titov committed
205
  /*! \brief stores minimum and maximum constraints for each leaf */
206
  std::unique_ptr<LeafConstraintsBase> constraints_;
Guolin Ke's avatar
Guolin Ke committed
207
208

  /*! \brief stores best thresholds for all feature for smaller leaf */
Guolin Ke's avatar
Guolin Ke committed
209
  std::unique_ptr<LeafSplits> smaller_leaf_splits_;
Guolin Ke's avatar
Guolin Ke committed
210
  /*! \brief stores best thresholds for all feature for larger leaf */
Guolin Ke's avatar
Guolin Ke committed
211
  std::unique_ptr<LeafSplits> larger_leaf_splits_;
212
#if defined(USE_GPU)
213
  /*! \brief gradients of current iteration, ordered for cache optimized, aligned to 4K page */
214
  std::vector<score_t, boost::alignment::aligned_allocator<score_t, 4096>> ordered_gradients_;
215
  /*! \brief hessians of current iteration, ordered for cache optimized, aligned to 4K page */
216
  std::vector<score_t, boost::alignment::aligned_allocator<score_t, 4096>> ordered_hessians_;
217
#elif defined(USE_CUDA)
218
219
220
221
  /*! \brief gradients of current iteration, ordered for cache optimized */
  std::vector<score_t, CHAllocator<score_t>> ordered_gradients_;
  /*! \brief hessians of current iteration, ordered for cache optimized */
  std::vector<score_t, CHAllocator<score_t>> ordered_hessians_;
222
#else
Guolin Ke's avatar
Guolin Ke committed
223
  /*! \brief gradients of current iteration, ordered for cache optimized */
224
  std::vector<score_t, Common::AlignmentAllocator<score_t, kAlignedSize>> ordered_gradients_;
Guolin Ke's avatar
Guolin Ke committed
225
  /*! \brief hessians of current iteration, ordered for cache optimized */
226
  std::vector<score_t, Common::AlignmentAllocator<score_t, kAlignedSize>> ordered_hessians_;
227
#endif
228
  /*! \brief used to cache historical histogram to speed up*/
Guolin Ke's avatar
Guolin Ke committed
229
  HistogramPool histogram_pool_;
Guolin Ke's avatar
Guolin Ke committed
230
  /*! \brief config of tree learner*/
Guolin Ke's avatar
Guolin Ke committed
231
  const Config* config_;
232
233
  ColSampler col_sampler_;
  const Json* forced_split_json_;
234
  std::unique_ptr<TrainingShareStates> share_state_;
235
  std::unique_ptr<CostEfficientGradientBoosting> cegb_;
236
  std::unique_ptr<GradientDiscretizer> gradient_discretizer_;
Guolin Ke's avatar
Guolin Ke committed
237
238
};

Luke Gallagher's avatar
Luke Gallagher committed
239
240
241
inline data_size_t SerialTreeLearner::GetGlobalDataCountInLeaf(int leaf_idx) const {
  if (leaf_idx >= 0) {
    return data_partition_->leaf_count(leaf_idx);
Guolin Ke's avatar
Guolin Ke committed
242
243
244
245
246
247
  } else {
    return 0;
  }
}

}  // namespace LightGBM
Guolin Ke's avatar
Guolin Ke committed
248
#endif   // LightGBM_TREELEARNER_SERIAL_TREE_LEARNER_H_