regression_objective.hpp 13.6 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

namespace LightGBM {
10

Guolin Ke's avatar
Guolin Ke committed
11
/*!
12
* \brief Objective function for regression
Guolin Ke's avatar
Guolin Ke committed
13
14
15
*/
class RegressionL2loss: public ObjectiveFunction {
public:
16
17
  explicit RegressionL2loss(const ObjectiveConfig& config) {
    sqrt_ = config.reg_sqrt;
Guolin Ke's avatar
Guolin Ke committed
18
19
  }

20
21
22
23
24
25
26
  explicit RegressionL2loss(const std::vector<std::string>& strs) {
    sqrt_ = false;
    for (auto str : strs) {
      if (str == std::string("sqrt")) {
        sqrt_ = true;
      }
    }
27
28
  }

Guolin Ke's avatar
Guolin Ke committed
29
30
31
32
33
34
  ~RegressionL2loss() {
  }

  void Init(const Metadata& metadata, data_size_t num_data) override {
    num_data_ = num_data;
    label_ = metadata.label();
35
36
37
38
39
40
41
    if (sqrt_) {
      trans_label_.resize(num_data_);
      for (data_size_t i = 0; i < num_data; ++i) {
        trans_label_[i] = std::copysign(std::sqrt(std::fabs(label_[i])), label_[i]);
      }
      label_ = trans_label_.data();
    }
Guolin Ke's avatar
Guolin Ke committed
42
43
44
    weights_ = metadata.weights();
  }

45
46
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
Guolin Ke's avatar
Guolin Ke committed
47
    if (weights_ == nullptr) {
48
      #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
49
      for (data_size_t i = 0; i < num_data_; ++i) {
50
        gradients[i] = static_cast<score_t>(score[i] - label_[i]);
51
        hessians[i] = 1.0f;
Guolin Ke's avatar
Guolin Ke committed
52
53
      }
    } else {
54
      #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
55
      for (data_size_t i = 0; i < num_data_; ++i) {
56
57
        gradients[i] = static_cast<score_t>((score[i] - label_[i]) * weights_[i]);
        hessians[i] = static_cast<score_t>(weights_[i]);
Guolin Ke's avatar
Guolin Ke committed
58
59
60
61
      }
    }
  }

Guolin Ke's avatar
Guolin Ke committed
62
63
  const char* GetName() const override {
    return "regression";
Guolin Ke's avatar
Guolin Ke committed
64
65
  }

66
67
68
69
70
71
72
73
  void ConvertOutput(const double* input, double* output) const override {
    if (sqrt_) {
      output[0] = std::copysign(input[0] * input[0], input[0]);
    } else {
      output[0] = input[0];
    }
  }

74
75
76
  std::string ToString() const override {
    std::stringstream str_buf;
    str_buf << GetName();
77
78
79
    if (sqrt_) {
      str_buf << " sqrt";
    }
80
81
82
    return str_buf.str();
  }

83
84
85
86
87
88
89
90
  bool IsConstantHessian() const override {
    if (weights_ == nullptr) {
      return true;
    } else {
      return false;
    }
  }

91
92
93
94
95
96
97
  bool BoostFromAverage() const override { 
    if (sqrt_) {
      return false;
    } else {
      return true;
    }
  }
98

99
100
protected:
  bool sqrt_;
Guolin Ke's avatar
Guolin Ke committed
101
102
103
  /*! \brief Number of data */
  data_size_t num_data_;
  /*! \brief Pointer of label */
104
  const label_t* label_;
Guolin Ke's avatar
Guolin Ke committed
105
  /*! \brief Pointer of weights */
106
107
  const label_t* weights_;
  std::vector<label_t> trans_label_;
Guolin Ke's avatar
Guolin Ke committed
108
109
};

Guolin Ke's avatar
Guolin Ke committed
110
111
112
/*!
* \brief L1 regression loss
*/
113
class RegressionL1loss: public RegressionL2loss {
114
public:
115
  explicit RegressionL1loss(const ObjectiveConfig& config): RegressionL2loss(config) {
116
    eta_ = static_cast<double>(config.gaussian_eta);
117
  }
118

119
  explicit RegressionL1loss(const std::vector<std::string>& strs): RegressionL2loss(strs) {
120
121
122

  }

123
124
  ~RegressionL1loss() {}

125
126
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
127
    if (weights_ == nullptr) {
128
      #pragma omp parallel for schedule(static)
129
      for (data_size_t i = 0; i < num_data_; ++i) {
130
        const double diff = score[i] - label_[i];
Guolin Ke's avatar
Guolin Ke committed
131
132
        if (diff >= 0.0f) {
          gradients[i] = 1.0f;
133
        } else {
Guolin Ke's avatar
Guolin Ke committed
134
          gradients[i] = -1.0f;
135
        }
136
        hessians[i] = static_cast<score_t>(Common::ApproximateHessianWithGaussian(score[i], label_[i], gradients[i], eta_));
137
138
      }
    } else {
139
      #pragma omp parallel for schedule(static)
140
      for (data_size_t i = 0; i < num_data_; ++i) {
141
        const double diff = score[i] - label_[i];
Guolin Ke's avatar
Guolin Ke committed
142
        if (diff >= 0.0f) {
143
          gradients[i] = static_cast<score_t>(weights_[i]);
144
        } else {
145
          gradients[i] = static_cast<score_t>(-weights_[i]);
146
        }
147
        hessians[i] = static_cast<score_t>(Common::ApproximateHessianWithGaussian(score[i], label_[i], gradients[i], eta_, weights_[i]));
148
149
150
151
152
153
154
155
      }
    }
  }

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

