NEWSIO+ 教育系列01 自己訓練一個 AI02 從照片還原 3D03 深度學習
主線 C · 模組 C1動手做 // 四個實驗台

卷積到底
在做什麼

為什麼圖片不能直接接全連接層?一個 3×3 的 filter 怎麼「看見」邊緣?

A1 把整張圖拉直成一排數字,每個像素都接到每個神經元。圖一大,權重就多到訓練不動,而且左上角和右下角的像素被當成一樣有關係。卷積換一個做法:拿一塊 3×3 的小模板在圖上滑,每個位置做一次乘加,同一塊模板到處重複用。這個模組讓你親手調這 9 個數字,看它找出邊緣,再算它省了多少參數、一路疊上去能看多遠。

SEC.01 / CONVOLUTION

一個 3×3 的小模板,在圖上滑過去

卷積的動作只有一個:把 3×3 的 kernel(也叫 filter)蓋在圖上某個位置,9 個像素各乘上 kernel 對應的數字,加起來,寫進輸出的那一格。然後往右移一格,再做一次。整張圖做完,得到一張新的圖,叫 feature map。kernel 長什麼樣,feature map 就把什麼樣的圖案「點亮」。電腦視覺很早就用手寫的 kernel 找邊緣,例如 Sobel。下面這張 48×48 的圖是程式畫的:有直線、一條「\」方向的細斜線、斜邊、圓、格紋。

PROBE A / 拖一個 kernel動手做 // 改 9 個數字,游標移到右圖

輸入 48×48 灰階

feature map 灰階

kernel(3×3,可直接改)
選到的輸出格(第幾列, 第幾行)(12, 6)
這一格的值 = 9 個乘積加起來0
0
kernel 九個數的總和0
整張 feature map 的範圍0
選 Sobel x:直線的左右兩側亮起來(一邊正、一邊負),平坦的地方是 0,因為這個 kernel 九個數加起來是 0,「左右一樣亮」就互相抵消。換 Sobel y,換成水平的邊亮。模糊九個數都是 1/9,總和是 1,所以亮度不變,格紋被抹平。按講義 Filter 1:只有右上那條「\」方向的細斜線最亮,因為 kernel 對角線三個 1 剛好對上線上三個亮點;同樣是斜的,「/」方向的斜邊反而是負的。圖的外框也會亮:圖外面補的是 0(zero padding),跟背景不一樣亮,所以被當成邊。圖是程式畫的示意圖,每一格都是真的照公式算出來的。
kernel 就是一個圖案它和圖上某一塊越像(同號的地方對到亮的像素),乘加出來的數字越大。卷積 = 拿一張小模板到處比對。
同一塊模板到處用不管邊在左上還是右下,都用同一組 9 個數字找。這叫 parameter sharing,也是卷積省參數的關鍵。
深度學習不手寫 kernelSobel 是人設計的。CNN 把這 9 個數字當成權重,用 A2 的反向傳播自己學。
細看 · Sobel、講義的 6×6 例子、translation equivariance

找影像特徵 = convolution filter:講義用 Sobel edge filter 說明「一個 filter 可以看成一個 pattern」,接著問:filter 能不能自動學?這就是深度學習的切入點。

講義的 6×6 例子:一張 0 和 1 的 6×6 圖,Filter 1 是對角線為 1、其他為 −1 的 3×3。stride 1 時,左上角第一格算出 3(對角線三個 1 全對上),往右一格是 −1。整張圖做完得到 4×4 的 feature map。Filter 2(中間一直行為 1)再做一次,得到第二張 4×4。每個 filter 各產生一張 feature map,疊在一起就是新圖的 channel。上面的「講義 Filter 1」鈕就是這個 kernel,可以看它只在右上那條「\」方向的細斜線上最亮。

Translation equivariance:先平移再卷積,等於先卷積再平移,Conv(Translate(X)) = Translate(Conv(X))。圖案搬到哪裡,feature map 上的反應就跟著搬到哪裡。講義提醒:卷積只對平移有這個性質,旋轉和縮放要靠別的機制,講義舉的例子是 pooling。

為什麼叫「卷積」:數學上的 convolution 會把 kernel 先翻轉再乘;深度學習框架實際做的是不翻轉的 cross-correlation。kernel 是學出來的,翻不翻沒有差別,所以大家都叫它卷積。

