6.2 CNN卷積核與特徵圖視覺化
眾所周知,深度學習仍是一個黑盒子,模型內部的邏輯含義仍舊無法解釋,越是未知的東西,越能激起人們的好奇心。
在卷積神經網路中,有時會對卷積核以及特徵圖進行視覺化,以此觀察卷積神經網路學習到了何種模式。
作為深度卷積神經網路的開山之作,AlexNet(2012年)就已經對卷積核的模式進行了分析,論文中發現卷積核的學習具有偏向性,一部分學習顏色特徵,一部分學習邊緣特徵,詳見下圖:
緊接著AlexNet之後的2013年,ZFNet對CNN的特徵圖進行了視覺化,進一步的探究卷積神經網路的奧秘。
AlexNet:《ImageNet Classification with Deep Convolutional Neural Networks》
ZFNet:《Visualizing and understanding convolutional networks》
本節就利用tensorboard以及pytorch的函數對AlexNet的卷積核與特徵圖進行視覺化。
make_grid 函數
在圖像任務中,往往需要人眼審核、觀察大批量圖像資料,如果一張一張的觀察,效率會非常低。
通常會將一批資料繪製成網格圖片,類似大排檔的功能表一樣,這樣便於觀察。
在pytorch的torchvision庫中,提供了make_grid函數説明大家完成網格圖片製作。下面先學習make_grid函數,再用它繪製卷積核與特徵圖。
`torchvision.utils.make_grid(tensor: Union[torch.Tensor, List[torch.Tensor]], nrow: int = 8, padding: int = 2, normalize: bool = False, value_range: Optional[Tuple[int, int]] = None, scale_each: bool = False, pad_value: float = 0.0, **kwargs)
功能:
將一組圖片拼接成一張網格圖片,便於視覺化。
參數:
tensor(Tensor or list)- 需視覺化的數據,shape:(B x C x H x W) ,B表示batch數,即幾張圖片
nrow(int)- 一行顯示幾張圖,預設值為8。
padding(int)- 每張圖片之間的間隔,預設值為2。
normalize(bool)- 是否進行歸一化至(0,1)。
value_range(tuple)- 設置歸一化的min和max,若不設置,默認從tensor中找min和max。
scale_each(bool)- 每張圖片是否單獨進行歸一化,還是min和max的一個選擇。
pad_value(float)- 填充部分的圖元值,預設為0,即黑色。
關於輸入:make_grid的輸入可以分為兩種:
- 一種是4D張量,函數自動將第一維度作為圖片數量進行拆解。
- 一種是list,元素必須是張量形式,並且張量的shape必須一致。
這兩種輸入分別對應兩種常用場景:
- 4D張量:卷積核大小與特徵圖張量都適合用這種形式。
- list:對普通圖片進行視覺化觀察,一次載入一張圖片時使用。
另外,對於圖元的轉換也需要注意相應策略,對於float類型的資料,需要設置歸一化的策略,策略由value_range和scale_each構成,請自行調整觀察變化。
請看代碼使用效果:
Alexnet卷積核視覺化
要對卷積核進行視覺化,就需要對pytorch的nn.Module類非常熟悉,要瞭解卷積核以怎樣的形式?存儲在哪裡?
2D卷積的卷積核權重是一個4D張量,包含輸入通道,輸出通道,高,寬。
注意:除了第一層可以將 輸入通道 *高*寬作為 RGB圖像進行視覺化之外,其餘網路層只能將高*寬作為灰度圖像(2D)進行視覺化。
卷積核存儲在nn.Conv2D的weight變數中,下面就可以通過如下代碼獲得。
for sub_module in alexnet.modules():
# 非卷積層則跳過
if isinstance(sub_module, nn.Conv2d):
# 獲取conv2d層的權重,即卷積核權重
kernels = sub_module.weight
Copy
有了4D張量,剩下就按部就班的繪製到grid中視覺化即可,完整代碼生成的視覺化圖像如下所示:
可以看到,alexnet模型的第一層的卷積核確實學習到了不同模式,有邊緣模式,有色彩模式。
Alexnet特徵圖視覺化
特徵圖視覺化與卷積核視覺化類似,需要知道特徵圖以怎樣的形式?從哪裡獲得?
常規任務中,特徵圖是4D張量(BCHW)。
但是獲得就沒有那麼簡單,因為特徵圖是中間資料,通常不會保留,在前向運算過程中,不再使用的特徵圖會被捨棄。
因此需要特殊方法獲得特徵圖,本節介紹一種笨辦法,但容易理解,就是將對應層(仍舊是一個nn.Module)拿出來,然後把圖片仍給網路層(仍舊是一個nn.Module),其輸出的就是特徵圖了。
更高級的方法是利用hook函數機制完成中間特徵圖的獲取,這個在本章的後半部分會介紹。
請看核心代碼
alexnet = models.alexnet(pretrained=True)
# forward
convlayer1 = alexnet.features[0]
fmap_1 = convlayer1(img_tensor)
Copy
這樣就獲得了(1, 64, 55, 55)的4D張量(fmap_1),然後將其轉換成能視覺化的形式,再利用tensorboard繪製。
完整代碼生成的視覺化圖像如下所示:
小結
本節介紹了tensorboard對卷積核與特徵圖的繪製,其中涉及非常實用的函數make_grid,make_grid可以説明開發人員高效的審核圖片,人眼看圖是演算法工程師最重要的一步,因為模型是沒辦法知道哪張圖片的標籤搞錯了!
下一小節將介紹在模型訓練過程中,如何基於tensorboard進行監控模型狀態。
最後留一個思考題,將卷積核與特徵圖放到一起去比較,大家能否找到什麼規律?
邊緣模式的卷積核似乎能過濾掉大部分細節,僅留下邊緣;還能看到清晰原圖資訊的特徵圖所對應的卷積核,都是顏色卷積核。