我们知道,CNN由通道channel以及与通道相连的filter组成,我们可以可视化的内容就有这二者,
其一,是卷积神经网络的中间输出,帮助我们了解层是如何对输入进行变换的。
其二,是卷积神经网络的过滤器,帮助我们精确理解卷积神经网络中每个过滤器最容易接受的视觉模式。(即使得过滤器所有元素值最大的输入)
此外,还有类激活的热力图,帮助我们理解哪个部分被识别属于某个类别,从而定位图像中的物体。
1.可视化中间输出
即将神经网络中间的卷积层或池化层后的结果展示出来,通过这一步可以看到神经网络对于原图所作的变化。
首先,我们载入某一个使用卷积神经网络的模型
from tensorflow.keras import models
model = models.load_model('dogs_vs_cats_small_without_data_increase.h5')
然后我们载入一张图片
from keras.preprocessing import image
img_addr = 'D:\\猫狗大战\\kaggle\\train\\cat.1.jpg'
img = image.load_img(img_addr,target_size=(150,150))
将图片转为张量形式并进行标准化
img_tensor = image.img_to_array(img)
img_tensor *= 1./255
img_tensor = img_tensor.reshape((1,)+img_tensor.shape)
查看图片
import matplotlib.pyplot as plt
plt.imshow(img_tensor[0])
后面我们要显示出其中一层的输出,可以构建一个多输出的神经网络。
out_tensor = [layer.output for layer in model.layers[:8]]
network = models.Model(inputs=model.input,outputs=out_tensor)
获取输出
res_tensor = network.predict(img_tensor)
tensor1=res_tensor[0]
plt.imshow(tensor1[0,:,:,13])
输出的结果类似由斜纹构成,由之前的文章可知,卷积核对符合其特点的图像内容锐化了,比如这里就锐化了斜纹的特点。
2.可视化神经网络过滤器
在这里我们不是视觉化权重参数,而是希望找到可以使得过滤器响应最大化的图像 。之前说过,某一层卷积后的图片不单是卷积核锐化后的结果,也是图片部分内容与卷积核多相似的打分。实际上这里我们用到的就是第二点,我们使用卷积后的输出各值做累加并平均,以代表我们的图片的特征有多像卷积核,这就是我们的目标函数,令我们的目标函数对模型输入做梯度上升,就可以得到使得filter激活最大化的输入图片。
def generate_pattern(layer_name, filter_index, size=150):
layer_output = model.get_layer(layer_name).output # 获取指定层经filter后的输出
loss = K.mean(layer_output[:, :, :, filter_index]) # 指定某filter后结果均值作为损失函数
grads = K.gradients(loss, model.input)[0] # 获取损失函数对模型输入的梯度,结果为列表
grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5) # 对梯度进行l2标准化防止更新过大
iterate = K.function([model.input], [loss, grads]) # 拼成一个函数,输入在前,输出在后
input_img_data = np.random.random((1, size, size, 3)) * 20 + 128. # 期望值在138左右
step = 1. # 学习率设为1
for i in range(40):
loss_value, grads_value = iterate([input_img_data]) # 获取损失和梯度值
input_img_data += grads_value * step # 对图片的所有值梯度上升
img = input_img_data[0]
return deprocess_image(img)
plt.imshow(generate_pattern('block3_conv1',0))