"vscode:/vscode.git/clone" did not exist on "b4535b8d843c9f0881d9f30a0869def4a1aec83f"
regression_objective.hpp 27.3 KB
Newer Older
1
2
3
4
/*!
 * Copyright (c) 2016 Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See LICENSE file in the project root for license information.
 */
Guolin Ke's avatar
Guolin Ke committed
5
6
7
#ifndef LIGHTGBM_OBJECTIVE_REGRESSION_OBJECTIVE_HPP_
#define LIGHTGBM_OBJECTIVE_REGRESSION_OBJECTIVE_HPP_

8
#include <LightGBM/meta.h>
9
#include <LightGBM/objective_function.h>
10
#include <LightGBM/utils/array_args.h>
Guolin Ke's avatar
Guolin Ke committed
11

12
13
14
15
#include <string>
#include <algorithm>
#include <vector>

Guolin Ke's avatar
Guolin Ke committed
16
namespace LightGBM {
17

18
#define PercentileFun(T, data_reader, cnt_data, alpha) {\
19
  if (cnt_data <= 1) { return  data_reader(0); }\
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  std::vector<T> ref_data(cnt_data);\
  for (data_size_t i = 0; i < cnt_data; ++i) {\
    ref_data[i] = data_reader(i);\
  }\
  const double float_pos = (1.0f - alpha) * cnt_data;\
  const data_size_t pos = static_cast<data_size_t>(float_pos);\
  if (pos < 1) {\
    return ref_data[ArrayArgs<T>::ArgMax(ref_data)];\
  } else if (pos >= cnt_data) {\
    return ref_data[ArrayArgs<T>::ArgMin(ref_data)];\
  } else {\
    const double bias = float_pos - pos;\
    if (pos > cnt_data / 2) {\
      ArrayArgs<T>::ArgMaxAtK(&ref_data, 0, cnt_data, pos - 1);\
      T v1 = ref_data[pos - 1];\
      T v2 = ref_data[pos + ArrayArgs<T>::ArgMax(ref_data.data() + pos, cnt_data - pos)];\
      return static_cast<T>(v1 - (v1 - v2) * bias);\
    } else {\
      ArrayArgs<T>::ArgMaxAtK(&ref_data, 0, cnt_data, pos);\
      T v2 = ref_data[pos];\
      T v1 = ref_data[ArrayArgs<T>::ArgMin(ref_data.data(), pos)];\
      return static_cast<T>(v1 - (v1 - v2) * bias);\
    }\
  }\
}\

#define WeightedPercentileFun(T, data_reader, weight_reader, cnt_data, alpha) {\
47
  if (cnt_data <= 1) { return  data_reader(0); }\
48
49
50
51
  std::vector<data_size_t> sorted_idx(cnt_data);\
  for (data_size_t i = 0; i < cnt_data; ++i) {\
    sorted_idx[i] = i;\
  }\
52
  std::stable_sort(sorted_idx.begin(), sorted_idx.end(), [=](data_size_t a, data_size_t b) {return data_reader(a) < data_reader(b); });\
53
54
55
56
57
58
59
  std::vector<double> weighted_cdf(cnt_data);\
  weighted_cdf[0] = weight_reader(sorted_idx[0]);\
  for (data_size_t i = 1; i < cnt_data; ++i) {\
    weighted_cdf[i] = weighted_cdf[i - 1] + weight_reader(sorted_idx[i]);\
  }\
  double threshold = weighted_cdf[cnt_data - 1] * alpha;\
  size_t pos = std::upper_bound(weighted_cdf.begin(), weighted_cdf.end(), threshold) - weighted_cdf.begin();\
60
  pos = std::min(pos, static_cast<size_t>(cnt_data -1));\
Guolin Ke's avatar
Guolin Ke committed
61
  if (pos == 0 || pos ==  static_cast<size_t>(cnt_data - 1)) {\
62
    return data_reader(sorted_idx[pos]);\
63
64
65
66
67
  }\
  CHECK(threshold >= weighted_cdf[pos - 1]);\
  CHECK(threshold < weighted_cdf[pos]);\
  T v1 = data_reader(sorted_idx[pos - 1]);\
  T v2 = data_reader(sorted_idx[pos]);\
