rf.hpp 6.97 KB
Newer Older
Guolin Ke's avatar
Guolin Ke committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef LIGHTGBM_BOOSTING_RF_H_
#define LIGHTGBM_BOOSTING_RF_H_

#include <LightGBM/boosting.h>
#include <LightGBM/metric.h>
#include "score_updater.hpp"
#include "gbdt.h"

#include <cstdio>
#include <vector>
#include <string>
#include <fstream>

namespace LightGBM {
/*!
* \brief Rondom Forest implementation
*/
class RF: public GBDT {
public:

  RF() : GBDT() { 
    average_output_ = true;
  }

  ~RF() {}

Guolin Ke's avatar
Guolin Ke committed
27
  void Init(const Config* config, const Dataset* train_data, const ObjectiveFunction* objective_function,
Guolin Ke's avatar
Guolin Ke committed
28
29
            const std::vector<const Metric*>& training_metrics) override {
    CHECK(config->bagging_freq > 0 && config->bagging_fraction < 1.0f && config->bagging_fraction > 0.0f);
30
    CHECK(config->feature_fraction <= 1.0f && config->feature_fraction > 0.0f);
Guolin Ke's avatar
Guolin Ke committed
31
32
33
34
35
36
37
38
39
40
    GBDT::Init(config, train_data, objective_function, training_metrics);

    if (num_init_iteration_ > 0) {
      for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
        MultiplyScore(cur_tree_id, 1.0f / num_init_iteration_);
      }
    } else {
      CHECK(train_data->metadata().init_score() == nullptr);
    }
    // cannot use RF for multi-class. 
41
    CHECK(num_tree_per_iteration_ == num_class_);
Guolin Ke's avatar
Guolin Ke committed
42
43
44
    // not shrinkage rate for the RF
    shrinkage_rate_ = 1.0f;
    // only boosting one time
45
    GetRFTargets(train_data);
Guolin Ke's avatar
Guolin Ke committed
46
    if (is_use_subset_ && bag_data_cnt_ < num_data_) {
47
48
      tmp_grad_.resize(num_data_);
      tmp_hess_.resize(num_data_);
Guolin Ke's avatar
Guolin Ke committed
49
    }
Guolin Ke's avatar
Guolin Ke committed
50
    tmp_score_.resize(num_data_, 0.0);
Guolin Ke's avatar
Guolin Ke committed
51
52
  }

Guolin Ke's avatar
Guolin Ke committed
53
  void ResetConfig(const Config* config) override {
Guolin Ke's avatar
Guolin Ke committed
54
    CHECK(config->bagging_freq > 0 && config->bagging_fraction < 1.0f && config->bagging_fraction > 0.0f);
55
    CHECK(config->feature_fraction <= 1.0f && config->feature_fraction > 0.0f);
Guolin Ke's avatar
Guolin Ke committed
56
57
58
59
60
61
62
63
64
65
66
67
68
69
    GBDT::ResetConfig(config);
    // not shrinkage rate for the RF
    shrinkage_rate_ = 1.0f;
  }

  void ResetTrainingData(const Dataset* train_data, const ObjectiveFunction* objective_function,
                         const std::vector<const Metric*>& training_metrics) override {
    GBDT::ResetTrainingData(train_data, objective_function, training_metrics);
    if (iter_ + num_init_iteration_ > 0) {
      for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
        train_score_updater_->MultiplyScore(1.0f / (iter_ + num_init_iteration_), cur_tree_id);
      }
    }
    // cannot use RF for multi-class.
70
    CHECK(num_tree_per_iteration_ == num_class_);
Guolin Ke's avatar
Guolin Ke committed
71
    // only boosting one time
72
    GetRFTargets(train_data);
Guolin Ke's avatar
Guolin Ke committed
73
    if (is_use_subset_ && bag_data_cnt_ < num_data_) {
74
75
      tmp_grad_.resize(num_data_);
      tmp_hess_.resize(num_data_);
Guolin Ke's avatar
Guolin Ke committed
76
    }
Guolin Ke's avatar
Guolin Ke committed
77
    tmp_score_.resize(num_data_, 0.0);
Guolin Ke's avatar
Guolin Ke committed
78
79
  }

80
81
  void GetRFTargets(const Dataset* train_data) {
    auto label_ptr = train_data->metadata().label();
Guolin Ke's avatar
Guolin Ke committed
82
    std::fill(hessians_.begin(), hessians_.end(), 1.0f);
83
84
85
86
87
    if (num_tree_per_iteration_ == 1) {
      OMP_INIT_EX();
      #pragma omp parallel for schedule(static,1)
      for (data_size_t i = 0; i < train_data->num_data(); ++i) {
        OMP_LOOP_EX_BEGIN();
Guolin Ke's avatar
Guolin Ke committed
88
        score_t label = label_ptr[i];
89
90
91
92
93
94
        gradients_[i] = static_cast<score_t>(-label);
        OMP_LOOP_EX_END();
      }
      OMP_THROW_EX();
    }
    else {
Guolin Ke's avatar
Guolin Ke committed
95
      std::fill(gradients_.begin(), gradients_.end(), 0.0f);
96
97
98
99
      OMP_INIT_EX();
      #pragma omp parallel for schedule(static,1)
      for (data_size_t i = 0; i < train_data->num_data(); ++i) {
        OMP_LOOP_EX_BEGIN();
Guolin Ke's avatar
Guolin Ke committed
100
101
        score_t label = label_ptr[i];
        gradients_[i + static_cast<int>(label) * num_data_] = -1.0f;
102
103
104
        OMP_LOOP_EX_END();
      }
      OMP_THROW_EX();
Guolin Ke's avatar
Guolin Ke committed
105
    }
106
107
108
109
  }

  void Boosting() override {

Guolin Ke's avatar
Guolin Ke committed
110
111
  }

