regression_objective.hpp 11.4 KB
Newer Older
Guolin Ke's avatar
Guolin Ke committed
1
2
3
#ifndef LIGHTGBM_OBJECTIVE_REGRESSION_OBJECTIVE_HPP_
#define LIGHTGBM_OBJECTIVE_REGRESSION_OBJECTIVE_HPP_

4
5
#include <LightGBM/meta.h>

Guolin Ke's avatar
Guolin Ke committed
6
#include <LightGBM/objective_function.h>
7
#include <LightGBM/utils/common.h>
Guolin Ke's avatar
Guolin Ke committed
8
9
10

namespace LightGBM {
/*!
11
* \brief Objective function for regression
Guolin Ke's avatar
Guolin Ke committed
12
13
14
15
16
17
*/
class RegressionL2loss: public ObjectiveFunction {
public:
  explicit RegressionL2loss(const ObjectiveConfig&) {
  }

18
19
20
21
  explicit RegressionL2loss(const std::vector<std::string>&) {

  }

Guolin Ke's avatar
Guolin Ke committed
22
23
24
25
26
27
28
29
30
  ~RegressionL2loss() {
  }

  void Init(const Metadata& metadata, data_size_t num_data) override {
    num_data_ = num_data;
    label_ = metadata.label();
    weights_ = metadata.weights();
  }

31
32
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
Guolin Ke's avatar
Guolin Ke committed
33
    if (weights_ == nullptr) {
34
      #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
35
      for (data_size_t i = 0; i < num_data_; ++i) {
36
        gradients[i] = static_cast<score_t>(score[i] - label_[i]);
37
        hessians[i] = 1.0f;
Guolin Ke's avatar
Guolin Ke committed
38
39
      }
    } else {
40
      #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
41
      for (data_size_t i = 0; i < num_data_; ++i) {
42
        gradients[i] = static_cast<score_t>(score[i] - label_[i]) * weights_[i];
Guolin Ke's avatar
Guolin Ke committed
43
44
45
46
47
        hessians[i] = weights_[i];
      }
    }
  }

Guolin Ke's avatar
Guolin Ke committed
48
49
  const char* GetName() const override {
    return "regression";
Guolin Ke's avatar
Guolin Ke committed
50
51
  }

52
53
54
55
56
57
  std::string ToString() const override {
    std::stringstream str_buf;
    str_buf << GetName();
    return str_buf.str();
  }

58
59
60
61
62
63
64
65
  bool IsConstantHessian() const override {
    if (weights_ == nullptr) {
      return true;
    } else {
      return false;
    }
  }

66
67
  bool BoostFromAverage() const override { return true; }

Guolin Ke's avatar
Guolin Ke committed
68
69
70
71
72
73
74
75
76
private:
  /*! \brief Number of data */
  data_size_t num_data_;
  /*! \brief Pointer of label */
  const float* label_;
  /*! \brief Pointer of weights */
  const float* weights_;
};

Guolin Ke's avatar
Guolin Ke committed
77
78
79
/*!
* \brief L1 regression loss
*/
80
81
class RegressionL1loss: public ObjectiveFunction {
public:
82
  explicit RegressionL1loss(const ObjectiveConfig& config) {
83
    eta_ = static_cast<double>(config.gaussian_eta);
84
  }
85

86
87
88
89
  explicit RegressionL1loss(const std::vector<std::string>&) {

  }

90
91
92
93
94
95
96
97
  ~RegressionL1loss() {}

  void Init(const Metadata& metadata, data_size_t num_data) override {
    num_data_ = num_data;
    label_ = metadata.label();
    weights_ = metadata.weights();
  }

98
99
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
100
    if (weights_ == nullptr) {
101
      #pragma omp parallel for schedule(static)
102
      for (data_size_t i = 0; i < num_data_; ++i) {
103
        const double diff = score[i] - label_[i];
Guolin Ke's avatar
Guolin Ke committed
104
105
        if (diff >= 0.0f) {
          gradients[i] = 1.0f;
106
        } else {
Guolin Ke's avatar
Guolin Ke committed
107
          gradients[i] = -1.0f;
108
        }
109
        hessians[i] = static_cast<score_t>(Common::ApproximateHessianWithGaussian(score[i], label_[i], gradients[i], eta_));
110
111
      }
    } else {
112
      #pragma omp parallel for schedule(static)
113
      for (data_size_t i = 0; i < num_data_; ++i) {
114
        const double diff = score[i] - label_[i];
Guolin Ke's avatar
Guolin Ke committed
115
        if (diff >= 0.0f) {
116
117
118
119
          gradients[i] = weights_[i];
        } else {
          gradients[i] = -weights_[i];
        }
120
        hessians[i] = static_cast<score_t>(Common::ApproximateHessianWithGaussian(score[i], label_[i], gradients[i], eta_, weights_[i]));
121
122
123
124
125
126
127
128
      }
    }
  }

  const char* GetName() const override {
    return "regression_l1";
  }

129
130
131
132
133
134
135
136
  std::string ToString() const override {
    std::stringstream str_buf;
    str_buf << GetName();
    return str_buf.str();
  }

  bool BoostFromAverage() const override { return true; }