68
  if (weighted_cdf[pos + 1] - weighted_cdf[pos] > kEpsilon) {\
Guolin Ke's avatar
Guolin Ke committed
69
70
71
72
    return static_cast<T>((threshold - weighted_cdf[pos]) / (weighted_cdf[pos + 1] - weighted_cdf[pos]) * (v2 - v1) + v1); \
  } else {\
    return static_cast<T>(v2);\
  }\
73
74
}\

Guolin Ke's avatar
Guolin Ke committed
75
/*!
76
* \brief Objective function for regression
Guolin Ke's avatar
Guolin Ke committed
77
78
*/
class RegressionL2loss: public ObjectiveFunction {
Nikita Titov's avatar
Nikita Titov committed
79
 public:
Guolin Ke's avatar
Guolin Ke committed
80
  explicit RegressionL2loss(const Config& config) {
81
    sqrt_ = config.reg_sqrt;
Guolin Ke's avatar
Guolin Ke committed
82
83
  }

84
85
86
87
88
89
90
  explicit RegressionL2loss(const std::vector<std::string>& strs) {
    sqrt_ = false;
    for (auto str : strs) {
      if (str == std::string("sqrt")) {
        sqrt_ = true;
      }
    }
91
  }
92

Guolin Ke's avatar
Guolin Ke committed
93
94
95
96
97
98
  ~RegressionL2loss() {
  }

  void Init(const Metadata& metadata, data_size_t num_data) override {
    num_data_ = num_data;
    label_ = metadata.label();
99
100
    if (sqrt_) {
      trans_label_.resize(num_data_);
101
      #pragma omp parallel for schedule(static)
102
      for (data_size_t i = 0; i < num_data; ++i) {
103
        trans_label_[i] = Common::Sign(label_[i]) * std::sqrt(std::fabs(label_[i]));
104
105
106
      }
      label_ = trans_label_.data();
    }
Guolin Ke's avatar
Guolin Ke committed
107
108
109
    weights_ = metadata.weights();
  }

110
111
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
Guolin Ke's avatar
Guolin Ke committed
112
    if (weights_ == nullptr) {
113
      #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
114
      for (data_size_t i = 0; i < num_data_; ++i) {
115
        gradients[i] = static_cast<score_t>(score[i] - label_[i]);
116
        hessians[i] = 1.0f;
Guolin Ke's avatar
Guolin Ke committed
117
118
      }
    } else {
119
      #pragma omp parallel for schedule(static)
Guolin Ke's avatar
Guolin Ke committed
120
      for (data_size_t i = 0; i < num_data_; ++i) {
121
122
        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
123
124
125
126
      }
    }
  }

Guolin Ke's avatar
Guolin Ke committed
127
128
  const char* GetName() const override {
    return "regression";
Guolin Ke's avatar
Guolin Ke committed
129
130
  }

131
132
  void ConvertOutput(const double* input, double* output) const override {
    if (sqrt_) {
133
      output[0] = Common::Sign(input[0]) * input[0] * input[0];
134
135
136
137
138
    } else {
      output[0] = input[0];
    }
  }

139
140
141
  std::string ToString() const override {
    std::stringstream str_buf;
    str_buf << GetName();
142
143
144
    if (sqrt_) {
      str_buf << " sqrt";
    }
145
146
147
    return str_buf.str();
  }

148
149
150
151
152
153
154
155
  bool IsConstantHessian() const override {
    if (weights_ == nullptr) {
      return true;
    } else {
      return false;
    }
  }

156
  double BoostFromScore(int) const override {
157
158
159
    double suml = 0.0f;
    double sumw = 0.0f;
    if (weights_ != nullptr) {
160
      #pragma omp parallel for schedule(static) reduction(+:suml, sumw)
161
162
163
164
      for (data_size_t i = 0; i < num_data_; ++i) {
        suml += label_[i] * weights_[i];
        sumw += weights_[i];
      }
165
    } else {
166
167
168
169
170
      sumw = static_cast<double>(num_data_);
      #pragma omp parallel for schedule(static) reduction(+:suml)
      for (data_size_t i = 0; i < num_data_; ++i) {
        suml += label_[i];
      }
171
    }
172
    return suml / sumw;
173
  }
