谈谈机器学习模型的可解释性

深度学习一直被认为是一个黑盒子,但是试图对模型的理解仍然是非常必要的。先从一个例子来说明解释神经网络的重要性:古代一个小镇上的一匹马能够做数学题,比如给它一个题目 2+3 ,它踏马蹄 5 下后就会停下,这匹马被当地称作神马汉斯。后来人们发现,汉斯其实并不会做数学题,它通过观察主人的反应来判断什么时候停下。因此当一个训练好的网络模型根据输入来输出他的答案时,有时候虽然也能得到正确的结果,但是我们更多时候还需要知道网络输出这个结果的原因,这样能够帮助我们来判断模型得到的这个结果是否可靠。进一步地,我们可以凭借解释的结果来进一步优化我们的模型,而不仅仅是从结果层面来进行约束

在之前的一篇研究中,网络能够准确地将一幅大熊猫地图片识别为大熊猫,但是当这张图片混有一定的噪声时,网络识别的结果是长臂猿。由此可见,网络有时候只能根据数据集和损失函数来优化它的拟合过程,但是网络具体做出决定依靠的是图像的哪些特征则很难进行判断。就像考试的目的是为了让学生更好地掌握书本知识,然后取得高分,往往我们会通过题海战术来更方便快捷地总结出题规律,能达到更好的效果,这显然与素质教育的本质背道而驰。

笔者曾在2021年CVPR上发表过一篇关于图像去噪的文章,里面提到了“提高神经网络的可解释性”,最近在面试中经常被问到相关的问题,但是我回答的总是不能令面试官满意,直到真正了解到针对神经网络可解释性的文章,我才明白其中的区别:

  1. 我们文章的可解释性是针对在网络设计时网络结构,模块等都是经验性地选取,因此我们根据传统的算法进行理论推导,然后使用神经网络的拟合能力对理论进行建模,完成整个网络的设计过程;
  2. 而本篇文章提到的可解释性是对网络输出结果的原因进行讨论,试图解释网络输出结果的原因,来帮助我们判断模型得到的结果是否可靠。

Local Explanation主要解释做出决定的原因,比如一张图片模型输出是猫,那么local explanation的目的就是解释为什么它觉得这张图片是猫?很容易想到关于Attention机制的一些研究,通过 Attention 机制能够给特征赋予不同的空间和通道权重,从而使网络更加重视重要信息。通常在解释Attention的效果时,会采用 Salience Map 来表示。如下图所示:

在 Salience Map 中,较亮的部分代表对网络影响最重要的区域。比如在分类任务中,判断是否是猴子网络主要感兴趣的区域确实是猴子,而不是其他的内容。因此,Salience Map 就相当于网络做出决定所给出的理由。那么 Salience Map 是怎么得出来的呢?

想象一下:如果我对图片的某个区域做干扰,那么如果干扰后影响了网络的判断结果,证明该干扰区域在网络做出判断时是重要的,反之则不重要。数学描述为:对于输入 \(X = [x_{1},x_{2},...x_{n}]\),其中 \(x_{i}\) 代表了每个输入,对于图片而言就是每个像素点。如果判断 \(x_{i}\) 对输出 \(y\) 的影响,那么可以给 \(x_{i}\) 增加一个扰动 \(\Delta x_{i}\),相应地输出结果的变化量为 \(\Delta y\),于是 \(x_{i}\) 的重要程度就可以描述为:

\[\dfrac{\Delta y}{\Delta x_{i}} = \dfrac{\partial y}{ \partial x_{i}} \]

计算 Salience Map 的PyTorch代码如下:

def compute_saliency_maps(X, y, model):
    """
    X表示图片, y表示分类结果, model表示使用的与训练好的分类模型
    输出为salience map: A Tensor of shape (N, H, W)
    """
    # 确保model是test模式
    model.eval()
    # 确保X是需要gradient
    X.requires_grad_()
    saliency = None
    logits = model.forward(X)
    logits.backward(torch.FloatTensor([1.]))  # 计算梯度
    saliency = abs(X.grad.data)  # 返回X的梯度绝对值大小
    saliency, _ = torch.max(saliency, dim=1)
    return saliency.squeeze()

输出结果如下:

但是 Salience Map 会存在一些问题,比如判断一个动物是否是大象,长鼻子是其中一个非常明显的特征,但是当鼻子长到一定程度时,再增加鼻子的长度丝毫不会改变网络的判断结果,此时鼻子的长度对网络判断的贡献就很小。

除了 Salience Map 外,类激活的热力图也可以显示原始图片的不同区域对某个CNN输出结果的 “贡献” 程度,如下图所示:

类激活的热力图在文献 "Grad-CAM: visual explanations from deep networks via gradient-based localization" 首次被提出,方法可以总结为:给定一张输入图像,对于一个卷积层的输出特征图,用类别相对于通道的梯度对这个特征图中的每个通道进行加权。直观上来看,理解这个技巧的一种方法是,你是用 “每个通道对类别的重要程度” 对 “输入图像对不同通道的激活强度” 的空间图进行加权,从而得到了 “输入图像对类别的激活强度” 的空间图。参考代码链接:https://github.com/jacobgil/pytorch-grad-cam

其实对于神经网络而言,它所看到的只是一堆数据而已,通过损失函数可以使模型将这一堆数据映射为我们想要的值,其实人是无法真正理解机器想的是什么的,这些只是想找一些合理的方法来将机器想的内容合理化为人们所期望的那个样子而已。所以对一些新入门的朋友而言,不必过分纠结于神经网络到底是怎么实现特定的功能的,就像一位大佬说过:The question of whether machines could think was “about as relevant as the question of whether Submarines Can Swim. 个人理解就是:思考和游泳都是人类具有的属性,目的能够帮助我们解决事情或者能够在水里生存,但是反过来不一定成立,解决事情或者在水里生存不一定非要是我们人类具有的属性,机器也能拥有这些,但是他们在以什么样的方式进行工作,我们不得而知,讨论这个问题也就没什么意义,我们只需要关心结果是否是我们所需要的就行了。

posted @ 2021-07-29 16:39  ZhiboZhao  阅读(504)  评论(0编辑  收藏  举报