137
138
139
140
141
142
143
private:
  /*! \brief Number of data */
  data_size_t num_data_;
  /*! \brief Pointer of label */
  const float* label_;
  /*! \brief Pointer of weights */
  const float* weights_;
144
  /*! \brief a parameter to control the width of Gaussian function to approximate hessian */
145
  double eta_;
146
147
};

Guolin Ke's avatar
Guolin Ke committed
148
149
150
/*!
* \brief Huber regression loss
*/
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
151
class RegressionHuberLoss: public ObjectiveFunction {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
152
public:
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
153
  explicit RegressionHuberLoss(const ObjectiveConfig& config) {
154
155
    delta_ = static_cast<double>(config.huber_delta);
    eta_ = static_cast<double>(config.gaussian_eta);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
156
157
  }

158
159
160
161
  explicit RegressionHuberLoss(const std::vector<std::string>&) {

  }

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
162
  ~RegressionHuberLoss() {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
163
164
165
166
167
168
169
170
  }

  void Init(const Metadata& metadata, data_size_t num_data) override {
    num_data_ = num_data;
    label_ = metadata.label();
    weights_ = metadata.weights();
  }

171
172
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
173
    if (weights_ == nullptr) {
174
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
175
      for (data_size_t i = 0; i < num_data_; ++i) {
176
        const double diff = score[i] - label_[i];
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
177

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
178
        if (std::abs(diff) <= delta_) {
179
          gradients[i] = static_cast<score_t>(diff);
Guolin Ke's avatar
Guolin Ke committed
180
          hessians[i] = 1.0f;
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
181
        } else {
Guolin Ke's avatar
Guolin Ke committed
182
          if (diff >= 0.0f) {
183
            gradients[i] = static_cast<score_t>(delta_);
Guolin Ke's avatar
Guolin Ke committed
184
          } else {
185
            gradients[i] = static_cast<score_t>(-delta_);
Guolin Ke's avatar
Guolin Ke committed
186
          }
187
          hessians[i] = static_cast<score_t>(Common::ApproximateHessianWithGaussian(score[i], label_[i], gradients[i], eta_));
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
188
189
190
        }
      }
    } else {
191
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
192
      for (data_size_t i = 0; i < num_data_; ++i) {
193
        const double diff = score[i] - label_[i];
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
194

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
195
        if (std::abs(diff) <= delta_) {
196
          gradients[i] = static_cast<score_t>(diff * weights_[i]);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
197
198
          hessians[i] = weights_[i];
        } else {
Guolin Ke's avatar
Guolin Ke committed
199
          if (diff >= 0.0f) {
200
            gradients[i] = static_cast<score_t>(delta_ * weights_[i]);
Guolin Ke's avatar
Guolin Ke committed
201
          } else {
202
            gradients[i] = static_cast<score_t>(-delta_ * weights_[i]);
Guolin Ke's avatar
Guolin Ke committed
203
          }
204
          hessians[i] = static_cast<score_t>(Common::ApproximateHessianWithGaussian(score[i], label_[i], gradients[i], eta_, weights_[i]));
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
205
206
207
208
209
210
211
212
213
        }
      }
    }
  }

  const char* GetName() const override {
    return "huber";
  }

214
215
216
217
218
219
220
221
  std::string ToString() const override {
    std::stringstream str_buf;
    str_buf << GetName();
    return str_buf.str();
  }

  bool BoostFromAverage() const override { return true; }

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
222
223
224
225
226
227
228
229
private:
  /*! \brief Number of data */
  data_size_t num_data_;
  /*! \brief Pointer of label */
  const float* label_;
  /*! \brief Pointer of weights */
  const float* weights_;
  /*! \brief delta for Huber loss */
230
  double delta_;
231
  /*! \brief a parameter to control the width of Gaussian function to approximate hessian */
232
  double eta_;
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
233
234
};

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
235
236
237
238
239

// http://research.microsoft.com/en-us/um/people/zhang/INRIA/Publis/Tutorial-Estim/node24.html
class RegressionFairLoss: public ObjectiveFunction {
public:
  explicit RegressionFairLoss(const ObjectiveConfig& config) {
240
    c_ = static_cast<double>(config.fair_c);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
241
242
  }

243
244
245
246
  explicit RegressionFairLoss(const std::vector<std::string>&) {

  }

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
247
248
249
250
251
252
253
254
  ~RegressionFairLoss() {}

  void Init(const Metadata& metadata, data_size_t num_data) override {
    num_data_ = num_data;
    label_ = metadata.label();
    weights_ = metadata.weights();
  }

255
256
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
257
    if (weights_ == nullptr) {
258
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
259
      for (data_size_t i = 0; i < num_data_; ++i) {
260
        const double x = score[i] - label_[i];
261
262
        gradients[i] = static_cast<score_t>(c_ * x / (std::fabs(x) + c_));
        hessians[i] = static_cast<score_t>(c_ * c_ / ((std::fabs(x) + c_) * (std::fabs(x) + c_)));
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
263
264
      }
    } else {
265
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
266
      for (data_size_t i = 0; i < num_data_; ++i) {
267
        const double x = score[i] - label_[i];
268
269
        gradients[i] = static_cast<score_t>(c_ * x / (std::fabs(x) + c_) * weights_[i]);
        hessians[i] = static_cast<score_t>(c_ * c_ / ((std::fabs(x) + c_) * (std::fabs(x) + c_)) * weights_[i]);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
270
271
272
273
274
275
276
277
      }
    }
  }

  const char* GetName() const override {
    return "fair";
  }

