regression_objective.hpp 23.7 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]);\
Guolin Ke's avatar
Guolin Ke committed
68
  if (weighted_cdf[pos + 1] - weighted_cdf[pos] >= 1.0f) {\
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, std::function<double(const label_t*, int)> residual_getter,
236
237
238
239
240
241
                         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) {
242
        #define data_reader(i) (residual_getter(label_, index_mapper[i]))
243
244
245
        PercentileFun(double, data_reader, num_data_in_leaf, alpha);
        #undef data_reader
      } else {
246
        #define data_reader(i) (residual_getter(label_, bagging_mapper[index_mapper[i]]))
247
248
249
250
251
        PercentileFun(double, data_reader, num_data_in_leaf, alpha);
        #undef data_reader
      }
    } else {
      if (bagging_mapper == nullptr) {
252
        #define data_reader(i) (residual_getter(label_, index_mapper[i]))
253
254
255
256
257
        #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 {
258
        #define data_reader(i) (residual_getter(label_, bagging_mapper[index_mapper[i]]))
Guolin Ke's avatar
Guolin Ke committed
259
260
261
262
263
264
265
266
        #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
      }
    }
  }

267
268
269
  const char* GetName() const override {
    return "regression_l1";
  }
270
271
};

Guolin Ke's avatar
Guolin Ke committed
272
273
274
/*!
* \brief Huber regression loss
*/
275
class RegressionHuberLoss: public RegressionL2loss {
Nikita Titov's avatar
Nikita Titov committed
276
 public:
Guolin Ke's avatar
Guolin Ke committed
277
  explicit RegressionHuberLoss(const Config& config): RegressionL2loss(config) {
278
    alpha_ = static_cast<double>(config.alpha);
Guolin Ke's avatar
Guolin Ke committed
279
280
281
282
    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
283
284
  }

285
  explicit RegressionHuberLoss(const std::vector<std::string>& strs): RegressionL2loss(strs) {
Guolin Ke's avatar
Guolin Ke committed
286
287
288
289
    if (sqrt_) {
      Log::Warning("Cannot use sqrt transform in %s Regression, will auto disable it", GetName());
      sqrt_ = false;
    }
290
291
  }

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
292
  ~RegressionHuberLoss() {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
293
294
  }

295
296
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
297
    if (weights_ == nullptr) {
298
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
299
      for (data_size_t i = 0; i < num_data_; ++i) {
300
        const double diff = score[i] - label_[i];
301
        if (std::abs(diff) <= alpha_) {
302
          gradients[i] = static_cast<score_t>(diff);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
303
        } else {
304
          gradients[i] = static_cast<score_t>(Common::Sign(diff) * alpha_);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
305
        }
306
        hessians[i] = 1.0f;
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
307
308
      }
    } else {
309
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
310
      for (data_size_t i = 0; i < num_data_; ++i) {
311
        const double diff = score[i] - label_[i];
312
        if (std::abs(diff) <= alpha_) {
313
          gradients[i] = static_cast<score_t>(diff * weights_[i]);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
314
        } else {
315
          gradients[i] = static_cast<score_t>(Common::Sign(diff) * weights_[i] * alpha_);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
316
        }
317
        hessians[i] = static_cast<score_t>(weights_[i]);
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
318
319
320
321
322
323
324
325
      }
    }
  }

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

326
327
  bool IsConstantHessian() const override {
    return false;
328
329
  }

Nikita Titov's avatar
Nikita Titov committed
330
 private:
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
331
  /*! \brief delta for Huber loss */
332
  double alpha_;
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
333
334
};

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
335
336

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

343
  explicit RegressionFairLoss(const std::vector<std::string>& strs): RegressionL2loss(strs) {
344
345
  }

Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
346
347
  ~RegressionFairLoss() {}

348
349
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
350
    if (weights_ == nullptr) {
351
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
352
      for (data_size_t i = 0; i < num_data_; ++i) {
353
        const double x = score[i] - label_[i];
354
355
        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
356
357
      }
    } else {
358
      #pragma omp parallel for schedule(static)
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
359
      for (data_size_t i = 0; i < num_data_; ++i) {
360
        const double x = score[i] - label_[i];
361
362
        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
363
364
365
366
367
368
369
370
      }
    }
  }

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

371
372
  bool IsConstantHessian() const override {
    return false;
373
374
  }

Nikita Titov's avatar
Nikita Titov committed
375
 private:
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
376
  /*! \brief c for Fair loss */
377
  double c_;
Tsukasa OMOTO's avatar
Tsukasa OMOTO committed
378
379
};