156
157
  bool IsConstantHessian() const override {
    return false;
158
159
  }

160
private:
161
  double eta_;
162
163
};

Guolin Ke's avatar
Guolin Ke committed
164
165
166
/*!
* \brief Huber regression loss
*/
167
class RegressionHuberLoss: public RegressionL2loss {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
168
public:
169
170
  explicit RegressionHuberLoss(const ObjectiveConfig& config): RegressionL2loss(config) {
    alpha_ = static_cast<double>(config.alpha);
171
    eta_ = static_cast<double>(config.gaussian_eta);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
172
173
  }

174
  explicit RegressionHuberLoss(const std::vector<std::string>& strs): RegressionL2loss(strs) {
175
176
177

  }

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
178
  ~RegressionHuberLoss() {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
179
180
  }

181
182
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
183
    if (weights_ == nullptr) {
184
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
185
      for (data_size_t i = 0; i < num_data_; ++i) {
186
        const double diff = score[i] - label_[i];
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
187

188
        if (std::abs(diff) <= alpha_) {
189
          gradients[i] = static_cast<score_t>(diff);
Guolin Ke's avatar
Guolin Ke committed
190
          hessians[i] = 1.0f;
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
191
        } else {
Guolin Ke's avatar
Guolin Ke committed
192
          if (diff >= 0.0f) {
193
            gradients[i] = static_cast<score_t>(alpha_);
Guolin Ke's avatar
Guolin Ke committed
194
          } else {
195
            gradients[i] = static_cast<score_t>(-alpha_);
Guolin Ke's avatar
Guolin Ke committed
196
          }
197
          hessians[i] = static_cast<score_t>(Common::ApproximateHessianWithGaussian(score[i], label_[i], gradients[i], eta_));
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
198
199
200
        }
      }
    } else {
201
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
202
      for (data_size_t i = 0; i < num_data_; ++i) {
203
        const double diff = score[i] - label_[i];
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
204

205
        if (std::abs(diff) <= alpha_) {
206
          gradients[i] = static_cast<score_t>(diff * weights_[i]);
207
          hessians[i] = static_cast<score_t>(weights_[i]);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
208
        } else {
Guolin Ke's avatar
Guolin Ke committed
209
          if (diff >= 0.0f) {
210
            gradients[i] = static_cast<score_t>(alpha_ * weights_[i]);
Guolin Ke's avatar
Guolin Ke committed
211
          } else {
212
            gradients[i] = static_cast<score_t>(-alpha_ * weights_[i]);
Guolin Ke's avatar
Guolin Ke committed
213
          }
214
          hessians[i] = static_cast<score_t>(Common::ApproximateHessianWithGaussian(score[i], label_[i], gradients[i], eta_, weights_[i]));
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
215
216
217
218
219
220
221
222
223
        }
      }
    }
  }

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

224
225
  bool IsConstantHessian() const override {
    return false;
226
227
  }

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
228
229
private:
  /*! \brief delta for Huber loss */
230
  double alpha_;
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

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

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

  }

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
247
248
  ~RegressionFairLoss() {}

249
250
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
251
    if (weights_ == nullptr) {
252
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
253
      for (data_size_t i = 0; i < num_data_; ++i) {
254
        const double x = score[i] - label_[i];
255
256
        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
257
258
      }
    } else {
259
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
260
      for (data_size_t i = 0; i < num_data_; ++i) {
261
        const double x = score[i] - label_[i];
262
263
        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
264
265
266
267
268
269
270
271
      }
    }
  }

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

272
273
  bool IsConstantHessian() const override {
    return false;
274
275
  }

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
276
277
private:
  /*! \brief c for Fair loss */
278
  double c_;
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
279
280
};

281
282
283
284