174

Nikita Titov's avatar
Nikita Titov committed
175
 protected:
176
  bool sqrt_;
Guolin Ke's avatar
Guolin Ke committed
177
178
179
  /*! \brief Number of data */
  data_size_t num_data_;
  /*! \brief Pointer of label */
180
  const label_t* label_;
Guolin Ke's avatar
Guolin Ke committed
181
  /*! \brief Pointer of weights */
182
183
  const label_t* weights_;
  std::vector<label_t> trans_label_;
Guolin Ke's avatar
Guolin Ke committed
184
185
};

Guolin Ke's avatar
Guolin Ke committed
186
187
188
/*!
* \brief L1 regression loss
*/
189
class RegressionL1loss: public RegressionL2loss {
Nikita Titov's avatar
Nikita Titov committed
190
 public:
Guolin Ke's avatar
Guolin Ke committed
191
  explicit RegressionL1loss(const Config& config): RegressionL2loss(config) {
192
  }
193

194
  explicit RegressionL1loss(const std::vector<std::string>& strs): RegressionL2loss(strs) {
195
196
  }

197
198
  ~RegressionL1loss() {}

199
200
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
201
    if (weights_ == nullptr) {
202
      #pragma omp parallel for schedule(static)
203
      for (data_size_t i = 0; i < num_data_; ++i) {
204
        const double diff = score[i] - label_[i];
205
206
        gradients[i] = static_cast<score_t>(Common::Sign(diff));
        hessians[i] = 1.0f;
207
208
      }
    } else {
209
      #pragma omp parallel for schedule(static)
210
      for (data_size_t i = 0; i < num_data_; ++i) {
211
        const double diff = score[i] - label_[i];
212
213
        gradients[i] = static_cast<score_t>(Common::Sign(diff) * weights_[i]);
        hessians[i] = weights_[i];
214
215
216
217
      }
    }
  }

218
  double BoostFromScore(int) const override {
219
220
221
222
223
224
225
226
227
228
229
230
    const double alpha = 0.5;
    if (weights_ != nullptr) {
      #define data_reader(i) (label_[i])
      #define weight_reader(i) (weights_[i])
      WeightedPercentileFun(label_t, data_reader, weight_reader, num_data_, alpha);
      #undef data_reader
      #undef weight_reader
    } else {
      #define data_reader(i) (label_[i])
      PercentileFun(label_t, data_reader, num_data_, alpha);
      #undef data_reader
    }
231
232
  }

233
234
  bool IsRenewTreeOutput() const override { return true; }

235
  double RenewTreeOutput(double, const double* pred,
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
                         const data_size_t* index_mapper,
                         const data_size_t* bagging_mapper,
                         data_size_t num_data_in_leaf) const override {
    const double alpha = 0.5;
    if (weights_ == nullptr) {
      if (bagging_mapper == nullptr) {
        #define data_reader(i) (label_[index_mapper[i]] - pred[index_mapper[i]])
        PercentileFun(double, data_reader, num_data_in_leaf, alpha);
        #undef data_reader
      } else {
        #define data_reader(i) (label_[bagging_mapper[index_mapper[i]]] - pred[bagging_mapper[index_mapper[i]]])
        PercentileFun(double, data_reader, num_data_in_leaf, alpha);
        #undef data_reader
      }
    } else {
      if (bagging_mapper == nullptr) {
        #define data_reader(i) (label_[index_mapper[i]] - pred[index_mapper[i]])
        #define weight_reader(i) (weights_[index_mapper[i]])
        WeightedPercentileFun(double, data_reader, weight_reader, num_data_in_leaf, alpha);
        #undef data_reader
        #undef weight_reader
      } else {
        #define data_reader(i) (label_[bagging_mapper[index_mapper[i]]] - pred[bagging_mapper[index_mapper[i]]])
        #define weight_reader(i) (weights_[bagging_mapper[index_mapper[i]]])
        WeightedPercentileFun(double, data_reader, weight_reader, num_data_in_leaf, alpha);
        #undef data_reader
        #undef weight_reader
      }
    }
265
266
  }

