goss.hpp 7.76 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
#ifndef LIGHTGBM_BOOSTING_GOSS_H_
#define LIGHTGBM_BOOSTING_GOSS_H_

#include <LightGBM/utils/array_args.h>
#include <LightGBM/utils/log.h>
#include <LightGBM/utils/openmp_wrapper.h>
#include <LightGBM/boosting.h>

#include "score_updater.hpp"
#include "gbdt.h"

#include <cstdio>
#include <vector>
#include <string>
#include <fstream>
#include <chrono>
17
#include <algorithm>
Guolin Ke's avatar
Guolin Ke committed
18
19
20

namespace LightGBM {

21
22
23
24
25
#ifdef TIMETAG
std::chrono::duration<double, std::milli> subset_time;
std::chrono::duration<double, std::milli> re_init_tree_time;
#endif

Guolin Ke's avatar
Guolin Ke committed
26
class GOSS: public GBDT {
Nikita Titov's avatar
Nikita Titov committed
27
 public:
Guolin Ke's avatar
Guolin Ke committed
28
29
30
  /*!
  * \brief Constructor
  */
31
  GOSS() : GBDT() {
Guolin Ke's avatar
Guolin Ke committed
32
33
34
  }

  ~GOSS() {
35
    #ifdef TIMETAG
36
37
    Log::Info("GOSS::subset costs %f", subset_time * 1e-3);
    Log::Info("GOSS::re_init_tree costs %f", re_init_tree_time * 1e-3);
38
    #endif
Guolin Ke's avatar
Guolin Ke committed
39
40
  }

Guolin Ke's avatar
Guolin Ke committed
41
  void Init(const Config* config, const Dataset* train_data, const ObjectiveFunction* objective_function,
42
43
            const std::vector<const Metric*>& training_metrics) override {
    GBDT::Init(config, train_data, objective_function, training_metrics);
44
45
46
47
48
49
50
51
52
    ResetGoss();
  }

  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);
    ResetGoss();
  }

Guolin Ke's avatar
Guolin Ke committed
53
  void ResetConfig(const Config* config) override {
54
55
56
57
58
    GBDT::ResetConfig(config);
    ResetGoss();
  }

  void ResetGoss() {
Guolin Ke's avatar
Guolin Ke committed
59
60
61
    CHECK(config_->top_rate + config_->other_rate <= 1.0f);
    CHECK(config_->top_rate > 0.0f && config_->other_rate > 0.0f);
    if (config_->bagging_freq > 0 && config_->bagging_fraction != 1.0f) {
62
      Log::Fatal("Cannot use bagging in GOSS");
Guolin Ke's avatar
Guolin Ke committed
63
    }
64
    Log::Info("Using GOSS");
Guolin Ke's avatar
Guolin Ke committed
65
66
67
68
69
70
71
72
73
74
75

    bag_data_indices_.resize(num_data_);
    tmp_indices_.resize(num_data_);
    tmp_indice_right_.resize(num_data_);
    offsets_buf_.resize(num_threads_);
    left_cnts_buf_.resize(num_threads_);
    right_cnts_buf_.resize(num_threads_);
    left_write_pos_buf_.resize(num_threads_);
    right_write_pos_buf_.resize(num_threads_);

    is_use_subset_ = false;
Guolin Ke's avatar
Guolin Ke committed
76
77
    if (config_->top_rate + config_->other_rate <= 0.5) {
      auto bag_data_cnt = static_cast<data_size_t>((config_->top_rate + config_->other_rate) * num_data_);
78
      bag_data_cnt = std::max(1, bag_data_cnt);
Guolin Ke's avatar
Guolin Ke committed
79
80
81
82
83
84
85
86
87
      tmp_subset_.reset(new Dataset(bag_data_cnt));
      tmp_subset_->CopyFeatureMapperFrom(train_data_);
      is_use_subset_ = true;
    }
    // flag to not bagging first
    bag_data_cnt_ = num_data_;
  }

  data_size_t BaggingHelper(Random& cur_rand, data_size_t start, data_size_t cnt, data_size_t* buffer, data_size_t* buffer_right) {
88
    if (cnt <= 0) {
89
90
      return 0;
    }
91
    std::vector<score_t> tmp_gradients(cnt, 0.0f);
Guolin Ke's avatar
Guolin Ke committed
92
    for (data_size_t i = 0; i < cnt; ++i) {
93
      for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
94
        size_t idx = static_cast<size_t>(cur_tree_id) * num_data_ + start + i;
Guolin Ke's avatar
Guolin Ke committed
95
96
        tmp_gradients[i] += std::fabs(gradients_[idx] * hessians_[idx]);
      }
Guolin Ke's avatar
Guolin Ke committed
97
    }
