赞
踩
【时间】2018.12.26
【题目】Keras中可视化卷积层的类激活热力图
本文是对《Deep Learning with python》一书中第5.4.3节Keras中可视化类激活的热力图的整理与总结,参考链接:https://blog.csdn.net/einstellung/article/details/82858974
类激活图(CAM,class activation map)是与特定输出类别相关的二维分数网格,对于输入图像的每个位置进行计算,它表示每个位置对该类别的重要程度。
类激活图可视化有助于了解一张图像的哪一部分让卷积神经网络做出了最终的分类决策。这有助于对卷积神经网络的决策过程进行调试,特别是分类错误的情况下。同时,这种方法可以定位图像中的特定目标。这里,我们介绍的具体实现方式是“Grad-CAM:visual explanations from deep networks via gradiaent-based localization',它是在2017年提出来的,具体实现方法是:给定一张输入图像,对于一个卷积层的输出特征图,用类别相对于每一个通道的梯度对这个特征图中的每个通道进行加权。
- from keras.applications.vgg16 import VGG16
-
- model = VGG16(weights='imagenet')
-
- model.summary()
【VGG-16】模型结构如下:最后输出的是1000维的向量,表示属于1000个类别的概率
如图所示,这是两只非洲象的图片。我们将这张图片转换为VGG16能够读取的格式:大小为224X224的图像,这些训练图像都根据keras.applications.vgg16.preprocess_input函数中的内置的规则进行预处理。因此,我们需要加载图像,将其大小调整为224X224,然后将其转化为float32格式的Numpy张量,并应用这些预处理规则。
- from keras.preprocessing import image
-
- from keras.applications.vgg16 import preprocess_input, decode_predictions
-
- import numpy as np
-
- img_path = '/Users/fchollet/Downloads/creative_commons_elephant.jpg'
-
- img = image.load_img(img_path, target_size=(224, 224)) # 大小为224*224的Python图像库图像
-
- x = image.img_to_array(img) # 形状为(224, 224, 3)的float32格式Numpy数组
-
- x = np.expand_dims(x, axis=0) # 添加一个维度,将数组转化为(1, 224, 224, 3)的形状批量
-
- x = preprocess_input(x) #按批量进行预处理(按通道颜色进行标准化)
这时,可以在图像上运行预训练的VGG16网络,并将预测向量解码为我们可以读的形式。
【代码】
- preds = model.predict(x)
-
- print('Predicted:', decode_predictions(preds, top=3)[0])
【运行结果】:
- Predicted: [(‘n02504458’, ‘African_elephant’, 0.90942144),
-
- (‘n01871265’, ‘tusker’, 0.08618243),
-
- (‘n02504013’, ‘Indian_elephant’, 0.0043545929)]
对这个图像预测的前三个类别分别是:
网络认为预测向量中最大激活的元素对应是“非洲象”类别的元素,索引编号386
np.argmax(preds[0])
386
- african_elephant_output = model.output[:, 386] # 预测向量中的非洲象元素
-
- last_conv_layer = model.get_layer('block5_conv3') # block5_conv3层的输出特征图,它是VGG16的最后一个卷积层
-
- grads = K.gradients(african_elephant_output, last_conv_layer.output)[0] # 非洲象类别相对于block5_conv3输出特征图的梯度
-
- pooled_grads = K.mean(grads, axis=(0, 1, 2)) # 形状是(512, )的向量,每个元素是特定特征图通道的梯度平均大小
-
- iterate = K.function([model.input], [pooled_grads, last_conv_layer.output[0]]) # 这个函数允许我们获取刚刚定义量的值:对于给定样本图像,pooled_grads和block5_conv3层的输出特征图
-
- pooled_grads_value, conv_layer_output_value = iterate([x]) # 给我们两个大象样本图像,这两个量都是Numpy数组
-
- for i in range(512):
-
- conv_layer_output_value[:, :, i] *= pooled_grads_value[i] # 将特征图数组的每个通道乘以这个通道对大象类别重要程度
-
- heatmap = np.mean(conv_layer_output_value, axis=-1) # 得到的特征图的逐通道的平均值即为类激活的热力图

【注意】
为了便于可视化,我们需要将热力图标准化到0~1范围内,如下。
- heatmap = np.maximum(heatmap, 0) # heatmap与0比较,取其大者
-
- heatmap /= np.max(heatmap)
-
- plt.matshow(heatmap)
-
- plt.show()
【结果】
最后,我们可以用OpenCV来生成一张图像,将原始图像叠加在刚刚得到的热力图上
- import cv2
-
- img = cv2.imread(img_path) # 用cv2加载原始图像
-
- heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0])) # 将热力图的大小调整为与原始图像相同
-
- heatmap = np.uint8(255 * heatmap) # 将热力图转换为RGB格式
-
- heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET) # 将热力图应用于原始图像
-
- superimposed_img = heatmap * 0.4 + img # 这里的0.4是热力图强度因子
-
- cv2.imwrite('/Users/fchollet/Downloads/elephant_cam.jpg', superimposed_img) # 将图像保存到硬盘
【注意】cv2.applyColorMap()是opencv中的伪彩色函数,用于画色度图(colarmap),具体可看:opencv中伪彩色applyColorMap函数
【最终结果】
- from keras.applications.vgg16 import VGG16
- from keras.preprocessing import image
- from keras.applications.vgg16 import preprocess_input, decode_predictions
- from keras import backend as K
- import numpy as np
- import cv2
- model = VGG16(weights='imagenet')
- model.summary()
-
- img_path = '/Users/fchollet/Downloads/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)
-
- african_elephant_output = model.output[:, 386]
-
- last_conv_layer = model.get_layer('block5_conv3')
-
- grads = K.gradients(african_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与0比较,取其大者
-
- heatmap /= np.max(heatmap)
-
- 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('/Users/fchollet/Downloads/elephant_cam.jpg', superimposed_img)

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。