267
  double RenewTreeOutput(double, double pred,
Guolin Ke's avatar
Guolin Ke committed
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
                         const data_size_t* index_mapper,
                         const data_size_t* bagging_mapper,
                         data_size_t num_data_in_leaf) const override {
    const double alpha = 0.5;
    if (weights_ == nullptr) {
      if (bagging_mapper == nullptr) {
        #define data_reader(i) (label_[index_mapper[i]] - pred)
        PercentileFun(double, data_reader, num_data_in_leaf, alpha);
        #undef data_reader
      } else {
        #define data_reader(i) (label_[bagging_mapper[index_mapper[i]]] - pred)
        PercentileFun(double, data_reader, num_data_in_leaf, alpha);
        #undef data_reader
      }
    } else {
      if (bagging_mapper == nullptr) {
        #define data_reader(i) (label_[index_mapper[i]] - pred)
        #define weight_reader(i) (weights_[index_mapper[i]])
        WeightedPercentileFun(double, data_reader, weight_reader, num_data_in_leaf, alpha);
        #undef data_reader
        #undef weight_reader
      } else {
        #define data_reader(i) (label_[bagging_mapper[index_mapper[i]]] - pred)
        #define weight_reader(i) (weights_[bagging_mapper[index_mapper[i]]])
        WeightedPercentileFun(double, data_reader, weight_reader, num_data_in_leaf, alpha);
        #undef data_reader
        #undef weight_reader
      }
    }
  }

299
300
301
  const char* GetName() const override {
    return "regression_l1";
  }
302
303
};

Guolin Ke's avatar
Guolin Ke committed
304
305
306
/*!
* \brief Huber regression loss
*/
307
class RegressionHuberLoss: public RegressionL2loss {
Nikita Titov's avatar
Nikita Titov committed
308
 public:
Guolin Ke's avatar
Guolin Ke committed
309
  explicit RegressionHuberLoss(const Config& config): RegressionL2loss(config) {
310
    alpha_ = static_cast<double>(config.alpha);
Guolin Ke's avatar
Guolin Ke committed
311
312
313
314
    if (sqrt_) {
      Log::Warning("Cannot use sqrt transform in %s Regression, will auto disable it", GetName());
      sqrt_ = false;
    }
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
315
316
  }

317
  explicit RegressionHuberLoss(const std::vector<std::string>& strs): RegressionL2loss(strs) {
Guolin Ke's avatar
Guolin Ke committed
318
319
320
321
    if (sqrt_) {
      Log::Warning("Cannot use sqrt transform in %s Regression, will auto disable it", GetName());
      sqrt_ = false;
    }
322
323
  }

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
324
  ~RegressionHuberLoss() {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
325
326
  }

327
328
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
329
    if (weights_ == nullptr) {
330
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
331
      for (data_size_t i = 0; i < num_data_; ++i) {
332
        const double diff = score[i] - label_[i];
333
        if (std::abs(diff) <= alpha_) {
334
          gradients[i] = static_cast<score_t>(diff);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
335
        } else {
336
          gradients[i] = static_cast<score_t>(Common::Sign(diff) * alpha_);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
337
        }
338
        hessians[i] = 1.0f;
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
339
340
      }
    } else {
341
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
342
      for (data_size_t i = 0; i < num_data_; ++i) {
343
        const double diff = score[i] - label_[i];
344
        if (std::abs(diff) <= alpha_) {
345
          gradients[i] = static_cast<score_t>(diff * weights_[i]);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
346
        } else {
347
          gradients[i] = static_cast<score_t>(Common::Sign(diff) * weights_[i] * alpha_);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
348
        }
349
        hessians[i] = static_cast<score_t>(weights_[i]);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
350
351
352
353
354
355
356
357
      }
    }
  }

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

358
359
  bool IsConstantHessian() const override {
    return false;
360
361
  }

Nikita Titov's avatar
Nikita Titov committed
362
 private:
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
363
  /*! \brief delta for Huber loss */
364
  double alpha_;
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
365
366
};

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
367
368

