卷積神經網絡的參數計算
前言
這篇文章會簡單寫一下卷積神經網絡上參數的計算方法,然后計算各個常見神經網絡的參數。一個是加強對網絡結構的了解,另一方面對網絡參數的量級有一個大概的認識,也可以當作備忘錄,免得想知道的時候還要再算。
此外,還有個比較有爭議的點,評論區里也有人指出,關於全連接中bias(偏置)的計算,我看的一些資料里參數的數量是1,但是在用的深度學習框架中(tensorflow和pytorch)基本都是和輸出層的元素的數量相同,我的看法的話,第二種可能效果更好一些吧,這樣會有更高的自由度,或許有相關的論文,但是我沒發現。不過這一點其實影響不大的,因為bias所占的參數量的比重很小,所以無論用哪種計算方法最終的結果基本沒什么差別。
參數計算方法
全連接的參數計算就不說了,比較簡單。
首先,簡單說一下卷積網絡的參數計算。下圖中是一個32x32x3的輸入,然后用一個5x5x3的卷積對其中某個位置的計算,這里算的是一個點積,所以輸出是一個單獨的標量的值。
因為卷積的操作是通過一個滑動窗口實現的,那么通過卷積操作,我們就得到了一個28x28x1的輸出。
如果我有6個上面說的filter,那么,我就會得到一個28x28x6的輸出。
這就是一個最基礎的卷積操作,那么這里用到的參數是多少呢?我們只需要把每個filter的參數累加起來,當然,不要忘了加上bias:5x5x3x6 + 6 = 456
另外一個需要計算的就是進行卷積以后的輸出的大小,從下面的圖上看就很好理解了,用公式直接算就好了。其中N是輸入圖像的size,F是filter的size,stride是滑動的步長。
然后從上圖中最后一個例子可以看到,stride大於1的時候不一定能整除,這個時候,就需要在原圖像上加上一層padding層,這樣圖像的大小就變化了,然后再用前面的公式算就行了。
然后還有一個maxpooling操作,這個會改變輸入輸出,但是不會有參數。所以使用和計算卷積一樣的公式算就行了。
LeNet
首先計算一下最簡單的LeNet。網絡結構如下:
網絡層(操作) | 輸入 | filter | stride | padding | 輸出 | 計算公式 | 參數量 |
---|---|---|---|---|---|---|---|
Input | 32x32x1 | 32x32x1 | 0 | ||||
Conv1 | 32x32x1 | 5x5x6 | 1 | 0 | 28x28x6 | 5x5x1x6+6 | 156 |
MaxPool1 | 28x28x6 | 2x2 | 2 | 0 | 14x14x6 | 0 | |
Conv2 | 14x14x6 | 5x5x16 | 1 | 0 | 10x10x16 | 5x5x6x16+16 | 2416 |
MaxPool2 | 10x10x16 | 2x2 | 2 | 0 | 5x5x16 | 0 | |
FC1 | 5x5x16 | 120 | 5x5x16x120+1 | 48001 | |||
FC2 | 120 | 84 | 120x84+1 | 10081 | |||
FC3 | 84 | 10 | 84x10+1 | 841 |
參數總量: 61495
參數內存消耗: 240.214KB
AlexNet
Alexnet的結構圖有些奇怪。但其實是因為要把網絡拆分到兩個GPU上,才畫成了兩層,兩層的結構是一樣的,下面計算的時候的結構相當於合並以后的網絡。
網絡層(操作) | 輸入 | filter | stride | padding | 輸出 | 計算公式 | 參數量 |
---|---|---|---|---|---|---|---|
Input | 227x227x3 | 227x227x3 | 0 | ||||
Conv1 | 227x227x3 | 11x11x96 | 4 | 0 | 55x55x96 | 11x11x3x96+96 | 34944 |
MaxPool1 | 55x55x96 | 3x3 | 2 | 0 | 27x27x96 | 0 | |
Norm1 | 27x27x96 | 27x27x96 | 0 | ||||
Conv2 | 27x27x96 | 5x5x256 | 1 | 2 | 27x27x256 | 5x5x96x256+256 | 614656 |
MaxPool2 | 27x27x256 | 3x3 | 2 | 0 | 13x13x256 | 0 | |
Norml2 | 13x13x256 | 13x13x256 | 0 | ||||
Conv3 | 13x13x256 | 3x3x384 | 1 | 1 | 13x13x384 | 3x3x256x384+384 | 885120 |
Conv4 | 13x13x384 | 3x3x384 | 1 | 1 | 13x13x384 | 3x3x384x384+384 | 1327488 |
Conv5 | 13x13x384 | 3x3x256 | 1 | 1 | 13x13x256 | 3x3x384x256+256 | 884992 |
MaxPool3 | 13x13x256 | 3x3 | 2 | 0 | 6x6x256 | 0 | |
FC6 | 6x6x256 | 4096 | 6x6x256x4096+1 | 37748737 | |||
FC7 | 4096 | 4096 | 4096x4096+1 | 16777217 | |||
FC8 | 4096 | 1000 | 4096x1000+1 | 4096001 |
參數總量: 62369155
參數內存消耗: 237.9195MB
VGG
VGG常見有16層和19層的,這里以16層為例,下面是模型結構圖。
網絡層(操作) | 輸入 | filter | stride | padding | 輸出 | 計算公式 | 參數量 |
---|---|---|---|---|---|---|---|
Input | 224x224x3 | 224x224x3 | 0 | ||||
Conv3-64 | 224x224x3 | 3x3x64 | 1 | 1 | 224x224x64 | 3x3x3x64 + 64 | 1792 |
Conv3-64 | 224x224x64 | 3x3x64 | 1 | 1 | 224x224x64 | 3x3x64x64 + 64 | 36928 |
MaxPool2 | 224x224x64 | 2x2 | 2 | 0 | 112x112x64 | 0 | |
Conv3-128 | 112x112x64 | 3x3x128 | 1 | 1 | 112x112x128 | 3x3x64x128 + 128 | 73856 |
Conv3-128 | 112x112x128 | 3x3x128 | 1 | 1 | 112x112x128 | 3x3x128x128 + 128 | 147584 |
MaxPool2 | 112x112x128 | 2x2 | 2 | 0 | 56x56x128 | 0 | |
Conv3-256 | 56x56x128 | 3x3x256 | 1 | 1 | 56x56x256 | 3x3x128x256 + 256 | 295168 |
Conv3-256 | 56x56x256 | 3x3x256 | 1 | 1 | 56x56x256 | 3x3x256x256 + 256 | 590080 |
Conv3-256 | 56x56x256 | 3x3x256 | 1 | 1 | 56x56x256 | 3x3x256x256 + 256 | 590080 |
MaxPool2 | 56x56x256 | 2x2 | 2 | 0 | 28x28x256 | 0 | |
Conv3-512 | 28x28x256 | 3x3x512 | 1 | 1 | 28x28x512 | 3x3x256x512 + 512 | 1180160 |
Conv3-512 | 28x28x512 | 3x3x512 | 1 | 1 | 28x28x512 | 3x3x512x512 + 512 | 2359808 |
Conv3-512 | 28x28x512 | 3x3x512 | 1 | 1 | 28x28x512 | 3x3x512x512 + 512 | 2359808 |
MaxPool2 | 28x28x512 | 2x2 | 2 | 0 | 14x14x512 | 0 | |
Conv3-512 | 14x14x512 | 3x3x512 | 1 | 1 | 14x14x512 | 3x3x512x512 + 512 | 2359808 |
Conv3-512 | 14x14x512 | 3x3x512 | 1 | 1 | 14x14x512 | 3x3x512x512 + 512 | 2359808 |
Conv3-512 | 14x14x512 | 3x3x512 | 1 | 1 | 14x14x512 | 3x3x512x512 + 512 | 2359808 |
MaxPool2 | 14x14x512 | 2x2 | 2 | 0 | 7x7x512 | 0 | |
FC1 | 7x7x512 | 4096 | 7x7x512x4096 + 1 | 102760449 | |||
FC2 | 4096 | 4096 | 4096*4096 + 1 | 16777217 | |||
FC3 | 4096 | 1000 | 4096*1000 + 1 | 4096001 |
參數總量: 138357544 138348355
參數內存消耗: 527.7570MB
GoogleNet
googlenet 提出了inception的概念,用於增加網絡深度和寬度,提高深度神經網絡性能。下面是googlenet的網絡結構:
inception的結構如下:
可以看出,inception的結構是多個卷積堆疊,組合而成的。
還有,從上面的網絡結構中,可以看到一共有三個輸出的分類層:
這個是為了解決深層網絡訓練的時候梯度消失的問題,所以在中間加入了幾個全連接層輔助訓練。
最后,貼一個論文上給出的模型的結構圖:
在這個圖上,已經給出了參數的數量和使用的內存,不過我還是說一下inception模塊的計算方法和一些注意事項。
- 首先是輸入,輸入的size應該為224x224x3
- 注意第一層的卷積,沒有注明padding,直接算的話,結果是不對的,這里的padding計算方法和tensorflow中卷積方法padding參數設置為’SAME’是一樣的。簡單來說,就是ceil(size/kernel_size),這個對於下面的計算也是一樣的,總之,就是要填適當的0,使得輸出結果和上圖相對應就是了。
3.在上圖中5~10列對應inception module中的各個卷積操作,對應的值是輸出的feature的數量,對於maxpool操作,他的padding為2,stride為1。
4.當一個inception模塊計算完后,它的輸出為各個卷積操作輸出的結果連接起來,也就是如果輸出分別為28x28x64、28x28x128、28x28x32、28x28x32,那么最終輸出就是28x28x(63+128+32+32)。
下面的圖給出了inception module內部計算的輸出結果。
可以看出googlenet的參數量要比vgg少很多,但是效果確更優秀。
Resnet
關於resnet,我就不打算計算參數了,因為實在量很大,而且實際上,resnet的基本結構也比較簡單,計算方法和前面的沒什么差別。這里就簡單貼一下結構圖好了。
可以看出來,如果沒有中間一條條連線,其實就是一個很深的普通的卷積網絡,中間的連線可以保證梯度可以傳遞到低層,防止梯度消失的問題。