Guolin Ke's avatar
Guolin Ke committed
98
99
    data_size_t top_k = static_cast<data_size_t>(cnt * config_->top_rate);
    data_size_t other_k = static_cast<data_size_t>(cnt * config_->other_rate);
Guolin Ke's avatar
Guolin Ke committed
100
    top_k = std::max(1, top_k);
Guolin Ke's avatar
Guolin Ke committed
101
    ArrayArgs<score_t>::ArgMaxAtK(&tmp_gradients, 0, static_cast<int>(tmp_gradients.size()), top_k - 1);
102
    score_t threshold = tmp_gradients[top_k - 1];
Guolin Ke's avatar
Guolin Ke committed
103

104
    score_t multiply = static_cast<score_t>(cnt - top_k) / other_k;
Guolin Ke's avatar
Guolin Ke committed
105
106
107
108
    data_size_t cur_left_cnt = 0;
    data_size_t cur_right_cnt = 0;
    data_size_t big_weight_cnt = 0;
    for (data_size_t i = 0; i < cnt; ++i) {
109
      score_t grad = 0.0f;
110
      for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
111
        size_t idx = static_cast<size_t>(cur_tree_id) * num_data_ + start + i;
Guolin Ke's avatar
Guolin Ke committed
112
113
114
        grad += std::fabs(gradients_[idx] * hessians_[idx]);
      }
      if (grad >= threshold) {
Guolin Ke's avatar
Guolin Ke committed
115
116
117
118
119
120
121
122
123
        buffer[cur_left_cnt++] = start + i;
        ++big_weight_cnt;
      } else {
        data_size_t sampled = cur_left_cnt - big_weight_cnt;
        data_size_t rest_need = other_k - sampled;
        data_size_t rest_all = (cnt - i) - (top_k - big_weight_cnt);
        double prob = (rest_need) / static_cast<double>(rest_all);
        if (cur_rand.NextFloat() < prob) {
          buffer[cur_left_cnt++] = start + i;
124
          for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
125
            size_t idx = static_cast<size_t>(cur_tree_id) * num_data_ + start + i;
Guolin Ke's avatar
Guolin Ke committed
126
127
128
            gradients_[idx] *= multiply;
            hessians_[idx] *= multiply;
          }
Guolin Ke's avatar
Guolin Ke committed
129
130
131
132
133
134
135
136
137
138
139
        } else {
          buffer_right[cur_right_cnt++] = start + i;
        }
      }
    }
    return cur_left_cnt;
  }

  void Bagging(int iter) override {
    bag_data_cnt_ = num_data_;
    // not subsample for first iterations
Guolin Ke's avatar
Guolin Ke committed
140
    if (iter < static_cast<int>(1.0f / config_->learning_rate)) { return; }
Guolin Ke's avatar
Guolin Ke committed
141

Guolin Ke's avatar
Guolin Ke committed
142
    const data_size_t min_inner_size = 100;
Guolin Ke's avatar
Guolin Ke committed
143
144
    data_size_t inner_size = (num_data_ + num_threads_ - 1) / num_threads_;
    if (inner_size < min_inner_size) { inner_size = min_inner_size; }
145
    OMP_INIT_EX();
146
    #pragma omp parallel for schedule(static, 1)
Guolin Ke's avatar
Guolin Ke committed
147
    for (int i = 0; i < num_threads_; ++i) {
148
      OMP_LOOP_EX_BEGIN();
Guolin Ke's avatar
Guolin Ke committed
149
150
151
152
153
154
      left_cnts_buf_[i] = 0;
      right_cnts_buf_[i] = 0;
      data_size_t cur_start = i * inner_size;
      if (cur_start > num_data_) { continue; }
      data_size_t cur_cnt = inner_size;
      if (cur_start + cur_cnt > num_data_) { cur_cnt = num_data_ - cur_start; }
Guolin Ke's avatar
Guolin Ke committed
155
      Random cur_rand(config_->bagging_seed + iter * num_threads_ + i);
Guolin Ke's avatar
Guolin Ke committed
156
      data_size_t cur_left_count = BaggingHelper(cur_rand, cur_start, cur_cnt,
157
                                                 tmp_indices_.data() + cur_start, tmp_indice_right_.data() + cur_start);
Guolin Ke's avatar
Guolin Ke committed
158
159
160
      offsets_buf_[i] = cur_start;
      left_cnts_buf_[i] = cur_left_count;
      right_cnts_buf_[i] = cur_cnt - cur_left_count;
161
      OMP_LOOP_EX_END();
Guolin Ke's avatar
Guolin Ke committed
162
    }
163
    OMP_THROW_EX();
Guolin Ke's avatar
Guolin Ke committed
164
165
166
167
168
169
170
171
172
    data_size_t left_cnt = 0;
    left_write_pos_buf_[0] = 0;
    right_write_pos_buf_[0] = 0;
    for (int i = 1; i < num_threads_; ++i) {
      left_write_pos_buf_[i] = left_write_pos_buf_[i - 1] + left_cnts_buf_[i - 1];
      right_write_pos_buf_[i] = right_write_pos_buf_[i - 1] + right_cnts_buf_[i - 1];
    }
    left_cnt = left_write_pos_buf_[num_threads_ - 1] + left_cnts_buf_[num_threads_ - 1];

173
    #pragma omp parallel for schedule(static, 1)
Guolin Ke's avatar
Guolin Ke committed
174
    for (int i = 0; i < num_threads_; ++i) {
175
      OMP_LOOP_EX_BEGIN();
Guolin Ke's avatar
Guolin Ke committed
176
177
      if (left_cnts_buf_[i] > 0) {
        std::memcpy(bag_data_indices_.data() + left_write_pos_buf_[i],
178
                    tmp_indices_.data() + offsets_buf_[i], left_cnts_buf_[i] * sizeof(data_size_t));
Guolin Ke's avatar
Guolin Ke committed
179
180
181
      }
      if (right_cnts_buf_[i] > 0) {
        std::memcpy(bag_data_indices_.data() + left_cnt + right_write_pos_buf_[i],
182
                    tmp_indice_right_.data() + offsets_buf_[i], right_cnts_buf_[i] * sizeof(data_size_t));
Guolin Ke's avatar
Guolin Ke committed
183
      }
184
      OMP_LOOP_EX_END();
Guolin Ke's avatar
Guolin Ke committed
185
    }
186
    OMP_THROW_EX();
Guolin Ke's avatar
Guolin Ke committed
187
188
189
190
191
192
    bag_data_cnt_ = left_cnt;
    // set bagging data to tree learner
    if (!is_use_subset_) {
      tree_learner_->SetBaggingData(bag_data_indices_.data(), bag_data_cnt_);
    } else {
      // get subset
193
      #ifdef TIMETAG
194
      auto start_time = std::chrono::steady_clock::now();
195
      #endif
Guolin Ke's avatar
Guolin Ke committed
196
197
      tmp_subset_->ReSize(bag_data_cnt_);
      tmp_subset_->CopySubset(train_data_, bag_data_indices_.data(), bag_data_cnt_, false);
198
      #ifdef TIMETAG
199
      subset_time += std::chrono::steady_clock::now() - start_time;
200
201
      #endif
      #ifdef TIMETAG
202
      start_time = std::chrono::steady_clock::now();
203
      #endif
Guolin Ke's avatar
Guolin Ke committed
204
      tree_learner_->ResetTrainingData(tmp_subset_.get());
205
      #ifdef TIMETAG
206
      re_init_tree_time += std::chrono::steady_clock::now() - start_time;
207
      #endif
Guolin Ke's avatar
Guolin Ke committed
208
209
210
    }
  }

Nikita Titov's avatar
Nikita Titov committed
211
 private:
Guolin Ke's avatar
Guolin Ke committed
212
213
214
215
216
  std::vector<data_size_t> tmp_indice_right_;
};

}  // namespace LightGBM
#endif   // LIGHTGBM_BOOSTING_GOSS_H_