close

6.4 CAM視覺化與hook函數使用

前文說到,在本章第二節介紹CNN的視覺化時我們知道,深度學習模型仍是一個黑箱,大家想盡辦法對其進行視覺化,本節就介紹一個實用的分析方法CAM(Class activation mapping,類啟動圖),如下圖所示:

​​<<AI人工智慧 PyTorch自學>> 6.4 CAM視覺

 

以上圖為例,CAM方法是探究模型將圖片分為“Dog”的依據是什麼,即圖片中哪些區域支撐了模型將圖片判別為"Dog"

其背後的原理是將網路中間的特徵圖進行加權並視覺化,而權重的得來就有多種方法,不同的方法就有了不同的CAM演算法,如CAMGrad CAMGrad CAM++

CAM:《2016-CAM-Learning Deep Features for Discriminative Localization

Grad-CAM:《2017-Grad-CAM Visual Explanations from Deep Networks via Gradient-based Localization

Grad-CAM++:《2018-Grad-CAM++ Generalized Gradient-based Visual Explanations for Deep Convolutional Networks

本文將介紹其演變過程,並用手寫代碼實現Grad CAM,同時借助強大的github倉庫,實現多種熱力圖對比,如下圖所示:

<<AI人工智慧 PyTorch自學>> 6.4 CAM視覺

 

CAM

論文名稱:《Learning Deep Features for Discriminative Localization

原理解釋:CAM方法需要將CNN模型修改後重新訓練,修改的目的就是為了獲得加權權重,具體方式如下圖所示:

<<AI人工智慧 PyTorch自學>> 6.4 CAM視覺

 

將最後一層特徵圖之後的層移除,並接入一個全連接層用於分類,全連接層的輸入是特徵圖的全域池化後的特徵向量。

最終熱力圖通過分類類別神經元所對應的權重w,乘以最後一層特徵圖,再求和即可。

背後邏輯有兩個:

1、最後一層特徵圖是原圖經過一系列卷積後保留下來的特徵,其與原圖的空間關係一一對應,空間關係指左上角對應左上角,右下角對應原圖右下角,等等。

2、特徵圖對於分類類別(如上圖的AustralianTerrier)的貢獻程度與全連接層權重密切相關

因此,只需要利用全連接層的權重乘以對應通道,即可得到熱力圖。

Grad CAM

論文名稱:《Grad-CAM Visual Explanations from Deep Networks via Gradient-based Localization

CAM的思路非常巧妙,但缺點很明顯,它需要對模型進行修改,並且重新訓練,不適用於大多數場景。

為此,研究者提出額Grad CAM,可以對模型直接進行觀察,無需改動模型。

Grad CAM的思路也非常巧妙,在CAM思路中有兩個要素:

1、特徵圖

2、特徵圖對應指定類別的權重

特徵圖很容易獲得,但特徵圖的重要性(加權權重)應該如何尋找?

Grad CAM給出了不一樣的答案,Grad CAM利用梯度求導獲得特徵圖的重要性權重。

原理分析:

假設最後一層feature maps10個,那麼如何尋找10個權值用來指示這些feature maps對某一類別的重要性呢?

CAM是通過對feature maps進行GAP,然後採用該類別與這個10GAP後的神經元的連接權值作為權值;

Grad-CAM採用的則是梯度,是該類別對於這10feature maps的求取梯度。

注意:最終求取的梯度是一個110的向量,即每個feature map對應一個標量,而對feature maps求取梯度是一個矩陣,作者是通過對*矩陣求均值得到的標量。

對於類別c,特徵圖的權值 a_k^c 計算公式如下:

<<AI人工智慧 PyTorch自學>> 6.4 CAM視覺

c表示第c類,k表示第k個特徵圖,Z表示特徵圖圖元點總數,i表示行,j表示列,A表示特徵圖。

公式可以分兩部分看,最右邊 gradients via backprop,即對類別c求得特徵圖的梯度,是一個二維的矩陣;

再通過左邊的global average pooling,對二維的梯度矩陣求平均值,得到第k個特徵圖對於第c類的權值。

示意圖如下:

<<AI人工智慧 PyTorch自學>> 6.4 CAM視覺

最終的熱力圖通過以下公式進行求取:

CAM不同的是,Grad-CAM在加權求和後還加上了ReLU函數,計算公式如下:

<<AI人工智慧 PyTorch自學>> 6.4 CAM視覺

之所以加上ReLU,是因為在這裡只關注正的梯度值,而不關注負的。

最後將Discriminative Localization Map直接resize至原圖大小即可,如最後一層feature maps14*14的,原圖是224*224,則將14*14的圖縮放到224*224即可。

Grad-CAM++

論文名稱:《Grad-CAM++ Generalized Gradient-based Visual Explanations for Deep Convolutional Networks

事物總是在不斷的發展,Grad CAM還存在以下缺點:

  1. 當圖片出現多個同類物體時,無法定位;(1-3列)
  2. 單物體圖片中,定位錯誤。(4-6列)

 

<<AI人工智慧 PyTorch自學>> 6.4 CAM視覺

並且, Grad-CAM中一個特徵圖對應的權重是對特徵圖的梯度求平均,即認為特徵圖的梯度(2維資料)上的每個元素同等重要。

Grad-CAM++則認為特徵圖的梯度上的每個元素重要程度應當不一樣,因此對Grad CAM進行了改進。

Grad-CAM++ 熱力圖的權重計算通過以下公式:

<<AI人工智慧 PyTorch自學>> 6.4 CAM視覺

CAM系列對比

CAMGrad-CAMGrad-CAM++的區別如下圖所示:

<<AI人工智慧 PyTorch自學>> 6.4 CAM視覺

關於CAM還有很多反覆運算改進,可參考這個repo

repo中對數十個CAM進行了實現,建議使用。

本節將用代碼手動的實現Grad CAM,並通過演算法的實現來學習nn.module中的hook函數使用。

nn.module中的hook函數

CAM系列演算法中知道,需要利用中間層的特徵圖,可nn.module並不會返回、保留中間層的特徵圖。這時,就要用到nn.module中的hook函數,把中間層特徵圖給拿出來。

什麼是hook函數?

pytorch中的hook是一個非常有意思的概念,hook意為鉤、掛鉤、魚鉤。 引用知乎用戶馬索萌hook的解釋:“(hook)相當於外掛程式。可以實現一些額外的功能,而又不用修改主體代碼。把這些額外功能實現了掛在主代碼上,所以叫鉤子,很形象。簡單講,就是不修改主體,而實現額外功能。對應到在pytorch中,主體就是forwardbackward,而額外的功能就是對模型的變數進行操作

pytorch提供的hook

1. torch.Tensor.register_hook

功能:註冊一個反向傳播hook函數,這個函數是Tensor類裡的,當計算tensor的梯度時自動執行。

形式: hook(grad) -> Tensor or None ,其中grad就是這個tensor的梯度。

返回值:a handle that can be used to remove the added hook by calling handle.remove()

案例請看配套代碼

2. torch.nn.Module.register_forward_hook

功能:Module前向傳播中的hook,module在前向傳播後,自動調用hook函數。

形式:hook(module, input, output) -> None。注意不能修改inputoutput 返回值

其中,module是當前網路層,input是網路層的輸入資料, output是網路層的輸出資料

應用場景:如用於提取特徵圖

案例請看配套代碼

3. torch.nn.Module.register_forward_pre_hook

功能:執行forward()之前調用hook函數。

形式:hook(module, input) -> None or modified input

應用場景舉例:暫時沒碰到過,希望讀者朋友補充register_forward_pre_hook相關應用場景。

registerforwardprehookforwardhook一樣,是在module.__call中註冊的,與forward_hook不同的是,其在module執行forward之前就運行了,具體可看module.__call中的代碼,第一行就是執行forward_pre_hook的相關操作。

4. torch.nn.Module.register_full_backward_hook

功能:Module反向傳播中的hook,每次計算module的梯度後,自動調用hook函數。

形式:hook(module, grad_input, grad_output) -> tuple(Tensor) or None

注意事項:當module有多個輸入或輸出時,grad_inputgrad_output是一個tuple

返回值:a handle that can be used to remove the added hook by calling handle.remove()

應用場景舉例:例如提取特徵圖的梯度

Grad CAM 手動實現

下面就利用 register_forward_hook register_full_backward_hook 來實現Grad CAM

詳情請看配套代碼

整體思路如下:

  1. 對模型最後一個卷積層進行hook函數註冊,兩個hook分別記錄特徵圖於梯度

def backward_hook(module, grad_in, grad_out):

    grad_block.append(grad_out[0].detach())

def farward_hook(module, input, output):

    fmap_block.append(output)

------------------------------------------------------------

# 註冊hook

resnet_50.layer4[-1].register_forward_hook(farward_hook)

resnet_50.layer4[-1].register_full_backward_hook(backward_hook)

Copy

  1. 獲取類別loss,類別loss為分類類別最大的那個神經元的值,具體由comp_class_vec函數實現

if not index:

    index = np.argmax(ouput_vec.cpu().data.numpy())

else:

    index = np.array(index)

index = index[np.newaxis, np.newaxis]

index = torch.from_numpy(index)

one_hot = torch.zeros(1, 1000).scatter_(1, index, 1)

one_hot.requires_grad = True

class_vec = torch.sum(one_hot * ouput_vec)  # one_hot = 11.8605

Copy

  1. 執行backward,得到梯度
  2. 通過gen_cam()函數得到CAM
  3. CAM與原圖進行融合視覺化,如下圖所示

<<AI人工智慧 PyTorch自學>> 6.4 CAM視覺

CAM 系列演算法統一實現

CAM2016年提出以來,已經有多種改進,並可運用於圖像分割和目標檢測,詳細的CAM演算法參見倉庫

pytorch-grad-cam提供了豐富的演算法及簡單的介面應用,下面就以resnet50為例,繪製6CAM演算法的熱力圖,效果如下圖所示。

代碼就不剖析了,grad-cam的介面已經非常清晰。請運行代碼,查看結果如下圖所示:

<<AI人工智慧 PyTorch自學>> 6.4 CAM視覺

小結

CAM系列演算法對理解深度卷積神經網路非常有説明,建議仔細學習本節內容並進行拓展。

通過CAM分析:

  1. 可診斷模型是否學到真正特徵
  2. 可通過熱力圖資訊做對應的資料增強(如對非啟動區域進行隨機擦除和Cutout處理),類似YOLOv4中的CutMix資料增強方法。
  3. 還可以將熱力圖作為語義分割的弱監督標籤進行訓練分割模型,可參考《Tell Me Where to Look: Guided Attention Inference Network

 

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 HCHUNGW 的頭像
    HCHUNGW

    HCHUNGW的部落格

    HCHUNGW 發表在 痞客邦 留言(0) 人氣()