当我使用Caret包中的’train’函数创建一个带有权重的梯度提升模型时,在使用’varImp’函数时会出现错误,提示未检测到树模型。但当我移除权重后,’varImp’函数就能正常工作。
以下代码会产生错误:
set.seed(123)model_weights <- ifelse(modelo_df_sseg$FATALIDADES == 1, yes = (1/table(modelo_df_sseg$FATALIDADES)[2]) * 0.5, no = (1/table(modelo_df_sseg$FATALIDADES)[1]) * 0.5 )model <- train( as.factor(FATALIDADES) ~., data = modelo_df_sseg, method = "xgbTree", trControl = trainControl("cv", number = 10), weights = model_weights )varImp(model)
但如果我不应用权重,它就能正常工作。
为什么varImp函数不识别我的树模型呢?
编辑 2020年9月4日
在评论区,有人建议使用wts而不是weights。现在我得到了下面的错误:
Error in nominalTrainWorkflow(x = x, y = y, wts = weights, info = trainInfo, : formal argument 'wts' matched by multiple actual arguments
我用R内置数据集创建了一个小代码,以便您可以自己测试:
set.seed(123)basex <- Arrestsmodel_weights <- ifelse(basex$released == 2, yes = (1/table(basex$released)[2]) * 0.5, no = (1/table(basex$released)[1]) * 0.5 )y = basex$releasedx = basextc = trainControl("cv", number = 10)mtd = "xgbTree"model <- train( x, y, method = mtd, trControl = tc, wts = model_weights, verbose = TRUE )
也许我创建权重向量的方式不对。但我找不到关于’wts’参数的任何文档。
回答:
示例代码存在多个问题。
在caret中应用权重的正确方法是使用train
函数的weights
参数。
我在评论中推荐使用wts
参数是错误的。我的错误源于xgbTree源码,特别是以下这行代码:
if (!is.null(wts)) xgboost::setinfo(x, 'weight', wts)
这表明wts
可能是正确的答案。
让我们逐步审查示例并修复所有问题
library(caret)library(car) #用于数据集library(tidyverse) #因为我喜欢使用它data(Arrests)basex <- Arreststable(basex$released) #released是结果类别 No Yes 892 4334
在这里我们看到”Yes”结果的频率远高于”No”结果。这将导致预测概率偏向于倾向于预测”Yes”的模型。一种解决方法是给”No”观测值更高的权重。给”No”观测值一个有意义的权重应该是”Yes”类别的比例,而给”Yes”观测值一个有意义的权重应该是”No”类别的比例:
model_weights <- ifelse(basex$released == "Yes", table(basex$released)[1]/nrow(basex), table(basex$released)[2]/nrow(basex))
权重的总和为1
head(data.frame(basex, weights = model_weights)) released colour year age sex employed citizen checks weights1 Yes White 2002 21 Male Yes Yes 3 0.1706852 No Black 1999 17 Male Yes Yes 3 0.8293153 Yes White 2000 24 Male Yes Yes 3 0.1706854 No Black 2000 46 Male Yes Yes 1 0.8293155 Yes Black 1999 27 Female Yes Yes 1 0.1706856 Yes Black 1998 16 Female Yes Yes 0 0.170685
“Yes”更频繁,所以我们给它一个较低的权重。
从上面的数据框中可以看出,有几个分类预测变量(如颜色、性别等)。xgbTree
无法处理它们,所以在建模之前需要将它们转换为数值。将分类预测变量转换为数值的一种方法是虚拟编码。还有其他方法,但这不在本回答的范围内。
要使用虚拟编码:
dummies <- dummyVars(released ~ ., data = basex)x <- predict(dummies, newdata = basex)head(x)colour.Black colour.White year age sex.Female sex.Male employed.No employed.Yes citizen.No citizen.Yes checks1 0 1 2002 21 0 1 0 1 0 1 32 1 0 1999 17 0 1 0 1 0 1 33 0 1 2000 24 0 1 0 1 0 1 34 1 0 2000 46 0 1 0 1 0 1 15 1 0 1999 27 1 0 0 1 0 1 16 1 0 1998 16 1 0 0 1 0 1 0y <- basex$released
现在我们有了权重、x和y
由于我将要拟合多个模型,我将首先创建重采样折叠,并在每个train调用中使用它们,这样它们就不会不同。
folds <- createFolds(basex$released, 10)
由于类别频率不平衡,我将使用twoClassSummary
,这样我们可以看到训练模型的敏感性和特异性
tc <- trainControl(method = "cv", number = 10, summaryFunction = twoClassSummary, index = folds, #预定义折叠 classProbs = TRUE) #需要twoClassSummarymtd <- "xgbTree"model <- train(x = x, y = y, method = mtd, trControl = tc, weights = model_weights, verbose = TRUE, metric = "ROC")
#无错误
model$results %>% filter(ROC == max(ROC)) eta max_depth gamma colsample_bytree min_child_weight subsample nrounds ROC Sens Spec ROCSD SensSD SpecSD1 0.3 1 0 0.8 1 1 50 0.7031076 0.6185944 0.693945 0.009074758 0.03516597 0.01536701
在这里我们看到,如果我们使用模型权重,最高AUC的模型具有0.6185944的敏感性和0.693945的特异性。
不使用权重
model2 <- train(x = x, y = y, method = mtd, trControl = tc, verbose = TRUE, metric = "ROC")
#无错误
model2$results %>% filter(ROC == max(ROC)) eta max_depth gamma colsample_bytree min_child_weight subsample nrounds ROC Sens Spec ROCSD SensSD SpecSD1 0.3 1 0 0.8 1 0.75 50 0.701109 0.1000325 0.9713885 0.0101395 0.03343579 0.01236701
不使用权重的模型具有0.1000325的敏感性和0.9713885的特异性。
所以有意义的权重参数修正了模型总是预测”Yes”的倾向。