Grad-CAM熱力圖可視化

今天聽師弟的匯報浩习,講了熱力圖的原理静暂,一直想去學(xué)習(xí),一直沒提上日程谱秽,特此記錄今日學(xué)習(xí)內(nèi)容洽蛀。
感謝師弟的分享!

論文鏈接:
Grad-CAM: Visual Explanations from Deep Networks via Gradient-Based Localization | SpringerLink

-代碼:
GitHub - jacobgil/pytorch-grad-cam: PyTorch implementation of Grad-CAM

Grad-CAM(Gradient-weighted Class Activation Map), 指對輸入圖像生成類激活的熱力圖疟赊。它是與特定輸出類別相關(guān)的二維特征分?jǐn)?shù)網(wǎng)絡(luò)郊供,網(wǎng)格的每個位置表示該類別的重要程度。對于一張輸入到CNN模型且被分類成“狗”的圖片近哟,該技術(shù)可以以熱力圖形式呈現(xiàn)圖片中每個位置與“狗”類的相似程度驮审。有助于了解一張原始圖像的哪一個局部位置讓CNN模型做出了最終的分類決策。


相關(guān)圖

核心公式

相關(guān)步驟






    1. 模型輸入
from keras.applications.vgg16 import VGG16
# 特別注意,在之前的實驗中疯淫,我們都把頂層的分類器丟棄掉了地来,include_top = False
model = VGG16(weights='imagenet')
print("模型調(diào)取成功")
    1. 數(shù)據(jù)輸入
from keras import backend as K
from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input, decode_predictions
import numpy as np

# The local path to our target image
img_path = '/home/som/lab/rongqian/hangtian/grad-cam/data/timgqqq.jpg'


# `img` is a PIL image of size 224x224
img = image.load_img(img_path, target_size=(224, 224))

# 一轉(zhuǎn),`x` is a float32 Numpy array of shape (224, 224, 3)
x0 = image.img_to_array(img)

# 二擴(kuò)熙掺,We add a dimension to transform our array into a "batch"
# of size (1, 224, 224, 3)
x1 = np.expand_dims(x0, axis=0)

# 三標(biāo)未斑,F(xiàn)inally we preprocess the batch
# (this does channel-wise color normalization)
x = preprocess_input(x1)

    1. 結(jié)果輸出
preds = model.predict(x)
print('Predicted:', decode_predictions(preds, top=3)[0])
num = np.argmax(preds)#求最大的類別的索引
    1. 求熱力圖矩陣
african_elephant_output = model.output[:, num]#獲取索引為num的類的預(yù)測輸出  shape: (batch_size,)
last_conv_layer = model.get_layer('block5_conv3')#獲取最后一個卷積層激活輸出 shape (batch_size, 14, 14, 512)
grads = K.gradients(african_elephant_output, last_conv_layer.output)[0]#求模型輸出針對最后一個卷積層激活輸出的梯度 shape(batch_size,14,14,512)

#梯度均值化,即求各通道平均值币绩,平均數(shù),即對每一層 14 x 14的矩陣求均值, (batch_size,14,14, 512) ----> (512,)
pooled_grads = K.mean(grads, axis=(0, 1, 2))
print('pooled_grads:',pooled_grads.shape)
#建立模型輸出蜡秽、最后一個卷積層激活輸出、梯度均值三者之間的函數(shù)關(guān)系
iterate = K.function([model.input], [pooled_grads, last_conv_layer.output[0]])
# 以真實的數(shù)據(jù)作為輸入类浪,得到結(jié)果
pooled_grads_value, conv_layer_output_value = iterate([x])
print(pooled_grads_value.shape,conv_layer_output_value.shape)#(512,) (14, 14, 512)
##乘梯度
# We multiply each channel in the feature map array
# by "how important this channel is" with regard to the elephant class
#表征出最后卷積層激活輸出各點對決策模型分類的重要程度载城。
for i in range(len(pooled_grads_value)):
    conv_layer_output_value[:, :, i] *= pooled_grads_value[i]

# The channel-wise mean of the resulting feature map
# is our heatmap of class activation
heatmap = np.mean(conv_layer_output_value, axis=-1) # #shape:14*14
#Relu函數(shù)
heatmap = np.maximum(heatmap, 0)
#歸一化處理
heatmap /= np.max(heatmap)  #shape:14*14

  • 4.畫熱力圖
