作者:数据取经团-数据栗子
前言
喂,这位同学,这个感觉上应该是A,你的模型怎么预测的是B?
做模型的小伙伴,平时肯定少不了这样的追问;
为了模型准确率只能放弃可解释性,线性模型固然好解释,但是大家都知道现实中线性的问题还是很少,大部分都是很复杂的非线性问题,面对这样的问题,该怎么办???
下面我就带大家一块看看在R中是怎么操作的?
针对诸如:随机森林、XGB等树模型都有了相关的Explainer工具包;
randomForestExplainer:https://github.com/MI2DataLab/randomForestExplainer
xgboostExplainer:https://github.com/AppliedDataSciencePartners/xgboostExplainer
感兴趣的小伙伴可以点进去仔细瞅瞅。
当然,也有通用性比较强的解释工具,也就是今天的主角:Lime(局部可理解的与模型无关的解释),在由Marco Tulio Ribeiro,Sameer Singh和Carlos Guestrin合作发表于 ACM 知识发现和数据挖掘年会(KDD2016)上的论文《"Why Should I Trust You?": Explaining the Predictions of Any Classifier》中,我们对信任和解释的问题进行了精细地探索。我们提出了局部可理解的与模型无关的解释技术(Local Interpretable Model-Agnostic Explanations: LIME),一种用于解释任何机器学习分类器的预测的技术,并在多种与信任相关的任务中评估了它的可用性。
论文地址:https://arxiv.org/abs/1602.04938
一些中文解读:https://www.oreilly.com.cn/ideas/?p=563;http://geek.csdn.net/news/detail/66259
当然最近R也有了相应的包,我们今天就以情感分析为例来试试这个包。
环境
R语言及版本信息
最终效果
备注:绿色标注的词对于预测结果的特征权重为正值,说明对预测结果起到正向作用;红色标注的词对于预测结果的特征权重为负值,说明对预测结果起到负向作用。
代码实现
以爬取京东某款红酒的评价数据为例,我们借助lime来解读到底为什么xgboost将“京东的物流是没得说了,很快。这次买酒水类,包装很仔细,没有出现意外。酒到手了,绝对是正品。感觉很不错哟!”这句话预测为正面的:
library(lime)
library(stringi)
library(text2vec)
library(data.table)
library(magrittr)
library(purrr)
library(xgboost)
library(jiebaR)
上面引用的包里面,比较核心的有lime、text2vec、xgboost、jiebaR,对文本分析的核心就是讲文本向量化,而text2vec包绝B是R语言玩转NLP的支柱,其具有高性能、简洁、灵活、快速等特点,详细介绍见:https://cndocr.github.io/text2vec-doc-cn/index.html
如果后面有必要可以单独对这个包进行下详细介绍(包括:tf-idf、N-grams、GloVe 词向量、LSA/LDA主题模型、基于多种距离的文档相似性等);
读入数据:
#读入爬取的评价数据
good_sentences <- fread("C:/Users/topnet/Desktop/好评.csv",stringsAsFactors = F,encoding = "UTF-8")
bad_sentences <- fread("C:/Users/topnet/Desktop/差评.csv",stringsAsFactors = F,encoding = "UTF-8")
#将正面评论的label设为1,负面的设为0
good_sentences[, label := 1]
bad_sentences[, label := 0]
#合并
all_sentences <- rbind(good_sentences,bad_sentences)
使用text2vec处理中文和英文的区别就在于分词,英文可以直接通过空格切开,中文需要借助中文分词工具先切开,本文使用的是jieba的分词包
分词:
#使用全切模式full,定义一个切词引擎
cutter <- worker(type = "full",bylines = T,stop_word = "d:/stop_words.utf8")
#对评论comment进行分词
article_words <- sapply(all_sentences$comment, function(x) cutter <= x)
构建DTM矩阵:
#定义预处理函数以及 tokenization 函数
prep_fun = tolower
tok_fun = word_tokenizer
it_token = itoken(article_words,preprocessor = prep_fun,tokenizer = tok_fun, ids = rownames(all_sentences), progressbar = T)
vocab = create_vocabulary(it_token)
bigram_vectorizer = vocab_vectorizer(vocab)
dtm_token = create_dtm(it_token, bigram_vectorizer)
构建XGBOOST二分类模型:
#对数据进行转换,整理成xgb的数据格式
dtrain <- dtm_token %>%
xgb.DMatrix(label = all_sentences$label)
#设置参数
param <- list(max_depth = 10, eta = 0.1, objective = "binary:logistic", eval_metric = "error", nthread = 2)
#训练模型
bst <- xgb.train(param, dtrain, nrounds = 500)
到此,我们已经建立了一个简单的情感分类模型,下面借助lime工具对新的数据进行预测解释。
首先需要建立一些function,可以将输入的文本直接处理,输出DTM矩阵:
#首先是分词的函数
get.jieba <- function(data) sapply(data, function(x) cutter <= x)
#其次是token的函数
get.iterator <- function(data) itoken(get.jieba(data), preprocessor = prep_fun, tokenizer = tok_fun, progressbar = T)
#再次是得到DTM矩阵的函数
get.matrix <- function(data) {
i <- get.iterator(data)
create_dtm(i, bigram_vectorizer)
}
#最后根据DTM得到xgb的数据格式
get.features.matrix <- . %>%
get.matrix() %>%
xgb.DMatrix()
好啦,万事俱备,只等检验了。
#要预测和解释的文本
test_sentences <- "卧槽,这酒也太好喝了,简直没的说,绝对的物有所值,下来再买"
#Run lime() on training set
explanation <- lime(unlist(article_words), bst, get.features.matrix, keep_word_position = F)
#Run explain() on explainer
results <- explain(test_sentences,explanation,n_labels = 1,n_features = 10)
plot_features(results)
结果是这样:
不好,翻船了!什么,怎么预测成负面的了(明明是正面的啊)。。。。
仔细看下,发现“没”这个单词居然对结果起到了决定性的作用。别急,下面让我们回去瞅瞅原始的文本数据:
我们发现:包含“没”的这么多label为0的样本,而且居然把“没有”这个词分开了(可能是jieba分词选择full模式导致的),通过反向分析为我们优化模型指明了方向:优化分词规则,优化训练集样本。
看到了没有,看出什么没有,同学们,这样还可以用来修正你的模型!!!