380
381
382
383

/*!
* \brief Objective function for Poisson regression
*/
384
class RegressionPoissonLoss: public RegressionL2loss {
Nikita Titov's avatar
Nikita Titov committed
385
 public:
Guolin Ke's avatar
Guolin Ke committed
386
  explicit RegressionPoissonLoss(const Config& config): RegressionL2loss(config) {
387
    max_delta_step_ = static_cast<double>(config.poisson_max_delta_step);
388
    if (sqrt_) {
389
      Log::Warning("Cannot use sqrt transform in %s Regression, will auto disable it", GetName());
390
391
      sqrt_ = false;
    }
392
393
  }

394
  explicit RegressionPoissonLoss(const std::vector<std::string>& strs): RegressionL2loss(strs) {
395
396
  }

397
398
399
  ~RegressionPoissonLoss() {}

  void Init(const Metadata& metadata, data_size_t num_data) override {
400
    if (sqrt_) {
401
      Log::Warning("Cannot use sqrt transform in %s Regression, will auto disable it", GetName());
402
403
      sqrt_ = false;
    }
404
    RegressionL2loss::Init(metadata, num_data);
405
    // Safety check of labels
406
    label_t miny;
407
    double sumy;
408
    Common::ObtainMinMaxSum(label_, num_data_, &miny, static_cast<label_t*>(nullptr), &sumy);
409
    if (miny < 0.0f) {
410
      Log::Fatal("[%s]: at least one target label is negative", GetName());
411
412
    }
    if (sumy == 0.0f) {
413
      Log::Fatal("[%s]: sum of labels is zero", GetName());
414
    }
415
416
  }

417
418
419
420
421
422
423
424
425
  /* 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.
   *
   */
426
427
  void GetGradients(const double* score, score_t* gradients,
                    score_t* hessians) const override {
428
    if (weights_ == nullptr) {
429
      #pragma omp parallel for schedule(static)
430
      for (data_size_t i = 0; i < num_data_; ++i) {
431
432
        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_));
433
434
      }
    } else {
435
      #pragma omp parallel for schedule(static)
436
      for (data_size_t i = 0; i < num_data_; ++i) {
437
438
        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]);
439
440
441
442
      }
    }
  }

443
444
445
446
  void ConvertOutput(const double* input, double* output) const override {
    output[0] = std::exp(input[0]);
  }

447
448
449
450
  const char* GetName() const override {
    return "poisson";
  }

451
  double BoostFromScore(int) const override {
Guolin Ke's avatar
Guolin Ke committed
452
    return Common::SafeLog(RegressionL2loss::BoostFromScore(0));
453
454
  }

455
456
457
458
  bool IsConstantHessian() const override {
    return false;
  }

Nikita Titov's avatar
Nikita Titov committed
459
 private:
460
461
462
463
  /*! \brief used to safeguard optimization */
  double max_delta_step_;
};

464
class RegressionQuantileloss : public RegressionL2loss {
Nikita Titov's avatar
Nikita Titov committed
465
 public:
Guolin Ke's avatar
Guolin Ke committed
466
  explicit RegressionQuantileloss(const Config& config): RegressionL2loss(config) {
467
    alpha_ = static_cast<score_t>(config.alpha);
Guolin Ke's avatar
Guolin Ke committed
468
    CHECK(alpha_ > 0 && alpha_ < 1);
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
  }

  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) {
494
          gradients[i] = static_cast<score_t>((1.0f - alpha_) * weights_[i]);
495
        } else {
496
          gradients[i] = static_cast<score_t>(-alpha_ * weights_[i]);
497
        }
498
        hessians[i] = static_cast<score_t>(weights_[i]);
499
500
501
502
503
504
505
506
      }
    }
  }

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

507
  double BoostFromScore(int) const override {
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
    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; }

