dart.hpp 7.22 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_BOOSTING_DART_H_
#define LIGHTGBM_BOOSTING_DART_H_

8
9
#include <LightGBM/boosting.h>

Guolin Ke's avatar
Guolin Ke committed
10
#include <string>
11
12
#include <algorithm>
#include <cstdio>
Guolin Ke's avatar
Guolin Ke committed
13
#include <fstream>
14
15
16
17
#include <vector>

#include "gbdt.h"
#include "score_updater.hpp"
Guolin Ke's avatar
Guolin Ke committed
18
19
20
21
22
23

namespace LightGBM {
/*!
* \brief DART algorithm implementation. including Training, prediction, bagging.
*/
class DART: public GBDT {
Nikita Titov's avatar
Nikita Titov committed
24
 public:
Guolin Ke's avatar
Guolin Ke committed
25
26
27
  /*!
  * \brief Constructor
  */
28
  DART() : GBDT() { }
Guolin Ke's avatar
Guolin Ke committed
29
30
31
32
33
34
35
36
  /*!
  * \brief Destructor
  */
  ~DART() { }
  /*!
  * \brief Initialization logic
  * \param config Config for boosting
  * \param train_data Training data
37
  * \param objective_function Training objective function
Guolin Ke's avatar
Guolin Ke committed
38
39
40
  * \param training_metrics Training metrics
  * \param output_model_filename Filename of output model
  */
Guolin Ke's avatar
Guolin Ke committed
41
  void Init(const Config* config, const Dataset* train_data,
42
            const ObjectiveFunction* objective_function,
43
44
            const std::vector<const Metric*>& training_metrics) override {
    GBDT::Init(config, train_data, objective_function, training_metrics);
Guolin Ke's avatar
Guolin Ke committed
45
    random_for_drop_ = Random(config_->drop_seed);
46
    sum_weight_ = 0.0f;
Guolin Ke's avatar
Guolin Ke committed
47
  }
Guolin Ke's avatar
Guolin Ke committed
48

Guolin Ke's avatar
Guolin Ke committed
49
  void ResetConfig(const Config* config) override {
Guolin Ke's avatar
Guolin Ke committed
50
    GBDT::ResetConfig(config);
Guolin Ke's avatar
Guolin Ke committed
51
    random_for_drop_ = Random(config_->drop_seed);
Guolin Ke's avatar
Guolin Ke committed
52
53
54
    sum_weight_ = 0.0f;
  }

Guolin Ke's avatar
Guolin Ke committed
55
56
57
  /*!
  * \brief one training iteration
  */
Guolin Ke's avatar
Guolin Ke committed
58
  bool TrainOneIter(const score_t* gradient, const score_t* hessian) override {
Guolin Ke's avatar
Guolin Ke committed
59
    is_update_score_cur_iter_ = false;
Guolin Ke's avatar
Guolin Ke committed
60
61
62
63
    bool ret = GBDT::TrainOneIter(gradient, hessian);
    if (ret) {
      return ret;
    }
Guolin Ke's avatar
Guolin Ke committed
64
65
    // normalize
    Normalize();
Guolin Ke's avatar
Guolin Ke committed
66
    if (!config_->uniform_drop) {
67
68
69
      tree_weight_.push_back(shrinkage_rate_);
      sum_weight_ += shrinkage_rate_;
    }
Guolin Ke's avatar
Guolin Ke committed
70
    return false;
Guolin Ke's avatar
Guolin Ke committed
71
  }
Guolin Ke's avatar
Guolin Ke committed
72

Guolin Ke's avatar
Guolin Ke committed
73
74
75
76
77
  /*!
  * \brief Get current training score
  * \param out_len length of returned score
  * \return training score
  */
78
  const double* GetTrainingScore(int64_t* out_len) override {
Guolin Ke's avatar
Guolin Ke committed
79
80
81
82
83
    if (!is_update_score_cur_iter_) {
      // only drop one time in one iteration
      DroppingTrees();
      is_update_score_cur_iter_ = true;
    }
84
    *out_len = static_cast<int64_t>(train_score_updater_->num_data()) * num_class_;
Guolin Ke's avatar
Guolin Ke committed
85
86
    return train_score_updater_->score();
  }
87

88
89
90
91
  bool EvalAndCheckEarlyStopping() override {
    GBDT::OutputMetric(iter_);
    return false;
  }
Guolin Ke's avatar
Guolin Ke committed
92

Nikita Titov's avatar
Nikita Titov committed
93
 private:
Guolin Ke's avatar
Guolin Ke committed
94
95
96
97
98
  /*!
  * \brief drop trees based on drop_rate
  */
  void DroppingTrees() {
    drop_index_.clear();
Guolin Ke's avatar
Guolin Ke committed
99
    bool is_skip = random_for_drop_.NextFloat() < config_->skip_drop;
zhangyafeikimi's avatar
zhangyafeikimi committed
100
    // select dropping tree indices based on drop_rate and tree weights
101
    if (!is_skip) {
Guolin Ke's avatar
Guolin Ke committed
102
103
      double drop_rate = config_->drop_rate;
      if (!config_->uniform_drop) {
104
        double inv_average_weight = static_cast<double>(tree_weight_.size()) / sum_weight_;
Guolin Ke's avatar
Guolin Ke committed
105
106
        if (config_->max_drop > 0) {
          drop_rate = std::min(drop_rate, config_->max_drop * inv_average_weight / sum_weight_);
107
108
        }
        for (int i = 0; i < iter_; ++i) {
Guolin Ke's avatar
Guolin Ke committed
109
          if (random_for_drop_.NextFloat() < drop_rate * tree_weight_[i] * inv_average_weight) {
Guolin Ke's avatar
Guolin Ke committed
110
            drop_index_.push_back(num_init_iteration_ + i);
Guolin Ke's avatar
Guolin Ke committed
111
            if (drop_index_.size() >= static_cast<size_t>(config_->max_drop)) {
112
113
              break;
            }
114
115
116
          }
        }
      } else {
Guolin Ke's avatar
Guolin Ke committed
117
118
        if (config_->max_drop > 0) {
          drop_rate = std::min(drop_rate, config_->max_drop / static_cast<double>(iter_));
119
120
        }
        for (int i = 0; i < iter_; ++i) {
Guolin Ke's avatar
Guolin Ke committed
121
          if (random_for_drop_.NextFloat() < drop_rate) {
Guolin Ke's avatar
Guolin Ke committed
122
            drop_index_.push_back(num_init_iteration_ + i);
Guolin Ke's avatar
Guolin Ke committed
123
            if (drop_index_.size() >= static_cast<size_t>(config_->max_drop)) {
124
125
              break;
            }
126
          }
Guolin Ke's avatar
Guolin Ke committed
127
128
129
130
131
        }
      }
    }
    // drop trees
    for (auto i : drop_index_) {
132
133
      for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
        auto curr_tree = i * num_tree_per_iteration_ + cur_tree_id;
Guolin Ke's avatar
Guolin Ke committed
134
        models_[curr_tree]->Shrinkage(-1.0);
135
        train_score_updater_->AddScore(models_[curr_tree].get(), cur_tree_id);
Guolin Ke's avatar
Guolin Ke committed
136
137
      }
    }
Guolin Ke's avatar
Guolin Ke committed
138
139
    if (!config_->xgboost_dart_mode) {
      shrinkage_rate_ = config_->learning_rate / (1.0f + static_cast<double>(drop_index_.size()));
140
141
    } else {
      if (drop_index_.empty()) {
Guolin Ke's avatar
Guolin Ke committed
142
        shrinkage_rate_ = config_->learning_rate;
143
      } else {
Guolin Ke's avatar
Guolin Ke committed
144
        shrinkage_rate_ = config_->learning_rate / (config_->learning_rate + static_cast<double>(drop_index_.size()));
145
146
      }
    }
Guolin Ke's avatar
Guolin Ke committed
147
148
149
  }
  /*!
  * \brief normalize dropped trees
150
  * NOTE: num_drop_tree(k), learning_rate(lr), shrinkage_rate_ = lr / (k + 1)
wxchan's avatar
wxchan committed
151
  *       step 1: shrink tree to -1 -> drop tree
152
  *       step 2: shrink tree to k / (k + 1) - 1 from -1, by 1/(k+1)
wxchan's avatar
wxchan committed
153
  *               -> normalize for valid data
154
  *       step 3: shrink tree to k / (k + 1) from k / (k + 1) - 1, by -k
wxchan's avatar
wxchan committed
155
  *               -> normalize for train data
156
  *       end with tree weight = (k / (k + 1)) * old_weight
Guolin Ke's avatar
Guolin Ke committed
157
158
159
  */
  void Normalize() {
    double k = static_cast<double>(drop_index_.size());
Guolin Ke's avatar
Guolin Ke committed
160
    if (!config_->xgboost_dart_mode) {
161
      for (auto i : drop_index_) {
162
163
        for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
          auto curr_tree = i * num_tree_per_iteration_ + cur_tree_id;
164
165
166
          // update validation score
          models_[curr_tree]->Shrinkage(1.0f / (k + 1.0f));
          for (auto& score_updater : valid_score_updater_) {
167
            score_updater->AddScore(models_[curr_tree].get(), cur_tree_id);
168
169
170
          }
          // update training score
          models_[curr_tree]->Shrinkage(-k);
171
          train_score_updater_->AddScore(models_[curr_tree].get(), cur_tree_id);
172
        }
Guolin Ke's avatar
Guolin Ke committed
173
        if (!config_->uniform_drop) {
174
175
          sum_weight_ -= tree_weight_[i - num_init_iteration_] * (1.0f / (k + 1.0f));
          tree_weight_[i - num_init_iteration_] *= (k / (k + 1.0f));
176
177
178
179
        }
      }
    } else {
      for (auto i : drop_index_) {
180
181
        for (int cur_tree_id = 0; cur_tree_id < num_tree_per_iteration_; ++cur_tree_id) {
          auto curr_tree = i * num_tree_per_iteration_ + cur_tree_id;
182
183
184
          // update validation score
          models_[curr_tree]->Shrinkage(shrinkage_rate_);
          for (auto& score_updater : valid_score_updater_) {
185
            score_updater->AddScore(models_[curr_tree].get(), cur_tree_id);
186
187
          }
          // update training score
Guolin Ke's avatar
Guolin Ke committed
188
          models_[curr_tree]->Shrinkage(-k / config_->learning_rate);
189
          train_score_updater_->AddScore(models_[curr_tree].get(), cur_tree_id);
190
        }
Guolin Ke's avatar
Guolin Ke committed
191
        if (!config_->uniform_drop) {
192
193
          sum_weight_ -= tree_weight_[i - num_init_iteration_] * (1.0f / (k + config_->learning_rate));;
          tree_weight_[i - num_init_iteration_] *= (k / (k + config_->learning_rate));
Guolin Ke's avatar
Guolin Ke committed
194
195
196
197
        }
      }
    }
  }
198
199
200
201
  /*! \brief The weights of all trees, used to choose drop trees */
  std::vector<double> tree_weight_;
  /*! \brief sum weights of all trees */
  double sum_weight_;
zhangyafeikimi's avatar
zhangyafeikimi committed
202
  /*! \brief The indices of dropping trees */
203
  std::vector<int> drop_index_;
Guolin Ke's avatar
Guolin Ke committed
204
205
  /*! \brief Random generator, used to select dropping trees */
  Random random_for_drop_;
Guolin Ke's avatar
Guolin Ke committed
206
207
  /*! \brief Flag that the score is update on current iter or not*/
  bool is_update_score_cur_iter_;
Guolin Ke's avatar
Guolin Ke committed
208
209
210
211
};

}  // namespace LightGBM
#endif   // LightGBM_BOOSTING_DART_H_