积分梯度:一种新颖的神经网络可视化方法
By 苏剑林 | 2020-06-28 | 102399位读者 |本文介绍一种神经网络的可视化方法:积分梯度(Integrated Gradients),它首先在论文《Gradients of Counterfactuals》中提出,后来《Axiomatic Attribution for Deep Networks》再次介绍了它,两篇论文作者都是一样的,内容也大体上相同,后一篇相对来说更易懂一些,如果要读原论文的话,建议大家优先读后一篇。当然,它已经是2016~2017年间的工作了,“新颖”说的是它思路上的创新有趣,而不是指最近发表。
所谓可视化,简单来说就是对于给定的输入x以及模型F(x),我们想办法指出x的哪些分量对模型的决策有重要影响,或者说对x各个分量的重要性做个排序,用专业的话术来说那就是“归因”。一个朴素的思路是直接使用梯度∇xF(x)来作为x各个分量的重要性指标,而积分梯度是对它的改进。然而,笔者认为,很多介绍积分梯度方法的文章(包括原论文),都过于“生硬”(形式化),没有很好地突出积分梯度能比朴素梯度更有效的本质原因。本文试图用自己的思路介绍一下积分梯度方法。
朴素梯度 #
首先,我们来学习一下基于梯度的方法,其实它就是基于泰勒展开:
F(x+Δx)−F(x)≈⟨∇xF(x),Δx⟩=∑i[∇xF(x)]iΔxi
我们知道∇xF(x)是大小跟x一样的向量,这里[∇xF(x)]i为它的第i个分量,那么对于同样大小的Δxi,[∇xF(x)]i的绝对值越大,那么F(x+Δx)相对于F(x)的变化就越大,也就是说:
[∇xF(x)]i衡量了模型对输入的第i个分量的敏感程度,所以我们用|[∇xF(x)]i|作为第i个分量的重要性指标。
这种思路比较简单直接,在论文《How to Explain Individual Classification Decisions》和《Deep Inside Convolutional Networks: Visualising Image Classification Models and Saliency Maps》都有描述,在很多时候它确实也可以成功解释一些预测结果,但它也有明显的缺点。很多文章提到了饱和区的情况,也就是一旦进入到了饱和区(典型的就是relu的负半轴),梯度就为0了,那就揭示不出什么有效信息了。
从实践角度看,这种理解是合理的,但是笔者认为还不够深刻。从之前的文章《对抗训练浅谈:意义、方法和思考(附Keras实现)》可以看出,对抗训练的目标可以理解为就是在推动着‖∇xF(x)‖2→0,这也就可以理解为,梯度是可以被“操控”的,哪怕不影响模型的预测准确率的情况下,我们都可以让梯度尽可能接近于0。所以,回到本文的主题,那就是:[∇xF(x)]i确实衡量了模型对输入的第i个分量的敏感程度,但敏感程度不足以作为重要性的良好度量。
积分梯度 #
鉴于直接使用梯度的上述缺点,一些新的改进相继被提出来,如LRP、DeepLift等,不过相对而言,笔者还是觉得积分梯度的改进更为简洁漂亮。
参照背景 #
首先,我们需要换个角度来理解原始问题:我们的目的是找出比较重要的分量,但是这个重要性不应该是绝对的,而应该是相对的。比如,我们要找出近来比较热门的流行词,我们就不能单根据词频来找,不然找出来肯定是“的”、“了”之类的停用词,我们应当准备一个平衡语料统计出来的“参照”词频表,然后对比词频差异而不是绝对值。这就告诉我们,为了衡量x各个分量的重要性,我们也需要有一个“参照背景”ˉx。
当然,很多场景下我们可以简单地让ˉx=0,但这未必是最优的,比如我们还可以选择ˉx为所有训练样本的均值。我们期望F(ˉx)应当给一个比较平凡的预测结果,比如分类模型的话,ˉx的预测结果应该是每个类的概率都很均衡。于是我们去考虑F(ˉx)−F(x),我们可以想象为这是从x移动到ˉx的成本。
如果还是用近似展开(1),那么我们将得到
F(ˉx)−F(x)≈∑i[∇xF(x)]i[ˉx−x]i
对于上式,我们就可以有一种新的理解:
从x移动到ˉx的总成本为F(ˉx)−F(x),它是每个分量的成本之和,而每个分量的成本近似为[∇xF(x)]i[ˉx−x]i,所以我们可以用|[∇xF(x)]i[ˉx−x]i|作为第i个分量的重要性指标。
当然,不管是[∇xF(x)]i还是|[∇xF(x)]i[ˉx−x]i|,它们的缺陷在数学上都是一样的(梯度消失),但是对应的解释却并不一样。前面说了,[∇xF(x)]i的缺陷源于“敏感程度不足以作为重要性的良好度量”,而纵观这一小节的推理过程,|[∇xF(x)]i[ˉx−x]i|的缺陷则只是因为“等式(2)仅仅是近似成立的”,但整个逻辑推理是没毛病的。
积分恒等 #
很多时候一种新的解释能带给我们新的视角,继而启发我们做出新的改进。比如前面对缺陷的分析,说白了就是说“|[∇xF(x)]i[ˉx−x]i|不够好是因为式(2)不够精确”,那如果我们直接能找到一个精确相等的类似表达式,那么就可以解决这个问题了。积分梯度正是找到了这样的一个表达式:设γ(α),α∈[0,1]代表连接x和ˉx的一条参数曲线,其中γ(0)=x,γ(1)=ˉx,那么我们有
F(ˉx)−F(x)=F(γ(1))−F(γ(0))=∫10dF(γ(α))dαdα=∫10⟨∇γF(γ(α)),γ′(α)⟩dα=∑i∫10[∇γF(γ(α))]i[γ′(α)]idα
可以看到,式(3)具有跟(2)一样的形式,只不过将[∇xF(x)]i[ˉx−x]i换成了∫10[∇γF(γ(α))]i[γ′(α)]idα。但式(3)是精确的积分恒等式,所以积分梯度就提出使用
|∫10[∇γF(γ(α))]i[γ′(α)]idα|
作为第i个分量的重要性度量。作为最简单的方案,自然就是将γ(α)取为两点间的直线,即
γ(α)=(1−α)x+αˉx
这时候积分梯度具体化为
|[∫10∇γF(γ(α))|γ(α)=(1−α)x+αˉxdα]i[ˉx−x]i|
所以相比|[∇xF(x)]i[ˉx−x]i|的话,就是用梯度的积分∫10∇γF(γ(α))|γ(α)=(1−α)x+αˉxdα替换∇xF(x),也就是从x到ˉx的直线上每一点的梯度的平均结果。直观来看,由于考虑了整条路径上的所有点的梯度,因此就不再受某一点梯度为0的限制了。
如果读者看了积分梯度的两篇原始论文,就会发现原论文的介绍是反过来的:先莫名其妙地给出式(6),然后再证明它满足两点莫名其妙的性质(敏感性和不变性),接着证明它满足式(3)。总之就是带着读者做了一大圈,就是没说清楚它是一个更好的重要性度量的本质原因——大家都是基于对F(ˉx)−F(x)的分解,而式(3)比式(2)更为精确。
离散近似 #
最后就是这个积分形式的量怎么算呢?深度学习框架没有算积分的功能呀。其实也简单,根据积分的“近似-取极限”定义,我们直接用离散近似就好,以式(6)为例,它近似于:
|[1nn∑k=1(∇γF(γ(α))|γ(α)=(1−α)x+αˉx,α=k/n)]i[ˉx−x]i|
所以还是那句话,本质上就是“从x到ˉx的直线上每一点的梯度的平均”,比单点处的梯度效果更好。
实验效果 #
看完了理论,我们再来看看实验效果。
原始效果 #
原始论文实现:https://github.com/ankurtaly/Integrated-Gradients
下面是原论文的一些效果图:
个人实现 #
虽然Keras官网已经给出了参考实现了(请看这里),但代码实在是太长,看着太累,笔者根据自己的理解也用Keras实现了一个,并应用到了NLP中,具体代码见“task_sentiment_integrated_gradients.py”。目前的代码仅仅是简单的demo,欢迎读者在此基础上派生出更强大的代码。
上图中笔者给出了几个样本的效果(模型对上述样本的情感标签预测都是正确的),由此我们可以推测原模型进行情感分类的原理。从上图我们可以看到,对于负样本,积分梯度可以比较合理地定位到句子中的负面词语,而对于正样本,哪怕它的语法格式跟负样本一样,却无法定位到句子中的正面词语。这个现象表明,原模型做情感分类的思路可能是“负面检测”,也就是说主要做负面情绪检测,而检测不到负面情绪则视为正样本,这大概是因为没有“中性”样本训练所带来的结果。
又到文末 #
本文介绍了一种称为“积分梯度”的神经网络可视化方法,利用它可以一定程度上更好描述输入的各个分量的重要程度。积分梯度通过沿着路径对梯度进行积分来构建了精确的等式,弥补了泰勒展开的不足,从而达到了比直接使用梯度更好的可视化效果。
转载到请包括本文地址:https://spaces.ac.cn/archives/7533
更详细的转载事宜请参考:《科学空间FAQ》
如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。
如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!
如果您需要引用本文,请参考:
苏剑林. (Jun. 28, 2020). 《积分梯度:一种新颖的神经网络可视化方法 》[Blog post]. Retrieved from https://spaces.ac.cn/archives/7533
@online{kexuefm-7533,
title={积分梯度:一种新颖的神经网络可视化方法},
author={苏剑林},
year={2020},
month={Jun},
url={\url{https://spaces.ac.cn/archives/7533}},
}
July 12th, 2020
朴素梯度法由于泰勒展开到一阶导数,而‖∇xF(x)‖2→0,所以才有问题。但一阶导数为0,二阶导数是大于0的,为什么不考虑展开到二阶导数或更fa'o呢?从计算量考虑,实现代码算了20次梯度,而展开到二阶导数只要算两次梯度。
有两个问题:
1、一旦进入了饱和区,其实几乎所有阶导数都为0,另一方面一阶导数也未必为0,我那里举的例子只是说明可以改变一阶导数而不改变最后的性能,所以一阶导数不能作为指标。归根结底,本质的原因还是泰勒展开不够精确。
2、二阶导数并不是求两次导数那么简单,假如x有n个分量,那么∇xF(x)也有n个分量,而二阶导数是将∇xF(x)的每个分量都求一次梯度,得到n2个数。如果结合Δx的内积来考虑,那么可能得到一些简化,但依然不少,而且避免不了问题1。
October 26th, 2020
苏老师,您好,请问如何将句子对相似的任务改为孪生的albert网络?
不知道。先学好keras?
想在您的二分类任务中修改孪生网络,将输入修改为两个句子,这样的话得修改构造模型的部分,您有好点的建议吗?
如果你用的是keras,那我的建议是先好好学一两个月keras;如果是其他框架,那我也不会~
December 20th, 2020
确实,原文我看了半天,真的费解!楼主的解释很漂亮了,谢谢分享!
January 25th, 2021
感谢楼主,写得非常清楚。我看有的paper里设ˉx=0,γ(α)取直线,感觉实质上是退化成了(2)式的情况...
这样设置积分梯度也不能退化为(2)式啊。
请问一下,最终不是化成了这样的形式吗?
(xi−x′i)×m∑k=1∂F(x′+km×(x−x′))∂xi×1m=xi×m∑k=1∂F(km×x)∂xi×1m=m∑k=1km2×∂F(x)∂xi×xi
最后一个等号不成立。
March 29th, 2021
苏神,请教一下 task_sentiment_integrated_gradients.py 中这段代码 ,
# nlp任务中参照背景通常直接选零向量,所以这里
# 让embedding层从零渐变到原始值,以实现路径变换。
alpha = 1.0 * i / (n - 1)
K.set_value(embeddings, alpha * values)
实现路径变换不知道具体什么含义,能否多解释一下?
本文一整篇文章都在解释,还不够多?
不好意思,看代码还是没有看明白,还是想请教一下,这个对应文中公式几?或者哪几个公式与K.set_value(embeddings, alpha * values) 想对照。
看代码怎么会看得懂呢?正确的学习顺序:
1、阅读文章;
2、理解文章;
3、尝试自己写代码;
4、对比参考代码。
顺序是1、2、3、4,不是4、3、2、1。
很明显,本文的风格是先理论后实验,在“离散近似”一节我们就提到“最后就是这个积分形式的量怎么算呢?”,所以很明显公式(7)就是实验用的公式,所以很显然就是对应公式(7),也没有别的选择了,怎么会找不到呢?
不明白的是, K.set_value(embeddings, alpha * values),设置的value值是从0,到embeddings的值,而
γ(α)=(1−α)x+αˉx的值是从x到ˉx,不知道两者是怎么对照转换的?
ˉx取0不就行了?
March 31st, 2021
多谢,我以为假定 ˉx是与x比较接近的一个值
September 13th, 2021
蘇老師您好,想請問一下(1)式的中間的角括號代表什麼意思,還有怎麼來的?
向量内积
November 10th, 2021
你好苏神, 我最近用梯度积分方法可视化ViT模型, 出来的效果完全不对, 是Attention的计算梯度传递有什么特别的地方吗? 那么为什么NLP的Transformer就没有这样的问题呢?
这个我不清楚,不大了解。但按道理梯度积分是通用的,或者哪怕不积分,直接求一下梯度,在很多场景下都work~
January 9th, 2022
好奇,对于预测错的负样本,积分梯度给出的重要性还是可解释的吗?是模型没有capture到负向词,还是由于样本存在一些负向词、但本身情感确实正向的?不知道老师有看过嘛~
积分梯度或者其他类似的方法,主要是找出那些特征对输出的影响较大,不关心最终答案的对错。如果预测错了,积分梯度或者能帮助我们猜测预测错的原因。
February 12th, 2022
恩恩,我就是这个意思,老师文中给的例子都是帮助理解[为何能预测正确的例子],想知道有没有case可以看:对于预测错误的例子,积分梯度能给出什么样的解释、以及对模型改进能有什么启发?
这模型我现在也没保存了,如果着急可以先自己跑跑看哈。