前言
卷積神經網路 (Convolution Neural Network, CNN) 已經是一個耳熟能詳的網路架構,CNN 在影像辨識上展現了非常強大的能力,而且近幾年更是有許多基於 CNN 的網路架構被提出,例如 VGG、ResNet、DenseNet 等網路皆擁有比一般 CNN 更好的泛化能力。
但是儘管我們知道 CNN-based 的神經網路在影像辨識上甚至比人還更加精準,但是我們卻無法輕易得知神經網路中究竟做了什麼事情,神經網路就像是一個黑盒子,只知道輸入跟輸出而已,所以有人就會懷疑透過 CNN-based 的神經網路訓練的模型究竟是不是可靠的。假設我們在訓練辨識動物的神經網路時,有許多輸入的影像都包含綠色的植物,如何知道辨識的依據是狗的形狀,還是背景的綠色植物。
因此,為了證明 CNN 辨識影像的依據是合理的,CAM 在 2015 年被提出,但是 CAM 有一個明顯的缺點 — — 網路架構的限制,所以在 2016 年 Grad-CAM 被提出,讓 CAM 不用再讓神經網路視覺化分類的關注位置更容易實現。
CAM (Class Activation Mapping)
CAM 的思想非常的直覺,我們先從神經網路是如何分類下手。如下圖中,我們想要知道該影像分類為何,最後一層的卷積層生成的每張特徵圖經過 GAP 變成一個像素,該像素包含了整張特徵圖的訊息,最後再將一維的像素陣列乘以權重 w 後,經過 softmax 知道「Australian terrier」該分類的值最大,所以這張影像被分為 Australian terrier。
接著,我們反向思考,經過 GAP 後的像素陣列會乘以權重 w,權重 w 的值越大代表該像素所代表的影像影響越大。而既然 w 是指每張特徵圖可以被分為 Australian terrier 的重要程度,不如把整張特徵圖的像素點皆乘以權重 w 後疊加,便能夠依據每張特徵圖的重要性關注不同的區域。對應到該分類的權重 w 越大,該特徵圖影響越大;反之,權重越趨近於 0 的特徵圖越不重要。
如果對於 GAP 不熟可以看看這篇 🔗文章。
雖然 CAM 可以視覺化神經網路的分類關注區域,但是如果要使用 CAM 的先決條件是必須使用 GAP (Global Average Pooling) 接在最後一層的卷積層後,因此在過去訓練時不是使用 GAP 的模型都必須修改模型架構,且必須重新訓練。
因此,接下來我們要介紹一種方法能夠不用修改模型就可以使用 CAM 視覺化模型分類的關注區域。
Grad-CAM
CAM 的缺點是如果最後一層的神經網路如果不是使用 GAP (Global Average Pooling),就必須修改模型的架構,且重新訓練。為了解決這個問題,Grad-CAM 在 2016 年被發表出來。
Grad-CAM 的思想即是「不論模型在卷積層後使用的是何種神經網路,不用修改模型就可以實現 CAM」,從下圖中就可以看到最後不論是全連接層、RNN、LSTM 或是更複雜的網路模型,都可以藉由 Grad-CAM 取得神經網路的分類關注區域熱力圖。
而 Grad-CAM 關鍵是能夠透過反向傳播 (Back Propagation) 計算在 CAM 中使用的權重 w。
如果想看 Grad-CAM 的推導可以看我寫了另一篇 ✍️文章。
使用 Keras 實現 Grad-CAM
在這篇文章,使用的 keras 版本為 2.2.4,而網路模型是 keras.applications 中提供的的 ResNet50,如果你使用其他 keras.applications 提供的網路模型,只要依照下方的教學修改,也能夠實現 Grad-CAM。
在這邊文章中使用的是已經訓練完的 Imagenet 權重,如果要使用自行訓練的權重只要把一開始載入 ResNet50(weight="imagenet")
的部分修改成 load_model()
就可以了。
引入相依套件
語言與套件版本:
* Python 3.6.4
* Keras 2.2.4
* OpenCV 3.4.0.12
輸入影像與影像前處理
在輸入影像時記得檢查 channel 的大小,在 Keras 提供的 ResNet50 輸入的影像必須為 (224, 224, 3),所以必須事先確認影像的是否符合該大小,或者使用前處理的方法 resize 影像。
實現 Grad-CAM
在這篇文章中使用的模型架構為 ResNet50,其最後一層卷積層的名稱為 res5c_branch2c,可以利用 model.get_layer()
的方法取得特徵圖。
使用 ResNet50 以外的模型也是一樣,只要透過名稱指定,就能夠取得特徵圖。如果不知道最後一層的名稱為何,可以透過 model.summary()
輸出模型的架構,並尋找最後一個 Conv2D 的名稱。
接著,我們知道實現 CAM 時我們要取得 GAP 後面的權重,但是其缺點在於模型接必須在最後的卷積層後接上 GAP, Grad-CAM 改良了其方法,不用有 GAP 也可以透過計算的方式取得權重。
該權重的計算方法是求得神經元對於最後一層卷積層的特徵圖的偏微分,也就是利用反向傳播計算梯度。而在反向傳播時計算出的是跟特徵圖一樣大小的梯度矩陣,所以必須將將結果加總作為權重,例如將大小為 (1, 7, 7, 2048) 的梯度矩陣加總後,結果為 (2048, ) 的向量,該向量即為 GAP 接到預測類別的的權重 w。
在 Keras 中可以自行定義方法,在定義完後使用
K.function()
將自定義方法包裝起來,再用像是 lambda 的方法以變數作為函數名稱進行呼叫。
視覺化神經網路關注區域
我們經過 gradcam()
計算出的 heatmap
其大小與最後一層卷積層的特徵圖大小有關,使用 model.summary()
看到其大小為 (None, 7, 7, 2048)。所以為了將其放大至與原始影像一樣大小可以使用 cv2.resize()
,而在使用 OpenCV 輸入影像時也要特別注意 RGB 的通道位置,否則輸出的影像顏色會非常奇怪。
heatmap
切記要乘以 255,因為在先前我們使用過preprocessing_input()
處理過影像,除了將 RGB 變成了 BGR,同時也將像素值減去各通道的平均值,所以最後要將其數值回復成原始影像的像素分佈。
此外,在使用 matplotlib 繪圖時,colormap 可以選擇 jet,jet 是經常使用的映射表,像素值越大則越靠近紅色,像素值越小則越靠近藍色。
使用 Imagenet 權重視覺化分類關注區域
在這邊我們使用 Imagenet 已經訓練好的權重,最後依序呼叫上述已經定義好的函式。可以看到最後分類為 bull mastiff (鬥牛獒),其關注的區域也確實是鬥牛獒的位置。
結論
在這篇文章中,我們使用 Imagenet 的權重以及 Keras 提供的 ResNet50 架構實驗了 Grad-CAM 實際上運行的效果,讓 CNN 不再是一個黑盒子,而是可以透過方法視覺化分類的關注位置。
而在 Grad-CAM 論文發表不久後,又有一篇 Grad-CAM++: Improved Visual Explanations for Deep Convolutional Networks,如果對於解釋 CNN 分類結果的朋友有興趣,不仿可以閱讀這篇論文,會更多的新發現。
Reference
- Learning Deep Features for Discriminative Localization
- Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization
- WHERE CNN IS LOOKING? — GRAD CAM
- “卷積可視化”:Grad-CAM