regression_metric.hpp 8.75 KB
Newer Older
Guolin Ke's avatar
Guolin Ke committed
1
2
3
4
5
#ifndef LIGHTGBM_METRIC_REGRESSION_METRIC_HPP_
#define LIGHTGBM_METRIC_REGRESSION_METRIC_HPP_

#include <LightGBM/metric.h>

Guolin Ke's avatar
Guolin Ke committed
6
7
#include <LightGBM/utils/log.h>

Guolin Ke's avatar
Guolin Ke committed
8
9
10
11
12
13
14
15
16
17
#include <cmath>

namespace LightGBM {
/*!
* \brief Metric for regression task.
* Use static class "PointWiseLossCalculator" to calculate loss point-wise
*/
template<typename PointWiseLossCalculator>
class RegressionMetric: public Metric {
public:
Guolin Ke's avatar
Guolin Ke committed
18
  explicit RegressionMetric(const Config& config) :config_(config) {
Guolin Ke's avatar
Guolin Ke committed
19
20
21
22
23
24
  }

  virtual ~RegressionMetric() {

  }

Guolin Ke's avatar
Guolin Ke committed
25
  const std::vector<std::string>& GetName() const override {
26
    return name_;
27
28
  }

29
  double factor_to_bigger_better() const override {
30
    return -1.0f;
31
32
  }

Guolin Ke's avatar
Guolin Ke committed
33
34
  void Init(const Metadata& metadata, data_size_t num_data) override {
    name_.emplace_back(PointWiseLossCalculator::Name());
Guolin Ke's avatar
Guolin Ke committed
35
36
37
38
39
40
    num_data_ = num_data;
    // get label
    label_ = metadata.label();
    // get weights
    weights_ = metadata.weights();
    if (weights_ == nullptr) {
41
      sum_weights_ = static_cast<double>(num_data_);
Guolin Ke's avatar
Guolin Ke committed
42
43
44
45
46
47
48
    } else {
      sum_weights_ = 0.0f;
      for (data_size_t i = 0; i < num_data_; ++i) {
        sum_weights_ += weights_[i];
      }
    }
  }
49

Guolin Ke's avatar
Guolin Ke committed
50
  std::vector<double> Eval(const double* score, const ObjectiveFunction* objective) const override {
51
    double sum_loss = 0.0f;
52
53
54
55
56
    if (objective == nullptr) {
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          // add loss
57
          sum_loss += PointWiseLossCalculator::LossOnPoint(label_[i], score[i], config_);
58
59
60
61
62
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          // add loss
63
          sum_loss += PointWiseLossCalculator::LossOnPoint(label_[i], score[i], config_) * weights_[i];
64
        }
Guolin Ke's avatar
Guolin Ke committed
65
      }
66
    } else {
67
68
69
70
      if (weights_ == nullptr) {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          // add loss
Guolin Ke's avatar
Guolin Ke committed
71
72
          double t = 0;
          objective->ConvertOutput(&score[i], &t);
73
          sum_loss += PointWiseLossCalculator::LossOnPoint(label_[i], t, config_);
74
75
76
77
78
        }
      } else {
        #pragma omp parallel for schedule(static) reduction(+:sum_loss)
        for (data_size_t i = 0; i < num_data_; ++i) {
          // add loss
Guolin Ke's avatar
Guolin Ke committed
79
80
          double t = 0;
          objective->ConvertOutput(&score[i], &t);
81
          sum_loss += PointWiseLossCalculator::LossOnPoint(label_[i], t, config_) * weights_[i];
82
        }
wxchan's avatar
wxchan committed
83
      }
Guolin Ke's avatar
Guolin Ke committed
84
    }
85
86
    double loss = PointWiseLossCalculator::AverageLoss(sum_loss, sum_weights_);
    return std::vector<double>(1, loss);
87

Guolin Ke's avatar
Guolin Ke committed
88
89
  }

90
  inline static double AverageLoss(double sum_loss, double sum_weights) {
Guolin Ke's avatar
Guolin Ke committed
91
92
93
94
95
96
    return sum_loss / sum_weights;
  }
private:
  /*! \brief Number of data */
  data_size_t num_data_;
  /*! \brief Pointer of label */
97
  const label_t* label_;
Guolin Ke's avatar
Guolin Ke committed
98
  /*! \brief Pointer of weighs */
99
  const label_t* weights_;
Guolin Ke's avatar
Guolin Ke committed
100
  /*! \brief Sum weights */
101
  double sum_weights_;
Guolin Ke's avatar
Guolin Ke committed
102
  /*! \brief Name of this test set */
Guolin Ke's avatar
Guolin Ke committed
103
  Config config_;
104
  std::vector<std::string> name_;
Guolin Ke's avatar
Guolin Ke committed
105
106
};

