卷积神经网络是一种黑盒技术,通过理论推导,以及梯度传播,去不断逼近局部最优解。但神经网络究竟是怎么做出判断和决策的,需要不断地探究。近些年来有一些这方面的研究,类激活热力图(CAM)就是一个方面。
热力图通常是用来对类别进行划分的图像,它有点像红外成像图,温度高的地方就很红,温度低的部分就呈现蓝色。同理,我们使用热力图可以以权重的形式来展现,神经网络对图片的哪一部分激活值最大。比如输入到猫狗分类,如果卷积神经网络判别是猫,那它的热力图普遍分散在猫身上,而至于它是根据猫的哪一部分来判别,就利用了热力图的原理
使用卷积神经网络进行分类,最后一层通常是softmax层,其最大值对应的就是分类网络预测的类别
我们从这个最大概率分类类别的节点出发,进行反向传播,对最后一层卷积层求得梯度,然后对每一张特征图求出均值,最后我们取出 最后一层卷积层的激活值,与前面我们对梯度特征图的均值进行相乘,这个过程可以理解为,每个通道的重要程度与我们卷积激活值进行相乘,就相当于是一个加权操作。最后根据这个乘积值生成一个热力图,与原图进行叠加,得到了可视化的CAM图。
框架:keras
功能:cam热力图的实现
前提:已经训练完成了,得到了后缀名为.h5的模型文件
from keras import backend as K
from keras.models import load_model
from keras.preprocessing import image
from keras.utils import plot_model
from keras import Model
import numpy as np
import matplotlib.pyplot as plt
from keras.applications.vgg16 import preprocess_input
import cv2
def load_model_h5(model_file):
"""
载入原始keras模型文件
:param model_file: 模型文件,h5类型
:return: 模型
"""
return load_model(model_file)
def load_img_preprocess(img_path, target_size):
"""
加载图片并进行预处理
:param img_path: 图片文件名
target_size: 要加载图片的缩放大小
这是一个tuple元组类型
:return: 预处理过的图像文件
"""
img = image.load_img(img_path, target_size=target_size)
img = image.img_to_array(img) # 转换成数组形式
img = np.expand_dims(img, axis=0) # 为图片增加一维batchsize,直接设置为1
img = preprocess_input(img) # 对图像进行标准化
return img
def gradient_compute(model, layername, img):
"""
计算模型最后输出与你的layer的梯度
并将每个特征图的梯度进行平均
再将其与卷积层输出相乘
:param model: 模型
:param layername: 你想可视化热力的层名
:param img: 预处理后的图像
:return:
卷积层与平均梯度相乘的输出值
"""
preds = model.predict(img)
idx = np.argmax(preds[0]) # 返回预测图片最大可能性的index索引
output = model.output[:, idx] # 获取到我们对应索引的输出张量
last_layer = model.get_layer(layername)
grads = K.gradients(output, last_layer.output)[0]
pooled_grads = K.mean(grads, axis=(0, 1, 2)) # 对每张梯度特征图进行平均,
# 返回的是一个大小是通道维数的张量
iterate = K.function([model.input], [pooled_grads, last_layer.output[0]])
pooled_grads_value, conv_layer_output_value = iterate([img])
for i in range(pooled_grads.shape[0]):
conv_layer_output_value[:, :, i] *= pooled_grads_value[i]
return conv_layer_output_value
def plot_heatmap(conv_layer_output_value, img_in_path, img_out_path):
"""
绘制热力图
:param conv_layer_output_value: 卷积层输出值
:param img_in_path: 输入图像的路径
:param img_out_path: 输出热力图的路径
:return:
"""
heatmap = np.mean(conv_layer_output_value, axis=-1)
heatmap = np.maximum(heatmap, 0)
heatmap /= np.max(heatmap)
img = cv2.imread(img_in_path)
heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0]))
heatmap = np.uint8(255 * heatmap)
heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)
superimopsed_img = heatmap * 0.4 + img
cv2.imwrite(img_out_path, superimopsed_img)
img_path = r'./train_data/daisy/1.jpg'
model_path = r'./five_flowers_categorical_vgg16.h5'
layername = r'separable_conv2d_6'
img = load_img_preprocess(img_path, (224, 224))
model = load_model_h5(model_path)
conv_value = gradient_compute(model, layername, img)
plot_heatmap(conv_value, img_path, './packagetest.jpg')
参考博客地址:https://blog.csdn.net/weixin_44106928/article/details/103323970?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~sobaiduend~default-2-103323970.nonecase&utm_term=cam%E7%83%AD%E5%8A%9B%E5%9B%BE%E5%8F%AF%E8%A7%86%E5%8C%96&spm=1000.2123.3001.4430
后续我自己再完善和补充。