SEC.02 / SIZE & PARAMETERS

輸出多大、要幾個權重:和全連接比一比

講義算過一筆帳:一張 300×300 的彩色圖拉直是 270,000 個數字,只接到 100 個神經元,就要 27 × 106 個權重。卷積只看一小塊(sparse connection),而且同一組權重到處用(shared weights),所以權重數量和圖多大無關,只和 kernel 大小、輸入輸出的 channel 數有關。下面拉滑桿,看輸出尺寸公式和參數量怎麼變。

PROBE B / 尺寸與參數動手做 // 拉 8 個滑桿
輸出尺寸 H′ × W′ × C_out0
寬度公式代入:(W − k′ + 2p) / s + 1,k′ = d(k − 1) + 100
卷積層參數:k·k·C_in·C_out + C_out0
同樣輸入輸出改接全連接層0
全連接 ÷ 卷積0
對照講義:輸入只接 100 個神經元0
講義的例子:
上圖是輸入的其中一列:灰色格子是補的 0(padding),三排色點是前三個輸出各自用到哪幾格。拉 stride,色點之間跳得更遠;拉 dilation,同一排的點中間空出洞,k 不變但涵蓋的範圍變大。按「stride 3 放不下」:(7 − 3)/3 + 1 不是整數,最後一格蓋不滿,框架會直接捨去。注意卷積參數完全不受 H、W 影響,全連接卻隨 H×W 的平方長。預設鈕裡的輸入大小和 kernel(7×7 配 3×3、32×32×3 配 5×5、300×300×3)取自講義,C_out 和其他設定是示意。
只看一小塊辨識需要的圖案(鳥嘴、眼睛)遠比整張圖小,一個神經元不需要看整張圖。這是第一個先驗。
同一個圖案出現在不同位置左上的鳥嘴和中間的鳥嘴,用同一組參數去找就好。這是第二個先驗。
縮小不改變物體把圖的像素抽掉一半,鳥還是鳥。所以可以一路縮小,讓後面的層處理更少的數字。這是第三個先驗。
細看 · 全連接的問題、彩色影像的 kernel、1×1 conv、FC 和 conv 的等價

全連接的兩個問題:一是權重太多,訓練時間太長;300×300×3 接 100 個神經元就是 27 × 106。二是它不利用空間結構:拉直之後,相鄰像素和相距很遠的像素地位一樣,左上角的圖形跟右下角的圖形被當成一樣有關係。物體移動或縮放之後,同一個物體落到不同的權重上,等於要重新學一次。

講義的總結:卷積用三個想法改善學習。Sparse interactions:kernel 比輸入小,參數和計算都少。Parameter sharing:同一個 kernel 用在每個位置;推論時間不會因此變短,但記憶體只要存 K 個 filter。Equivariant representation:輸入平移,輸出跟著平移。另外,卷積可以吃不同大小的輸入,全連接的輸入大小是固定的。

彩色影像:kernel 永遠延伸到輸入的整個深度。32×32×3 的圖配 5×5×3 的 filter,每個位置是一次 5·5·3 = 75 維的內積再加 bias。一個 filter 產生一張 feature map;10 個 filter 就是 10 個 channel。所以參數量是 k·k·C_in·C_out + C_out。

W′ = (W − k + 2p) / s + 1 講義:(32 − 5)/1 + 1 = 28,再一層 (28 − 5)/1 + 1 = 24。常見做法是 stride 1、padding (k − 1)/2,尺寸不變:k = 3 補 1,k = 5 補 2,k = 7 補 3。

1×1 conv:k = 1 時,卷積在空間上什麼都不混,只在 depth 方向運算:每個位置把 C_in 個數字做一次內積,變成 C_out 個。它是在每個像素上各跑一個小的全連接層,常用來改 channel 數。

FC-conv:卷積層是全連接層的特例(把沒連到的權重設成 0、共享的權重綁在一起)。反過來,全連接層也能寫成卷積,講義給兩種寫法:用和輸入 feature map 一樣大的 kernel,或先攤平再用多個 channel 的 1×1 conv。這讓整個網路可以只用卷積寫成,吃任意大小的圖。

SEC.03 / RECEPTIVE FIELD

疊幾層之後,一格看得到多大的範圍

