LightGBM中的predict_proba()函数是如何内部工作的?

18
这是关于理解如何使用LightGBM预测类别概率的内部细节。
其他软件包,如sklearn,为它们的分类器提供了详细的说明。例如:

概率估计。

所有类别的返回估计值都按类别标签排序。

对于多类问题,如果将multi_class设置为“multinomial”,则使用softmax函数查找每个类别的预测概率。否则,使用一对多方法,即使用逻辑函数计算假定为正的每个类别的概率,并在所有类别上规范化这些值。

预测X的类别概率。

输入样本的预测类别概率计算为森林中树的平均预测类别概率。单棵树的类别概率是叶子中相同类别样本的比例。

还有其他Stack Overflow问题提供了额外的细节,例如: 我试图揭示LightGBM的predict_proba函数的相同细节。文档没有列出如何计算概率的详细信息。
文档仅说明:

返回每个样本的每个类别的预测概率。

以下是源代码:
def predict_proba(self, X, raw_score=False, start_iteration=0, num_iteration=None,
                  pred_leaf=False, pred_contrib=False, **kwargs):
    """Return the predicted probability for each class for each sample.

    Parameters
    ----------
    X : array-like or sparse matrix of shape = [n_samples, n_features]
        Input features matrix.
    raw_score : bool, optional (default=False)
        Whether to predict raw scores.
    start_iteration : int, optional (default=0)
        Start index of the iteration to predict.
        If <= 0, starts from the first iteration.
    num_iteration : int or None, optional (default=None)
        Total number of iterations used in the prediction.
        If None, if the best iteration exists and start_iteration <= 0, the best iteration is used;
        otherwise, all iterations from ``start_iteration`` are used (no limits).
        If <= 0, all iterations from ``start_iteration`` are used (no limits).
    pred_leaf : bool, optional (default=False)
        Whether to predict leaf index.
    pred_contrib : bool, optional (default=False)
        Whether to predict feature contributions.

        .. note::

            If you want to get more explanations for your model's predictions using SHAP values,
            like SHAP interaction values,
            you can install the shap package (https://github.com/slundberg/shap).
            Note that unlike the shap package, with ``pred_contrib`` we return a matrix with an extra
            column, where the last column is the expected value.

    **kwargs
        Other parameters for the prediction.

    Returns
    -------
    predicted_probability : array-like of shape = [n_samples, n_classes]
        The predicted probability for each class for each sample.
    X_leaves : array-like of shape = [n_samples, n_trees * n_classes]
        If ``pred_leaf=True``, the predicted leaf of every tree for each sample.
    X_SHAP_values : array-like of shape = [n_samples, (n_features + 1) * n_classes] or list with n_classes length of such objects
        If ``pred_contrib=True``, the feature contributions for each sample.
    """
    result = super(LGBMClassifier, self).predict(X, raw_score, start_iteration, num_iteration,
                                                 pred_leaf, pred_contrib, **kwargs)
    if callable(self._objective) and not (raw_score or pred_leaf or pred_contrib):
        warnings.warn("Cannot compute class probabilities or labels "
                      "due to the usage of customized objective function.\n"
                      "Returning raw scores instead.")
        return result
    elif self._n_classes > 2 or raw_score or pred_leaf or pred_contrib:
        return result
    else:
        return np.vstack((1. - result, result)).transpose()

我该如何理解LightGBMpredict_proba函数的内部工作原理?

2个回答

15
LightGBM是一种基于梯度提升方法的分类算法,它将决策树和逻辑回归结合起来。首先,我们使用相同的逻辑函数表示概率(也称为softmax):P(y = 1 | X) = 1/(1 + exp(Xw))。有趣之处在于,特征矩阵X由决策树集合的终端节点组成。然后,这些节点都由权重参数w加权,该参数必须学习。用于学习权重的机制取决于所使用的精确学习算法。同样,X的构建也取决于算法。例如,LightGBM引入了两个新颖的功能“基于梯度的单侧采样”和“独家特征捆绑”,从而赢得了比XGBoost更好的性能提升。通常,每行收集每个样本的终端叶子,而列则表示终端叶子。因此,文档可以这样描述:输入样本的预测类别概率是由对应于提供的样本的决策树集合的加权终端叶子的softmax计算得出的。要了解更多详细信息,您需要深入了解提升、XGBoost和最终的LightGBM论文,但考虑到您提供的其他文档示例,这似乎有些过于严格了。

5

简要解释

下面我们可以看到每种方法在底层调用了什么。首先,类LGBMClassifier predict_proba()方法调用了LGBMModelpredict()方法(它从该方法继承而来)。

