使用 OneVsRestClassifier 时如何传递 XGBoost 拟合参数?

Posted

技术标签:

【中文标题】使用 OneVsRestClassifier 时如何传递 XGBoost 拟合参数?【英文标题】:How to pass XGBoost fit parameters when using OneVsRestClassifier? 【发布时间】:2019-12-08 10:03:00 【问题描述】:

我想通过OneVsRestClassifier的fit方法传递xgboost的fit参数。

clf = OneVsRestClassifier( XGBClassifier(objective='binary:logistic', seed=0))
# Want to pass `eval_set` and 'eval_metric' to xgboost model.
clf.fit(X_train, y_train, estimator__eval_metric='aucpr', estimator__eval_set= eval_set_xgboost)

错误:fit() 得到了一个意外的关键字参数“estimator__eval_metric”

您能帮我如何使用OneVsRestClassifier fit 方法传递XGBoost fit 参数吗?

【问题讨论】:

【参考方案1】:

XGBoost 默认处理多类分类。更多解释请参考this示例。

使用当前框架,您不能将fit_params 传递给OneVsRestClassifier。有关详细信息,请参阅此issue。

可能,如果您可以与OneVsRestClassifier 分享您的包装意图,我们可以为您提供适当的指导。

更新:

我不认为用一个 Vs 分类器包装会减少过度拟合。

使用简单的 XGBoost,但微调超参数。

首选是降低学习率,同时增加训练的迭代次数。

介绍了减少过拟合的其他最佳选择here

【讨论】:

在数据集中,我有四个类。我想尝试 OneVsRestClassifier,其中估计器是 XGBoost。为了避免过拟合,我需要传递拟合参数。 我有一个高度不平衡的数据集。我需要使用sample_weight,您知道在使用OneVsRestClassifier 时如何传递此参数吗? 我再次强调,使用OneVsRestClassifier 不会提高模型性能。尝试更改基础 xgboost 本身的参数。 “迭代次数”是什么意思?这是XGBClassifier中的哪个参数? 它是train方法中的num_boost_round参数【参考方案2】:

Chandler Sekai 的回答很有帮助。但是需要更改一行:

weight_array = y * weight + 1 (否则你给负类 0 权重......)

【讨论】:

如果您添加了 Chandler Sekiai 的完整答案,这个答案可能会更好,因为对我来说,看到代码更改的一个更改令人困惑,但原始答案的内容却没有。 您已经发现其他人的答案有潜在的改进,甚至得到了该作者的验证(尽管暗示可能不是最佳的)。尽管您是新的贡献者,但您非常小心地不将他们的作品展示为您的作品。带着这种态度,你应该很快就会在别处声名鹊起。对这个答案投赞成票,因为它很有用。我希望以后在更新另一个答案时不要误解它。我写这篇文章是为了让其他用户了解这篇文章的历史。祝你好运。【参考方案3】:

我最近遇到了这个问题来动态传递 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://***.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 这样的整个类。

并且此方法将整个班级的权重(每个标签内)同等对待。

【讨论】:

这是对已发布问题的回答。@Basil 您能否检查一下 hogius 在其他答案中的贡献是否有效?我引用它,以防它被删除: weight_array = y * weight + 1 (否则你给负类 0 权重......)引用结束 @Yunnosch 嘿,我已经检查了 hogius 的答案。他是对的,需要更改 weight_array 的方程,以便我们在这里进行一些平滑处理。 y*weight + 1 可以是选项之一。我会更新我的答案。 @hogius 谢谢你的回答。我会相应地更新答案,但是 y*weight + 1 可能不是最准确的值。请稍后查看我的更新。

以上是关于使用 OneVsRestClassifier 时如何传递 XGBoost 拟合参数?的主要内容,如果未能解决你的问题,请参考以下文章

如何用 OneVsRestClassifier 做 GridSearchCV?

如何使用带有 SVC 估计器的 OneVsRestClassifier 的 GridSearchCV?

使用 OneVsRestClassifier 时全为零

OneVsRestClassifier(svm.SVC()).predict() 给出连续值

Scikit-learn 多输出分类器使用:GridSearchCV、Pipeline、OneVsRestClassifier、SGDClassifier

GridSearch 用于 OneVsRestClassifier 中的估计器