本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.
持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情
0. 前言
在 使用预训练VGG16模型实现性别分类 中,我们构建了一个卷积神经网络 ( Convolutional Neural Network
, CNN
) 模型,该模型可以以95%的准确率对图像中人物的性别进行分类。但是,有关 CNN
模型学习到的内容,对于我们来说仍然是一个黑匣子。
在本节中,我们将学习如何提取模型中各种卷积核学习到的内容特征。此外,我们将对比开始几个卷积层中的卷积核学习到的内容与最后几个卷积层中的卷积核学习到的内容。
1. 可视化神经网络中间层输出
为了提取卷积核学习到的内容,我们采用以下策略:
选择要进行分析的图像
选择模型的第一个卷积层,以了解第一个卷积层中的各个卷积核学习到的内容
计算第一层中卷积的输出,要提取第一层的输出,需要使用函数式 API
:
API
在模型的最后一个卷积层上同样执行的步骤,以可视化最后一卷积层的输出
然后,我们将可视化所有通道上的卷积运算的输出
最后,我们还将可视化所有图像上给定通道的输出
在本节中,我们将使用 Keras
实现以上策略用于可视化第一层和最后一层的所有卷积核学习到的内容。
1.1 数据加载
我们重用在 使用预训练VGG16模型实现性别分类 中中使用的数据加载代码,并查看加载的图片:
x = [] y = [] for i in glob('man_woman/a_resized/*.jpg')[:800]: try: image = io.imread(i) x.append(image) y.append(0) except: continue  for i in glob('man_woman/b_resized/*.jpg')[:800]: try: image = io.imread(i) x.append(image) y.append(1) except: continue
查看要用于可视化的输入图像:
from matplotlib import pyplot as plt plt.imshow(x[-3]) plt.show()
1.2 可视化第一个卷积层的输出
定义函数式 API
,将以上图像作为输入,将第一个卷积层的输出作为输出:
from keras.models import Model vgg16_model = VGG16(include_top=False, weights='imagenet', input_shape=(256, 256, 3)) input_img = preprocess_input(x[-3].reshape(1, 256, 256, 3)) activation_model = Model(inputs=vgg16_model.input, outputs=vgg16_model.layers[1].output) activations = activation_model.predict(preprocess_input(input_img))
在以上代码中,我们定义了一个名为 activation_model
的模型用于获取模型第一个卷积层的输出,在该模型中,我们图像作为输入传递,并提取第一层的输出作为 activation_model
模型的输出。一旦定义了模型,我们可以向模型中传入输入图像来提取第一层的输出。需要注意的是,我们必须整形输入图像的形状,以便能够将其输入到模型中。 可视化得到的第一个卷积层中的前 49
个卷积核的输出,如下所示:
fig, axs = plt.subplots(7, 7, figsize=(10, 10)) for i in range(7): for j in range(7): try: axs[i,j].set_ylim((224, 0)) axs[i,j].contourf(first_layer_activation[0,:,:,((7*i)+j)],7,cmap='viridis') axs[i,j].set_title('filter: '+str((7*i)+j)) axs[i,j].axis('off') except: continue
在以上中,我们创建了一个 7 x 7
的画布,以便在其中绘制 49
张图像。此外,我们遍历 first_layer_activation
中的前49个通道,并绘制得到的输出,如下所示:
在这里,我们可以看到某些卷积核可以提取原始图像的轮廓(例如,卷积核 0
、 11
、 30
、 34
)。另外,某些卷积核学会了如何识别图像的部分特征,例如耳朵,眼睛,头发和鼻子(例如卷积核 12
、 27
)。
我们继续对此进行验证,使多张图像(这里使用 49
张)通过第一个卷积层后提取第 11
个卷积核的输出(即得到的特征图的第 11
个通道),来获取原始图像的轮廓,如下所示:
input_images = preprocess_input(np.array(x[:49]).reshape(49,256,256,3)) activations = activation_model.predict(input_images) fig, axs = plt.subplots(7, 7, figsize=(10, 10)) fig.subplots_adjust(hspace = .5, wspace=.5) first_layer_activation = activations for i in range(7): for j in range(7): try: axs[i,j].set_ylim((224, 0)) axs[i,j].contourf(first_layer_activation[((7*i)+j),:,:,11],7,cmap='viridis') axs[i,j].set_title('image: '+str((7*i)+j)) axs[i,j].axis('off') except: continue plt.show()
在前面的代码中,我们遍历前 49
张图像,并使用这些图像绘制第一个卷积层中第 11
个卷积核的输出:
从上图可以看出,在所有图像中,第 11
个卷积核均学习了图像中的轮廓。
1.2 可视化最后一个卷积层的输出
接下来,我们继续了解最后一个卷积层中的卷积核学习了什幺。为了了解最后一个卷积层在模型中的索引,我们提取模型中的各个层,并输出各层的名字:
for i, layer in enumerate(vgg16_model.layers): print(i, layer.name)
通过执行以上代码,将打印出图层名称,如下:
0 input_1 1 block1_conv1 2 block1_conv2 3 block1_pool 4 block2_conv1 5 block2_conv2 6 block2_pool 7 block3_conv1 8 block3_conv2 9 block3_conv3 10 block3_pool 11 block4_conv1 12 block4_conv2 13 block4_conv3 14 block4_pool 15 block5_conv1 16 block5_conv2 17 block5_conv3 18 block5_pool
可以看到,最后一个卷积层在模型中的索引为 17
,可以按以下方式提取:
activation_model = Model(inputs=vgg16_model.input,outputs=vgg16_model.layers[-2].output) input_img = preprocess_input(x[-3].reshape(1, 256, 256, 3)) last_layer_activation = activation_model.predict(input_img)
由于在网络中执行了多个池化操作,因此得到的图像尺寸已被多次缩小,缩小为 1 x 8 x 8 x 512
,最后一个卷积层中的各个卷积核的输出可视化如下:
count = 0 for i in range(7): for j in range(11): try: count+=1 axs[i,j].set_ylim((6, 0)) axs[i,j].contourf(last_layer_activation[0,:,:,((7*i)+j)],11,cmap='viridis') axs[i,j].set_title('filter: '+str(count)) axs[i,j].axis('off') except: continue plt.show()
如上所示,我们并不能直观的看出最后一个卷积层的卷积核学习到了什幺,因为很难将低级特征与原始图像相对应,这些低级特征可以逐渐得到组合以得到直观的图像轮廓。
Be First to Comment