在通常的认知中,神经网络的模型是一个“黑盒”,即模型学到的内容很难用人能够理解的方式来提取和表现,虽然对于某些类型的深度学习模型来说,这种表述部分正确,但对卷积神经网络来说绝对不是这样,卷积神经网络学到的表示非常适合可视化,很大程度上是因为它们是视觉概念的表示。到现在为止,人们开发了多种技术来对这些表示进行可视化和理解,这里介绍3种最容易理解也是最有效的方法。

  1. 可视化卷积神经网络的中间输出(中间激活):有助于理解卷积神经网络连续的层如何对输入进行变换,也有助于初步了解卷积神经网络每个过滤器的含义。
  2. 可视化卷积神经网络的过滤器:有助于精确理解卷积神经网络中每个过滤器容易接受的视觉模式或视觉概念。
  3. 可视化图像中类激活的热力图:有助于理解图像的哪个部分被识别为属于某个类别,从
    而可以定位图像中的物体。

1 可视化卷积神经网络的中间输出

可视化中间激活,是指对于给定输入,展示网络中各个卷积层和池化层输出的特征图(层的输出通常被称为该层的激活,即激活函数的输出)。这让我们可以看到输入如何被分解为网络学到的不同过滤器。我们希望在三个维度对特征图进行可视化:宽度、高度和深度(通道)。每个通道都对应相对独立的特征,所以将这些特征图可视化的正确方法是将每个通道的内容分别绘制成二维图像。
我们使用这篇博客保存的模型来进行中间输出的可视化:keras深度学习之猫狗分类二(数据增强)
加载该模型,打印其网络架构:

