LIME算法:模型的可解释性(代码实现)

在这篇笔记中分享前段时间我对于LIME算法的调研。

一、算法简介

LIME算法是Marco Tulio Ribeiro2016年发表的论文《“Why Should I Trust You?” Explaining the Predictions of Any Classifier》中介绍的局部可解释性模型算法。该算法主要是用在文本类与图像类的模型中。
LIME算法:模型的可解释性(代码实现)

1.算法主要用途

在算法建模过程中,我们一般会用测试集的准确率与召回率衡量一个模型的好坏。但在和客户的实际沟通时,单单抛出一个数字就想要客户信任我们,那肯定是不够的,这就要求我们摆出规则,解释模型。但不是所有的模型都是规则模型,一些黑盒模型(比如神经网络)有着更高的准确率,但是无法给出具体的规则,无法让普通人理解和信任模型的预测结果,这种时候,就可以利用本文要介绍的LIME算法,全称Local Interpretable Model-agnostic Explanations,可以理解为模型的解释器。
LIME算法:模型的可解释性(代码实现)
可以从两个方面说明我们为什么需要模型解释器,一方面是用户需要信任预测结果,另一方面是建模者需要信任模型。

2、用户需要信任预测结果

在很多行业,比如医生、法律行业,都需要就模型的预测结果给出充分的理由,否则就无法取得用户的信任。举一个论文中提到的例子,一个医学模型判断病人患病,通过LIME算法同时给出病人的病史症状包括喷嚏、头痛等特征,医生利用自己的先验知识,就能更好地借助模型做出决定。
LIME算法:模型的可解释性(代码实现)
图1:模型会预测患者患有flu,LIME会突出显示导致该预测的患者病史中的症状。喷嚏和头痛被描述为flu预测的贡献者,而“无疲劳”则相反。有了这些,医生可以就是否信任模型的预测做出明智的决定。

3、建模者需要信任模型

每个算法都需要对模型整体信任度有一定的衡量,意思就是模型是否学到了我们所预期的东西。论文中举出一个文章分类的例子,判断该文章与“基督教“有关还是与”无神论教“有关,分类器本身达到了90%的准确率。但是利用LIME解释器,发现”无神论教“的重要特征,是”Posting“(邮件标头的一部分),这个词与无神论本身并没有太多的联系。这意味着尽管模型准确率很高,但所学习到的特征是错误的。

LIME算法:模型的可解释性(代码实现)
图2:试图确定该份文档是关于“基督教”还是“无神论”。 条形图表示给予最相关词的重要性,该词也在文本中突出显示。 颜色表示单词对哪个类别有所贡献(橘色表示“基督教”,蓝色表示“无神论”)。

通过以上两个方面的介绍,大致可以了解LIME算法的主要用途。

二、算法介绍

1.解释器的基本特征

我们需要建立一个解释器来解释黑盒模型,那么这个解释器必须满足以下特征:

-可解释性

要求解释器的模型与特征都必须是可解释的,像决策树、线性模型都是很适合拿来解释的模型;而可解释的模型必须搭配可解释的特征,才是真正的可解释性,让不了解机器学习的人也能通过解释器理解模型。

-局部保真度

既然我们已经使用了可解释的模型与特征,就不可能期望简单的可解释模型在效果上等同于复杂模型(比如原始CNN分类器)。所以解释器不需要在全局上达到复杂模型的效果,但至少在局部上效果要很接近,而此处的局部代表我们想观察的那个样本的周围。

-与模型无关

这里所指的是与复杂模型无关,换句话说无论多复杂的模型,像是SVM或神经网络,该解释器都可以工作。

2.算法原理

对于一个分类器(复杂模型),想用一个可解释的模型(简单模型如线性规划),搭配可解释的特征进行适配,并且这个可解释模型再局部的表现上很接近复杂模型的效果
LIME算法:模型的可解释性(代码实现)
如图所示,红色和蓝色区域表示一个复杂的分类模型(黑盒),图中加粗的红色十字表示需要解释的样本,显然,我们很难从全局用一个可解释的模型(例如线性模型)去逼近拟合它。但是,当我们把关注点从全局放到局部时,可以看到在某些局部是可以用线性模型去拟合的。具体来说,我们从加粗的红色十字样本周围采样,所谓采样就是对原始样本的特征做一些扰动,将采样出的样本用分类模型分类并得到结果(红十字和蓝色点),同时根据采样样本与加粗红十字的距离赋予权重(权重以标志的大小表示)。虚线表示通过这些采样样本学到的局部可解释模型,在这个例子中就是一个简单的线性分类器。在此基础上,我们就可以依据这个局部的可解释模型对这个分类结果进行解释了。

3、核心算法实现

(1)目标函数:

解释模型定义为模型g∈G,我们进一步使用πx(z)作为实例z与x之间的接近度,以定义x周围的局部性。定义一个目标函数ξ,这里的L函数作为一个度量,描述如何通过πx在局部定义中,不忠诚的g如何逼近f(复杂模型),在当Ω(g)(解释模型复杂度)足够低可以被人类理解时,我们最小化L函数得到目标函数的最优解。LIME产生的解释如下:

LIME算法:模型的可解释性(代码实现)

(2)引入相似度后的目标函数:

对这个样本进行可解释的扰动,论文中还对扰动前后的样本相似度的距离进行了定义,这取决于样本的类型(文本的话就是余弦相似性,图像的话就是L2范数距离)。则相似度计算公式如下:

LIME算法:模型的可解释性(代码实现)

(3)最终函数:

有了相似度的定义,便可以将原先的目标函数改写成如下的形式。其中f(z)就是扰动样本,在d维空间(原始特征)上的预测值,并把该预测值作为答案,g(z’)则是在d’维空间(可解释特征)上的预测值,然后以相似度作为权重,因此上述的目标函数便可以通过线性回归的方式进行优化。
LIME算法:模型的可解释性(代码实现)

(4)伪代码实现:

1-首先要有一个好的分类器(复杂模型)。
2-选定一个要解释的样本x,以及在可解释纬度上的x’。
LIME算法:模型的可解释性(代码实现)
3-定义一个相似度计算方式,以及要选取的K个特征来解释。
4-进行N次perturb扰动,z’,从x’扰动而来。
LIME算法:模型的可解释性(代码实现)
5-将z‘还原到d维度,并计算预测值f(z)以及相似度。
LIME算法:模型的可解释性(代码实现)
根据距离进行加权
LIME算法:模型的可解释性(代码实现)
6-收集到N次扰动的样本后,利用岭回归取得对这个样本有影响力的系数。
LIME算法:模型的可解释性(代码实现)

三、算法优缺点

(1)LIME算法有很强的通用性,效果好。

LIME除了能够对图像的分类结果进行解释外,还可以应用到自然语言处理的相关任务中,如主题分类、词性标注等。因为LIME本身的出发点就是模型无关的,具有广泛的适用性。

(2)LIME算法速度慢

LIME在采样完成后,每张采样出来的图片都要通过原模型预测一次结果,所以在速度上没有明显优势。

(3)LIME算法拓展方向

本文的作者在18年新提出了Anchors的方法,指的是复杂模型在局部所呈现出来的很强的规则性的规律,注意和LIME的区别,LIME是在局部建立一个可理解的线性可分模型,而Anchors的目的是建立一套更精细的规则系统。在和文本相关的任务上有不错的表现。有待我们继续研究。

四、实际实用案例

1.图像案例

以树蛙图像分类为例,将LIME的过程展示一遍。
下图为我们假设的CNN模型的预测结果,预测一张图片中可能有哪些物体。模型此时告诉我们有54%的概率这张图像是一只树蛙,但也有7%的概率是台球和5%的概率是热气球。现在我们想使用LIME来理解模型为何做出下图的判断。
LIME算法:模型的可解释性(代码实现)
先把原始图片转成可解释的特征表示,通过可解释的特征表示对样本进行扰动,得到N个扰动后的样本。然后再将这N个样本还原到原始特征空间,并把预测值作为真实值,用可解释的特征数据表示建立简单的数据表示,观察哪些超像素的系数较大。
LIME算法:模型的可解释性(代码实现)
将这些较大的系数进行可视化可以得到下图的样子,从而理解模型为什么会做出这种判断。青蛙的眼睛和台球很相似,特别是在绿色的背景下。同理红色的心脏也与热气球类似。
LIME算法:模型的可解释性(代码实现)

2、文本案例

现在有一批名单,特征包括年龄、职业、教育、工龄、收入等字段,来判断目标是否会产生违约。在利用随机森林建模得出判断结果后,可以利用LIME算法来查看目标个体在模型中被分类的理由。

import lime 
import sklearn
import numpy as np
import sklearn.ensemble
import sklearn.metrics
from sklearn.datasets import fetch_20newsgroups
#读取数据
categories = ['alt.atheism', 'soc.religion.christian']
newsgroups_train = fetch_20newsgroups(subset='train', categories=categories)
newsgroups_test = fetch_20newsgroups(subset='test', categories=categories)
class_names = ['atheism', 'christian']

LIME算法:模型的可解释性(代码实现)

#利用GBDT分类模型区分是否违约
from sklearn.ensemble import GradientBoostingClassifier

x =data.iloc[:,:8].as_matrix()
y = data.iloc[:,8].as_matrix()

gbdt = GradientBoostingClassifier()
gbdt = gbdt.fit(x,y)
#直接将训练数据作为预测数据
pred = gbdt.score(x,y)

预测准确率达到92%。

import matplotlib.pyplot as plt  
#中文字体显示  
plt.rc('font', family='SimHei', size=13)
from lime.lime_tabular import LimeTabularExplainer
#建立解释器
explainer = LimeTabularExplainer(x, feature_names=feature_names, class_names=class_names)
#解释第81个样本的规则
exp = explainer.explain_instance(x[81], gbdt.predict_proba)
#画图
fig = exp.as_pyplot_figure()

选择第81个样本利用LIME解释器解释规则,发现特征负债率与信用卡负债是支持模型判断该样本为违约的主要理由:
LIME算法:模型的可解释性(代码实现)

#画分析图
exp.show_in_notebook(show_table=True, show_all=False)

LIME算法:模型的可解释性(代码实现)

五、参考资料

1、https://github.com/marcotcr/lime
2、https://blog.csdn.net/John159151/article/details/94363740
3、https://blog.csdn.net/evilhunter222/article/details/80043251

上一篇:LIME算法原理


下一篇:LIME算法:图像分类解释器(代码实现)