第一層的一格只看得到 3×3。第二層的一格看的是第一層的 3×3,而第一層每格又看 3×3,所以回推到原圖是 5×5。這個「影響輸出值的輸入範圍」叫 receptive field。講義的公式:兩層 3×3 是 (3 − 1) + (3 − 1) + 1 = 5。下面的格子是原圖,橘框是理論範圍,顏色是每個輸入像素有幾條路徑通到中心那一格輸出。

PROBE C / 感受野動手做 // 拉層數,開 dilation
理論 receptive field0
公式 r = 1 + Σ (k′ − 1),k′ = d(k − 1) + 10
一半的影響力集中在中間0
中心像素 vs 角落像素的路徑數0
權重數(每一對 channel)0
一般卷積下,N 層 3×3 的範圍是 2N + 1:N = 3 時剛好 7×7,和一層 7×7 一樣大,權重卻是 27 比 49,中間還多了兩次非線性。但看顏色:中心的路徑數遠多於邊緣,影響力像一團高斯,真正有用的範圍比橘框小,這就是 effective receptive field。換成「逐層加倍」:範圍變成 2N+1 − 1,權重數照樣只多 9。「全部 d = 2」會出現棋盤狀的空洞,有些像素根本沒有路徑可以通到輸出(gridding effect)。顏色深淺取平方根,讓淡的格子也看得到;格子大小會跟著範圍調整,最多畫 65×65,超出的部分不畫。
越深,看得越廣每多一層 3×3,範圍每邊多 1 格。看懂「這是一張臉」需要的範圍,比看懂「這裡有條邊」大得多。
中間比邊緣重要邊緣的像素只有少數幾條路徑通到輸出,中間的像素有很多條。理論範圍是框,實際影響是一團霧。
放大範圍有三招多疊層(線性長大)、pooling 或 stride(乘法長大)、dilated convolution(不縮小解析度也能指數長大)。
細看 · 為什麼在意感受野、effective receptive field、closed-form、dilated convolution

為什麼重要:在語意分割和光流這類「每個像素都要輸出答案」的任務,每個輸出格最好能看到整個相關物體;在物件偵測,太小的感受野認不出大物體,所以常用多尺度的做法。講義引用的觀察:分類準確率和感受野大小呈對數關係,大的感受野是高階辨識的必要條件,但報酬遞減。

Effective receptive field(Luo et al. 2016):感受野裡每個像素的貢獻不一樣。forward 時,中心像素有很多條路徑把資訊送到輸出,外圍只有很少;backward 時,中心像素拿到的梯度也大得多。沒有非線性時,分佈就是一個 2D 高斯;加了 ReLU 會偏離高斯。講義的重點:ERF 長得比理論慢很多;訓練之後 ERF 會變大一些。Pooling 和 dilated convolution 是快速放大的有效方法。

ri−1 = si · ri + (ki − si)  r0 = Σi (ki − 1) · Πj<i sj + 1 從最後一層往輸入回推。kernel 全是 1 時範圍就是 1;stride 全是 1 時就是 Σ(k − 1) + 1;某一層 stride 大於 1,它下面每一層的貢獻都跟著放大。padding 不影響。

Dilated convolution:不是在滑動時跳格,而是在 kernel 的格子之間留空。1D 例子:一般是 w[0]x[0] + w[1]x[1] + w[2]x[2];中間空一格就變成 w[0]x[0] + w[1]x[2] + w[2]x[4]。等效 kernel 大小 k′ = d(k − 1) + 1,3×3 配 d = 2 的範圍等於 5×5,只用 9 個權重。逐層 1、2、4 疊三層,範圍是 3、7、15,指數長大但參數線性長大,而且不像 pooling 那樣把 feature map 縮小、丟掉資訊,所以適合語意分割。缺點:計算量高;全部用同一個 dilation 會有 gridding effect,常見的修法是用沒有公因數的鋸齒狀 dilation,例如 1, 2, 5。

對回原圖:第一層的感受野就是 kernel 大小;更深層取決於前面所有層的 kernel 和 stride。feature map 上一格對應到原圖並不唯一,SPP-net 的做法是對到感受野的中心。

SEC.04 / POOLING

Pooling:每一小塊只留一個數

