神经网络可视化和可解释性(Visualization and Explanation of Neural Network )
相对于传统的ML模型,Deep NN由于其自身所特有的多层非线性的结构而导致难以对其工作原理进行透彻的理解。比如,我们很难理解网络将一个输入x判断为某一类别c时,其输入向量x中的每一个特征分别对这个结果贡献了多大,找出哪些输入特征起到了关键作用,这对判断网络是否正常工作是很重要的。尤其在NLP领域,由于现在大多数模型的输入都是稠密的Embedding向量,而每一个维度上的值并没有一个显式的含义,这就使得我们很难像图像领域那样,直接可视化CNN中的filters,分析网络所学习到的特征。
1.现有的方法
现有的网络解释思路大致可以分为两大类:模型解释(Interpreting Models)和决策解释(Explaining Decision)
1.1 Interpreting Models
这类方法的主要解释手段是:
a. find prototypical example of a category
找到一个类别的典型样本,比如1-10的手写字体图片分类,对于类别1, 我们的目的就是找到最能代表数字1的样本,即找到使分类函数f(x)输出值最大的x。
b. find pattern maximizing activity of a neuron
找到能够最大化激活神经元的pattern,以上边的例子来说,即找到能够最大化激活网络的pixel空间组合方式(比如在手写数字识别任务中,CNN模型的高层filter里边,通常每个pattern对应于一个模糊的数字,如:“1”)
这一类的方法倾向于得到一个整体上的(ensemble)的结论,即找出某个数字(比如“1”)最常见的图片模式应该是怎样
1.2 Explaining decisions
a.“why” does the model arrive at this particular prediction
试图解释为什么模型能够做出这样的预测,比如,为啥将一张图片分类为数字“1”而不是其它
b.verify that model behaves as expected
验证模型是否按期望运行,以上边的例子来说,当模型将一个输入x预测为“1”时,起主要作用的特征(pixels)应该是组成数字“1”的附近的pixels
在这一思路下,大致有两类方法:Sensitivity Analysis(SA)和Decomposition。
2. SA & Decomposition
为了说明方便,我们先定义几个符号:
神经网络分类函数: f ( x ) f(x) f(x)
输入向量: x x x
对于最终生成的heatmapping R ( x ) = { R p ( x ) } \boldsymbol{R(x)}=\{R_p(x)\} R(x)={
Rp(x)},
接下来,再定义如下性质:
- 1. Conservative, 即 ∀ x : f ( x ) = ∑ p R p ( x ) \forall x: f(x)=\sum_p R_p(x) ∀x:f(x)=∑pRp(x)
这里粗体的 R ( x ) \boldsymbol{R(x)} R(x)表示整个heatmapping的元素集合(可以看做一个向量, 其对应的输入 x \boldsymbol{x} x也是一个向量), R p ( x ) R_p(x) Rp(x)表示heatmapping上的第 p p p个元素, 也称为Relevance, 对应输入向量中的一个分量 x p x_p xp. Conservative 确保在向后传播时, 每层所有神经元对应的Relevance之和能够始终等于最终分类的预测值. 最终 R ( x ) \boldsymbol{R(x)} R(x)将以图片的形式展示出来,这就是所谓的Visualization了。
- 2. Positive, 即 ∀ x , p : R p ( x ) ≥ 0 \forall x, p: R_p(x)≥0 ∀x,p:Rp(x)≥0
这个性质确保heatmapping中的元素不存在相互矛盾的对象, 即某些元素是与最终结果正相关, 某些元素是负相关.这样做的好处是可以简化最终的relevance分布.
最后,同时具备1和2的, 我们就称之为"一致的"( c o n s i s t e n t consistent consistent), 需要注意的是, c o n s i s t e n t consistent consistent对于heatmapping虽然不是一个硬性的要求, 但满足一致性条件的heatmapping将具备一系列优良的性质,因此我们的方法要尽可能的符合 c o n s i s t e n t consistent consistent约束。
2.1 SA
SA方法,主要原理是通过输入x对输出y的影响程度来对x的每一个特征(分量)进行打分, 通过对打分进行可视化,最终将形成一张heatmapping.而打分的方法, 使用非常符合直觉的梯度, 即:
R p ( x ) = ( ∂ f / ∂ x p ) 2 R_p(x)= (\partial f / \partial x_p )^2 Rp(x)=(∂f/∂xp)2
∴ R ( x ) = ∥ ∇ ​ x f ( x ) ∥ 2 ∴\boldsymbol{R(x)}=\|\nabla_{\!\boldsymbol{x}} f (\boldsymbol{x})\|^2 ∴R(x)=∥∇xf(x)∥2
有时,为了区分出positive和negative的影响,可以去掉平方运算:
R p ( x ) = ( ∂ f / ∂ x p ) R_p(x)= (\partial f / \partial x_p ) Rp(x)=(∂f/∂xp)
SA方法的优点是实现容易, 很符合直观感受,但缺点也很明显:输出的heatmap只是反映了增加(或减小)对应输入位置的值对预测结果的影响,并未直接反映出该点输入值本身对结果的影响。e.g. 当前位置梯度 R p > 0 R_p>0 Rp>0,而对应的输入 x p < 0 x_p<0 xp<0
2.2 Decomposition
2.2.1 LRP Framework
从字面上看,Decomposition就是分解,这种方法的本质也是将最终输出的Relevance进行分解再向上传递。该怎么分解呢?我们先看下图的DNN网络结构:
在上图所示的网络中,最终输出出值为 f ( x ) f(x) f(x), 显然,对于输出层而言,其Relevance就为: R f = f ( x ) R_f=f(x) Rf=f(x)
那么第 l l l层的第 i i i个节点的Relevance R i ( l ) R_i^{(l)} Ri(l)应该为什么呢?首先根据性质2, 每层所有节点的Relevance之和应该相等:
∑ d R d ( 1 ) = ∑ k R k ( 2 ) = . . . = ∑ i R i ( l ) = ∑ j R j ( l + 1 ) = . . . = R f \sum_d R_d^{(1)} = \sum_k R_k^{(2)} =...=\sum_i R_i^{(l)}=\sum_j R_j^{(l+1)}=...=R_f d∑Rd(1)=k∑Rk(2)=...=i∑Ri(l)=j∑Rj(l+1)=...=Rf
我们可以把Relevance看做一种沿着网络连线流动的信息,其流动方向为输出节点到输入节点,其值总和为 R f R_f Rf. 那么我们可以参考反向传播的思想,将网络结构倒过来(上图右), 沿着节点间的子路径将Relevance逐层分解, 比如从层 6 → 5 6 \rightarrow 5 6→5 , 这里我们始终假设 i i i代表低层神经元的序号, j j j代表高层神经元的序号:
R i ← j ( 5 , 6 ) = f a c t o r i j ( 5 , 6 ) ⋅ R j ( 6 ) R^{(5, 6)}_{i \leftarrow j} = factor_{ij}^{(5,6)} \cdot R^{(6)}_j Ri←j(5,6)=factorij(5,6)⋅Rj(6)
其中 f a c t o r i j factor_{ij} factorij表示分配因子,是一个介于[0,1]之间的数, 并满足:
∑ i f a c t o r i j ( 5 , 6 ) = 1 \sum_i factor_{ij}^{(5,6)} = 1 i∑factorij(5,6)=1
对于任意一个上层神经元, 其输入 z j ( l ) = W j ( l ) a ( l − 1 ) z^{(l)}_j = W^{(l)}_j a^{(l-1)} zj(l)=Wj(l)a(l−1), 其中 a ( l − 1 ) a^{(l-1)} a(l−1)为低层神经元的激活输出向量. 上层单元j的最终输出为 σ ( z j ( l ) ) \sigma(z^{(l)}_j) σ(zj(l)). 由于神经元的激活函数通常为单调递增函数,所以, 输入值越大,那么对应的激活值也就越大。因此 z j ( l ) z^{(l)}_j zj(l)的每个分量 z i j ( l ) z^{(l)}_{ij} zij(l)可以看做是每个下层单元 i i i与上层单元 j j j之间的Relevance分配因子, 由于还需要满足归一化的约束, 我们可以对其除以一个归一化参数 z j ( l ) z^{(l)}_j zj(l):
f a c t o r i j = z i j ( l ) z j ( l ) = w i j ( l ) a i ( l − 1 ) ∑ i w i j ( l ) a i ( l − 1 ) factor_{ij} = \frac {z^{(l)}_{ij}} {z^{(l)}_j} = \frac {w^{(l)}_{ij} a_i^{(l-1)}} {\sum_i w^{(l)}_{ij} a_i^{(l-1)}} factorij=zj(l)zij(l)=∑iwij(l)ai(l−1)w<