Keras中可視化卷積層的類激活熱力圖
C小C 2018-12-26 17:05:12 5546 收藏 15
展開
【時間】2018.12.26
【題目】Keras中可視化卷積層的類激活熱力圖
概述
本文是對《Deep Learning with python》一書中第5.4.3節Keras中可視化類激活的熱力圖的整理與總結,參考鏈接:https://blog.csdn.net/einstellung/article/details/82858974
一、什么是類激活圖(CAP)?
類激活圖(CAM,class activation map)是與特定輸出類別相關的二維分數網格,對於輸入圖像的每個位置進行計算,它表示每個位置對該類別的重要程度。
二、類激活圖可視化
類激活圖可視化有助於了解一張圖像的哪一部分讓卷積神經網絡做出了最終的分類決策。這有助於對卷積神經網絡的決策過程進行調試,特別是分類錯誤的情況下。同時,這種方法可以定位圖像中的特定目標。這里,我們介紹的具體實現方式是“Grad-CAM:visual explanations from deep networks via gradiaent-based localization',它是在2017年提出來的,具體實現方法是:給定一張輸入圖像,對於一個卷積層的輸出特征圖,用類別相對於每一個通道的梯度對這個特征圖中的每個通道進行加權。
三、使用VGG16演示類激活圖可視化
3.1 加載預訓練的VGG-16:
from keras.applications.vgg16 import VGG16
model = VGG16(weights='imagenet')
model.summary()
【VGG-16】模型結構如下:最后輸出的是1000維的向量,表示屬於1000個類別的概率
3.2 載入輸入圖像及預處理
如圖所示,這是兩只非洲象的圖片。我們將這張圖片轉換為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)]
對這個圖像預測的前三個類別分別是:
非洲象:92.5%的概率
長牙動物:7%的概率
印度象:0.4%的概率
網絡認為預測向量中最大激活的元素對應是“非洲象”類別的元素,索引編號386
np.argmax(preds[0])
386
3.3. 應用Grad-CAM算法
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) # 得到的特征圖的逐通道的平均值即為類激活的熱力圖
【注意】
K.gradients(y,x)用於求y關於x 的導數(梯度),(y和x可以是張量tensor也可以是張量列表,形如 [tensor1, tensor2, …, tensorn]),返回的是一個張量列表,列表長度是張量列表y的長度,列表元素是與x具有一樣shape的張量。在本例中,grads = K.gradients(african_elephant_output, last_conv_layer.output)[0],african_elephant_output是(1,)張量,last_conv_layer.output是(1,7,7,512)張量,所以返回的是只有一個元素的(1,7,7,512)張量列表,grads取了列表中唯一一個元素。
3.4 熱力圖后處理
為了便於可視化,我們需要將熱力圖標准化到0~1范圍內,如下。
heatmap = np.maximum(heatmap, 0) # heatmap與0比較,取其大者
heatmap /= np.max(heatmap)
plt.matshow(heatmap)
plt.show()
【結果】
3.5 將熱力圖與原始圖疊加,實現可視化
最后,我們可以用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)
————————————————
版權聲明:本文為CSDN博主「C小C」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/C_chuxin/java/article/details/85265082