CNN 的基本骨架是「卷積 → pooling → 卷積 → pooling → 攤平 → 全連接」,可以重複很多次。Pooling 沒有權重:把 feature map 切成 2×2 的小塊,每塊只留一個數。Max pooling 留最大的,average pooling 留平均。結果是一張比較小的新圖,channel 數不變。改下面 4×4 的數字(0 到 9),或按「只有一個亮點」再按「整塊右移一格」,看位置偏一點時輸出變不變。

PROBE D / Max vs average pooling動手做 // 改 16 個數字,按右移
輸入 4×4(每個顏色框是一塊 2×2)
Global average pooling:整張 4×4 變一個數0
Probe A 現在那張 48×48 feature map 做 GAP0
0
最後一層 feature map 接 10 類:攤平 + FC vs GAP + FC0
按「只有一個亮點」再按「右移一格」:亮點還在同一塊 2×2 裡時,max pooling 的輸出一格都沒變,average 也一樣不變,但只要跨到隔壁那一塊就會跟著搬。Max pooling 留的是「這一塊裡有沒有出現某個圖案」,不在乎它在塊裡的哪一格,這是一點點平移不變性。GAP 把整張 feature map 平均成一個數,常放在最後一層取代攤平,最下面那列就是參數差多少。這些 4×4 的數字和 feature map 大小都是示意。
Pooling 不只是縮小它讓後面的層看得更廣(感受野成倍長大),也讓小幅平移不改變答案。縮小省計算只是附帶的好處。
Max 留特徵,average 留背景max 記得「有沒有出現」,average 記得「整體多亮」。講義說實務上 max pooling 表現比較好。
越深越抽象第一層學到的是方向邊緣和顏色對比,後面的層把它們組合成紋理、零件,最後是物體。
細看 · max vs average、all-convolutional network、GAP、CNN 學到什麼

Max vs average:講義備註整理了一個解釋。特徵提取的誤差主要有兩種:鄰域太小造成的估計變異變大,和卷積參數誤差造成的平均值偏移。Average pooling 減少第一種,保留比較多背景資訊;max pooling 減少第二種,保留比較多紋理資訊。講義的結論:max pooling 表現比較好,比較常用。Pooling 的目的也不只是減少特徵和參數,而是保持某種不變性(平移、小幅縮放)。

All convolutional network(Striving for Simplicity,講義用 CIFAR-10 的結果說明):把 pooling 換成 stride 2 的卷積,讓「縮小」這件事也變成可以學的。結果不比有 pooling 的網路差,整個網路只剩卷積。

Global average pooling:每個 channel 的整張 feature map 平均成一個數,C 個 channel 就變成長度 C 的向量,直接接分類器。講義的重點是它省下大部分參數:攤平後接全連接的權重數會乘上 feature map 的面積,GAP 不會。它也讓網路吃任意大小的圖。

CNN 學到什麼:線性分類器是「每類一張模板」(A1),MLP 是「一堆整張圖的模板」,而卷積的第一層是局部的小模板,常常學成方向不同的邊緣和相反的顏色對比;講義的例子是 AlexNet 第一層 64 個 3×11×11 的 filter。越往後越抽象,是一層一層組合起來的 hierarchical feature representation。至於「越深越好嗎」,那是 C2 的題目。

SEC.05 / MISCONCEPTIONS

常見誤解

這個模組最常被講錯的幾句話。

疊得越深,理論感受野越大,模型就真的看得越廣。理論範圍是框,實際影響集中在中間一團(effective receptive field),而且長得比理論慢很多。要快速放大,pooling 和 dilated convolution 比單純疊層有效。
Pooling 只是為了把圖縮小、省計算。它同時讓感受野成倍長大,並讓小幅平移不改變結果。省計算是附帶的。也可以用 stride 2 的卷積取代它,讓縮小變成可學的。
卷積就是把圖模糊。模糊只是其中一種 kernel(九個數都是正的、加起來是 1)。九個數加起來是 0 的 kernel 會找邊緣,中間大四周負的會銳化。kernel 長什麼樣,就找什麼圖案。
filter 要靠人設計,像 Sobel 那樣。那是傳統電腦視覺。CNN 把 kernel 的數字當成權重,用反向傳播從資料學出來;學出來的第一層常常長得像方向邊緣,但沒有人告訴它要這樣。