Guolin Ke's avatar
Guolin Ke committed
112
  bool TrainOneIter(const score_t* gradients, const score_t* hessians) override {
Guolin Ke's avatar
Guolin Ke committed
113
114
    // bagging logic
    Bagging(iter_);
115
116
    CHECK(gradients == nullptr);
    CHECK(hessians == nullptr);
Guolin Ke's avatar
Guolin Ke committed
117

118
119
    gradients = gradients_.data();
    hessians = hessians_.data();
Guolin Ke's avatar
Guolin Ke committed
120
121
    for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
      std::unique_ptr<Tree> new_tree(new Tree(2));
122
123
124
125
126
127
128
129
130
      size_t bias = static_cast<size_t>(cur_tree_id)* num_data_;
      auto grad = gradients + bias;
      auto hess = hessians + bias;

      // need to copy gradients for bagging subset.
      if (is_use_subset_ && bag_data_cnt_ < num_data_) {
        for (int i = 0; i < bag_data_cnt_; ++i) {
          tmp_grad_[i] = grad[bag_data_indices_[i]];
          tmp_hess_[i] = hess[bag_data_indices_[i]];
Guolin Ke's avatar
Guolin Ke committed
131
        }
132
133
        grad = tmp_grad_.data();
        hess = tmp_hess_.data();
Guolin Ke's avatar
Guolin Ke committed
134
      }
135
136
      new_tree.reset(tree_learner_->Train(grad, hess, is_constant_hessian_,
        forced_splits_json_));
Guolin Ke's avatar
Guolin Ke committed
137
      if (new_tree->num_leaves() > 1) {
138
139
        tree_learner_->RenewTreeOutput(new_tree.get(), objective_function_, tmp_score_.data(),
          num_data_, bag_data_indices_.data(), bag_data_cnt_);
Guolin Ke's avatar
Guolin Ke committed
140
141
142
143
144
145
146
147
148
        // update score
        MultiplyScore(cur_tree_id, (iter_ + num_init_iteration_));
        UpdateScore(new_tree.get(), cur_tree_id);
        MultiplyScore(cur_tree_id, 1.0 / (iter_ + num_init_iteration_ + 1));
      }
      // add model
      models_.push_back(std::move(new_tree));
    }
    ++iter_;
Guolin Ke's avatar
Guolin Ke committed
149
    return false;
Guolin Ke's avatar
Guolin Ke committed
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
  }

  void RollbackOneIter() override {
    if (iter_ <= 0) { return; }
    int cur_iter = iter_ + num_init_iteration_ - 1;
    // reset score
    for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
      auto curr_tree = cur_iter * num_tree_per_iteration_ + cur_tree_id;
      models_[curr_tree]->Shrinkage(-1.0);
      MultiplyScore(cur_tree_id, (iter_ + num_init_iteration_));
      train_score_updater_->AddScore(models_[curr_tree].get(), cur_tree_id);
      for (auto& score_updater : valid_score_updater_) {
        score_updater->AddScore(models_[curr_tree].get(), cur_tree_id);
      }
      MultiplyScore(cur_tree_id, 1.0f / (iter_ + num_init_iteration_ - 1));
    }
    // remove model
    for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
      models_.pop_back();
    }
    --iter_;
  }

  void MultiplyScore(const int cur_tree_id, double val) {
    train_score_updater_->MultiplyScore(val, cur_tree_id);
    for (auto& score_updater : valid_score_updater_) {
      score_updater->MultiplyScore(val, cur_tree_id);
    }
  }

  void AddValidDataset(const Dataset* valid_data,
                       const std::vector<const Metric*>& valid_metrics) override {
    GBDT::AddValidDataset(valid_data, valid_metrics);
    if (iter_ + num_init_iteration_ > 0) {
      for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
        valid_score_updater_.back()->MultiplyScore(1.0f / (iter_ + num_init_iteration_), cur_tree_id);
      }
    }
  }

  bool NeedAccuratePrediction() const override {
    // No early stopping for prediction
    return true;
  };

  std::vector<double> EvalOneMetric(const Metric* metric, const double* score) const override {
    return metric->Eval(score, nullptr);
  }

private:

  std::vector<score_t> tmp_grad_;
  std::vector<score_t> tmp_hess_;
203
  std::vector<double> tmp_score_;
Guolin Ke's avatar
Guolin Ke committed
204
205
206
207
208

};

}  // namespace LightGBM
#endif   // LIGHTGBM_BOOSTING_RF_H_