为什么 xgb.cv 表现良好但 xgb.train 没有

机器算法验证 r 正则化 过拟合
2022-03-21 09:40:57

我正在尝试使用 R 中的 xgboost 来控制过拟合,eta但是当我将 xgb.cv 读数的过拟合与 xgb.train 读数进行比较时,我不知道为什么xgb.cv似乎并没有过拟合xgb.train我怎样才能得到 in 的同样好的向下mlogloss进展xgb.train在运行模型之前,我已经平衡了我的课程。

[1] "###########          i is 1 and j 1            ##################"
[1] "Creating cv..."
# this part is good -------------------
[0] train-mlogloss:1.609325+0.000006    test-mlogloss:1.609315+0.000009
[100]   train-mlogloss:1.601508+0.001238    test-mlogloss:1.602480+0.001071
[200]   train-mlogloss:1.594359+0.002151    test-mlogloss:1.596278+0.001812
[300]   train-mlogloss:1.587120+0.002100    test-mlogloss:1.589944+0.001546
[400]   train-mlogloss:1.580558+0.001839    test-mlogloss:1.584062+0.001251
[1] "Took 160 seconds to cv train with 500 rounds..."

[1] "Creating model..."
# this part is bad -------------------
[0] train-mlogloss:1.609341 test-mlogloss:1.609383
[100]   train-mlogloss:1.602439 test-mlogloss:1.609435
[200]   train-mlogloss:1.594991 test-mlogloss:1.609580
[300]   train-mlogloss:1.587814 test-mlogloss:1.609732

我的代码cvtrain我的参数是:

param = list("objective" = "multi:softprob"
             , "eval_metric" = "mlogloss"
             , 'num_class' = 5
             , 'eta' = 0.001)

bst.cv = xgb.cv(param = param
                , data = ce.dmatrix
                , nrounds  = nrounds
                , nfold = 4
                , stratified = T
                , print.every.n = 100
                , watchlist = watchlist
                , early.stop.round = 10
)
bst = xgb.train(param = param
                , data = ce.dmatrix
                , nrounds  = nrounds
                , print.every.n = 100
                , watchlist = watchlist
                # , early.stop.round = 10
)
3个回答

我只是在同样的问题上失去了几天。 TL;DR:您确定您的关注列表与您的 ce.dmatrix 具有相同的列数和顺序吗?

xgb.cv的当前实现中,任何传入的监视列表参数都将被忽略。xgb.cv 最终调用xgb.cv.mknfold,它强制为每个折叠设置监视列表,如下所示:

for (k in 1:nfold) {
    dtest <- slice(dall, folds[[k]])
    didx <- c()
    for (i in 1:nfold) {
      if (i != k) {
        didx <- append(didx, folds[[i]])
      }
    }
    dtrain <- slice(dall, didx)
    bst <- xgb.Booster(param, list(dtrain, dtest))
    watchlist <- list(train=dtrain, test=dtest)
    ret[[k]] <- list(dtrain=dtrain, booster=bst, watchlist=watchlist, index=folds[[k]])
  }

这是有道理的,因为正如其他人所说,将监视列表传递给 xgb.cv 并没有多大意义。因此,您的 cv 输出中显示的“测试”与您的 xgb.train 输出中显示的“测试”不同的数据集

xgb.train 调用 xgb.iter.eval 以评估样本内和观察列表数据的测试统计信息。xgb.iter.eval 的实际计算如下:

 msg <- paste("[", iter, "]", sep="")
      for (j in 1:length(watchlist)) {
        w <- watchlist[j]
        if (length(names(w)) == 0) {
          stop("xgb.eval: name tag must be presented for every elements in watchlist")
        }
        preds <- predict(booster, w[[1]])
        ret <- feval(preds, w[[1]])
        msg <- paste(msg, "\t", names(w), "-", ret$metric, ":", ret$value, sep="")
      }

所以它使用 booster 句柄调用 predict() 。由于这是从调用 xgb.train 返回的同一增强器句柄类,这相当于您使用完成的模型调用 predict()。

在 Booster 的 C++ 实现的内部的某个地方,predict() 似乎没有验证您传入的数据的列名是否与构建模型的数据的列名匹配。它甚至不检查是否有正确的列数。通过检查以下调用的输出,您可以自己轻松地看到这一点:

head(predict(bst, newdata=ce.dmatrix))
#predict using only the first 10 columns, missing values default to 0
head(predict(bst, newdata=ce.dmatrix[,1:10]))
#predict using the wrong columns, because we ignore column names
head(predict(bst, newdata=ce.dmatrix[,sample(ncol(ce.dmatrix))]))

因此,如果您的监视列表“测试”集定义不正确,您将看到您所看到的那种奇怪的行为。您可以通过执行以下操作来检查它们是否相同:

colnames(ce.dmatrix)[!(colnames(ce.dmatrix) %in% colnames(watchlist[[1]]))]
colnames(watchlist[[1]])[!(colnames(watchlist[[1]]) %in% colnames(ce.dmatrix))]

在我的例子中,我分别清理了我的测试和训练数据,因为一些因子水平出现在训练中但没有出现在测试中,我的测试数据在不正确的地方有错误的列数/列数。

希望有帮助。

文档对我来说有点模糊,但交叉验证的重点是选择最佳超参数以避免过度拟合。所以 xgb.cv 在测试之前使用交叉验证来调整参数,从而避免过度拟合。

为什么在 CV 方法中需要一个监视列表?相应的简历折叠是关注列表!我不知道 R 命令,但在 Python 中,verbose_eval=True返回您正在寻找的正确输出。我的猜测是,由于 CV 仅用于超参数调整并且本身不返回模型,因此参数监视列表的使用会以某种方式干扰正确触发 early.stop.round。

PS:你的 eta 参数很低。我从未使用过低于 0.01 的 eta 值...