107
108
109
/*! \brief RMSE loss for regression task */
class RMSEMetric: public RegressionMetric<RMSEMetric> {
public:
Guolin Ke's avatar
Guolin Ke committed
110
  explicit RMSEMetric(const Config& config) :RegressionMetric<RMSEMetric>(config) {}
111

Guolin Ke's avatar
Guolin Ke committed
112
  inline static double LossOnPoint(label_t label, double score, const Config&) {
113
114
115
116
117
118
119
120
121
122
123
124
125
    return (score - label)*(score - label);
  }

  inline static double AverageLoss(double sum_loss, double sum_weights) {
    // need sqrt the result for RMSE loss
    return std::sqrt(sum_loss / sum_weights);
  }

  inline static const char* Name() {
    return "rmse";
  }
};

Guolin Ke's avatar
Guolin Ke committed
126
127
128
/*! \brief L2 loss for regression task */
class L2Metric: public RegressionMetric<L2Metric> {
public:
Guolin Ke's avatar
Guolin Ke committed
129
  explicit L2Metric(const Config& config) :RegressionMetric<L2Metric>(config) {}
Guolin Ke's avatar
Guolin Ke committed
130

Guolin Ke's avatar
Guolin Ke committed
131
  inline static double LossOnPoint(label_t label, double score, const Config&) {
Guolin Ke's avatar
Guolin Ke committed
132
133
134
    return (score - label)*(score - label);
  }

135
136
137
138
139
140
141
142
  inline static const char* Name() {
    return "l2";
  }
};

/*! \brief L2 loss for regression task */
class QuantileMetric : public RegressionMetric<QuantileMetric> {
public:
Guolin Ke's avatar
Guolin Ke committed
143
  explicit QuantileMetric(const Config& config) :RegressionMetric<QuantileMetric>(config) {
144
145
  }

Guolin Ke's avatar
Guolin Ke committed
146
  inline static double LossOnPoint(label_t label, double score, const Config& config) {
147
148
149
150
151
152
    double delta = label - score;
    if (delta < 0) {
      return (config.alpha - 1.0f) * delta;
    } else {
      return config.alpha * delta;
    }
Guolin Ke's avatar
Guolin Ke committed
153
154
155
  }

  inline static const char* Name() {
156
    return "quantile";
Guolin Ke's avatar
Guolin Ke committed
157
158
159
  }
};

160