278
279
280
281
282
283
284
285
  std::string ToString() const override {
    std::stringstream str_buf;
    str_buf << GetName();
    return str_buf.str();
  }

  bool BoostFromAverage() const override { return true; }

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
286
287
288
289
290
291
292
293
private:
  /*! \brief Number of data */
  data_size_t num_data_;
  /*! \brief Pointer of label */
  const float* label_;
  /*! \brief Pointer of weights */
  const float* weights_;
  /*! \brief c for Fair loss */
294
  double c_;
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
295
296
};

297
298
299
300
301
302
303

/*!
* \brief Objective function for Poisson regression
*/
class RegressionPoissonLoss: public ObjectiveFunction {
public:
  explicit RegressionPoissonLoss(const ObjectiveConfig& config) {
304
    max_delta_step_ = static_cast<double>(config.poisson_max_delta_step);
305
306
  }

307
308
309
310
  explicit RegressionPoissonLoss(const std::vector<std::string>&) {

  }

311
312
313
314
315
316
  ~RegressionPoissonLoss() {}

  void Init(const Metadata& metadata, data_size_t num_data) override {
    num_data_ = num_data;
    label_ = metadata.label();
    weights_ = metadata.weights();
317
318
319
320
321
322
323
324
325
326
327

    // Safety check of labels
    float miny;
    double sumy;
    Common::obtain_min_max_sum(label_, num_data_, &miny, nullptr, &sumy);
    if (miny < 0.0f) {
      Log::Fatal("[%s]: at least one target label is negative.", GetName());
    }
    if (sumy == 0.0f) {
      Log::Fatal("[%s]: sum of labels is zero.", GetName());
    }
328
329
  }

330
331
332
333
334
335
336
337
338
  /* Parametrize with unbounded internal score "f"; then
   *  loss = exp(f) - label * f
   *  grad = exp(f) - label
   *  hess = exp(f)
   *
   * And the output is exp(f); so the associated metric get s=exp(f)
   * so that its loss = s - label * log(s); a little awkward maybe.
   *
   */
339
340
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
341
    if (weights_ == nullptr) {
342
      #pragma omp parallel for schedule(static)
343
      for (data_size_t i = 0; i < num_data_; ++i) {
344
345
346
        const double ef = std::exp(score[i]);
        gradients[i] = static_cast<score_t>(ef - label_[i]);
        hessians[i] = static_cast<score_t>(ef);
347
348
      }
    } else {
349
      #pragma omp parallel for schedule(static)
350
      for (data_size_t i = 0; i < num_data_; ++i) {
351
352
353
        const double ef = std::exp(score[i]);
        gradients[i] = static_cast<score_t>((ef - label_[i]) * weights_[i]);
        hessians[i] = static_cast<score_t>(ef * weights_[i]);
354
355
356
357
      }
    }
  }

358
359
360
361
  void ConvertOutput(const double* input, double* output) const override {
    output[0] = std::exp(input[0]);
  }

362
363
364
365
  const char* GetName() const override {
    return "poisson";
  }

366
367
368
369
370
371
372
373
  std::string ToString() const override {
    std::stringstream str_buf;
    str_buf << GetName();
    return str_buf.str();
  }

  bool BoostFromAverage() const override { return true; }

374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  bool GetCustomAverage(double *initscore) const override {
    if (initscore == nullptr) return false;
    double sumw = 0.0f;
    double sumy = 0.0f;
    if (weights_ == nullptr) {
      for (data_size_t i = 0; i < num_data_; i++) {
        sumy += label_[i];
      }
      sumw = static_cast<double>(num_data_);
    } else {
      for (data_size_t i = 0; i < num_data_; i++) {
        sumy += weights_[i] * label_[i];
        sumw += weights_[i];
      }
    }
    const double yavg = sumy / sumw;
    *initscore = std::log(yavg);
    Log::Info("[%s:%s]: yavg=%f -> initscore=%f",  GetName(), __func__, yavg, *initscore);
    return true;
  }

395
396
397
398
399
400
401
402
403
404
405
private:
  /*! \brief Number of data */
  data_size_t num_data_;
  /*! \brief Pointer of label */
  const float* label_;
  /*! \brief Pointer of weights */
  const float* weights_;
  /*! \brief used to safeguard optimization */
  double max_delta_step_;
};

Guolin Ke's avatar
Guolin Ke committed
406
}  // namespace LightGBM
Guolin Ke's avatar
Guolin Ke committed
407
#endif   // LightGBM_OBJECTIVE_REGRESSION_OBJECTIVE_HPP_