網(wǎng)絡CNN原理解析:從卷積池化到LeNet與PyTorch實戰(zhàn))
1. 從一張找貓的圖說起CNN到底在干什么先說個我自己的經(jīng)歷。幾年前剛接觸深度學習的時候我以為卷積神經(jīng)網(wǎng)絡是個特別高深的東西直到有位前輩拿了一張貓的照片問我你一眼就能看出這是貓靠的是什么我說靠眼睛。他說其實你的眼睛也沒那么聰明它只是先看到邊緣、再看到輪廓、最后才把這些拼成貓這個整體概念。CNN干的就是這件事只不過它把這三步變成了可訓練的數(shù)字流程。這句話基本就是卷積神經(jīng)網(wǎng)絡Convolutional Neural Network簡稱 CNN的核心思想。如果你搜過卷積神經(jīng)網(wǎng)絡結構圖大概率會看到一堆方塊層層堆疊——輸入層、卷積層、池化層、全連接層、輸出層。圖看得懂但不知道每層為什么要這么設計。這篇文章我就按一個做過圖像項目的人的視角把這些方塊一個個拆開講盡量不用那些讓人頭大的數(shù)學符號堆砌同時把每個設計背后的為什么講透。CNN 是什么一句話它是專門為處理具有網(wǎng)格狀結構數(shù)據(jù)最典型的就是圖像而設計的一類神經(jīng)網(wǎng)絡。它能做什么把一張原始像素圖逐步抽象成邊緣→紋理→局部形狀→整體物體的特征層級最終輸出這是貓或者這是狗的判斷。它適合誰看如果你滿足下面任意一條這篇內容就是寫給你的剛學完多層感知機前饋神經(jīng)網(wǎng)絡好奇圖像任務為什么不能直接用它看過李宏毅老師的 CNN 課程理論聽懂了但一到寫代碼就發(fā)懵已經(jīng)會調torchvision的模型但說不清每一層到底在算什么需要給別人講 CNN想找一個通俗但不失真的解釋口徑。我會從最基礎的為什么圖像不能直接喂給前饋網(wǎng)絡講起然后是卷積這個操作到底在算什么、池化層是干嗎的、一個經(jīng)典網(wǎng)絡長什么樣、怎么用幾行代碼把它跑起來最后聊幾個我在實際項目中踩過的坑。全程配場景別怕沒有微積分。2. 圖像任務為什么不能用前饋神經(jīng)網(wǎng)絡直接硬上2.1 一張圖展平之后空間信息就沒了先說前饋神經(jīng)網(wǎng)絡Fully Connected Network / MLP處理圖像的常規(guī)做法把圖像拉平成一維向量。一張 224×224 的彩色圖有 224×224×3 150528 個像素。第一層如果是 1000 個神經(jīng)元那么光是這一個全連接層就有 150528×1000 ≈ 1.5 億個參數(shù)。這還只是第一層。參數(shù)多不是最要命的要命的是展平這個動作本身就把圖像的空間結構毀掉了。在圖像里相鄰像素是強相關的——左上角的像素和它右邊的像素共同構成了一條邊橫著排的像素和豎著排的像素含義完全不同??梢坏├匠梢痪S數(shù)組原本上下左右的位置關系就變成了一串線性序號網(wǎng)絡得從零開始反復學習第 3 個像素和第 4 個像素挨著這種它本可以直接拿到的信息。打個比方。這就像你要認識一個人本來可以看他的正臉完整的二維構圖結果你把他的五官打印在一條長長的紙帶上還剪碎了打亂順序。網(wǎng)絡要花極大的代價才能把這些碎片重新拼回一張臉。2.2 權重不共享帶來的兩個致命后果前饋網(wǎng)絡處理圖像還有第二個問題同一類特征在不同位置出現(xiàn)時網(wǎng)絡要分別學一遍。假設我們要檢測豎直邊緣。在圖像左上角出現(xiàn)的豎直邊緣和右下角出現(xiàn)的豎直邊緣本質上是一模一樣的模式。但全連接網(wǎng)絡里左上角的像素對應一組權重右下角的像素對應另一組完全不同的權重網(wǎng)絡得用兩套獨立的參數(shù)去學同一個東西。這就帶來了兩個后果第一個后果是參數(shù)量爆炸也就是上面算的那 1.5 億。模型大到難以訓練過擬合幾乎是必然的。第二個后果更隱蔽——位置不變性極差。因為網(wǎng)絡是在特定的位置上學到的特征一旦物體換了個位置、挪動了幾十像素模型的判斷就可能崩掉。我早期做過一個數(shù)字識別的小項目用全連接網(wǎng)絡訓練測試集里數(shù)字略微偏移一點準確率就掉一大截當時百思不得其解后來才明白根子在這里。2.3 局部感受野與權值共享CNN 的兩把鑰匙CNN 用兩個核心設計同時解決了上面所有問題這也是它的精髓所在理解了這兩點CNN 就理解了一大半。第一把鑰匙是局部感受野Local Receptive Field。單個神經(jīng)元不再一次性看整張圖而只看一個很小的局部區(qū)域比如 3×3 或 5×5 的窗口。這符合圖像的天然特性關鍵的視覺模式邊緣、角點、紋理都是局部的沒必要讓一個神經(jīng)元去關心整張圖的所有像素。這直接把連接數(shù)從全圖降到局部窗口。第二把鑰匙是權值共享Weight Sharing。同一個卷積核一組權重在整張圖上滑動走到哪里都用同一組參數(shù)。檢測豎直邊緣的那個核在左上角用它在右下角還是用它。這就把參數(shù)量從每個位置一套參數(shù)降到了一個核一套參數(shù)。兩個設計疊加起來的效果相當驚人。前面那個 150528×1000 的全連接層有 1.5 億參數(shù)而一個 3×3 卷積核處理同樣輸入的參數(shù)是 3×3×3 27 個輸入三通道。即便是 64 個這樣的卷積核總共也才 27×64 1728 個參數(shù)。差了將近十萬倍。這就是為什么 CNN 能在圖像任務上碾壓前饋網(wǎng)絡——不是它更聰明而是它把圖像先驗知識局部性、平移不變性編碼進了網(wǎng)絡結構里。我把這個對比整理成表更直觀一些對比維度前饋神經(jīng)網(wǎng)絡卷積神經(jīng)網(wǎng)絡輸入處理方式展平為一維向量保持二維/三維結構單個神經(jīng)元感受范圍全圖所有像素局部窗口如 3×3參數(shù)是否共享不共享每個位置一套共享一個核掃全圖典型參數(shù)量級億級千級到萬級平移魯棒性弱位置一變就失效強同一特征任何位置都能識別對圖像先驗的利用幾乎不用直接編碼局部性與平移不變性提示如果你面試被問到圖像處理為啥用 CNN 不用前饋神經(jīng)網(wǎng)絡把參數(shù)爆炸和丟失空間結構兩點答出來是及格補上權值共享帶來的平移不變性才算答到點子上。3. 卷積這個操作到底在算什么3.1 卷積核是一塊特征探測器很多人被卷積這個詞唬住其實它做的事特別樸素拿一個小窗口卷積核在大圖上從左到右、從上到下滑動每滑到一個位置就把窗口覆蓋的那塊區(qū)域和卷積核做逐元素相乘再求和得到一個數(shù)?;暾麖垐D就得到一張新的二維圖叫特征圖Feature Map。舉個例子最清楚。假設圖像某個 3×3 局部是1 1 1 0 0 0 1 1 1卷積核是1 0 -1 1 0 -1 1 0 -1逐元素相乘求和(1×1 1×0 1×(-1)) (0×1 0×0 0×(-1)) (1×1 1×0 1×(-1)) 0 0 0 0。換一塊區(qū)域試試1 0 1 1 0 1 1 0 1同樣計算(1×1 0×0 1×(-1)) (1×1 0×0 1×(-1)) (1×1 0×0 1×(-1)) 3 3 3 9??闯鲆?guī)律了嗎這個卷積核對左邊亮右邊暗的豎直邊界響應很強輸出 9對左右對稱的區(qū)域響應為 0。它就是一臺豎直邊緣探測器。如果我把這個核轉置一下就變成了水平邊緣探測器。這就是 CNN 最底層的工作方式——網(wǎng)絡不需要人去設計這些核而是通過訓練自動學出來學到的核往往就是各種方向的邊緣、紋理、顏色斑塊檢測器。3.2 步長、填充、通道三個必須搞懂的參數(shù)實際寫代碼時卷積層總有三個參數(shù)繞不開步長stride、填充padding、輸出通道數(shù)。搞不清它們你連輸出尺寸都算不對。步長stride是卷積核每次滑動的格數(shù)。stride1 時逐像素滑動輸出圖尺寸和輸入差不多stride2 時每隔一個像素滑動輸出圖長寬各縮小一半。步長越大輸出越小計算越快但可能漏掉細節(jié)。實際項目里 1 和 2 是最常用的3 以上很少見。填充padding是在圖像邊界外補一圈像素通常是補 0。為什么需要它因為卷積核滑到邊緣時覆蓋范圍會超出圖像如果不填充輸出圖會比輸入小一圈多層疊加后圖像會越來越小。更關鍵的是邊緣像素參與卷積的次數(shù)遠少于中心像素邊緣信息容易被稀釋。padding1對 3×3 核能讓輸出尺寸和輸入保持一致這個配置被叫做 same padding是最常見的默認選項。輸出尺寸的計算公式必須記住我見過太多人手寫網(wǎng)絡時在這一步出錯輸出邊長 (輸入邊長 2 × padding - 卷積核邊長) / 步長 1拿個例子驗證輸入 224卷積核 3padding 1stride 1 —— (224 2 - 3)/1 1 224。確實保持不變。如果換成 stride2 —— (224 2 - 3)/2 1 112.5除不盡實際會向下取整得到 112。輸出通道數(shù)則取決于你用幾個卷積核。用 64 個核就得到 64 張?zhí)卣鲌D堆疊成的 64 通道輸出。每個通道代表一種被檢測出來的特征。這解釋了一個常見的疑惑為什么特征圖越往后通道越多因為淺層在檢測簡單特征邊緣、顏色種類有限深層需要組合出復雜特征眼睛、輪子、文字筆畫需要的特征種類自然就多了。3.3 激活函數(shù)沒有它深層卷積等于白搭每次卷積之后都會跟一個激活函數(shù)最常用的是 ReLUmax(0, x)。很多人把它當慣例照抄不知道為什么。關鍵在于卷積本身是線性運算。兩個線性操作堆在一起數(shù)學上仍然等價于一個線性操作。這意味著如果卷積層之間沒有非線性激活無論你疊多少層整個網(wǎng)絡的能力和只有一層是一樣的。加了 ReLU 這種非線性函數(shù)網(wǎng)絡才真正獲得了層層抽象的能力才能表達復雜的函數(shù)關系。ReLU 相比早期的 Sigmoid還有個實際好處是梯度不容易消失。Sigmoid 在輸入很大或很小時導數(shù)趨近于 0梯度反向傳播幾層之后就衰減到幾乎為零深層網(wǎng)絡根本訓不動。ReLU 在正區(qū)間導數(shù)恒為 1梯度能順暢傳到底層。這也是為什么 2012 年之后幾乎所有主流 CNN 都用 ReLU 家族?,F(xiàn)在一些新網(wǎng)絡會用 GELU、SiLU思路類似效果略好但差異不大新手階段用 ReLU 完全夠。4. 池化層和整體結構CNN 是怎么一層層抽象的4.1 池化真正解決的問題不是降維池化層Pooling最常見的說法是降維、減少計算量。這話對但只說了一半而且不是最重要的那一半。池化更本質的作用是帶來一定程度的平移不變性和空間壓縮。以最大池化Max Pooling為例2×2 窗口、步長 2就是在每個 2×2 的小塊里取最大值。假設某個邊緣在圖像里稍微移動了一兩個像素只要它還落在同一個 2×2 窗口內池化后的結果就完全一樣。也就是說細微的位置偏移被池化抹平了。我舉個生活化的類比。你在一張遠景照片里找一個人如果照片分辨率極高人稍微挪一步結果就變但如果先把照片按 2×2 網(wǎng)格縮小一半再找人挪一兩像素對縮小后的圖幾乎沒有影響。池化就是在做這件事——用分辨率的損失換取對位置的寬容度。順帶說池化也顯著降低了后續(xù)層的計算量和參數(shù)量。一個 2×2 池化讓特征圖長寬各減半面積變成原來的四分之一后面所有卷積的開銷都跟著降。4.2 卷積堆疊的順序為什么是 Conv-ReLU-Pool看經(jīng)典的 CNN 結構圖你會發(fā)現(xiàn)幾乎都是卷積 → 激活 → 池化這樣一組一組地重復。這個順序不是隨便定的每一步都有它的邏輯先卷積是為了提取局部特征緊接著 ReLU是為了引入非線性讓特征的組合能力增強最后池化是在當前抽象層級上做一次空間濃縮把已經(jīng)提取到的特征固化下來同時縮小尺寸為下一輪更抽象的提取做準備。重復這個組合網(wǎng)絡的特征抽象層級就逐級上升。第一組學到的是邊緣和顏色第二組把邊緣組合成紋理和簡單形狀第三組組合成物體的部件比如眼睛、耳朵、輪子更深的組則組合成完整的物體概念。這跟人類視覺皮層的信息處理路徑高度相似也是 CNN 被叫做仿生的原因之一。這里補一個容易被忽略的點池化的位置不是絕對的?,F(xiàn)在很多新式網(wǎng)絡比如各種 ResNet 變體會用 stride2 的卷積來代替池化效果往往還更好因為池化是固定的取最大值操作沒有可學習參數(shù)而帶步長的卷積是可學習的下采樣。新手階段用經(jīng)典池化沒問題但要記住它不是唯一選擇。4.3 全連接層與 Softmax從特征到分類結果經(jīng)過若干組卷積池化我們得到一堆特征圖。最后一步要把它變成這是幾號類別的結果靠的是全連接層和 Softmax。全連接層的作用是把二維的特征圖拉平然后用一組權重把所有特征綜合起來做加權判斷。如果說卷積層負責看清全連接層就負責下結論。它把分散在各個通道、各個位置的特征整合成一個全局判斷。Softmax 則把全連接層輸出的原始分數(shù)logits轉換成概率分布——所有類別的概率加起來等于 1最大的那個概率對應的類別就是預測結果。它的公式是exp(xi) / sum(exp(xj))本質是把任意實數(shù)映射到 (0,1) 區(qū)間并且歸一化。實際寫代碼時這一步通常和損失函數(shù)合并用CrossEntropyLoss一句搞定不需要單獨寫。順帶說一個坑全連接層是 CNN 中參數(shù)最密集的部分。以經(jīng)典結構為例卷積部分可能只占幾十萬參數(shù)而最后的全連接層能占到上千萬。這也是為什么后來的網(wǎng)絡設計越來越傾向于用全局平均池化Global Average Pooling替代全連接層——直接在每個通道上求平均值參數(shù)量為零效果還不差。我在一個移動端項目里就吃過這個虧全連接層讓模型體積大到部署不進去換成全局平均池化后瞬間輕松。5. 從 LeNet-5 到代碼落地一個能跑起來的完整流程5.1 LeNet-5理解 CNN 的最佳標本要理解完整的 CNN 結構LeNet-5 是最好的起點。它是 1998 年 Yann LeCun 提出的手寫數(shù)字識別網(wǎng)絡結構極簡但五臟俱全一共 7 層層類型配置輸出尺寸輸入輸入層單通道灰度圖32×32×1C1卷積層6 個 5×5 核stride 128×28×6S2平均池化2×2 窗口stride 214×14×6C3卷積層16 個 5×5 核10×10×16S4平均池化2×2 窗口stride 25×5×16C5卷積層120 個 5×5 核1×1×120F6全連接層84 個神經(jīng)元84輸出全連接 Softmax10 個類別10注意 C5 那層輸入已經(jīng)是 5×5卷積核也是 5×5所以輸出正好是 1×1相當于用一個卷積實現(xiàn)了全連接。這種卷積代替全連接的技巧至今仍在用。LeNet-5 用今天的眼光看很原始——平均池化早就被最大池化取代也沒有 BatchNorm、Dropout 這些現(xiàn)代組件。但它的層級邏輯和現(xiàn)代 CNN 完全一致看懂它再看 ResNet、VGG 只是層數(shù)多了、技巧多了本質沒變。5.2 用 PyTorch 復現(xiàn)一個 LeNet 風格網(wǎng)絡理論講完來點能跑的。下面這段代碼是我常用的 LeNet 改進版把平均池化換成最大池化加了 ReLU在 MNIST 上跑幾分鐘就能到 99% 左右準確率import torch import torch.nn as nn import torch.nn.functional as F class SimpleCNN(nn.Module): def __init__(self, num_classes10): super().__init__() # 第一組卷積 激活 池化 self.conv1 nn.Conv2d(in_channels1, out_channels6, kernel_size5, stride1, padding2) self.pool nn.MaxPool2d(kernel_size2, stride2) # 第二組 self.conv2 nn.Conv2d(6, 16, kernel_size5, stride1, padding0) # 全連接 self.fc1 nn.Linear(16 * 5 * 5, 120) self.fc2 nn.Linear(120, 84) self.fc3 nn.Linear(84, num_classes) def forward(self, x): x self.pool(F.relu(self.conv1(x))) # 32x32 - 16x16 x self.pool(F.relu(self.conv2(x))) # 16x16 - 5x5 x torch.flatten(x, 1) # 展平 x F.relu(self.fc1(x)) x F.relu(self.fc2(x)) return self.fc3(x)跑起來非常簡單model SimpleCNN() criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3) for epoch in range(5): for images, labels in train_loader: optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step()第一次寫這段代碼時最容易錯的地方是self.fc1 nn.Linear(16 * 5 * 5, 120)里的16 * 5 * 5。這個數(shù)字必須和卷積部分輸出的特征圖大小嚴絲合縫對上寫錯一個數(shù)就會在 forward 時報維度不匹配的錯。后面我會專門講怎么用打印尺寸的方式定位這類問題。5.3 現(xiàn)代架構的三個關鍵演進LeNet 之后的網(wǎng)絡演進理解了這三步基本就夠用了第一步是加深——從 AlexNet 到 VGG。AlexNet2012把網(wǎng)絡加深到 8 層引入 ReLU 和 Dropout一舉拿下 ImageNet 冠軍。VGG2014把深度加到 16-19 層核心貢獻是證明了堆疊小卷積核3×3比用大卷積核5×5、7×7更好——兩個 3×3 卷積的感受野等于一個 5×5但參數(shù)更少、非線性更強。這個結論至今仍是網(wǎng)絡設計的黃金準則。第二步是殘差連接——ResNet 的突破。網(wǎng)絡深到一定程度后準確率不升反降這不是過擬合而是梯度傳不下去導致的退化問題。ResNet2015引入跳躍連接skip connection讓梯度可以繞過卷積層直接向后傳一舉把網(wǎng)絡深度推到上百層甚至上千層。這個設計的思路其實非常樸素讓網(wǎng)絡學殘差輸入和輸出之間的差值比學完整映射更容易最壞情況下殘差為零、網(wǎng)絡退化成恒等映射至少不會變差。第三步是結構自動化與注意力——NAS、EfficientNet 與 Transformer 思路的引入。早期靠人手工調網(wǎng)絡結構后來出現(xiàn)了神經(jīng)架構搜索NAS讓機器自己搜EfficientNet 提出了用統(tǒng)一系數(shù)同時縮放深度、寬度和分辨率的復合縮放方法再往后Vision Transformer 等把自注意力機制引入圖像領域挑戰(zhàn)了卷積的統(tǒng)治地位。但要注意在數(shù)據(jù)量不極端大的場景下精心調優(yōu)的 CNN 仍然非常能打不是新架構一出現(xiàn)就要立刻換。6. 我踩過的那些坑尺寸對不上、過擬合與學習率6.1 維度不匹配最常見的報錯最好用的排查法寫 CNN 遇到的第一大報錯就是維度不匹配比如RuntimeError: mat1 and mat2 shapes cannot be multiplied。問題的根源永遠只有一個全連接層輸入維度算錯了。我的固定排查法是這樣在 forward 函數(shù)里加一行打印看在展平之前特征圖到底是什么尺寸。def forward(self, x): x self.pool(F.relu(self.conv1(x))) x self.pool(F.relu(self.conv2(x))) print(x.shape) # 調試用看清尺寸再決定fc1的輸入 x torch.flatten(x, 1) ...跑一遍控制臺會告訴你真實尺寸比如[64, 16, 5, 5]那正確答案就是16*5*5。不要靠腦子算人腦算卷積尺寸出錯率極高直接打印最快。這個習慣我從第一次踩坑之后就一直保持后來接手的每一個 CNN 項目開頭都會先跑一次尺寸檢查。還有一個高頻錯誤是輸入圖像的通道數(shù)對不上。如果你用單通道灰度的卷積核去接三通道彩色輸入或者反過來都會報錯。判斷方法是看數(shù)據(jù)集加載后的張量形狀——[batch, channels, height, width]第二個維度就是通道數(shù)千萬別跟 batch 搞混。6.2 過擬合訓練集 99%測試集 60%這是我做花分類項目時遇到的經(jīng)典情況訓練準確率一路飆到 99% 以上驗證集死死卡在 60% 上下。典型的過擬合。當時試了幾種手段按效果排序說說數(shù)據(jù)增強是性價比最高的。對圖像做隨機翻轉、隨機裁剪、輕微旋轉、顏色抖動本質上是憑空造出了大量新樣本。我用隨機水平翻轉 隨機裁剪這一招驗證準確率直接從 60% 提到了 78%代價只是幾行代碼。要注意增強幅度別太夸張——你把貓的圖片翻轉還是貓但你要是把它旋轉 180 度可能就變成了倒立的貓模型反而學亂。Dropout 也很有效。在全連接層之間加nn.Dropout(0.5)訓練時隨機關掉一半神經(jīng)元逼著網(wǎng)絡不要依賴某幾個特定神經(jīng)元泛化能力會明顯提升。但注意 Dropout 只應該在訓練時開啟評估和推理時應關閉PyTorch 里用model.eval()自動處理這一點很多人不知道推理時不切換模式導致結果不穩(wěn)定。權重衰減Weight Decay是另一種正則化思路通過在損失里加上權重的平方和抑制權重變得過大。配合 Adam 的weight_decay1e-4是個不錯的起點。最后實在不行就減模型容量。如果數(shù)據(jù)量本來就小堆一個很深的網(wǎng)絡必然會過擬合不如老老實實回到淺層網(wǎng)絡。6.3 學習率訓練不收斂的最大嫌疑犯學習率learning rate設錯會表現(xiàn)出兩種截然相反的癥狀認清它們能省下大量調參時間。癥狀一損失值震蕩甚至增大NaN。說明學習率太大每步邁得太狠直接跨過了最低點。我的做法是從大往下試每次除以 10先試 0.1不行試 0.01再不行試 0.001。絕大多數(shù)任務在 1e-3 附近能跑通。癥狀二損失下降極慢幾輪下來幾乎不動。說明學習率太小每步挪一點點半天走不到最低點。這種時候通??梢酝险{ 3 到 10 倍。更省心的做法是用學習率調度器比如CosineAnnealingLR或StepLR訓練初期用大學習率快速下降后期自動調小做精細收斂。我現(xiàn)在基本都會配一個調度器手動調學習率的次數(shù)少了很多。注意換了優(yōu)化器要重新調學習率。同一套學習率在 SGD 上合適喂給 Adam 可能就太大。Adam 的典型起點是 1e-3SGD 是 1e-2這是兩者更新方式差異決定的不能直接照搬。7. 卷積的幾個進階玩法與實際應用場景7.1 3D 卷積當數(shù)據(jù)多了一個維度前面講的都是 2D 卷積處理的是單張圖像長、寬兩個空間維度。但有些數(shù)據(jù)的自然結構本身就是三維的比如視頻多了一維時間、醫(yī)學 CT 掃描多了一維深度、點云體素。這時候就要用3D 卷積。3D 卷積核的形狀是(深度, 高, 寬)比如 3×3×3它在三個維度上同時滑動。相比把視頻拆成一張張獨立畫面用 2D 卷積處理3D 卷積能捕捉跨幀的時序信息——比如一個動作的姿態(tài)變化單幀看不出是揮手還是靜止但連續(xù)幾幀一起看就能判斷。代價是計算量暴增參數(shù)量和顯存占用通常是同等的 2D 卷積的數(shù)倍。我在做一個動作識別的小實驗時對比過同一份視頻數(shù)據(jù)2D CNN 只能靠把多幀堆疊成通道這種取巧方式間接利用時序而 3D CNN 直接建模時空準確率高了幾個點但訓練時間翻倍。所以選不選 3D CNN得看你的任務里時間維度重不重要以及你的算力扛不扛得住。7.2 CNN 不只用來做圖像分類很多人對 CNN 的印象停留在圖像分類其實它的應用范圍要廣得多凡是數(shù)據(jù)能表示成網(wǎng)格狀的都有 CNN 的用武之地目標檢測不僅要判斷圖里有什么還要框出在哪里。代表算法如 YOLO 系列、Faster R-CNN核心都是 CNN 提取特征后接檢測頭輸出邊界框和類別。語義分割對圖像中每個像素分類把不同物體用不同顏色區(qū)分開。醫(yī)療影像里勾畫腫瘤邊界、自動駕駛里區(qū)分路面和行人都用這類方法。人臉識別把一張人臉映射成一個特征向量再比對兩張臉的向量相似度判斷是不是同一個人。非圖像領域一維 CNN1D Conv在文本分類、語音識別、心電圖分析里也常用因為文本序列、音頻波形、心電信號本質上也是一種一維網(wǎng)格。我印象比較深的是一個用 CNN 做設備故障診斷的項目。把振動傳感器采集的時序信號轉成頻譜圖然后當成圖像喂給 CNN 做分類判斷設備處于正常、異常還是嚴重故障狀態(tài)。整套思路和圖像分類幾乎一樣只是輸入換了個來源。這種把非圖像問題轉換成圖像問題的思路是 CNN 落地中非常實用的一招。7.3 遷移學習小數(shù)據(jù)集下的現(xiàn)實解法實際項目里你手頭往往只有幾百上千張圖從零訓練一個 CNN 必然過擬合。這時候正確姿勢是遷移學習拿一個在大數(shù)據(jù)集上預訓練好的模型比如在 ImageNet 上訓過的 ResNet把它的卷積層權重直接搬過來只替換和重訓最后的分類層。為什么這樣做有效因為預訓練模型的淺層學到的是通用的邊緣、紋理、顏色特征這些特征對所有圖像任務都適用深層學到的是更抽象、更任務相關的特征才需要針對你的數(shù)據(jù)微調。這就是 CNN 特征層級通用性帶來的實際紅利。兩種常見做法做法操作適用場景特征提取凍結全部卷積層只訓最后的分類層數(shù)據(jù)量很小幾百張、任務和原任務接近微調解凍部分或全部卷積層用小學習率一起訓數(shù)據(jù)量中等、任務和原任務差異較大微調時有個關鍵細節(jié)學習率要調小通常設成從頭訓練時的 1/10 甚至 1/100。因為預訓練權重已經(jīng)是很優(yōu)的起點學習率太大反而會把這些寶貴的權重破壞掉。我第一次做遷移學習時沒注意這點直接沿用大學習率結果驗證準確率先掉一大截再慢慢爬回來白折騰了幾個小時。8. 給不同階段讀者的一點個人建議關于 CNN 的學習路徑我按自己的經(jīng)驗給幾條實在的建議不搞虛的。如果你是初學者先把 LeNet 手寫一遍別一上來就啃 ResNet。很多人卡在 CNN 門口是因為一開始就去看上百層的網(wǎng)絡結構圖被各種跳躍連接、分組卷積搞暈。LeNet 只有七層把它的每一層尺寸變化手動算一遍你就會對 CNN 有實感后面再看深層網(wǎng)絡只是量的積累。李宏毅老師的 CNN 課程講到感受野和卷積核的時候建議對照一張真實的圖像做一遍手動卷積拿紙筆算幾個位置比看十遍視頻印象都深。如果你已經(jīng)能調通模型但效果一般優(yōu)先在數(shù)據(jù)和增強上花時間而不是急著換架構。我這些年最大的體會是CNN 項目里數(shù)據(jù)質量 合理增強帶來的提升往往超過換個新架構。見過太多人花幾天調網(wǎng)絡結構準確率漲 1%而認真做一遍數(shù)據(jù)清洗和增強漲 10% 都不稀奇。如果你在做落地的工程記住推理效率和精度是要權衡的。課堂作業(yè)只看準確率但真部署到手機或嵌入式設備上模型大小、推理延遲、內存占用都是硬指標。全局平均池化替代全連接、用深度可分離卷積MobileNet 的核心替代普通卷積這些技巧能在精度損失有限的前提下大幅瘦身值得提前了解。還有一個我反復強調的習慣永遠先用小數(shù)據(jù)、少輪次把整條鏈路跑通再上全量數(shù)據(jù)和深層網(wǎng)絡。我見過太多人一上來就設 100 個 epoch、上最深的網(wǎng)絡結果跑了兩小時才發(fā)現(xiàn)數(shù)據(jù)路徑寫錯了。先用幾十張圖、2 個 epoch 驗證數(shù)據(jù)加載對、前向能過、損失能降、保存能存這條鏈路確認無誤后再放大規(guī)模能省下大量等待時間。這個習慣看起來笨但在我做過的每一個項目里都實實在在救過場。CNN 這套東西說到底就是把局部看、滑著看、層層抽象這三個直覺變成了可訓練的數(shù)學結構。理解了這三點剩下的都是工程細節(jié)和調參經(jīng)驗遇到新架構也不用怕拆開來還是這幾樣東西的組合。