深度学习模型常常被认为是不可解释的。但是人们正在探索不同的技术来解释这些模型内发生了什么。对于图像,由卷积神经网络学习的特征是可解释的。我们将探索两种流行的技术来理解卷积神经网络。
可视化中间层的输出
可视化中间层的输出将有助于我们理解输入图像如何在不同层之间进行转换。通常,每层的输出称为激活(activation)。为了可视化,我们需要提取中间层的输出,可以用几种不同的方式完成提取。PyTorch提供了一个名为register_forward_hook的方法,它允许传入一个可以提取特定层输出的函数。
默认情况下,为了以最佳方式使用内存,PyTorch 模型仅存储最后一层的输出。因此,在检查中间层的激活之前,需要了解如何从模型中提取输出。我们先看看下面用于提取的代码,然后再进行详细介绍:
vgg = models.vggl6(pretrained=True).cuda()
class LayerActivations():
features=None
def init (self,model,layer num):
self.hook = model[layer num].register forward hook(self.hook fn)
def hook fn(self,module,input,output):
self.features =output.cpu()
def remove(self):
self.hook.remove()
conv_out = LayerActivations(vgg.features,0)
o = vgg(Variable(img.cuda()))
conv_out.remove()
act = conv_out.features
首先创建一个预先训练的VGG模型,并从中提取特定层的输出。LayerActivations类指示PyTorch将一层的输出保存到features变量。让我们来看看LayerActivations类中的每个函数。
_init_函数取得模型以及用于将输出提取成参数的层的编号。我们在层上调用register_forward _hook方法并传入函数。当PyTorch 进行前向传播时——也就是说,当图像通过层传输时——调用传给register_forward_hook方法的函数。此方法返回一个句柄,该句柄可用于注销传递给register _forward_hook方法的函数。
register_forward_hook方法将3个值传入我们传给它的函数。参数module允许访问层本身。第二个参数是 input,它指的是流经层的数据。第三个参数是output,它允许访问层转换后的输入或激活。将输出存储到LayerActivations类中的features 变量。
第三个函数取得_init_函数的钩子并注销该函数。现在可以传入正在寻找的激活(activation)的模型和层的编号。让我们看看为图5.22创建的不同层的激活。
可视化第一个卷积层创建的激活和使用的代码:
fig=plt.figure(figsize=(20,50))
fig.subplots_adjust(left=0,right=l,bottom=0,top=0.8,hspace=0,wspace=0.2)
for i in range(30):
ax = fig.add_subplot(12,5,i+l,xticks=[],yticks=[])
ax.imshow(act[0][i])
可视化第五个卷积层创建的一些激活,如图5.23所示。
来看最后一个 CNN 层,如图5.24所示。
从不同的层生成的激活来看,可以看出前面的层检测线条和边缘,最后的层倾向于学习更高层次的特征,而解释性较差。在对权重可视化之前,让我们看看在ReLU层之后特征平面或激活如何自我表示。所以,让我们可视化第二层的输出。
如果快速查看图5.24第二行中的第5个图像,它看起来像是滤波器正在检测图像中的眼睛。当模型不能执行时,这些可视化技巧可以帮助我们理解模型可能无法正常工作的原因。
CNN层的可视化权重
获取特定层的模型权重非常简单。可以通过state_dict函数访问所有模型权重。state_dict函数返回一个字典,其中键是层,值是权重。以下代码演示了如何为特定层拉取(pull)权重并将其可视化:
vgg.state_dict().keys()
cnn_weights = vgg.state_dict()['features.0.weight'].cpu()
上述代码提供了如图 5.25 所示的输出。
每个框表示大小为3x3的滤波器的权重。每个滤波器都经过训练以识别图像中的某些模式。