這兩天在看CS231n的課程筆記,結合着原版英文和知乎上中文版翻譯在看,確實Andrej Karpathy寫的很棒,很多都是些實踐經驗不僅僅是理論知識. 我結合着自己的理解和Karpathy的介紹,重新看確實又收穫了不少,以前覺得不明白的地方現在也清晰了,所以重新寫這個再看篇,僅供參考
CNN的組成
- convolution layer
- pooling layer
- fc layer
- Batch Normalization layer
基礎的三大部件(卷積, 池化, 全連接),最近兩年Residual learning的流行導致大量的用到Batch Norm layer,此外還有像激活函數(Relu, PRelu, tanh等), LRU等.
我的工作中大量使用到的是Kaiming等人提出的Residual的形式,現今直接stack三大部件是很少見的,因爲會出現像梯度消失等的問題. 不瞭解的直接去看相關的論文吧,推薦兩篇:
(1) Residual Learning
(2) Wide Residual
基本的實現就是Residual Block.
細說卷積
卷積是CNN的重心,也是這篇博客的重點.
CNN的賣點
我的個人理解是兩個賣點:
- 局部連接
- 參數共享
局部連接
對待像圖像這樣的高維數據,直接讓神經元與前一層的所有神經元進行全連接是不現實的,這樣做的害處顯而易見: 參數過多,根本無法計算.
所以我們讓每個神經元只與輸入數據的一個局部區域連接,這個局部區域的大小就是局部感受野(receptive field),尺寸上等同於filter的空間尺寸(比如5*5*3).
爲何說局部連接是CNN的賣點呢?通過局部連接的方式避免了參數的爆炸式增長(對比全連接的方式). 通過下面的參數共享可以大大的縮減實際的參數量,爲訓練一個多層的CNN提供了可能.
參數共享
這裏直接上案例,來的更加直觀.
先給出計算公式:
ouput_width = (input_width-filter_width+2*padding)/stride+1
現給出如下假設:
input: 227*227*3(height, width, channel)
filter: 96個11*11的filter,strider=4, padding=0
我們可以算出output的shape是(55,55,96).
假設是全連接的方式
,意味着需要:
神經元總數是: 55*55*96=290400
每個神經元連接的參數是: 11*11*3+1=364(+1表示bias)
總共參數的個數是: 290400*364=105705600,差不多1億個參數!!!
這還只是一層(一般我們說網絡的層數是不算輸入層的),假設是10層呢,自己想想吧.
所以我們需要參數共享.
參數共享
的方式下:
不是每個神經元都有11*11*3,而是每個filter控制的實際大小(55*55)共享這個filter的參數,故此時的參數總共是:
(11*11*3+1)*96=34944,可以看到此時的參數相比於全連接減少非常多.
說的太通俗了,大家可以看知乎上的翻譯,說的很好,我只是按照自己的理解來說.
圖示
看圖示其實很明白了,input(7*7*3),2個filter(3*3, stride=2, pad=0). input的pad=1,所以5*5 -> 7*7. 由此可見filter的shape其實是(height, width, inChannel, outChannel),在這裏就是(3,3,3,2).
Tensorflow
中的conv2d的API
:
conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None,
data_format=None, name=None)
filter
: A Tensor. Must have the same type as input. A 4-D tensor of shape [filter_height, filter_width, in_channels, out_channels]
實現
卷積運算本質上就是在濾波器和輸入數據的局部區域間做點積,所以可以用矩陣乘法來實現. 當然可以用一個類似於劃窗那樣的方式去實現,但是考慮到實現效率一般都是用im2col
的方式實現,這樣可以高效的利用優化之後的矩陣乘法,具體可以參考Caffe
中的im2col的實現.
Karpathy的解釋方式是多通道的im2col,有可能比較抽象,所以咱們還是從單個channel看起吧.
假設input(4*4*3), filter(3*3*3*6, stride=1, pad=0)
現在只看一個channel的情況:
多(3)個channel的情況:
多個channel的im2col
的過程就是順序im2col
多個channel,首先im2col第一通道,然後在im2col第二通道,最後im2col第三通道,…
現在看下karpathy的總結就很清楚了:
1*1的卷積
用的也挺多的,Residual
和Inception
的實現裏面就有用1*1卷積的.
作用是兩個:
- 實現跨通道(cross-channel)的交互和信息整合
- 進行卷積核通道數的降維和升維,減少網絡參數
可以參考知乎上的回答,最早的論文是Network in Network
這篇,地址戳我
全卷積
我最早接觸到全卷積是用在Semantic Segmentation
的FCN
上,火了之後全卷積用在了很多地方,比如用於Object Detection
的RFCN
.
Caffe
上有將fc layer轉換爲全卷積的示例: net-surgery
原理上來說將fc轉換爲全卷積挺簡單的:
(1) 假設input(5*5*16),fc1(4096), fc2(1000)
(2) 可以把fc1看成是(1*1*4096),fc2同理
你只需要用5*5*16*4096的filter去和input卷積就可以得到(1*1*4096)這就是fc1,fc1和1*1*4096*1000的filter去卷積就可以得到(1*1*1000)也就是fc2了
.
將fc轉換爲全卷積的高效體現在下面的場景上:
讓卷積網絡在一張更大的輸入圖片上滑動,得到多個輸出,這樣的轉化可以讓我們在單個向前傳播的過程中完成上述的操作.
這裏原文引用知乎上的翻譯:
舉個例子,如果我們想讓224x224尺寸的浮窗,以步長爲32在384x384的圖片上滑動,把每個經停的位置都帶入卷積網絡,最後得到6x6個位置的類別得分。上述的把全連接層轉換成卷積層的做法會更簡便。如果224x224的輸入圖片經過卷積層和匯聚層之後得到了[7x7x512]的數組,那麼,384x384的大圖片直接經過同樣的卷積層和匯聚層之後會得到[12x12x512]的數組(因爲途徑5個匯聚層,尺寸變爲384/2/2/2/2/2 = 12)。然後再經過上面由3個全連接層轉化得到的3個卷積層,最終得到[6x6x1000]的輸出(因爲(12 - 7)/1 + 1 = 6)。這個結果正是浮窗在原圖經停的6x6個位置的得分!
面對384x384的圖像,讓(含全連接層)的初始卷積神經網絡以32像素的步長獨立對圖像中的224x224塊進行多次評價,其效果和使用把全連接層變換爲卷積層後的卷積神經網絡進行一次前向傳播是一樣的。
dilated convolution
dilated
在原始的convolution上加了個rate
參數,導致卷積的時候會skip rate-1個像素.
It performs convolution with holes, sampling the input values every rate pixels in the height and width dimensions. This is equivalent to convolving the input with a set of upsampled filters, produced by inserting rate - 1 zeros between two consecutive values of the filters along the height and width dimensions.
圖示如下:
在不增加參數的情況下,增大filter的感受野(receptive field).
It also allows us to effectively enlarge the field of view of filters without increasing the number of parameters or the amount of computation.
基本用在dense predictive
的場景中:
- Detection of fine-details by processing inputs in higher resolutions.
- Broader view of the input to capture more contextual information.
- Faster run-time with less parameters
Tensorflow
有API直接調用: atrous_conv2d
總結
總結下重點內容:
1. 參數共享
2. 卷積實現
3. 1*1卷積
4. 全卷積
參考