xgboost 文档有错吗? (早期停止轮次以及最佳和最后一次迭代)

Posted

技术标签:

【中文标题】xgboost 文档有错吗? (早期停止轮次以及最佳和最后一次迭代)【英文标题】:Is the xgboost documentation wrong ? (early stopping rounds and best and last iteration) 【发布时间】:2019-04-28 05:56:53 【问题描述】:

下面是一个关于 xgboost 提前停止轮次参数的问题,以及当它是拟合结束的原因时,它如何提供或不提供最佳迭代。

在 xgboost 文档中,可以在 scikit learn api 部分 (link) 中看到,当由于提前停止轮数参数而导致拟合停止时:

激活提前停止。验证错误至少需要每“early_stopping_rounds”轮减少一次才能继续训练。在 evals 中至少需要一项。如果有多个,将使用最后一个。返回上一次迭代的模型(不是最好的)。

reeding 时,返回的模型似乎不是最好的,而是最后一个。它说,要在预测时访问最好的,可以使用 ntree_limit 参数调用预测,并在拟合结束时给出 bst.best_ntree_limit。

从这个意义上说,它应该与 xgboost 的 train 以相同的方式工作,因为 scikitlearn api 的拟合似乎只是 train 和其他的嵌入。

在这里stack overflow discussion 或这里another discussion 进行了热烈的讨论

但是当我试图解决这个问题并检查它如何处理我的数据时,我没有找到我认为应该有的行为。事实上,我遇到的行为根本不是那些讨论和文档中描述的行为。

我这样称呼合适:

reg = xgb.XGBRegressor(n_jobs=6, n_estimators = 100, max_depth= 5)

reg.fit(
   X_train, 
   y_train, 
   eval_metric='rmse',    
   eval_set=[(X_train, y_train), (X_valid, y_valid)],
   verbose=True,
   early_stopping_rounds = 6)

这是我最后得到的:

[71]    validation_0-rmse:1.70071   validation_1-rmse:1.9382
[72]    validation_0-rmse:1.69806   validation_1-rmse:1.93825
[73]    validation_0-rmse:1.69732   validation_1-rmse:1.93803
Stopping. Best iteration:
[67]    validation_0-rmse:1.70768   validation_1-rmse:1.93734

当我检查我使用的验证值时:

y_pred_valid = reg.predict(X_valid)
y_pred_valid_df = pd.DataFrame(y_pred_valid)
sqrt(mse(y_valid, y_pred_valid_df[0]))

我明白了

1.9373418403889535

如果拟合返回最后一次迭代而不是最好的迭代,它应该给出一个 1.93803 左右的 rmse,但它给出了一个 1.93734 的 rmse,这正是最好的分数。

我通过两种方式再次检查: [编辑] 我根据@Eran Moshe 的回答编辑了下面的代码

y_pred_valid = reg.predict(X_valid, ntree_limit=reg.best_ntree_limit)
y_pred_valid_df = pd.DataFrame(y_pred_valid)
sqrt(mse(y_valid, y_pred_valid_df[0]))

1.9373418403889535

即使我只用 68 个估算器调用合适的(知道最好的迭代器是第 67 个),所以我确信最后一个是最好的:

reg = xgb.XGBRegressor(n_jobs=6, n_estimators = 68, max_depth= 5)

reg.fit(
   X_train, 
   y_train, 
   eval_metric='rmse',    
   eval_set=[(X_train, y_train), (X_valid, y_valid)],
   verbose=True,
   early_stopping_rounds = 6)

结果是一样的:

1.9373418403889535

因此,这似乎导致了这样一种想法,即,与文档以及关于它的众多讨论不同,xgboost 的适合性在被早期停止轮参数停止时,确实给出了最好的迭代器,而不是最后一个.

我错了吗,如果是,你在哪里以及如何解释我遇到的行为?

感谢关注

【问题讨论】:

【参考方案1】:

那里有代码错误。

注意方法

reg.predict(X_valid, ntree_limit=reg.best_ntree_limit)

应该是

y_pred_valid = reg.predict(X_valid, ntree_limit=reg.best_ntree_limit)

所以实际上你在计算时进行了同样的比较

sqrt(mse(y_valid, y_pred_valid_df[0]))

Xgboost 的工作方式与您所读到的一样。 early_stopping_round = x 将一直训练直到在 x 连续轮次中没有改善。

当使用 ntree_limit=y 进行预测时,它将仅使用第一个 y 助推器。

【讨论】:

您提到的确实存在代码错误,我已更正,但它并没有改变我提出的问题。当我使用 ntree_limit 参数进行预测时,它仍然给出了最佳答案。为什么? 不,不是。我自己测试过很多次,你完全错了。但我使用的是 xgboost.train 而不是 XGBRegressor 包装器。也许测试直接使用 xgboost 没有环绕 是的!我特别询问了环绕,文档确认它应该给最后一个不是最好的迭代,即使有环绕。因此问题。 我认为他们不会为环绕方式使用不同的功能。它可能继承了原生 xgboost 包所做的相同计算。也许你应该去看看代码..【参考方案2】:

我认为,没有错,只是不一致

predict 方法的文档是正确的(例如,参见here)。要 100% 确定最好查看代码:xgb github,因此 predict 的行为与其文档中所述相同,但 fit 文档已过时。请将其作为问题发布到 XGB github 上,他们将修复文档,或者您将成为 XGB 贡献者:)

【讨论】:

事实上,我确实在XGB github上发布了一个关于这个问题的问题,但直到现在还没有任何答案。拭目以待 仅供参考,我提出了更正文档的请求并被接受。请参阅拉取请求 #3967:github.com/dmlc/xgboost/pull/3967【参考方案3】:

更准确地说,据@Mykhailo Lisovyi 所说,scikit-learn api 部分中的文档非常不一致,因为 fit 段落告诉当早期停止轮发生时,最后一次迭代不是最好的,而是predict 段落告诉当在没有指定 ntree_limit 的情况下调用 predict 时,ntree_limit 等于 best_ntree_limit。

因此,在读取fit部分时,可能会认为调用predict时需要指定最佳iter,但读取predict部分时,默认给出最佳iter,它是最后一个如果需要,您必须指定它。

我在 xgboost 的 github 上发布了一个问题....等等看

[更新]:拉取请求已被接受:link

【讨论】:

以上是关于xgboost 文档有错吗? (早期停止轮次以及最佳和最后一次迭代)的主要内容,如果未能解决你的问题,请参考以下文章

Java面试题-1

Java面试170题

场景应用:short s1 = 1; s1 = s1 + 1;有错吗? short s1 = 1; s1 += 1;有错吗?

场景应用:short s1 = 1; s1 = s1 + 1;有错吗? short s1 = 1; s1 += 1;有错吗?

6short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?-Java面试题答案

在我的 ViewModel 中使用 Dispatcher 有错吗?