import matplotlib.pyplot as plt
plt.matshow(heatmap)
plt.show()
  • 5.熱力圖與原圖融合
#讀取原始圖像
import cv2
test = cv2.imread("/home/som/lab/rongqian/hangtian/grad-cam/data/timgqqq.jpg")
#heatmap為[0,1]之間的浮點數(shù),特別注意:cv2.resize(img, (x軸向長度费就,y軸向長度))
#調(diào)整熱圖尺寸,與原圖保持一致川队,resize()
heatmap_test = cv2.resize(heatmap, (test.shape[1], test.shape[0]))
#可視化熱力圖
plt.matshow(heatmap_test)
plt.show()

#將heatmap數(shù)組轉(zhuǎn)換為(0,255)之間的無符號的unit8數(shù)值
heatmap_test = np.uint8(255 * heatmap_test)
#將熱力圖轉(zhuǎn)換為噴射效果
heatmap_test = cv2.applyColorMap(heatmap_test, cv2.COLORMAP_JET)
#將熱力圖與原始圖像疊加力细, 0.5表示渲染強(qiáng)度, 有超出(0,255)范圍的,如果需要可視化固额,則需要clip裁剪
superimposed_img_test = heatmap_test * 0.5 + test
superimposed_img_test=np.clip(superimposed_img_test,0,255)
print(np.max(superimposed_img_test),superimposed_img_test.shape)
superimposed_img_test=superimposed_img_test.astype(np.uint8) ##必須做眠蚂,要不然會白屏
#用OpenCV中imread輸入照片后是一個數(shù)組對象,在進(jìn)行一系列的對數(shù)組操作后數(shù)組已經(jīng)變成了float類型斗躏,之后再對數(shù)組進(jìn)行imshow時即出現(xiàn)上面的第二種情況逝慧。倘若圖像矩陣(double型)的矩陣元素不在0-1之間,那么imshow會把超過1的元素都顯示為白色啄糙,即255笛臣。其實也好理解,因為double的矩陣并不是歸一化后的矩陣并不能保證元素范圍一定就在0-1之間隧饼,所以就會出錯沈堡。
cv2.imshow('1',superimposed_img_test)
cv2.waitKey(0)
cv2.imwrite('a.jpg',superimposed_img_test)#寫
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市燕雁,隨后出現(xiàn)的幾起案子诞丽,更是在濱河造成了極大的恐慌,老刑警劉巖拐格,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件僧免,死亡現(xiàn)場離奇詭異,居然都是意外死亡捏浊,警方通過查閱死者的電腦和手機(jī)懂衩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人勃痴,你說我怎么就攤上這事谒所。” “怎么了沛申?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵劣领,是天一觀的道長。 經(jīng)常有香客問我铁材,道長尖淘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任著觉,我火速辦了婚禮村生,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饼丘。我一直安慰自己趁桃,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布肄鸽。 她就那樣靜靜地躺著卫病,像睡著了一般。 火紅的嫁衣襯著肌膚如雪典徘。 梳的紋絲不亂的頭發(fā)上蟀苛,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機(jī)與錄音逮诲,去河邊找鬼帜平。 笑死,一個胖子當(dāng)著我的面吹牛梅鹦,可吹牛的內(nèi)容都是我干的裆甩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼帘瞭,長吁一口氣:“原來是場噩夢啊……” “哼淑掌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蝶念,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤抛腕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后媒殉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體担敌,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年廷蓉,在試婚紗的時候發(fā)現(xiàn)自己被綠了全封。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片马昙。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖刹悴,靈堂內(nèi)的尸體忽然破棺而出行楞,到底是詐尸還是另有隱情,我是刑警寧澤土匀,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布子房,位于F島的核電站,受9級特大地震影響就轧,放射性物質(zhì)發(fā)生泄漏证杭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一妒御、第九天 我趴在偏房一處隱蔽的房頂上張望解愤。 院中可真熱鬧,春花似錦乎莉、人聲如沸送讲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽李茫。三九已至,卻和暖如春肥橙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背秸侣。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工存筏, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人味榛。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓椭坚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親搏色。 傳聞我的和親對象是個殘疾皇子善茎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容