regression_metric.hpp 6.48 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:
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
18
  explicit RegressionMetric(const MetricConfig&) :huber_delta_(1.0f), fair_c_(1.0f) {
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());
35

Guolin Ke's avatar
Guolin Ke committed
36
37
38
39
40
41
    num_data_ = num_data;
    // get label
    label_ = metadata.label();
    // get weights
    weights_ = metadata.weights();
    if (weights_ == nullptr) {
42
      sum_weights_ = static_cast<double>(num_data_);
Guolin Ke's avatar
Guolin Ke committed
43
44
45
46
47
48
49
    } else {
      sum_weights_ = 0.0f;
      for (data_size_t i = 0; i < num_data_; ++i) {
        sum_weights_ += weights_[i];
      }
    }
  }
50

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

Guolin Ke's avatar
Guolin Ke committed
89
90
  }

91
  inline static double AverageLoss(double sum_loss, double sum_weights) {
Guolin Ke's avatar
Guolin Ke committed
92
93
94
    return sum_loss / sum_weights;
  }

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
95
96
protected:
  /*! \brief delta for Huber loss */
97
  double huber_delta_;
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
98
  /*! \brief c for Fair loss */
99
  double fair_c_;
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
100

Guolin Ke's avatar
Guolin Ke committed
101
102
103
104
105
106
107
108
private:
  /*! \brief Number of data */
  data_size_t num_data_;
  /*! \brief Pointer of label */
  const float* label_;
  /*! \brief Pointer of weighs */
  const float* weights_;
  /*! \brief Sum weights */
109
  double sum_weights_;
Guolin Ke's avatar
Guolin Ke committed
110
  /*! \brief Name of this test set */
111
  std::vector<std::string> name_;
Guolin Ke's avatar
Guolin Ke committed
112
113
};

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*! \brief RMSE loss for regression task */
class RMSEMetric: public RegressionMetric<RMSEMetric> {
public:
  explicit RMSEMetric(const MetricConfig& config) :RegressionMetric<RMSEMetric>(config) {}

  inline static double LossOnPoint(float label, double score, double, double) {
    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
133
134
135
136
137
/*! \brief L2 loss for regression task */
class L2Metric: public RegressionMetric<L2Metric> {
public:
  explicit L2Metric(const MetricConfig& config) :RegressionMetric<L2Metric>(config) {}

138
  inline static double LossOnPoint(float label, double score, double, double) {
Guolin Ke's avatar
Guolin Ke committed
139
140
141
    return (score - label)*(score - label);
  }

142
  inline static double AverageLoss(double sum_loss, double sum_weights) {
143
144
    // need mean of the result for L2 loss
    return sum_loss / sum_weights;
Guolin Ke's avatar
Guolin Ke committed
145
146
147
  }

  inline static const char* Name() {
148
    return "l2";
Guolin Ke's avatar
Guolin Ke committed
149
150
151
152
153
154
155
156
  }
};

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

157
  inline static double LossOnPoint(float label, double score, double, double) {
Guolin Ke's avatar
Guolin Ke committed
158
159
160
    return std::fabs(score - label);
  }
  inline static const char* Name() {
161
    return "l1";
Guolin Ke's avatar
Guolin Ke committed
162
163
164
  }
};

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
165
166
167
/*! \brief Huber loss for regression task */
class HuberLossMetric: public RegressionMetric<HuberLossMetric> {
public:
Guolin Ke's avatar
Guolin Ke committed
168
  explicit HuberLossMetric(const MetricConfig& config) :RegressionMetric<HuberLossMetric>(config) {
169
    huber_delta_ = static_cast<double>(config.huber_delta);
Guolin Ke's avatar
Guolin Ke committed
170
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
171

172
173
  inline static double LossOnPoint(float label, double score, double delta, double) {
    const double diff = score - label;
Guolin Ke's avatar
Guolin Ke committed
174
175
176
177
    if (std::abs(diff) <= delta) {
      return 0.5f * diff * diff;
    } else {
      return delta * (std::abs(diff) - 0.5f * delta);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
178
    }
Guolin Ke's avatar
Guolin Ke committed
179
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
180

Guolin Ke's avatar
Guolin Ke committed
181
182
183
  inline static const char* Name() {
    return "huber";
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
184
185
};

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
186
187
188
189
/*! \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
190
  explicit FairLossMetric(const MetricConfig& config) :RegressionMetric<FairLossMetric>(config) {
191
    fair_c_ = static_cast<double>(config.fair_c);
Guolin Ke's avatar
Guolin Ke committed
192
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
193

194
195
  inline static double LossOnPoint(float label, double score, double, double c) {
    const double x = std::fabs(score - label);
Guolin Ke's avatar
Guolin Ke committed
196
197
    return c * x - c * c * std::log(1.0f + x / c);
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
198

Guolin Ke's avatar
Guolin Ke committed
199
200
201
  inline static const char* Name() {
    return "fair";
  }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
202
203
};

204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/*! \brief Poisson regression loss for regression task */
class PoissonMetric: public RegressionMetric<PoissonMetric> {
public:
  explicit PoissonMetric(const MetricConfig& config) :RegressionMetric<PoissonMetric>(config) {
  }

  inline static double LossOnPoint(float label, double score, double, double) {
    const double eps = 1e-10f;
    if (score < eps) {
      score = eps;
    }
    return score - label * std::log(score);
  }
  inline static const char* Name() {
    return "poisson";
  }
};

Guolin Ke's avatar
Guolin Ke committed
222
}  // namespace LightGBM
Guolin Ke's avatar
Guolin Ke committed
223
#endif   // LightGBM_METRIC_REGRESSION_METRIC_HPP_