`object` 和 `newdata` 中存储的特征名称不同!使用 LIME 包解释 R 中的 xgboost 模型时

Posted

技术标签:

【中文标题】`object` 和 `newdata` 中存储的特征名称不同!使用 LIME 包解释 R 中的 xgboost 模型时【英文标题】:Feature names stored in `object` and `newdata` are different! when using LIME package to explain xgboost model in R 【发布时间】:2019-08-15 07:23:28 【问题描述】:

我正在尝试使用 LIME 来解释我使用 XGboost 训练的二元分类模型。从 LIME 调用 explain() 函数时遇到错误,这意味着我的模型(或解释器)中的列不匹配,并且我试图解释预测的新数据。

这个vignette for LIME 确实演示了一个带有 xgboost 的版本,但是它是一个文本问题,与我的表格数据有点不同。这个question 似乎遇到了同样的错误,但对于文档术语矩阵也是如此,这似乎掩盖了我的案例的解决方案。我用mtcars 做了一个最小的例子,它产生的错误与我在自己的更大数据集中得到的错误完全相同。

library(pacman)
p_load(tidyverse)
p_load(xgboost)
p_load(Matrix)
p_load(lime)

### Prepare data with partition
df <- mtcars %>% rownames_to_column()
length <- df %>% nrow()
df_train <- df %>% select(-rowname) %>% head((length-10))
df_test <- df %>% select(-rowname) %>% tail(10)

### Transform data into matrix objects for XGboost
train <- list(sparse.model.matrix(~., data = df_train %>% select(-vs)), (df_train$vs %>% as.factor()))
names(train) <- c("data", "label")
test <- list(sparse.model.matrix(~., data = df_test %>% select(-vs)), (df_test$vs %>% as.factor()))
names(test) <- c("data", "label")
dtrain <- xgb.DMatrix(data = train$data, label=train$label)
dtest <- xgb.DMatrix(data = test$data, label=test$label)


### Train model
watchlist <- list(train=dtrain, test=dtest)
mod_xgb_tree <- xgb.train(data = dtrain,  booster = "gbtree", eta = .1, nrounds = 15, watchlist = watchlist)

### Check prediction works
output <- predict(mod_xgb_tree, test$data) %>% tibble()

### attempt lime explanation
explainer <- df_train %>% select(-vs) %>% lime(model = mod_xgb_tree)  ### works, no error or warning
explanation <- df_test %>% select(-vs) %>% explain(explainer, n_features = 4) ### error, Features stored names in `object` and `newdata` are different!

names_test <- test$data@Dimnames[[2]]  ### 10 names
names_mod <- mod_xgb_tree$feature_names ### 11 names
names_explainer <- explainer$feature_type %>% enframe() %>% pull(name) ### 11 names


### see whether pre-processing helps
my_preprocess <- function(df)
  data <- df %>% select(-vs)
  label <- df$vs

  test <<- list(sparse.model.matrix( ~ ., data = data), label)
  names(test) <<- c("data", "label")

  dtest <- xgb.DMatrix(data = test$data, label=test$label)
  dtest


explanation <- df_test %>% explain(explainer, preprocess = my_preprocess(), n_features = 4) ### Error in feature_distribution[[i]] : subscript out of bounds

### check that the preprocessing is working ok
dtest_check <- df_test %>% my_preprocess()
output_check <- predict(mod_xgb_tree, dtest_check)

我假设因为 explainer 只有原始预测列的名称,其中转换状态的测试数据也有一个 (Intercept) 列,这是导致问题的原因。我只是还没有想出防止这种情况发生的巧妙方法。任何帮助将非常感激。我认为必须有一个巧妙的解决方案。

【问题讨论】:

【参考方案1】:

如果您查看此页面 (https://rdrr.io/cran/xgboost/src/R/xgb.Booster.R),您会看到一些 R 用户可能会收到以下错误消息:“存储在 objectnewdata 中的功能名称不同!”。

这是此页面中与错误消息相关的代码:

predict.xgb.Booster <- function(object, newdata, missing = NA, outputmargin = FALSE, ntreelimit = NULL,predleaf = FALSE, predcontrib = FALSE, approxcontrib = FALSE, predinteraction = FALSE,reshape = FALSE, ...)

object <- xgb.Booster.complete(object, saveraw = FALSE)
      if (!inherits(newdata, "xgb.DMatrix"))
        newdata <- xgb.DMatrix(newdata, missing = missing)
      if (!is.null(object[["feature_names"]]) &&
          !is.null(colnames(newdata)) &&
          !identical(object[["feature_names"]], colnames(newdata)))
        stop("Feature names stored in `object` and `newdata` are different!")

identical(object[["feature_names"]], colnames(newdata)) => 如果object 的列名(即基于您的训练集的模型)与newdata 的列名(即您的测试集)不同,您将收到错误消息.

更多详情:

train_matrix <- xgb.DMatrix(as.matrix(training %>% select(-target)), label = training$target, missing = NaN)
object <- xgb.train(data=train_matrix, params=..., nthread=2, nrounds=..., prediction = T)
newdata <- xgb.DMatrix(as.matrix(test %>% select(-target)), missing = NaN)

由于上面的代码,您可以自行设置objectnewdata 的数据,但您可以通过查看object[["feature_names"]]colnames(newdata) 之间的差异来解决此问题。可能是某些列的出现顺序不同之类的。

【讨论】:

【参考方案2】:

在你的新数据集中试试这个,

   colnames(test)<- make.names(colnames(test))

   newdataset<- test %>% mutate_all(as.numeric)

   newdataset<- as.matrix(newdataset)

   nwtest<-xgb.DMatrix(newdataset)

【讨论】:

第一行有问题。我应该在哪个对象上调用colnames()&lt;-?如果我在数据框df_test 上这样做,我会得到一堆用于 colnames 的 NA,并且无法继续。【参考方案3】:

我遇到了同样的问题,但列不是按字母顺序排列的。为了解决这个问题,我将 df_test 中列名的顺序与 df_train 匹配,以便列名的顺序相同。

按照与 df_train 相同的顺序创建 df_test 列号列表:

    idx<- match(colnames(df_train), colnames(df_test))

使用此列顺序创建新的 df_test 文件:

    df_test_match <- df_test[,idx]

【讨论】:

【参考方案4】:

为防止 (Intercept) 列出现,您需要在为测试数据创建稀疏矩阵时稍微更改代码。 换行:

test <- list(sparse.model.matrix( ~ ., data = data), label)

到:

test <- list(sparse.model.matrix( ~ .-1, data = data), label)

希望对你有帮助

【讨论】:

谢谢,我试了一下,但似乎没有用。

以上是关于`object` 和 `newdata` 中存储的特征名称不同!使用 LIME 包解释 R 中的 xgboost 模型时的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android Jetpack Workmanager 中输入和输出 AN Object 数据?

收到警告:“'newdata' 有 1 行,但找到的变量有 32 行”在 predict.lm

javascript的倒计时功能中newData().getTime()在iOS下会报错问题解决

不允许删除 Firebase 存储?

oracle 11g 从+DATA迁移到+newdata ASM 磁盘组迁移

R循环和data.frame