523
  double RenewTreeOutput(double, std::function<double(const label_t*, int)> residual_getter,
Guolin Ke's avatar
Guolin Ke committed
524
525
526
527
528
                         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) {
529
        #define data_reader(i) (residual_getter(label_, index_mapper[i]))
Guolin Ke's avatar
Guolin Ke committed
530
531
532
        PercentileFun(double, data_reader, num_data_in_leaf, alpha_);
        #undef data_reader
      } else {
533
        #define data_reader(i) (residual_getter(label_, bagging_mapper[index_mapper[i]]))
Guolin Ke's avatar
Guolin Ke committed
534
535
536
537
538
        PercentileFun(double, data_reader, num_data_in_leaf, alpha_);
        #undef data_reader
      }
    } else {
      if (bagging_mapper == nullptr) {
539
        #define data_reader(i) (residual_getter(label_, index_mapper[i]))
Guolin Ke's avatar
Guolin Ke committed
540
541
542
543
544
        #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 {
545
        #define data_reader(i) (residual_getter(label_, bagging_mapper[index_mapper[i]]))
Guolin Ke's avatar
Guolin Ke committed
546
547
548
549
550
551
552
553
        #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
554
 private:
555
556
557
  score_t alpha_;
};

558
559
560
561
562

/*!
* \brief Mape Regression Loss
*/
class RegressionMAPELOSS : public RegressionL1loss {
Nikita Titov's avatar
Nikita Titov committed
563
 public:
Guolin Ke's avatar
Guolin Ke committed
564
  explicit RegressionMAPELOSS(const Config& config) : RegressionL1loss(config) {
565
566
  }

567
  explicit RegressionMAPELOSS(const std::vector<std::string>& strs) : RegressionL1loss(strs) {
568
569
  }

570
571
572
573
574
575
  ~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) {
576
        Log::Warning("Met 'abs(label) < 1', will convert them to '1' in MAPE objective and metric");
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
        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];
      }
    }
  }
593
594
595
596
597
598

  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) {
599
600
601
        const double diff = score[i] - label_[i];
        gradients[i] = static_cast<score_t>(Common::Sign(diff) * label_weight_[i]);
        hessians[i] = 1.0f;
602
603
604
605
      }
    } else {
      #pragma omp parallel for schedule(static)
      for (data_size_t i = 0; i < num_data_; ++i) {
606
607
608
        const double diff = score[i] - label_[i];
        gradients[i] = static_cast<score_t>(Common::Sign(diff) * label_weight_[i]);
        hessians[i] = weights_[i];
609
610
611
612
      }
    }
  }

613
  double BoostFromScore(int) const override {
614
615
616
617
618
619
620
621
622
623
    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; }

624
  double RenewTreeOutput(double, std::function<double(const label_t*, int)> residual_getter,
Guolin Ke's avatar
Guolin Ke committed
625
626
627
628
629
                         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) {
630
      #define data_reader(i) (residual_getter(label_, index_mapper[i]))
Guolin Ke's avatar
Guolin Ke committed
631
632
633
634
635
      #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 {
636
      #define data_reader(i) (residual_getter(label_, bagging_mapper[index_mapper[i]]))
Guolin Ke's avatar
Guolin Ke committed
637
638
      #define weight_reader(i) (label_weight_[bagging_mapper[index_mapper[i]]])
      WeightedPercentileFun(double, data_reader, weight_reader, num_data_in_leaf, alpha);
639
640
641
      #undef data_reader
      #undef weight_reader
    }
642
643
644
  }

  const char* GetName() const override {
645
646
647
648
649
    return "mape";
  }

  bool IsConstantHessian() const override {
    return true;
650
651
  }

Nikita Titov's avatar
Nikita Titov committed
652
 private:
653
  std::vector<label_t> label_weight_;
654
655
};

Guolin Ke's avatar
Guolin Ke committed
656
657
658
659
660
661


/*!
* \brief Objective function for Gamma regression
*/
class RegressionGammaLoss : public RegressionPoissonLoss {
Nikita Titov's avatar
Nikita Titov committed
662
 public:
Guolin Ke's avatar
Guolin Ke committed
663
  explicit RegressionGammaLoss(const Config& config) : RegressionPoissonLoss(config) {
Guolin Ke's avatar
Guolin Ke committed
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
  }

  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
697
 public:
Guolin Ke's avatar
Guolin Ke committed
698
  explicit RegressionTweedieLoss(const Config& config) : RegressionPoissonLoss(config) {
Guolin Ke's avatar
Guolin Ke committed
699
700
701
702
703
704
705
706
707
708
709
710
711
712
    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]));
713
        hessians[i] = static_cast<score_t>(-label_[i] * (1 - rho_) * std::exp((1 - rho_) * score[i]) +
Guolin Ke's avatar
Guolin Ke committed
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
          (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";
  }
729

Nikita Titov's avatar
Nikita Titov committed
730
 private:
Guolin Ke's avatar
Guolin Ke committed
731
732
733
  double rho_;
};

734
735
736
#undef PercentileFun
#undef WeightedPercentileFun

Guolin Ke's avatar
Guolin Ke committed
737
}  // namespace LightGBM
Guolin Ke's avatar
Guolin Ke committed
738
#endif   // LightGBM_OBJECTIVE_REGRESSION_OBJECTIVE_HPP_