摘要:本文介紹一種卷積網絡的可視化方法,類激活圖可視化。使用預訓練的VGG16模型,對一張網上隨便找的動物圖片進行分析,並將生成的熱力圖與原圖像結合觀察網絡模型的分類依據。使用Keras框架。
目錄
- 類激活圖原理簡介
- Keras實現
主要參考文獻
【1】“Deep learning with python”
【2】“Grad-CAM: visual explanations from deep networks via gradientbased localization”
1. 類激活圖原理簡介
類激活圖可視化,可以針對指定的輸入圖像生成類激活的熱力圖。類激活熱力圖表示圖像的每個位置對該類別的重要程度。
Grad-CAM不同於CAM,理論上各種卷積網絡都可以直接用該方法進行可視化,不需要對網絡結構進行修改。
【2】中給出的方法如下:給定一張輸入圖像,對於一個卷積層的輸出特徵圖,用類別相對於通道的梯度對這個特徵圖中的每個通道進行加權。直觀理解,就是用每個通道對特定類別的重要程度對不同通道的激活強度進行加權,從而得到輸入圖像對該類別的激活強度。
落實到操作中,描述如下:取最後一層的卷積,算每個通道的梯度,然後對每個通道的梯度求全局平均,然後用這個全局平均加權原來的通道。下面使用Keras對上述步驟逐步實現。
2. Keras實現
使用預訓練的VGG16對可視化的具體操作進行介紹。參照【1】。
首先直接載入Keras中的VGG16。記得使用model.summary()方法查看最後一個卷積層的名字,這裏是block5_conv3。
from keras.applications.vgg16 import VGG16
model = VGG16(weights='imagenet')
model.summary()
同時準備輸入數據,讀取圖像並轉化爲numpy數組的格式。讀取的方法可以選擇scipy.misc(已經過時),cv2,或者keras.preprocessing自帶的image。最終得到的輸入數據維數爲[1, 224, 224, 3]。
import numpy as np
import scipy
from keras.applications.vgg16 import preprocess_input, decode_predictions
img_path = 'image.jfif'
img = scipy.misc.imread(img_path)
img = scipy.misc.imresize(img, (224, 224))
x = np.expand_dims(img, axis=0)
x = preprocess_input(x)
然後對該輸入數據進行預測,得到模型輸出。可以看到,對該輸入,VGG16認爲它有96.7%的概率是老虎。。最大概率的索引爲292。
preds = model.predict(x)
print('Predicted:', decode_predictions(preds, top=3)[0])
print(np.argmax(preds[0])) # 用該索引分析輸入和該類別之間的類激活圖。
>>> Predicted: [('n02129604', 'tiger', 0.96711606), ('n02123159', 'tiger_cat', 0.026872123), ('n02391049', 'zebra', 0.0046514785)]
292
下面開始說明網絡是通過哪一部分將其判斷爲老虎的,如果誤分類,則可以說明網絡看到了輸入圖像的什麼位置導致的誤分類。
import keras.backend as K
tiger_output = model.output[:, np.argmax(preds[0])]# 輸出中老虎對應的元素。
last_conv_layer = model.get_layer('block5_conv3')# 最後一個卷積層
grads = K.gradients(tiger_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)
對該熱力圖進行可視化。
import matplotlib.pyplot as plt
heatmap = np.maximum(heatmap, 0)
heatmap /= np.max(heatmap)
plt.matshow(heatmap)
熱力圖的可視化結果如下圖所示:
最後將熱力圖和原始圖像合併,得到最終想要的可視化結果。
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.5 + img
cv2.imwrite('cam.jpg', superimposed_img)
結果如圖所示:原始的老虎圖片來自網絡。