// http://research.microsoft.com/en-us/um/people/zhang/INRIA/Publis/Tutorial-Estim/node24.html
369
class RegressionFairLoss: public RegressionL2loss {
Nikita Titov's avatar
Nikita Titov committed
370
 public:
Guolin Ke's avatar
Guolin Ke committed
371
  explicit RegressionFairLoss(const Config& config): RegressionL2loss(config) {
372
    c_ = static_cast<double>(config.fair_c);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
373
374
  }

375
  explicit RegressionFairLoss(const std::vector<std::string>& strs): RegressionL2loss(strs) {
376
377
  }

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
378
379
  ~RegressionFairLoss() {}

380
381
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
382
    if (weights_ == nullptr) {
383
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
384
      for (data_size_t i = 0; i < num_data_; ++i) {
385
        const double x = score[i] - label_[i];
386
387
        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
388
389
      }
    } else {
390
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
391
      for (data_size_t i = 0; i < num_data_; ++i) {
392
        const double x = score[i] - label_[i];
393
394
        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
395
396
397
398
399
400
401
402
      }
    }
  }

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

403
404
  bool IsConstantHessian() const override {
    return false;
405
406
  }

Nikita Titov's avatar
Nikita Titov committed
407
 private:
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
408
  /*! \brief c for Fair loss */
409
  double c_;
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
410
411
};

412
413
414
415

/*!
* \brief Objective function for Poisson regression
*/
416
class RegressionPoissonLoss: public RegressionL2loss {
Nikita Titov's avatar
Nikita Titov committed
417
 public:
Guolin Ke's avatar
Guolin Ke committed
418
  explicit RegressionPoissonLoss(const Config& config): RegressionL2loss(config) {
419
    max_delta_step_ = static_cast<double>(config.poisson_max_delta_step);
420
    if (sqrt_) {
421
      Log::Warning("Cannot use sqrt transform in %s Regression, will auto disable it", GetName());
422
423
      sqrt_ = false;
    }
424
425
  }

426
  explicit RegressionPoissonLoss(const std::vector<std::string>& strs): RegressionL2loss(strs) {
427
428
  }

429
430
431
  ~RegressionPoissonLoss() {}

  void Init(const Metadata& metadata, data_size_t num_data) override {
432
    if (sqrt_) {
433
      Log::Warning("Cannot use sqrt transform in %s Regression, will auto disable it", GetName());
434
435
      sqrt_ = false;
    }
436
    RegressionL2loss::Init(metadata, num_data);
437
    // Safety check of labels
438
    label_t miny;
439
    double sumy;
440
    Common::ObtainMinMaxSum(label_, num_data_, &miny, static_cast<label_t*>(nullptr), &sumy);
441
    if (miny < 0.0f) {
442
      Log::Fatal("[%s]: at least one target label is negative", GetName());
443
444
    }
    if (sumy == 0.0f) {
445
      Log::Fatal("[%s]: sum of labels is zero", GetName());
446
    }
447
448
  }

449
450
451
452
453
454
455
456
457
  /* 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.
   *
   */
458
459
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
460
    if (weights_ == nullptr) {
461
      #pragma omp parallel for schedule(static)
462
      for (data_size_t i = 0; i < num_data_; ++i) {
463
464
        gradients[i] = static_cast<score_t>(std::exp(score[i]) - label_[i]);
        hessians[i] = static_cast<score_t>(std::exp(score[i] + max_delta_step_));
465
466
      }
    } else {
467
      #pragma omp parallel for schedule(static)
468
      for (data_size_t i = 0; i < num_data_; ++i) {
469
470
        gradients[i] = static_cast<score_t>((std::exp(score[i]) - label_[i]) * weights_[i]);
        hessians[i] = static_cast<score_t>(std::exp(score[i] + max_delta_step_) * weights_[i]);
471
472
473
474
      }
    }
  }

475
476
477
478
  void ConvertOutput(const double* input, double* output) const override {
    output[0] = std::exp(input[0]);
  }

479
480
481
482
  const char* GetName() const override {
    return "poisson";
  }

483
  double BoostFromScore(int) const override {
Guolin Ke's avatar
Guolin Ke committed
484
    return Common::SafeLog(RegressionL2loss::BoostFromScore(0));
485
486
  }

