我最近遇到了这个问题来动态传递 pos_scale_weight,下面是我如何解决这个问题。当前没有可用于 XGBClassifier 类的自动 pos_scale_weight。我们真的很想将 sklearn OneVsRestClassifier 与 xgboost 一起用于多标签或多类分类问题。
使用 OneVsRestClassifier 的常用方法如下:
clf = OneVsRestClassifier(XGBClassifier(booster='gbtree', objective='binary:logistic'))
clf.fit(X=X_train, y = y_train)
OneVsRestClassifier 所做的是:当你调用 clf.fit 时,它实际上调用了 XGBClassifier 中的 fit 方法来拟合 X_train,并调用 y_train 中的每个目标来拟合训练数据。在下面的示例中,clf.fit 方法将执行以下操作: XGBClassifier.fit(X_train, target1) -> XGBClassifier.fit(X_train, target2) -> XGBClassifier.fit(X_train, target3) -> 集成所有三个模型。如果您将 pos_scale_weight 设置为某个数字,则每次拟合都将使用相同的比例。如果所有三个目标的阳性率差异很大。它将不适合阳性率远低于其他目标的目标。
y_train.head()
| target1| target2 |target3|
|--------|---------|-------|
| 0 | 1 | 0 |
| 1 | 1 | 0 |
在我的挑战中,我预测的每个标签都有完全不同的 pos 和 neg 比率(范围从 0.1% 到 10%)。下面是我创建的一个方法。假设我们有 X_train 作为训练特征,y_train 是每个类的二进制标签矩阵。我们可以解决并创建一个继承 fit 函数的新类,并为每个 y_train 数组传递一个 weight_array。OneVsRestClassifier 将 y_train 中的每个 y 一个一个传递,因此 weight_array 将单独计算。此解决方案仅适用于多标签的二进制分类([0,1])。我们要确保 neg 类的权重为 1,pos 类的权重为 (num of neg)/(num of pos)。
class XGBClassifierNew(XGBClassifier):
"""
the inherited class with same method name will override.
if you start an XGBClassifierNew instance the fit method you called by default will be XGBClassifierNew.fit(). Check the link below for reference.
https://stackoverflow.com/questions/12764995/python-overriding-an-inherited-class-method
"""
def fit(self, X, y, **kwargs):
pos_ratio = y.sum()/len(y)
weight = len(y)/y.sum() - 1
weight_array = y * (weight-1) + 1
return super().fit(X=X, y=y, sample_weight = weight_array, **kwargs)
clf = OneVsRestClassifier(XGBClassifierNew())
clf.fit(X=X_train, y=y_train)
而 weight_array 之所以是一个数组,是因为 sample_weight 对每个实例进行加权,而不是像 pos_scale_weight 这样的整个类。
并且此方法将整个班级的权重(在每个标签内)同等对待。