Guolin Ke's avatar
Guolin Ke committed
161
162
163
/*! \brief L1 loss for regression task */
class L1Metric: public RegressionMetric<L1Metric> {
public:
Guolin Ke's avatar
Guolin Ke committed
164
  explicit L1Metric(const Config& config) :RegressionMetric<L1Metric>(config) {}
Guolin Ke's avatar
Guolin Ke committed
165

Guolin Ke's avatar
Guolin Ke committed
166
  inline static double LossOnPoint(label_t label, double score, const Config&) {
Guolin Ke's avatar
Guolin Ke committed
167
168
169
    return std::fabs(score - label);
  }
  inline static const char* Name() {
170
    return "l1";
Guolin Ke's avatar
Guolin Ke committed
171
172
173
  }
};

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
174
175
176
/*! \brief Huber loss for regression task */
class HuberLossMetric: public RegressionMetric<HuberLossMetric> {
public:
Guolin Ke's avatar
Guolin Ke committed
177
  explicit HuberLossMetric(const Config& config) :RegressionMetric<HuberLossMetric>(config) {
Guolin Ke's avatar
Guolin Ke committed
178
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
179

Guolin Ke's avatar
Guolin Ke committed
180
  inline static double LossOnPoint(label_t label, double score, const Config& config) {
181
    const double diff = score - label;
182
    if (std::abs(diff) <= config.alpha) {
Guolin Ke's avatar
Guolin Ke committed
183
184
      return 0.5f * diff * diff;
    } else {
185
      return config.alpha * (std::abs(diff) - 0.5f * config.alpha);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
186
    }
Guolin Ke's avatar
Guolin Ke committed
187
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
188

Guolin Ke's avatar
Guolin Ke committed
189
190
191
  inline static const char* Name() {
    return "huber";
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
192
193
};

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
194
195
196
197
/*! \brief Fair loss for regression task */
// http://research.microsoft.com/en-us/um/people/zhang/INRIA/Publis/Tutorial-Estim/node24.html
class FairLossMetric: public RegressionMetric<FairLossMetric> {
public:
Guolin Ke's avatar
Guolin Ke committed
198
  explicit FairLossMetric(const Config& config) :RegressionMetric<FairLossMetric>(config) {
Guolin Ke's avatar
Guolin Ke committed
199
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
200

Guolin Ke's avatar
Guolin Ke committed
201
  inline static double LossOnPoint(label_t label, double score, const Config& config) {
202
    const double x = std::fabs(score - label);
203
    const double c = config.fair_c;
Guolin Ke's avatar
Guolin Ke committed
204
205
    return c * x - c * c * std::log(1.0f + x / c);
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
206

Guolin Ke's avatar
Guolin Ke committed
207
208
209
  inline static const char* Name() {
    return "fair";
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
210
211
};

212
213
214
/*! \brief Poisson regression loss for regression task */
class PoissonMetric: public RegressionMetric<PoissonMetric> {
public:
Guolin Ke's avatar
Guolin Ke committed
215
  explicit PoissonMetric(const Config& config) :RegressionMetric<PoissonMetric>(config) {
216
217
  }

Guolin Ke's avatar
Guolin Ke committed
218
  inline static double LossOnPoint(label_t label, double score, const Config&) {
219
220
221
222
223
224
225
226
227
228
229
    const double eps = 1e-10f;
    if (score < eps) {
      score = eps;
    }
    return score - label * std::log(score);
  }
  inline static const char* Name() {
    return "poisson";
  }
};

230
231
232
233

/*! \brief Mape regression loss for regression task */
class MAPEMetric : public RegressionMetric<MAPEMetric> {
public:
Guolin Ke's avatar
Guolin Ke committed
234
  explicit MAPEMetric(const Config& config) :RegressionMetric<MAPEMetric>(config) {
235
236
  }

Guolin Ke's avatar
Guolin Ke committed
237
  inline static double LossOnPoint(label_t label, double score, const Config&) {
238
239
240
241
242
243
244
    return std::fabs((label - score)) / std::max(1.0f, std::fabs(label));
  }
  inline static const char* Name() {
    return "mape";
  }
};

Guolin Ke's avatar
Guolin Ke committed
245
246
class GammaMetric : public RegressionMetric<GammaMetric> {
public:
Guolin Ke's avatar
Guolin Ke committed
247
  explicit GammaMetric(const Config& config) :RegressionMetric<GammaMetric>(config) {
Guolin Ke's avatar
Guolin Ke committed
248
249
  }

Guolin Ke's avatar
Guolin Ke committed
250
  inline static double LossOnPoint(label_t label, double score, const Config&) {
Guolin Ke's avatar
Guolin Ke committed
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
    const double psi = 1.0;
    const double theta = -1.0 / score;
    const double a = psi;
    const double b = -std::log(-theta);
    const double c = 1. / psi * std::log(label / psi) - std::log(label) - std::lgamma(1.0 / psi);
    return -((label * theta - b) / a + c);
  }
  inline static const char* Name() {
    return "gamma";
  }
};


class GammaDevianceMetric : public RegressionMetric<GammaDevianceMetric> {
public:
Guolin Ke's avatar
Guolin Ke committed
266
  explicit GammaDevianceMetric(const Config& config) :RegressionMetric<GammaDevianceMetric>(config) {
Guolin Ke's avatar
Guolin Ke committed
267
268
  }

Guolin Ke's avatar
Guolin Ke committed
269
  inline static double LossOnPoint(label_t label, double score, const Config&) {
Guolin Ke's avatar
Guolin Ke committed
270
271
272
273
274
275
276
    const double epsilon = 1.0e-9;
    const double tmp = label / (score + epsilon);
    return tmp - std::log(tmp) - 1;
  }
  inline static const char* Name() {
    return "gamma-deviance";
  }
Guolin Ke's avatar
Guolin Ke committed
277
  inline static double AverageLoss(double sum_loss, double) {
Guolin Ke's avatar
Guolin Ke committed
278
279
280
281
282
283
    return sum_loss * 2;
  }
};

class TweedieMetric : public RegressionMetric<TweedieMetric> {
public:
Guolin Ke's avatar
Guolin Ke committed
284
  explicit TweedieMetric(const Config& config) :RegressionMetric<TweedieMetric>(config) {
Guolin Ke's avatar
Guolin Ke committed
285
286
  }

Guolin Ke's avatar
Guolin Ke committed
287
  inline static double LossOnPoint(label_t label, double score, const Config& config) {
Guolin Ke's avatar
Guolin Ke committed
288
    const double rho = config.tweedie_variance_power;
289
290
291
292
    const double eps = 1e-10f;
    if (score < eps) {
      score = eps;
    }
Guolin Ke's avatar
Guolin Ke committed
293
294
295
296
297
298
299
300
301
302
    const double a = label * std::exp((1 - rho) * std::log(score)) / (1 - rho);
    const double b = std::exp((2 - rho) * std::log(score)) / (2 - rho);
    return -a + b;
  }
  inline static const char* Name() {
    return "tweedie";
  }
};


Guolin Ke's avatar
Guolin Ke committed
303
}  // namespace LightGBM
Guolin Ke's avatar
Guolin Ke committed
304
#endif   // LightGBM_METRIC_REGRESSION_METRIC_HPP_