487
488
489
490
  bool IsConstantHessian() const override {
    return false;
  }

Nikita Titov's avatar
Nikita Titov committed
491
 private:
492
493
494
495
  /*! \brief used to safeguard optimization */
  double max_delta_step_;
};

496
class RegressionQuantileloss : public RegressionL2loss {
Nikita Titov's avatar
Nikita Titov committed
497
 public:
Guolin Ke's avatar
Guolin Ke committed
498
  explicit RegressionQuantileloss(const Config& config): RegressionL2loss(config) {
499
    alpha_ = static_cast<score_t>(config.alpha);
Guolin Ke's avatar
Guolin Ke committed
500
    CHECK(alpha_ > 0 && alpha_ < 1);
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
  }

  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) {
526
          gradients[i] = static_cast<score_t>((1.0f - alpha_) * weights_[i]);
527
        } else {
528
          gradients[i] = static_cast<score_t>(-alpha_ * weights_[i]);
529
        }
530
        hessians[i] = static_cast<score_t>(weights_[i]);
531
532
533
534
535
536
537
538
      }
    }
  }

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

539
  double BoostFromScore(int) const override {
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
    if (weights_ != nullptr) {
      #define data_reader(i) (label_[i])
      #define weight_reader(i) (weights_[i])
      WeightedPercentileFun(label_t, data_reader, weight_reader, num_data_, alpha_);
      #undef data_reader
      #undef weight_reader
    } else {
      #define data_reader(i) (label_[i])
      PercentileFun(label_t, data_reader, num_data_, alpha_);
      #undef data_reader
    }
  }

  bool IsRenewTreeOutput() const override { return true; }

  double RenewTreeOutput(double, const double* pred,
                         const data_size_t* index_mapper,
                         const data_size_t* bagging_mapper,
                         data_size_t num_data_in_leaf) const override {
    if (weights_ == nullptr) {
      if (bagging_mapper == nullptr) {
        #define data_reader(i) (label_[index_mapper[i]] - pred[index_mapper[i]])
        PercentileFun(double, data_reader, num_data_in_leaf, alpha_);
        #undef data_reader
      } else {
        #define data_reader(i) (label_[bagging_mapper[index_mapper[i]]] - pred[bagging_mapper[index_mapper[i]]])
        PercentileFun(double, data_reader, num_data_in_leaf, alpha_);
        #undef data_reader
      }
    } else {
      if (bagging_mapper == nullptr) {
        #define data_reader(i) (label_[index_mapper[i]] - pred[index_mapper[i]])
        #define weight_reader(i) (weights_[index_mapper[i]])
        WeightedPercentileFun(double, data_reader, weight_reader, num_data_in_leaf, alpha_);
        #undef data_reader
        #undef weight_reader
      } else {
        #define data_reader(i) (label_[bagging_mapper[index_mapper[i]]] - pred[bagging_mapper[index_mapper[i]]])
        #define weight_reader(i) (weights_[bagging_mapper[index_mapper[i]]])
        WeightedPercentileFun(double, data_reader, weight_reader, num_data_in_leaf, alpha_);
        #undef data_reader
        #undef weight_reader
      }
    }
  }

Guolin Ke's avatar
Guolin Ke committed
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
  double RenewTreeOutput(double, double pred,
                         const data_size_t* index_mapper,
                         const data_size_t* bagging_mapper,
                         data_size_t num_data_in_leaf) const override {
    if (weights_ == nullptr) {
      if (bagging_mapper == nullptr) {
        #define data_reader(i) (label_[index_mapper[i]] - pred)
        PercentileFun(double, data_reader, num_data_in_leaf, alpha_);
        #undef data_reader
      } else {
        #define data_reader(i) (label_[bagging_mapper[index_mapper[i]]] - pred)
        PercentileFun(double, data_reader, num_data_in_leaf, alpha_);
        #undef data_reader
      }
    } else {
      if (bagging_mapper == nullptr) {
        #define data_reader(i) (label_[index_mapper[i]] - pred)
        #define weight_reader(i) (weights_[index_mapper[i]])
        WeightedPercentileFun(double, data_reader, weight_reader, num_data_in_leaf, alpha_);
        #undef data_reader
        #undef weight_reader
      } else {
        #define data_reader(i) (label_[bagging_mapper[index_mapper[i]]] - pred)
        #define weight_reader(i) (weights_[bagging_mapper[index_mapper[i]]])
        WeightedPercentileFun(double, data_reader, weight_reader, num_data_in_leaf, alpha_);
        #undef data_reader
        #undef weight_reader
      }
    }
  }

