LightGBM和XGBoost的梯度和hessian计算的数值稳定性

LightGBM和XGBoost的梯度和hessian计算的数值稳定性,xgboost,lightgbm,Xgboost,Lightgbm,本文研究了LightGBM和XGBoost分类的数值稳定性。我认为一个好的开始是计算梯度和黑森。这些要求计算逻辑函数,据我所知,逻辑函数可能会变得不稳定,值非常小,因为这可能导致溢出 下面是XGBOOTS二进制逻辑丢失的实现。此处,epsilon值用于计算Hessian,但仅用于Hessian。为什么梯度或S形函数不需要这个?为什么需要黑森人 struct LogisticRegression { template <typename T> static T PredTransfor

本文研究了LightGBM和XGBoost分类的数值稳定性。我认为一个好的开始是计算梯度和黑森。这些要求计算逻辑函数,据我所知,逻辑函数可能会变得不稳定,值非常小,因为这可能导致溢出

下面是XGBOOTS二进制逻辑丢失的实现。此处,epsilon值用于计算Hessian,但仅用于Hessian。为什么梯度或S形函数不需要这个?为什么需要黑森人

struct LogisticRegression {
template <typename T>
static T PredTransform(T x) { return common::Sigmoid(x); }
static bool CheckLabel(bst_float x) { return x >= 0.0f && x <= 1.0f; }
template <typename T>
static T FirstOrderGradient(T predt, T label) { return predt - label; }
template <typename T>
static T SecondOrderGradient(T predt, T label) {
  const T eps = T(1e-16f);
  return std::max(predt * (T(1.0f) - predt), eps);
}
static bst_float ProbToMargin(bst_float base_score) {
  CHECK(base_score > 0.0f && base_score < 1.0f)
      << "base_score must be in (0,1) for logistic loss";
  return -std::log(1.0f / base_score - 1.0f);
}
static const char* LabelErrorMsg() {
  return "label must be in [0,1] for logistic regression";
}
static const char* DefaultEvalMetric() { return "rmse"; }
};

// logistic loss for binary classification task.
struct LogisticClassification : public LogisticRegression {
  static const char* DefaultEvalMetric() { return "error"; }
};


inline float Sigmoid(float x) {
   return 1.0f / (1.0f + std::exp(-x));
}
结构逻辑回归{ 模板 静态T变换(tx){返回公共::Sigmoid(x);} 静态布尔校验标签(bst_浮点x){返回x>=0.0f&&x 0.0f&&base_分数<1.0f)
LightGBM使用另一种方法来解决数值稳定性问题

  • LightGBM将限制树叶的最小/最大值:

  • 在计算叶输出时,它将添加一个ε: 参考:和

    黑森人的总和总是大于ε


  • 谢谢!我必须承认我很难在存储库中找到自己的方法。你知道为什么选择它来限制最小/最大叶数吗?我猜它是用来防止过度拟合的,但我发现它不是超参数,这很奇怪。可能是因为计算效率。你能解释为什么Hessian不能为0吗?我没有看到标准d牛顿法的计算,其中梯度除以Hessian。@SejlerMonster它不限制最小/最大叶数。它限制叶的输出。你可以使用
    num_leaves
    限制叶数。如果你使用二阶泰勒展开,最佳解是梯度/Hessian(详情请参阅)
    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) {
        // get label and label weights
        const int is_pos = is_pos_(label_[i]);
        const int label = label_val_[is_pos];
        const double label_weight = label_weights_[is_pos];
        // calculate gradients and hessians
        const double response = -label * sigmoid_ / (1.0f + std::exp(label * sigmoid_ * score[i]));
        const double abs_response = fabs(response);
        gradients[i] = static_cast<score_t>(response * label_weight);
        hessians[i] = static_cast<score_t>(abs_response * (sigmoid_ - abs_response) * label_weight);
      }