if __name__=='__main__':#加载保存的模型model=models.load_model('cats_and_dogs_1.h5')model.summary()
`````bash
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
conv2d (Conv2D)              (None, 148, 148, 32)      896
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 74, 74, 32)        0
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 72, 72, 64)        18496
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 36, 36, 64)        0
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 34, 34, 128)       73856
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 17, 17, 128)       0
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 15, 15, 128)       147584
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 7, 7, 128)         0
_________________________________________________________________
flatten (Flatten)            (None, 6272)              0
_________________________________________________________________
dropout (Dropout)            (None, 6272)              0
_________________________________________________________________
dense (Dense)                (None, 256)               1605888
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 257
=================================================================
Total params: 1,846,977
Trainable params: 1,846,977
Non-trainable params: 0
_________________________________________________________________

显示第一层输出的某个通道图像:

from tensorflow.keras.preprocessing.image import ImageDataGenerator
from PIL import Image
from tensorflow.keras import layers
from tensorflow.keras import models
from tensorflow.keras import optimizers
import matplotlib.pyplot as plt
from tensorflow.keras.preprocessing import image as kimageif __name__=='__main__':#加载保存的模型model=models.load_model('cats_and_dogs_1.h5')model.summary()#加载一张猫的图像img=kimage.load_img(path='./dataset/training_set/cats/cat.1700.jpg',target_size=(150,150))img_tensor=kimage.img_to_array(img)img_tensor=img_tensor.reshape((1,)+img_tensor.shape)img_tensor/=255.plt.imshow(img_tensor[0])plt.show()#提取前8层的输出layer_outputs=[layer.output for layer in model.layers[:8]]activation_model=models.Model(inputs=model.input,outputs=layer_outputs)#以预测模式运行模型 activations包含卷积层的8个输出activations=activation_model.predict(img_tensor)print(activations[0].shape)#(1, 148, 148, 32)first_layer_activation = activations[0]plt.matshow(first_layer_activation[0, :, :, 9], cmap='viridis')plt.show()

原图为:

第一层的输入第9个通道的特征图为:

将每个中间激活的所有通道可视化

from tensorflow.keras.preprocessing.image import ImageDataGenerator
from PIL import Image
from tensorflow.keras import layers
from tensorflow.keras import models
from tensorflow.keras import optimizers
import matplotlib.pyplot as plt
from tensorflow.keras.preprocessing import image as kimage
import numpy as npif __name__=='__main__':#加载保存的模型model=models.load_model('cats_and_dogs_1.h5')model.summary()#加载一张猫的图像img=kimage.load_img(path='./dataset/training_set/cats/cat.1700.jpg',target_size=(150,150))img_tensor=kimage.img_to_array(img)img_tensor=img_tensor.reshape((1,)+img_tensor.shape)img_tensor/=255.plt.imshow(img_tensor[0])plt.show()#提取前8层的输出layer_outputs=[layer.output for layer in model.layers[:8]]activation_model=models.Model(inputs=model.input,outputs=layer_outputs)#以预测模式运行模型 activations包含卷积层的8个输出activations=activation_model.predict(img_tensor)print(activations[0].shape)#(1, 148, 148, 32)first_layer_activation = activations[0]plt.matshow(first_layer_activation[0, :, :, 9], cmap='viridis')plt.show()#清空当前图像#plt.clf()#将每个中间激活的所有通道可视化layer_names = [] for layer in model.layers[:8]:layer_names.append(layer.name)images_per_row = 16for layer_name, layer_activation in zip(layer_names, activations): n_features = layer_activation.shape[-1] size = layer_activation.shape[1] 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 *= 64channel_image += 128channel_image = np.clip(channel_image, 0, 255).astype('uint8')display_grid[col * size : (col + 1) * size, row * size : (row + 1) * size] = channel_imagescale = 1. / sizeplt.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()

显示的各通道图像如下所示:
第1个:

第2个:

第3个:

第4个:

第5个:

第6个:

第7个:

第8个:

从上面显示的各个卷积层和池化层的输出特征图,我们能够得到如下几点:

  1. 第一层是各种边缘探测器的集合。在这一阶段,激活几乎保留了原始图像中的所有信息。
  2. 随着层数的加深,激活变得越来越抽象,并且越来越难以直观地理解。它们开始表示更高层次的概念,比如“猫耳朵”和“猫眼睛”。层数越深,其表示中关于图像视觉内容的信息就越少,而关于类别的信息就越多。
  3. 激活的稀疏度(sparsity)随着层数的加深而增大。在第一层里,所有过滤器都被输入图像激活,但在后面的层里,越来越多的过滤器是空白的。也就是说,输入图像中找不到这些过滤器所编码的模式。

深度神经网络有一个重要普遍特征:随着层数的加深,层所提取的特征变得越来越抽象。更高的层激活包含关于特定输入的信息越来越少,而关于目标的信息越来越多(本例中即图像的类别:猫或狗)。深度神经网络可以有效地作为信息蒸馏管道(information distillation pipeline),输入原始数据(本例中是 RGB 图像),反复对其进行变换,将无关信息过滤掉(比如图像的具体外观),并放大和细化有用的信息(比如图像的类别)。

2 可视化神经网络的过滤器

想要观察卷积神经网络学到的过滤器,另一种简单的方法是显示每个过滤器所响应的视觉模式。这可以通过在输入空间中进行梯度上升来实现:从空白输入图像开始,将梯度下降应用于卷积神经网络输入图像的值,其目的是让某个过滤器的响应最大化。得到的输入图像是选定过滤器具有最大响应的图像。
这个过程很简单:我们需要构建一个损失函数,其目的是让某个卷积层的某个过滤器的值最大化;然后,我们要使用随机梯度下降来调节输入图像的值,以便让这个激活值最大化。
在这里我们使用在ImageNet上训练的vgg16网络模型进行可视化滤波器。
通过如下代码可以查看block3_conv1 层第 0 个过滤器响应的是波尔卡点(polka-dot)图案。

from tensorflow.keras.applications import VGG16
from tensorflow.keras import backend as K
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
tf.compat.v1.disable_eager_execution()#为过滤器的可视化定义损失张量
model = VGG16(weights='imagenet',include_top=False)model.summary()layer_name = 'block3_conv1'
filter_index = 0layer_output = model.get_layer(layer_name).output
loss = K.mean(layer_output[:, :, :, filter_index])#获取损失相对于输入的梯度
grads = K.gradients(loss, model.input)[0]
#梯度标准化技巧
grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5)#给定numpy输入值,得到numpy输出值
iterate = K.function([model.input], [loss, grads])#通过随机梯度下降让损失最大化
input_img_data = np.random.random((1, 150, 150, 3)) * 20 + 128.
step = 1.
for i in range(40):loss_value, grads_value = iterate([input_img_data])input_img_data += grads_value * step#将张量转换为有效图像的实用函数
def deprocess_image(x):x -= x.mean() x /= (x.std() + 1e-5)x *= 0.1x += 0.5 x = np.clip(x, 0, 1)# x *= 255 # x = np.clip(x, 0, 255)# x/=255.return x#生成过滤器可视化的函数
#构建一个损失函数,将该层第 n 个过滤器的激活最大化
def generate_pattern(layer_name, filter_index, size=150):layer_output = model.get_layer(layer_name).output loss = K.mean(layer_output[:, :, :, filter_index])grads = K.gradients(loss, model.input)[0] grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5) iterate = K.function([model.input], [loss, grads]) input_img_data = np.random.random((1, size, size, 3)) * 20 + 128. step = 1.for i in range(40): loss_value, grads_value = iterate([input_img_data])input_img_data += grads_value * stepimg = input_img_data[0]return deprocess_image(img)#block3_conv1 层第 0 个过滤器响应的是波尔卡点(polka-dot)图案
plt.imshow(generate_pattern('block3_conv1', 0))
plt.show()


接下来我们把多个卷积层的每个层前64个过滤器的模式显示出来,代码如下:

from tensorflow.keras.applications import VGG16
from tensorflow.keras import backend as K
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
tf.compat.v1.disable_eager_execution()#为过滤器的可视化定义损失张量
model = VGG16(weights='imagenet',include_top=False)model.summary()layer_name = 'block3_conv1'
filter_index = 0layer_output = model.get_layer(layer_name).output
loss = K.mean(layer_output[:, :, :, filter_index])#获取损失相对于输入的梯度
grads = K.gradients(loss, model.input)[0]
#梯度标准化技巧
grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5)#给定numpy输入值,得到numpy输出值
iterate = K.function([model.input], [loss, grads])#通过随机梯度下降让损失最大化
input_img_data = np.random.random((1, 150, 150, 3)) * 20 + 128.
step = 1.
for i in range(40):loss_value, grads_value = iterate([input_img_data])input_img_data += grads_value * step#将张量转换为有效图像的实用函数
def deprocess_image(x):x -= x.mean() x /= (x.std() + 1e-5)x *= 0.1x += 0.5 x = np.clip(x, 0, 1)# x *= 255 # x = np.clip(x, 0, 255)# x/=255.return x#生成过滤器可视化的函数
#构建一个损失函数,将该层第 n 个过滤器的激活最大化
def generate_pattern(layer_name, filter_index, size=150):layer_output = model.get_layer(layer_name).output loss = K.mean(layer_output[:, :, :, filter_index])grads = K.gradients(loss, model.input)[0] grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5) iterate = K.function([model.input], [loss, grads]) input_img_data = np.random.random((1, size, size, 3)) * 20 + 128. step = 1.for i in range(40): loss_value, grads_value = iterate([input_img_data])input_img_data += grads_value * stepimg = input_img_data[0]return deprocess_image(img)#block3_conv1 层第 0 个过滤器响应的是波尔卡点(polka-dot)图案
# plt.imshow(generate_pattern('block3_conv1', 0))
# plt.show()# 生成某一层中所有过滤器响应模式组成的网格
#查看如下5个层的过滤器模式
layer_names=['block1_conv1','block2_conv1','block3_conv1','block4_conv1','block5_conv1']
for layer_name in layer_names:#显示通道中的前64个滤波器size = 64margin = 5results = np.zeros((8 * size + 7 * margin, 8 * size + 7 * margin, 3)) for i in range(8): for j in range(8): filter_img = generate_pattern(layer_name, i + (j * 8), size=size) horizontal_start = i * size + i * margin horizontal_end = horizontal_start + sizevertical_start = j * size + j * marginvertical_end = vertical_start + sizeresults[horizontal_start: horizontal_end,vertical_start: vertical_end, :] = filter_imgplt.figure(figsize=(20, 20)) plt.imshow(results)plt.show()

block1_conv1层过滤器模式为:

block2_conv1层过滤器模式为:

block3_conv1层过滤器模式为:

block4_conv1层过滤器模式为:

block5_conv1层过滤器模式为:

这些过滤器可视化包含卷积神经网络的层如何观察世界的很多信息:卷积神经网络中每一层都学习一组过滤器,以便将其输入表示为过滤器的组合。这类似于傅里叶变换将信号分解为一组余弦函数的过程。随着层数的加深,卷积神经网络中的过滤器变得越来越复杂,越来越精细。

  1. 模型第一层(block1_conv1)的过滤器对应简单的方向边缘和颜色(还有一些是彩色边缘)。
  2. block2_conv1 层的过滤器对应边缘和颜色组合而成的简单纹理。
  3. 更高层的过滤器类似于自然图像中的纹理:羽毛、眼睛、树叶等。

3 可视化类激活的热力图

我还要介绍另一种可视化方法,它有助于了解一张图像的哪一部分让卷积神经网络做出了最终的分类决策。这有助于对卷积神经网络的决策过程进行调试,特别是出现分类错误的情况下。这种方法还可以定位图像中的特定目标。
这种通用的技术叫作类激活图(CAM,class activation map)可视化,它是指对输入图像生成类激活的热力图。类激活热力图是与特定输出类别相关的二维分数网格,对任何输入图像的每个位置都要进行计算,它表示每个位置对该类别的重要程度。举例来说,对于输入到猫狗分类卷积神经网络的一张图像,CAM 可视化可以生成类别“猫”的热力图,表示图像的各个部分与“猫”的相似程度,CAM 可视化也会生成类别“狗”的热力图,表示图像的各个部分与“狗”的相似程度。
我们将使用的具体实现方式是“Grad-CAM: visual explanations from deep networks via gradient-based localization” 这篇论文中描述的方法。这种方法非常简单:给定一张输入图像,对于一个卷积层的输出特征图,用类别相对于通道的梯度对这个特征图中的每个通道进行加权。直观上来看,理解这个技巧的一种方法是,你是用“每个通道对类别的重要程度”对“输入图像对不同通道的激活强度”的空间图进行加权,从而得到了“输入图像对类别的激活强度”的空间图。
为模型预处理一张输入图像,在这里采用非洲象图像:

预测代码如下:

from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.vgg16 import preprocess_input, decode_predictions
from tensorflow.keras.applications import VGG16
import numpy as npimg_path = './creative_commons_elephant.jpg'
img = image.load_img(img_path, target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)model = VGG16(weights='imagenet')preds = model.predict(x)
print('Predicted:', decode_predictions(preds, top=3)[0])
print(np.argmax(preds[0]))

预测结果为:

Predicted: [('n02504458', 'African_elephant', 0.90988594), ('n01871265', 'tusker', 0.085724816), ('n02504013', 'Indian_elephant', 0.00434713)]

对这张图像预测的前三个类别分别为:

  • 非洲象(African elephant,92.5% 的概率)
  • 长牙动物(tusker,7% 的概率)
  • 印度象(Indian elephant,0.4% 的概率)

网络识别出图像中包含数量不确定的非洲象。预测向量中被最大激活的元素是对应“非洲象”类别的元素,索引编号为 386。

为了展示图像中哪些部分最像非洲象,我们来使用 Grad-CAM 算法。
测试图像的“非洲象”类激活热力图,代码为:

from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.vgg16 import preprocess_input,decode_predictions
import numpy as np
from tensorflow.keras.applications.vgg16 import VGG16
import matplotlib.pyplot as plt
from tensorflow.keras import backend as K
import cv2
import tensorflow as tf
tf.compat.v1.disable_eager_execution()model = VGG16(weights='imagenet')   # 包含最后的全连接层img_path = 'creative_commons_elephant.jpg'
img = image.load_img(img_path, target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)preds = model.predict(x)
print('predicted: ', decode_predictions(preds, top=3)[0])
print(np.argmax(preds[0]))elephant_output = model.output[:, 386]
last_conv_layer = model.get_layer('block5_conv3')
grads = K.gradients(elephant_output, last_conv_layer.output)[0]
pooled_grads = K.mean(grads, axis=(0, 1, 2))
iterate = K.function([model.input], [pooled_grads, last_conv_layer.output[0]])
pooled_grads_value, conv_layer_output_value = iterate([x])
for i in range(512):conv_layer_output_value[:, :, i] *= pooled_grads_value[i]heatmap = np.mean(conv_layer_output_value, axis=-1)
#热力图后处理
heatmap = np.maximum(heatmap, 0)
heatmap /= np.max(heatmap)
plt.matshow(heatmap)
plt.show()img = cv2.imread(img_path)
heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0]))
heatmap = np.uint8(255 * heatmap)
heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)
superimposed_img = heatmap * 0.4 + img
cv2.imwrite('elephant_cam.jpg', superimposed_img)

得到的图像为:

这种可视化方法回答了两个重要问题:

  • 网络为什么会认为这张图像中包含一头非洲象?
  • 非洲象在图像中的什么位置?
    尤其值得注意的是,小象耳朵的激活强度很大,这可能是网络找到的非洲象和印度象的不
    同之处。

卷积神经网络的可视化(基于keras)相关推荐

  1. 深度学习笔记:卷积神经网络的可视化--卷积核本征模式

    目录 1. 前言 2. 代码实验 2.1 加载模型 2.2 构造返回中间层激活输出的模型 2.3 目标函数 2.4 通过随机梯度上升最大化损失 2.5 生成滤波器模式可视化图像 2.6 将多维数组变换 ...

  2. 无人驾驶汽车系统入门(十二)——卷积神经网络入门,基于深度学习的车辆实时检测

    无人驾驶汽车系统入门(十二)--卷积神经网络入门,基于深度学习的车辆实时检测 上篇文章我们讲到能否尽可能利用上图像的二维特征来设计神经网络,以此来进一步提高识别的精度.在这篇博客中,我们学习一类专门用 ...

  3. EEGNet:一个小型的卷积神经网络,用于基于脑电的脑机接口

    脑机接口(BCI)利用神经活动作为控制信号,可以与计算机直接通信.这种神经信号通常从各种研究充分的脑电图(EEG)信号中选择.对于给定的脑机接口(BCI)范式,特征提取器和分类器是针对其所期望的脑电图 ...

  4. DeepDream、反向运行一个卷积神经网络在 DeepDream和卷积神经网络的可视化 中的应用

    日萌社 人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新) 反向运行一个卷积神经网络在 卷积神经网络的可视化 中的应用 D ...

  5. 《Python 深度学习》5.4 卷积神经网络的可视化(代码)

    Visualizing what convnets learn 卷积神经网络的可视化 人们常说,深度学习模型是"黑盒",即模型学到的表示很难用人类可以理解的方式来提取和呈现.虽然对 ...

  6. C++ 和 OpenCV 实现卷积神经网络并加载 Keras 训练好的参数进行预测

    C++ 和 OpenCV 实现卷积神经网络并加载 Keras 训练好的参数进行预测 一. 背景 二. Keras 定义神经网络结构 channels_first 与 channels_last cha ...

  7. flashtorch:卷积神经网络的可视化

    flashtorch:卷积神经网络的可视化 GitHub:FlashTorch saliency maps论文地址: Deep Inside Convolutional Networks: Visua ...

  8. 图卷积神经网络 | Python实现基于GCN-GRU图卷积门控循环单元网络模型

    图卷积神经网络 | Python实现基于GCN-GRU图卷积门控循环单元网络模型 目录 图卷积神经网络 | Python实现基于GCN-GRU图卷积门控循环单元网络模型 效果分析 基本描述 模型结构 ...

  9. 窥探黑盒-卷积神经网络的可视化

    这是笔者第N+1次听到专家说,深度学习模型是"黑盒".这个说法不能说他对,也不能说他错.但是这句话从专家那里说出来,感觉就有点不严谨了,想必专家应该长时间不在科研一线了...  对 ...

最新文章

  1. UA OPTI512R 傅立叶光学导论17 离散傅立叶变换简介
  2. mysql自动编号步进值_MySQL-自动编号
  3. NSWindowController的初始化创建代码
  4. 记一次el-input使用的坑
  5. python中scrapy加请求头_Python爬虫之scrapy框架随机请求头中间件的设置
  6. 区块链网络安全平台HAPI获Genesis Block Ventures投资
  7. 深入理解 Hive 分区分桶 (Inceptor)
  8. MacOS Big Sur 11.3 (20E232) Cl 5133/OC 0.6.9/PE 三分区原版黑苹果镜像
  9. Java根据纯真IP库获取具体的地址信息
  10. 基于密度聚类算法的改进
  11. Spring5学习详细笔记
  12. 计算机与科学技术专业毕业设计,计算机科学与技术专业毕业设计论文
  13. 《零基础入门学习Python》第031讲:永久存储:腌制一缸美味的泡菜
  14. Buffon投针(近似计算π)
  15. 做数据挖掘工作需要具备哪些思维原理?
  16. Stata结果输出:Excel结果表变身LaTeX表格
  17. java哪几种运算符_java运算符有哪些
  18. MyBatis的参数传递
  19. ExcelVBA之If then
  20. 计算机原理综合设计,《计算机组成原理综合设计报告》.doc

热门文章

  1. 上班一个月挣10万,居然全靠它...
  2. html5 spin,HTML5 number spinbox controls not triggering a cha
  3. 金融业预警| 黑客如何大摇大摆把钱从银行划出去?
  4. 计算机远程安装win7,初试使用Ghost工具远程安装win7操作系统
  5. ftp登录成功,无法取得列表
  6. 怎么查看一段时间的计算机ip,如何查看电脑中使用过的历史IP地址
  7. 百度地图离线开发demo(热力图)
  8. 文献:利用自驱动分子马达并行计算子集和问题 Parallel computation with molecular-motor-propelled agents...(PNAS)
  9. 用mac系统怎么连宽带连接服务器吗,苹果电脑怎么连宽带_MAC系统怎么连接有线宽带-win7之家...
  10. 体脂秤模块的原理和基本功能说明