LGBMClassifier.predict_proba() (inherits from LGBMModel)
  |---->LGBMModel().predict() (calls LightGBM Booster)
          |---->Booster.predict()

然后,它从LightGBM Booster(即Booster类)调用predict()方法。为了调用这个方法,必须先训练Booster。

基本上,Booster通过调用它的predict()方法为每个样本生成预测值。请参见下面,详细了解这个booster是如何工作的。

详细说明或LightGBM Booster如何工作?

我们试图回答LightGBM booster如何工作的问题。通过阅读Python代码,我们可以大致了解它是如何被训练和更新的。但是,还有一些涉及到LightGBM的C++库的进一步参考,我无法解释。然而,我们可以简单了解一下LightGBM的Booster工作流程。

A. 初始化和训练Booster

通过调用LGBMModeltrain()函数初始化_Booster。在sklearn.py的第595行可以看到以下代码:

self._Booster = train(params, train_set,
                      self.n_estimators, valid_sets=valid_sets, valid_names=eval_names,
                      early_stopping_rounds=early_stopping_rounds,
                      evals_result=evals_result, fobj=self._fobj, feval=feval,
                      verbose_eval=verbose, feature_name=feature_name,
                      callbacks=callbacks, init_model=init_model)

Note:此处的train()来自engine.py

train()函数中,我们可以看到Booster被初始化(第231行)。

# construct booster
try:
    booster = Booster(params=params, train_set=train_set)
...

并在每次训练迭代时更新(第242行)。

for i in range_(init_iteration, init_iteration + num_boost_round):
     ...
     ... 
     booster.update(fobj=fobj)
     ...

B. booster.update()是如何工作的?

为了理解update()方法的工作原理,我们应该查看basic.py中的第2315行。在这里,我们可以看到此函数更新一次Booster

根据是否提供目标函数,有两种更新Booster的方式。

  • 没有提供目标函数

在第2367行,我们可以看到以下代码:

if fobj is None:
    ...
    ...
    _safe_call(_LIB.LGBM_BoosterUpdateOneIter(
               self.handle,
               ctypes.byref(is_finished)))
    self.__is_predicted_cur_iter = [False for _ in range_(self.__num_dataset)]
    return is_finished.value == 1

请注意,由于没有提供目标函数(fobj),它将通过调用来自_LIBLGBM_BoosterUpdateOneIter 来更新增强器。简而言之,_LIB 是已加载的 C++ LightGBM 库。

_LIB 是什么?

_LIB 是一个变量,通过调用_load_lib()basic.py 的第29行)来存储已加载的LightGBM库

然后,_load_lib()会通过在您的系统上查找到lib_lightgbm.dll(Windows)或lib_lightgbm.so(Linux)的路径来加载LightGBM库。

  • 提供目标函数

当遇到自定义对象函数时,我们将进入以下情况

else:
    ...
    ...
    grad, hess = fobj(self.__inner_predict(0), self.train_set)

__inner_predict() 是 LightGBM 的 Booster 的一个方法(详见 basic.py 中第 1930 行的 Booster 类)。该方法用于对训练和验证数据进行预测。在 __inner_predict() 方法内部(详见 basic.py 中第 3142 行),它调用了来自 _LIBLGBM_BoosterGetPredict 来获取预测结果。

_safe_call(_LIB.LGBM_BoosterGetPredict(
                self.handle,
                ctypes.c_int(data_idx),
                ctypes.byref(tmp_out_len),
                data_ptr))

最终,在更新了range_(init_iteration, init_iteration + num_boost_round) 次增强器之后,它将被训练。因此,Booster.predict() 可以由 LightGBMClassifier.predict_proba() 调用。

注意。增强器是模型拟合步骤的一部分进行训练的,具体而言是通过 LGBMModel.fit() 完成的,请参见sklearn.py 的第 595 行代码获取代码详细信息。


感谢您追踪代码……有没有办法理解这意味着什么?再次寻求对问题中指定的概率如何计算以及在发布的4个示例中的解释。 - artemis
@wundermahn,甚至可以深入了解LGBM_BoosterGetPredict背后的C++代码。我不擅长C语言。你是在寻找这种解释吗(C源代码)? - Miguel Trejo
是的,我认为很多这些库最终都是用C++C编写的。目的是要有一个描述如何计算概率的答案;不一定是逻辑或代码流程。有几个问题的示例提供了类似于我上面寻找的答案。在我看来,最好的是SVM[1] [https://dev59.com/rGUp5IYBdhLWcg3wtZE0]。 - artemis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接