【Deep Learning with Python】C5 热力图-感兴趣区域可视化

什么用以及是什么

这个比较难以理解,尤其是实现部分的代码,因为需要对卷积核有更进一步的理解。

  • 目的
    • 我想知道这么多个filter(通道,卷积核,下文不做区分),哪几个更重要,我想知道它们对于结果的贡献
    • 我想知道输入的一个数据案例中哪一部分更加重要
  • 原理简图

【Deep Learning with Python】C5 热力图-感兴趣区域可视化

 

  1. 输入对每一个通道进行求导,得到输出对各个通道梯度,可以视为通道对结果的贡献权重
  2. 让所有通道 * 权重,权重越大,通道输出就会放大,反之,通道输出就会缩小
  3. 全通道叠加,得到14 * 14的统一通道,通道上每个位置都是各个通道的加权和

基本步骤就是上述,其实主要是理解两点:

  • 想知道某个东西对结果的影响权重,那就对它求导就可以,导数的意义就在这里
  • 得到各个通道的权重,让所有通道输出加权和即可

代码和注释

下面代码中关键的输出都注释了,理解需要。

 

african_elephant_output = model.output[:, 386]

# <tf.Tensor 'strided_slice_2:0' shape=(?,) dtype=float32>

# model.output <tf.Tensor 'predictions/Softmax:0' shape=(?, 1000) dtype=float32>

 

last_conv_layer = model.get_layer('block5_conv3')

 

from keras import backend as K

求最终预测值对卷积层最后一层输出的导数因为输出是一个列表,所以去列表化

grads = K.gradients(african_elephant_output, last_conv_layer.output)[0]

# grads = <tf.Tensor 'gradients_2/block5_pool/MaxPool_grad/MaxPoolGrad:0' shape=(?, 14, 14, 512) dtype=float32>

这和求导对象的大小是一样的,可以理解为,对512个通道(filter)求导

# african_elephant_output 可以说是一个数

 

求得512个通道各自的梯度平均值

pooled_grads = K.mean(grads, axis=(012))

# grads = <tf.Tensor 'gradients_2/block5_pool/MaxPool_grad/MaxPoolGrad:0' shape=(?, 14, 14, 512) dtype=float32>

# pooled_grads = <tf.Tensor 'Mean:0' shape=(512,) dtype=float32>

求平均后从上面变为下面,其实就是求512个通道导数的均值

 

# Multiplies each channel in the feature-map array by“how important this channel is” with regard to the“elephant” class

其实就是对512个通道求导,然后该通道越重要,导数越大,这个导数相当于该层次的权重

下面这么一乘,越重要的会越大,越不重要的只会越小,相当于对各个channel按梯度的结果加权

for i in range(512):

    conv_layer_output_value[:, :, i] *= pooled_grads_value[i]

 

已经得到了加权后的层次输出,现在对14*14上的512个值求平均

# ps mean的用法,不需要哪几个维度就输入哪几个维度

heatmap = np.mean(conv_layer_output_value, axis=-1)

# conv_layer_output_value (14, 14, 512)

# heatmap.shape (14, 14)

 

 

import matplotlib.pyplot as plt

# x = max(0, x)

heatmap = np.maximum(heatmap, 0)

除最大值归一化

heatmap /= np.max(heatmap)

plt.matshow(heatmap)

【Deep Learning with Python】C5 热力图-感兴趣区域可视化

 

把上述得到的heatmap拉升,和原图合成,用到了openCV

import cv2

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)

 

【Deep Learning with Python】C5 热力图-感兴趣区域可视化

 

注意,上述将一个14*14的合成filter,直接拉升到原图中,这是可行的,说明一个问题——卷积核虽然是每次处理局部,但最终结果确是针对全局。

我觉得其中最重要的是,我能够知道模型抽取了哪些的特征,哪些特征更重要,分别多重要,即每个特征的权重。