预测

XGBoost 中有许多预测函数,具有各种参数。本文档试图澄清一些关于预测的困惑,重点是 Python 绑定,当指定 strict_shape 时,R 包类似(见下文)。

预测选项

There are a number of different prediction options for the xgboost.Booster.predict() method, ranging from pred_contribs to pred_leaf. The output shape depends on types of prediction. Also for multi-class classification problem, XGBoost builds one tree for each class and the trees for each class are called a “group” of trees, so output dimension may change due to used model. After 1.4 release, we added a new parameter called strict_shape, one can set it to True to indicate a more restricted output is desired. Assuming you are using xgboost.Booster, here is a list of possible returns:

  • 当使用 strict_shape 设置为 True 的普通预测时:

    输出是一个二维数组,第一维为行,第二维为组。对于回归/生存/排序/二分类,这等同于一个 shape[1] == 1 的列向量。但对于多分类且使用 multi:softprob 时,列数等于类别数。如果 strict_shape 设置为 False,XGBoost 可能会输出 1 或 2 维数组。

  • 当使用 output_margin 避免转换并且 strict_shape 设置为 True 时:

    与前一种情况类似,输出是一个二维数组,除了 multi:softmax 由于被丢弃的转换而具有与 multi:softprob 等效的输出形状。如果严格形状设置为 False,则输出可以是 1 或 2 维,具体取决于使用的模型。

  • 当使用 pred_contribs 并将 strict_shape 设置为 True 时:

    输出是一个三维数组,形状为 (行数, 组数, 列数 + 1)。无论是否使用 approx_contribs,输出形状都不会改变。如果未设置严格的形状参数,它可能是二维或三维数组,具体取决于是否使用多类模型。

  • 当使用 pred_interactions 并将 strict_shape 设置为 True 时:

    输出是一个4维数组,形状为 (行数, 组数, 列数 + 1, 列数 + 1)。与预测贡献的情况类似,是否使用 approx_contribs 不会改变输出形状。如果严格形状设置为False,则可以根据底层模型具有3或4个维度。

  • 当使用 pred_leaf 并将 strict_shape 设置为 True 时:

    输出是一个 4 维数组,形状为 (n_samples, n_iterations, n_classes, n_trees_in_forest)n_trees_in_forest 在训练期间由 numb_parallel_tree 指定。当严格形状设置为 False 时,输出是一个 2 维数组,最后 3 个维度被合并为 1 个。如果最后一个维度等于 1,则也会被删除。在使用 scikit learn 接口的 apply 方法时,默认设置为 False。

对于R包,当指定 strict_shape 时,会返回一个 array ,其值与Python相同,只是R数组是列优先,而Python的numpy数组是行优先,因此所有维度都是相反的。例如,对于一个Python的 predict_leaf 输出,当 strict_shape=True 时,有4个维度: (n_samples, n_iterations, n_classes, n_trees_in_forest) ,而R在 strict_shape=TRUE 时输出 (n_trees_in_forest, n_classes, n_iterations, n_samples)

除了这些预测类型外,还有一个名为 iteration_range 的参数,类似于模型切片。但它并不是实际将模型分割成多个堆栈,而是简单地返回范围内树形成的预测。每次迭代中创建的树的数量等于 \(trees_i = num\_class \times num\_parallel\_tree\)。因此,如果你正在训练一个大小为4的增强随机森林,用于3类分类数据集,并且希望使用前2次迭代的树进行预测,你需要提供 iteration_range=(0, 2)。然后,前 \(2 \times 3 \times 4\) 棵树将用于此预测。

早停

When a model is trained with early stopping, there is an inconsistent behavior between native Python interface and sklearn/R interfaces. By default on R and sklearn interfaces, the best_iteration is automatically used so prediction comes from the best model. But with the native Python interface xgboost.Booster.predict() and xgboost.Booster.inplace_predict() uses the full model. Users can use best_iteration attribute with iteration_range parameter to achieve the same behavior. Also the save_best parameter from xgboost.callback.EarlyStopping might be useful.

基础边际

在 XGBoost 中有一个训练参数叫做 base_score,以及一个 DMatrix 的元数据叫做 base_margin``(如果你使用的是 scikit-learn 接口,可以在 ``fit 方法中设置)。它们指定了提升模型的全局偏差。如果后者被提供,前者将被忽略。base_margin 可以用于基于其他模型训练 XGBoost 模型。请参阅从预测中提升的演示。

分阶段预测

使用 DMatrix 的原生接口,预测可以分阶段(或缓存)进行。例如,可以先在前4棵树上进行预测,然后在8棵树上运行预测。在第一次预测运行后,前4棵树的结果会被缓存,因此当你在8棵树上运行预测时,XGBoost 可以重用之前的预测结果。如果缓存的 DMatrix 对象过期(例如超出作用域并被语言环境中的垃圾收集器收集),缓存会在下一次预测、训练或评估时自动失效。

就地预测

Traditionally XGBoost accepts only DMatrix for prediction, with wrappers like scikit-learn interface the construction happens internally. We added support for in-place predict to bypass the construction of DMatrix, which is slow and memory consuming. The new predict function has limited features but is often sufficient for simple inference tasks. It accepts some commonly found data types in Python like numpy.ndarray, scipy.sparse.csr_matrix and cudf.DataFrame instead of xgboost.DMatrix. You can call xgboost.Booster.inplace_predict() to use it. Be aware that the output of in-place prediction depends on input data type, when input is on GPU data output is cupy.ndarray, otherwise a numpy.ndarray is returned.

线程安全

在1.4版本发布后,所有预测函数,包括带有各种参数的普通 predict 以及计算shap值和 inplace_predict ,当底层增强器是 gbtreedart 时,都是线程安全的,这意味着只要使用树模型,预测本身应该是线程安全的。但这种安全性仅限于预测。如果在一条线程中训练模型,并在另一条线程中使用同一模型进行预测,其行为是未定义的。这种情况比人们预期的更容易发生,例如我们可能在预测函数内部意外调用 clf.set_params()

def predict_fn(clf: xgb.XGBClassifier, X):
    X = preprocess(X)
    clf.set_params(n_jobs=1)  # NOT safe!
    return clf.predict_proba(X, iteration_range=(0, 10))

with ThreadPoolExecutor(max_workers=10) as e:
    e.submit(predict_fn, ...)

隐私保护预测

Concrete ML 是一个由 Zama 开发的第三方开源库,它提出了类似于我们的梯度提升类,但可以直接在加密数据上进行预测,这得益于全同态加密。一个简单的例子如下:

from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from concrete.ml.sklearn import XGBClassifier

x, y = make_classification(n_samples=100, class_sep=2, n_features=30, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(
    x, y, test_size=10, random_state=42
)

# Train in the clear and quantize the weights
model = XGBClassifier()
model.fit(X_train, y_train)

# Simulate the predictions in the clear
y_pred_clear = model.predict(X_test)

# Compile in FHE
model.compile(X_train)

# Generate keys
model.fhe_circuit.keygen()

# Run the inference on encrypted inputs!
y_pred_fhe = model.predict(X_test, fhe="execute")

print("In clear  :", y_pred_clear)
print("In FHE    :", y_pred_fhe)
print(f"Similarity: {int((y_pred_fhe == y_pred_clear).mean()*100)}%")

更多信息和示例请参见 Concrete ML 文档