#ifndef LIGHTGBM_OBJECTIVE_REGRESSION_OBJECTIVE_HPP_ #define LIGHTGBM_OBJECTIVE_REGRESSION_OBJECTIVE_HPP_ #include #include #include namespace LightGBM { /*! * \brief Objective function for regression */ class RegressionL2loss: public ObjectiveFunction { public: explicit RegressionL2loss(const ObjectiveConfig&) { } explicit RegressionL2loss(const std::vector&) { } ~RegressionL2loss() { } void Init(const Metadata& metadata, data_size_t num_data) override { num_data_ = num_data; label_ = metadata.label(); weights_ = metadata.weights(); } 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[i] - label_[i]); hessians[i] = 1.0f; } } else { #pragma omp parallel for schedule(static) for (data_size_t i = 0; i < num_data_; ++i) { gradients[i] = static_cast(score[i] - label_[i]) * weights_[i]; hessians[i] = weights_[i]; } } } const char* GetName() const override { return "regression"; } std::string ToString() const override { std::stringstream str_buf; str_buf << GetName(); return str_buf.str(); } bool IsConstantHessian() const override { if (weights_ == nullptr) { return true; } else { return false; } } bool BoostFromAverage() const override { return true; } 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 L1 regression loss */ class RegressionL1loss: public ObjectiveFunction { public: explicit RegressionL1loss(const ObjectiveConfig& config) { eta_ = static_cast(config.gaussian_eta); } explicit RegressionL1loss(const std::vector&) { } ~RegressionL1loss() {} void Init(const Metadata& metadata, data_size_t num_data) override { num_data_ = num_data; label_ = metadata.label(); weights_ = metadata.weights(); } 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) { const double diff = score[i] - label_[i]; if (diff >= 0.0f) { gradients[i] = 1.0f; } else { gradients[i] = -1.0f; } hessians[i] = static_cast(Common::ApproximateHessianWithGaussian(score[i], label_[i], gradients[i], eta_)); } } else { #pragma omp parallel for schedule(static) for (data_size_t i = 0; i < num_data_; ++i) { const double diff = score[i] - label_[i]; if (diff >= 0.0f) { gradients[i] = weights_[i]; } else { gradients[i] = -weights_[i]; } hessians[i] = static_cast(Common::ApproximateHessianWithGaussian(score[i], label_[i], gradients[i], eta_, weights_[i])); } } } const char* GetName() const override { return "regression_l1"; } std::string ToString() const override { std::stringstream str_buf; str_buf << GetName(); return str_buf.str(); } bool BoostFromAverage() const override { return true; } 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 a parameter to control the width of Gaussian function to approximate hessian */ double eta_; }; /*! * \brief Huber regression loss */ class RegressionHuberLoss: public ObjectiveFunction { public: explicit RegressionHuberLoss(const ObjectiveConfig& config) { delta_ = static_cast(config.huber_delta); eta_ = static_cast(config.gaussian_eta); } explicit RegressionHuberLoss(const std::vector&) { } ~RegressionHuberLoss() { } void Init(const Metadata& metadata, data_size_t num_data) override { num_data_ = num_data; label_ = metadata.label(); weights_ = metadata.weights(); } 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) { const double diff = score[i] - label_[i]; if (std::abs(diff) <= delta_) { gradients[i] = static_cast(diff); hessians[i] = 1.0f; } else { if (diff >= 0.0f) { gradients[i] = static_cast(delta_); } else { gradients[i] = static_cast(-delta_); } hessians[i] = static_cast(Common::ApproximateHessianWithGaussian(score[i], label_[i], gradients[i], eta_)); } } } else { #pragma omp parallel for schedule(static) for (data_size_t i = 0; i < num_data_; ++i) { const double diff = score[i] - label_[i]; if (std::abs(diff) <= delta_) { gradients[i] = static_cast(diff * weights_[i]); hessians[i] = weights_[i]; } else { if (diff >= 0.0f) { gradients[i] = static_cast(delta_ * weights_[i]); } else { gradients[i] = static_cast(-delta_ * weights_[i]); } hessians[i] = static_cast(Common::ApproximateHessianWithGaussian(score[i], label_[i], gradients[i], eta_, weights_[i])); } } } } const char* GetName() const override { return "huber"; } std::string ToString() const override { std::stringstream str_buf; str_buf << GetName(); return str_buf.str(); } bool BoostFromAverage() const override { return true; } 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 */ double delta_; /*! \brief a parameter to control the width of Gaussian function to approximate hessian */ double eta_; }; // 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) { c_ = static_cast(config.fair_c); } explicit RegressionFairLoss(const std::vector&) { } ~RegressionFairLoss() {} void Init(const Metadata& metadata, data_size_t num_data) override { num_data_ = num_data; label_ = metadata.label(); weights_ = metadata.weights(); } 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) { const double x = score[i] - label_[i]; gradients[i] = static_cast(c_ * x / (std::fabs(x) + c_)); hessians[i] = static_cast(c_ * c_ / ((std::fabs(x) + c_) * (std::fabs(x) + c_))); } } else { #pragma omp parallel for schedule(static) for (data_size_t i = 0; i < num_data_; ++i) { const double x = score[i] - label_[i]; gradients[i] = static_cast(c_ * x / (std::fabs(x) + c_) * weights_[i]); hessians[i] = static_cast(c_ * c_ / ((std::fabs(x) + c_) * (std::fabs(x) + c_)) * weights_[i]); } } } const char* GetName() const override { return "fair"; } std::string ToString() const override { std::stringstream str_buf; str_buf << GetName(); return str_buf.str(); } bool BoostFromAverage() const override { return true; } 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 */ double c_; }; /*! * \brief Objective function for Poisson regression */ class RegressionPoissonLoss: public ObjectiveFunction { public: explicit RegressionPoissonLoss(const ObjectiveConfig& config) { max_delta_step_ = static_cast(config.poisson_max_delta_step); } explicit RegressionPoissonLoss(const std::vector&) { } ~RegressionPoissonLoss() {} void Init(const Metadata& metadata, data_size_t num_data) override { num_data_ = num_data; label_ = metadata.label(); weights_ = metadata.weights(); // Safety check of labels float miny; double sumy; Common::ObtainMinMaxSum(label_, num_data_, &miny, (float*)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()); } } /* 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. * */ 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) { const double ef = std::exp(score[i]); gradients[i] = static_cast(ef - label_[i]); hessians[i] = static_cast(ef); } } else { #pragma omp parallel for schedule(static) for (data_size_t i = 0; i < num_data_; ++i) { const double ef = std::exp(score[i]); gradients[i] = static_cast((ef - label_[i]) * weights_[i]); hessians[i] = static_cast(ef * weights_[i]); } } } void ConvertOutput(const double* input, double* output) const override { output[0] = std::exp(input[0]); } const char* GetName() const override { return "poisson"; } std::string ToString() const override { std::stringstream str_buf; str_buf << GetName(); return str_buf.str(); } bool BoostFromAverage() const override { return true; } 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(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; } 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_; }; } // namespace LightGBM #endif // LightGBM_OBJECTIVE_REGRESSION_OBJECTIVE_HPP_