【DS】使用LIME探索模型
笔者邀请您,先思考:
1 如何做模型解释?

编者按:模型的解释性对于模型的应用和可信,有着重要意义。同时,也可以让模型更透明,更公平,也更好地服务大众,以降低模型所带来的风险。如何对黑盒模型的解释性做探索和研究,本文的LIME方法在一定程度上可以发挥作用和达成目的。
最近在工作中,我被要求帮助一些临床医生理解为什么我的风险模型将特定的病人分类为高风险。就在这项工作之前,我在华盛顿大学偶然发现了一些数据科学家的工作,他们叫lime。LIME代表模型无关的局部性可解释算法。我的想法是,通过在数据点附近的参数空间局部拟合一个线性(又名“可解释”)模型,我可以回答临床医生对特定患者提出的那些问题。我决定将lime作为一种解决方案,过去几个月我一直专注于为我的风险模型实现这个解释器。幸运的是,我还发现了一个R包,它实现了这个源自python的解决方案。
样本数据
因此本文的第一步是找到一些公共数据来说明。我记得James, Witten, Hastie和Tibshirani在《统计学学习导论》中举过一个例子。
我要用Heart.csv数据,可以通过以下链接下载:
1library(readr)
2library(ranger)
3library(tidyverse)
4library(lime)
5
6dat <- read_csv("http://www-bcf.usc.edu/~gareth/ISL/Heart.csv")
7dat$X1 <- NULL
现在让我们快速看一下数据:
1Hmisc::describe(dat)
这个数据中的目标变量是AHD。这个标志标识病人是否患有冠状动脉疾病。如果我们能准确预测,临床医生可能会更好地治疗这些病人,并希望帮助他们避免类似胸痛或更严重的心脏病发作等AHD症状。
数据处理
对于预测模型,我选择使用ranger执行的随机森林模型,它并行化r中的随机森林算法。但是首先,一些数据清理是必要的。在替换缺失的值之后,我将把数据拆分为测试和训练数据框。
1# Replace missing values
2dat$Ca[is.na(dat$Ca)] <- -1
3dat$Thal[is.na(dat$Thal)] <- "missing"
4
5## 75% of the sample size
6smp_size <- floor(0.75 * nrow(dat))
7
8## set the seed to make your partition reproducible
9set.seed(123)
10train_ind <- sample(seq_len(nrow(dat)), size = smp_size)
11
12train <- dat[train_ind, ]
13test <- dat[-train_ind, ]
14
15mod <- ranger(AHD~., data=train, probability = TRUE, importance = "permutation")
16
17mod$prediction.error
18## [1] 0.1326235
我们对OOB预测错误的快速粗略检查告诉我们,我们的模型在预测AHR方面表现良好。现在的问题是向我们的医生和护士描述为什么我们相信某人是AHR的高危人群。在学习lime之前,我可能会做一些类似于下面代码的事情,首先查看树中哪些变量最重要。
1plot_importance <- function(mod){
2 tmp <- mod$variable.importance
3 dat <- data.frame(variable=names(tmp),importance=tmp)
4 ggplot(dat, aes(x=reorder(variable,importance), y=importance))+
5 geom_bar(stat="identity", position="dodge")+ coord_flip()+
6 ylab("Variable Importance")+
7 xlab("")
8}
9
10# Plot the variable importance
11plot_importance(mod)

之后,我可能会看一些部分依赖图来了解这些重要的变量在这个变量范围内是如何变化的。然而,这种方法的缺点通常是我需要保持所有其他变量不变。如果我真的相信我的变量之间存在相互作用,那么当其他变量发生变化时,部分依赖图就会发生巨大的变化。
使用LIME解释模型
进入LIME。如上所述,LIME的整个目的是提供一个本地可解释的模型,以帮助我们理解,如果我们在许多排列中稍微调整其他变量,我们的预测将如何变化。在这种特殊情况下使用lime的第一步是添加一些函数,以便lime包知道如何处理ranger包的输出。一旦我有了这些,我就可以使用lime()和explain()函数的组合来得到我需要的东西。在所有多元线性模型中,我们仍然有一个问题…相关的解释变量。根据原始模型中变量的数量,我们可能需要将模型进行配对,只查看最“有影响力”或“重要”的变量。在使用岭回归或L2惩罚校正多共线性后,lime默认使用正向选择或选择系数较大的变量。如下所示,您还可以使用Lasso(即L1惩罚)选择解释的变量,或者使用“树”方法使用xgboost最重要的变量。
1# Train LIME Explainer
2expln <- lime(train, model = mod)
3
4
5preds <- predict(mod,train,type = "response")
6# Add ranger to LIME
7predict_model.ranger <- function(x, newdata, type, ...) {
8 res <- predict(x, data = newdata, ...)
9 switch(
10 type,
11 raw = data.frame(Response = ifelse(res$predictions[,"Yes"] >= 0.5,"Yes","No"), stringsAsFactors = FALSE),
12 prob = as.data.frame(res$predictions[,"Yes"], check.names = FALSE)
13 )
14}
15
16model_type.ranger <- function(x, ...) 'classification'
17
18
19reasons.forward <- explain(x=test[,names(test)!="AHD"], explainer=expln, n_labels = 1, n_features = 4)
20reasons.ridge <- explain(x=test[,names(test)!="AHD"], explainer=expln, n_labels = 1, n_features = 4, feature_select = "highest_weights")
21reasons.lasso <- explain(x=test[,names(test)!="AHD"], explainer=expln, n_labels = 1, n_features = 4, feature_select = "lasso_path")
22reasons.tree <- explain(x=test[,names(test)!="AHD"], explainer=expln, n_labels = 1, n_features = 4, feature_select = "tree")
23
注意:使用当前版本的lime时,您可能会遇到feature_select = “lasso_path”选项的问题。要让上面的代码运行在上面,您可以在这里安装我的改进版lime。
绘制解释图
现在我们已经有了所有的解释,我最喜欢的lime包功能之一是plot_explain()函数。你可以很容易地为我们上面的每个选择方法显示最重要的变量,我们可以看到,在预测AHD的最具影响力的4个变量的选择中,它们都是非常一致的。
1plot_explanations(reasons.forward)

1plot_explanations(reasons.ridge)

1plot_explanations(reasons.lasso)

1plot_explanations(reasons.tree)

感谢您阅读关于lime的快速教程。我还想探讨这个包的更多内容。特别是它在图像和文本分类中的应用。
作者:Mark Nielsen
原文链接:
https://www.nielsenmark.us/2018/11/09/exploring-models-with-lime/
内容推荐
公众号推荐:
数据思践,数据的思考与践行。
请关注“恒诺新知”微信公众号,感谢“R语言“,”数据那些事儿“,”老俊俊的生信笔记“,”冷🈚️思“,“珞珈R”,“生信星球”的支持!