Nikita Titov's avatar
Nikita Titov committed
617
 private:
618
619
620
  score_t alpha_;
};

621
622
623
624
625

/*!
* \brief Mape Regression Loss
*/
class RegressionMAPELOSS : public RegressionL1loss {
Nikita Titov's avatar
Nikita Titov committed
626
 public:
Guolin Ke's avatar
Guolin Ke committed
627
  explicit RegressionMAPELOSS(const Config& config) : RegressionL1loss(config) {
628
629
  }

630
  explicit RegressionMAPELOSS(const std::vector<std::string>& strs) : RegressionL1loss(strs) {
631
632
  }

633
634
635
636
637
638
  ~RegressionMAPELOSS() {}

  void Init(const Metadata& metadata, data_size_t num_data) override {
    RegressionL2loss::Init(metadata, num_data);
    for (data_size_t i = 0; i < num_data_; ++i) {
      if (std::fabs(label_[i]) < 1) {
639
        Log::Warning("Met 'abs(label) < 1', will convert them to '1' in MAPE objective and metric");
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
        break;
      }
    }
    label_weight_.resize(num_data);
    if (weights_ == nullptr) {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        label_weight_[i] = 1.0f / std::max(1.0f, std::fabs(label_[i]));
      }
    } else {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        label_weight_[i] = 1.0f / std::max(1.0f, std::fabs(label_[i])) * weights_[i];
      }
    }
  }
656
657
658
659
660
661

  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) {
662
663
664
        const double diff = score[i] - label_[i];
        gradients[i] = static_cast<score_t>(Common::Sign(diff) * label_weight_[i]);
        hessians[i] = 1.0f;
665
666
667
668
      }
    } else {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
669
670
671
        const double diff = score[i] - label_[i];
        gradients[i] = static_cast<score_t>(Common::Sign(diff) * label_weight_[i]);
        hessians[i] = weights_[i];
672
673
674
675
      }
    }
  }

676
  double BoostFromScore(int) const override {
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
    const double alpha = 0.5;
    #define data_reader(i) (label_[i])
    #define weight_reader(i) (label_weight_[i])
    WeightedPercentileFun(label_t, data_reader, weight_reader, num_data_, alpha);
    #undef data_reader
    #undef weight_reader
  }

  bool IsRenewTreeOutput() const override { return true; }

  double RenewTreeOutput(double, const double* pred,
                         const data_size_t* index_mapper,
                         const data_size_t* bagging_mapper,
                         data_size_t num_data_in_leaf) const override {
    const double alpha = 0.5;
    if (bagging_mapper == nullptr) {
      #define data_reader(i) (label_[index_mapper[i]] - pred[index_mapper[i]])
      #define weight_reader(i) (label_weight_[index_mapper[i]])
      WeightedPercentileFun(double, data_reader, weight_reader, num_data_in_leaf, alpha);
      #undef data_reader
      #undef weight_reader
    } else {
      #define data_reader(i) (label_[bagging_mapper[index_mapper[i]]] - pred[bagging_mapper[index_mapper[i]]])
      #define weight_reader(i) (label_weight_[bagging_mapper[index_mapper[i]]])
      WeightedPercentileFun(double, data_reader, weight_reader, num_data_in_leaf, alpha);
Guolin Ke's avatar
Guolin Ke committed
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
      #undef data_reader
      #undef weight_reader
    }
  }

  double RenewTreeOutput(double, double pred,
                         const data_size_t* index_mapper,
                         const data_size_t* bagging_mapper,
                         data_size_t num_data_in_leaf) const override {
    const double alpha = 0.5;
    if (bagging_mapper == nullptr) {
      #define data_reader(i) (label_[index_mapper[i]] - pred)
      #define weight_reader(i) (label_weight_[index_mapper[i]])
      WeightedPercentileFun(double, data_reader, weight_reader, num_data_in_leaf, alpha);
      #undef data_reader
      #undef weight_reader
    } else {
      #define data_reader(i) (label_[bagging_mapper[index_mapper[i]]] - pred)
      #define weight_reader(i) (label_weight_[bagging_mapper[index_mapper[i]]])
      WeightedPercentileFun(double, data_reader, weight_reader, num_data_in_leaf, alpha);
722
723
724
      #undef data_reader
      #undef weight_reader
    }
725
726
727
  }

  const char* GetName() const override {
728
729
730
731
732
    return "mape";
  }

  bool IsConstantHessian() const override {
    return true;
733
734
  }