/*!
* \brief Objective function for Poisson regression
*/
285
class RegressionPoissonLoss: public RegressionL2loss {
286
public:
287
  explicit RegressionPoissonLoss(const ObjectiveConfig& config): RegressionL2loss(config) {
288
    max_delta_step_ = static_cast<double>(config.poisson_max_delta_step);
289
290
  }

291
  explicit RegressionPoissonLoss(const std::vector<std::string>& strs): RegressionL2loss(strs) {
292
293
294

  }

295
296
297
  ~RegressionPoissonLoss() {}

  void Init(const Metadata& metadata, data_size_t num_data) override {
298
    RegressionL2loss::Init(metadata, num_data);
299
    // Safety check of labels
300
    label_t miny;
301
    double sumy;
302
    Common::ObtainMinMaxSum(label_, num_data_, &miny, (label_t*)nullptr, &sumy);
303
304
305
306
307
308
    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());
    }
309
310
  }

311
312
313
314
315
316
317
318
319
  /* 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.
   *
   */
320
321
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
322
    if (weights_ == nullptr) {
323
      #pragma omp parallel for schedule(static)
324
      for (data_size_t i = 0; i < num_data_; ++i) {
325
326
327
        const double ef = std::exp(score[i]);
        gradients[i] = static_cast<score_t>(ef - label_[i]);
        hessians[i] = static_cast<score_t>(ef);
328
329
      }
    } else {
330
      #pragma omp parallel for schedule(static)
331
      for (data_size_t i = 0; i < num_data_; ++i) {
332
333
334
        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]);
335
336
337
338
      }
    }
  }

339
  void ConvertOutput(const double* input, double* output) const override {
340
    RegressionL2loss::ConvertOutput(input, output);
341
342
343
    output[0] = std::exp(input[0]);
  }

344
345
346
347
  const char* GetName() const override {
    return "poisson";
  }

348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
  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;
  }

369
370
371
372
  bool IsConstantHessian() const override {
    return false;
  }

373
374
375
376
377
private:
  /*! \brief used to safeguard optimization */
  double max_delta_step_;
};

378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
class RegressionQuantileloss : public RegressionL2loss {
public:
  explicit RegressionQuantileloss(const ObjectiveConfig& config): RegressionL2loss(config) {
    alpha_ = static_cast<score_t>(config.alpha);
  }

  explicit RegressionQuantileloss(const std::vector<std::string>& strs): RegressionL2loss(strs) {

  }

  ~RegressionQuantileloss() {}

  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
    if (weights_ == nullptr) {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        score_t delta = static_cast<score_t>(score[i] - label_[i]);
        if (delta >= 0) {
          gradients[i] = (1.0f - alpha_);
        } else {
          gradients[i] = -alpha_;
        }
        hessians[i] = 1.0f;
      }
    } else {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        score_t delta = static_cast<score_t>(score[i] - label_[i]);
        if (delta >= 0) {
408
          gradients[i] = static_cast<score_t>((1.0f - alpha_) * weights_[i]);
409
        } else {
410
          gradients[i] = static_cast<score_t>(-alpha_ * weights_[i]);
411
        }
412
        hessians[i] = static_cast<score_t>(weights_[i]);
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
      }
    }
  }

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

private:
  score_t alpha_;
};

class RegressionQuantileL2loss : public RegressionL2loss {
public:
  explicit RegressionQuantileL2loss(const ObjectiveConfig& config) : RegressionL2loss(config) {
    alpha_ = static_cast<score_t>(config.alpha);
  }

  explicit RegressionQuantileL2loss(const std::vector<std::string>& strs) : RegressionL2loss(strs) {

  }

  ~RegressionQuantileL2loss() {}

  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
    if (weights_ == nullptr) {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        score_t delta = static_cast<score_t>(score[i] - label_[i]);
        if (delta > 0) {
          gradients[i] = (1.0f - alpha_) * delta;
          hessians[i] = (1.0f - alpha_);
        } else {
          gradients[i] = alpha_ * delta;
          hessians[i] = alpha_;
        }

      }
    } else {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        score_t delta = static_cast<score_t>(score[i] - label_[i]);
        if (delta > 0) {
457
458
          gradients[i] = static_cast<score_t>((1.0f - alpha_) * delta * weights_[i]);
          hessians[i] = static_cast<score_t>((1.0f - alpha_) * weights_[i]);
459
        } else {
460
461
          gradients[i] = static_cast<score_t>(alpha_ * delta * weights_[i]);
          hessians[i] = static_cast<score_t>(alpha_ * weights_[i]);
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
        }
      }
    }
  }

  bool IsConstantHessian() const override {
    return false;
  }

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

private:
  score_t alpha_;
};

Guolin Ke's avatar
Guolin Ke committed
479
}  // namespace LightGBM
Guolin Ke's avatar
Guolin Ke committed
480
#endif   // LightGBM_OBJECTIVE_REGRESSION_OBJECTIVE_HPP_