对于中间激活的可视化,我们使用之前在猫狗分类中从头开始训练的小型卷积神经网络。
from keras.models import load_model
model = load_model('cats_and_dogs_small_2.h5')
model.summary()
Layer (type) Output Shape Param #
=================================================================
conv2d_5 (Conv2D) (None, 148, 148, 32) 896
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 74, 74, 32) 0
_________________________________________________________________
conv2d_6 (Conv2D) (None, 72, 72, 64) 18496
_________________________________________________________________
max_pooling2d_6 (MaxPooling2 (None, 36, 36, 64) 0
_________________________________________________________________
conv2d_7 (Conv2D) (None, 34, 34, 128) 73856
_________________________________________________________________
max_pooling2d_7 (MaxPooling2 (None, 17, 17, 128) 0
_________________________________________________________________
conv2d_8 (Conv2D) (None, 15, 15, 128) 147584
_________________________________________________________________
max_pooling2d_8 (MaxPooling2 (None, 7, 7, 128) 0
_________________________________________________________________
flatten_2 (Flatten) (None, 6272) 0
_________________________________________________________________
dropout_1 (Dropout) (None, 6272) 0
_________________________________________________________________
dense_3 (Dense) (None, 512) 3211776
_________________________________________________________________
dense_4 (Dense) (None, 1) 513
=================================================================
Total params: 3,453,121
Trainable params: 3,453,121
Non-trainable params: 0
接下面,输入一张不属于网络的猫的图像
img_path = '/Users/fchollet/Downloads/cats_and_dogs_small/test/cats/cat.1700.jpg'
from keras.preprocessing import image # 将图像处理成为一个4D张量
import numpy as np
img = image.load_img(img_path, target_size=(150, 150))
img_tensor = image.img_to_array(img)
img_tensor = np.expand_dims(img_tensor, axis=0)
img_tensor /= 255.
print(img_tensor.shape)
(1, 150, 150, 3)
显示测试图像
import matplotlib.pyplot as plt
plt.imshow(img_tensor[0])
plt.show()
为了提取想要查看的特征图,我们需要创建一个Keras模型,以图像批量作为输入,并输出所有卷积层和池化层的激活。为此,我们需要使用Keras的Model类。模型实例化需要两个参数:一个输入张量(或输入张量的列表)和一个输出张量(或输出张量的列表)。
from keras import models
layer_outputs = [layer.output for layer in model.layers[:8]] #提取前8层的输出
activation_model = models.Model(inputs=model.input, outputs=layer_outputs) #创建一个模型,给定模型的输入,可以返回这些输出
这段语句是输入一张图像,这个模型将返回原始模型的前8层激活值。这个模型有一个输入和8个输出,即每层激活对应一个输出。
activations = activation_model.predict(img_tensor) # 返回8个Numpy数组组成的列表,每个层激活对应一个Numpy数组
first_layer_activation = activations[0]
print(first_layer_activation.shape)
(1, 148, 148, 32)
它是大小为148*148的特征图,有32个通道。我们来绘制原始模型第3个通道:
import matplotlib.pyplot as plt
plt.matshow(first_layer_activation[0, :, :, 3], cmap='viridis')
plt.show()
plt.matshow(first_layer_activation[0, :, :, 30], cmap='viridis')
plt.show()
我们可以看到,似乎不同通道对于图像检测有不同侧重,比如第3个通道更侧重于边缘检测,第30个通道更侧于”绿色圆点“检测。
下面我们来绘制网络中所有激活的完整可视化图。我们需要在8个特征图里的每一个都提取并绘制一个通道,然后将结果叠加在一个大的图像张量中,按通道并排。
import keras
layer_names = []
for layer in model.layers[:8]:
layer_names.append(layer.name) # 用来存储层的名称,这样你就可以把层的名称画到图中
images_per_row = 16
for layer_name, layer_activation in zip(layer_names, activations): # 显示特征图
n_features = layer_activation.shape[-1] # 特征图中的特征个数
size = layer_activation.shape[1] # 特征图的形状为(1, size, size, n_features)
n_cols = n_features // images_per_row # 在这个矩阵中将激活通道平铺
display_grid = np.zeros((size * n_cols, images_per_row * size))
for col in range(n_cols): #将每个过滤器平铺到一个大的水平网格中
for row in range(images_per_row):
channel_image = layer_activation[0,
:, :,
col * images_per_row + row]
channel_image -= channel_image.mean() #对特征进行后处理,使其看起来更加美观
channel_image /= channel_image.std()
channel_image *= 64
channel_image += 128
channel_image = np.clip(channel_image, 0, 255).astype('uint8')
display_grid[col * size : (col + 1) * size,
row * size : (row + 1) * size] = channel_image # 显示网格
scale = 1. / size
plt.figure(figsize=(scale * display_grid.shape[1],
scale * display_grid.shape[0]))
plt.title(layer_name)
plt.grid(False)
plt.imshow(display_grid, aspect='auto', cmap='viridis')
plt.show()