Nikita Titov's avatar
Nikita Titov committed
735
 private:
736
  std::vector<label_t> label_weight_;
737
738
};

Guolin Ke's avatar
Guolin Ke committed
739
740
741
742
743
744


/*!
* \brief Objective function for Gamma regression
*/
class RegressionGammaLoss : public RegressionPoissonLoss {
Nikita Titov's avatar
Nikita Titov committed
745
 public:
Guolin Ke's avatar
Guolin Ke committed
746
  explicit RegressionGammaLoss(const Config& config) : RegressionPoissonLoss(config) {
Guolin Ke's avatar
Guolin Ke committed
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
  }

  explicit RegressionGammaLoss(const std::vector<std::string>& strs) : RegressionPoissonLoss(strs) {
  }

  ~RegressionGammaLoss() {}

  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) {
        gradients[i] = static_cast<score_t>(1.0 - label_[i] / std::exp(score[i]));
        hessians[i] = static_cast<score_t>(label_[i] / std::exp(score[i]));
      }
    } else {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        gradients[i] = static_cast<score_t>(1.0 - label_[i] / std::exp(score[i]) * weights_[i]);
        hessians[i] = static_cast<score_t>(label_[i] / std::exp(score[i]) * weights_[i]);
      }
    }
  }

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

/*!
* \brief Objective function for Tweedie regression
*/
class RegressionTweedieLoss: public RegressionPoissonLoss {
Nikita Titov's avatar
Nikita Titov committed
780
 public:
Guolin Ke's avatar
Guolin Ke committed
781
  explicit RegressionTweedieLoss(const Config& config) : RegressionPoissonLoss(config) {
Guolin Ke's avatar
Guolin Ke committed
782
783
784
785
786
787
788
789
790
791
792
793
794
795
    rho_ = config.tweedie_variance_power;
  }

  explicit RegressionTweedieLoss(const std::vector<std::string>& strs) : RegressionPoissonLoss(strs) {
  }

  ~RegressionTweedieLoss() {}

  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) {
        gradients[i] = static_cast<score_t>(-label_[i] * std::exp((1 - rho_) * score[i]) + std::exp((2 - rho_) * score[i]));
796
        hessians[i] = static_cast<score_t>(-label_[i] * (1 - rho_) * std::exp((1 - rho_) * score[i]) +
Guolin Ke's avatar
Guolin Ke committed
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
          (2 - rho_) * std::exp((2 - rho_) * score[i]));
      }
    } else {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
        gradients[i] = static_cast<score_t>((-label_[i] * std::exp((1 - rho_) * score[i]) + std::exp((2 - rho_) * score[i])) * weights_[i]);
        hessians[i] = static_cast<score_t>((-label_[i] * (1 - rho_) * std::exp((1 - rho_) * score[i]) +
          (2 - rho_) * std::exp((2 - rho_) * score[i])) * weights_[i]);
      }
    }
  }

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

Nikita Titov's avatar
Nikita Titov committed
813
 private:
Guolin Ke's avatar
Guolin Ke committed
814
815
816
  double rho_;
};

817
818
819
#undef PercentileFun
#undef WeightedPercentileFun

Guolin Ke's avatar
Guolin Ke committed
820
}  // namespace LightGBM
Guolin Ke's avatar
Guolin Ke committed
821
#endif   // LightGBM_OBJECTIVE_REGRESSION_OBJECTIVE_HPP_