regression_metric.hpp 8.82 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
#ifndef LIGHTGBM_METRIC_REGRESSION_METRIC_HPP_
#define LIGHTGBM_METRIC_REGRESSION_METRIC_HPP_

#include <LightGBM/utils/log.h>

#include <LightGBM/metric.h>

#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:
18
  explicit RegressionMetric(const MetricConfig& 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 */
103
  MetricConfig config_;
104
  std::vector<std::string> name_;
Guolin Ke's avatar
Guolin Ke committed
105
106
};

107
108
109
110
111
/*! \brief RMSE loss for regression task */
class RMSEMetric: public RegressionMetric<RMSEMetric> {
public:
  explicit RMSEMetric(const MetricConfig& config) :RegressionMetric<RMSEMetric>(config) {}

112
  inline static double LossOnPoint(label_t label, double score, const MetricConfig&) {
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
129
130
/*! \brief L2 loss for regression task */
class L2Metric: public RegressionMetric<L2Metric> {
public:
  explicit L2Metric(const MetricConfig& config) :RegressionMetric<L2Metric>(config) {}

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

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

/*! \brief L2 loss for regression task */
class QuantileMetric : public RegressionMetric<QuantileMetric> {
public:
  explicit QuantileMetric(const MetricConfig& config) :RegressionMetric<QuantileMetric>(config) {
  }

146
  inline static double LossOnPoint(label_t label, double score, const MetricConfig& 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
164
165
/*! \brief L1 loss for regression task */
class L1Metric: public RegressionMetric<L1Metric> {
public:
  explicit L1Metric(const MetricConfig& config) :RegressionMetric<L1Metric>(config) {}

166
  inline static double LossOnPoint(label_t label, double score, const MetricConfig&) {
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
178
  explicit HuberLossMetric(const MetricConfig& config) :RegressionMetric<HuberLossMetric>(config) {
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
179

180
  inline static double LossOnPoint(label_t label, double score, const MetricConfig& 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
199
  explicit FairLossMetric(const MetricConfig& config) :RegressionMetric<FairLossMetric>(config) {
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
200

201
  inline static double LossOnPoint(label_t label, double score, const MetricConfig& 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
215
216
217
/*! \brief Poisson regression loss for regression task */
class PoissonMetric: public RegressionMetric<PoissonMetric> {
public:
  explicit PoissonMetric(const MetricConfig& config) :RegressionMetric<PoissonMetric>(config) {
  }

218
  inline static double LossOnPoint(label_t label, double score, const MetricConfig&) {
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
234
235
236
237
238
239
240
241
242
243
244

/*! \brief Mape regression loss for regression task */
class MAPEMetric : public RegressionMetric<MAPEMetric> {
public:
  explicit MAPEMetric(const MetricConfig& config) :RegressionMetric<MAPEMetric>(config) {
  }

  inline static double LossOnPoint(label_t label, double score, const MetricConfig&) {
    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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
class GammaMetric : public RegressionMetric<GammaMetric> {
public:
  explicit GammaMetric(const MetricConfig& config) :RegressionMetric<GammaMetric>(config) {
  }

  inline static double LossOnPoint(label_t label, double score, const MetricConfig&) {
    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:
  explicit GammaDevianceMetric(const MetricConfig& config) :RegressionMetric<GammaDevianceMetric>(config) {
  }

  inline static double LossOnPoint(label_t label, double score, const MetricConfig&) {
    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
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
    return sum_loss * 2;
  }
};

class TweedieMetric : public RegressionMetric<TweedieMetric> {
public:
  explicit TweedieMetric(const MetricConfig& config) :RegressionMetric<TweedieMetric>(config) {
  }

  inline static double LossOnPoint(label_t label, double score, const MetricConfig& config) {
    const double rho = config.tweedie_variance_power;
    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
299
}  // namespace LightGBM
Guolin Ke's avatar
Guolin Ke committed
300
#endif   // LightGBM_METRIC_REGRESSION_METRIC_HPP_