:從作業(yè)到調(diào)參的完整指南)
1. 從一次作業(yè)說起為什么CNN成了圖像分類的必修課如果你正在跟著某位老師的機器學(xué)習(xí)課程做作業(yè)做到第三次作業(yè)發(fā)現(xiàn)題目是“用卷積神經(jīng)網(wǎng)絡(luò)做圖像分類”那你大概率已經(jīng)意識到一件事前面兩次作業(yè)還在讓你手推線性回歸和邏輯回歸的梯度這次突然就要你搭一個能識別圖片的模型了。這個跨度其實挺大的但恰恰是這種跨度把很多人從“調(diào)包俠”逼成了真正理解模型結(jié)構(gòu)的人。這份作業(yè)的核心任務(wù)用一句話概括就是給定一批帶標(biāo)簽的小尺寸彩色圖片訓(xùn)練一個卷積神經(jīng)網(wǎng)絡(luò)讓它學(xué)會分辨這些圖片屬于哪一類。聽起來簡單但里面藏著卷積核怎么設(shè)計、池化層要不要加、數(shù)據(jù)增強怎么做、訓(xùn)練不收斂怎么辦這一連串問題。我?guī)н^不少剛?cè)腴T的朋友做這個作業(yè)發(fā)現(xiàn)大家卡住的地方高度一致——不是不會寫代碼而是不知道每一層為什么這么設(shè)計參數(shù)為什么這么調(diào)。這篇文章就是把我自己踩過的坑、幫別人排查過的問題以及那些文檔里不會寫的經(jīng)驗完整地梳理一遍。不管你是正在做這份作業(yè)的學(xué)生還是想系統(tǒng)理解CNN實戰(zhàn)的轉(zhuǎn)行者都能從里面找到可以直接抄作業(yè)的東西。我會從整體設(shè)計思路講起然后拆解每一層的實現(xiàn)細節(jié)再給出完整的訓(xùn)練流程和參數(shù)選擇依據(jù)最后把常見報錯和排查方法整理成速查表。全程用大白話配合生活化的類比保證你看完能自己動手跑通一個像樣的模型。2. 整體設(shè)計思路為什么是CNN而不是全連接網(wǎng)絡(luò)2.1 圖像任務(wù)的特殊性決定了網(wǎng)絡(luò)結(jié)構(gòu)先想一個問題為什么圖像分類不用全連接網(wǎng)絡(luò)假設(shè)輸入是一張32x32的彩色圖片展開成一維向量就是32×32×33072個像素值。如果第一層隱藏層有1000個神經(jīng)元光這一層的參數(shù)量就是3072×1000≈300萬。這還只是一層要是堆個五六層參數(shù)量直接爆炸。更關(guān)鍵的是全連接網(wǎng)絡(luò)把每個像素都當(dāng)成獨立特征完全忽略了像素之間的空間關(guān)系——一張貓的圖片你把它的像素打亂順序全連接網(wǎng)絡(luò)照樣能訓(xùn)練但人眼已經(jīng)看不出是貓了。這說明全連接網(wǎng)絡(luò)沒有利用圖像最重要的特性局部相關(guān)性和平移不變性。卷積神經(jīng)網(wǎng)絡(luò)解決的就是這兩個問題。卷積核在圖像上滑動每次只看一個小區(qū)域比如3x3這就是局部感受野同一個卷積核在整個圖像上共享權(quán)重這就是參數(shù)共享。一個3x3的卷積核只有9個參數(shù)加上偏置是10個卻能掃描整張圖片參數(shù)量比全連接少了幾個數(shù)量級。而且因為權(quán)重共享貓在圖片左上角還是右下角卷積核都能檢測到同樣的特征這就是平移不變性的來源。提示很多人做這個作業(yè)時第一反應(yīng)是用全連接網(wǎng)絡(luò)先跑個baseline。我的建議是別浪費時間直接上CNN。全連接網(wǎng)絡(luò)在這個任務(wù)上不僅參數(shù)多、訓(xùn)練慢準(zhǔn)確率還很難看容易打擊信心。2.2 作業(yè)場景下的網(wǎng)絡(luò)深度取舍這份作業(yè)用的數(shù)據(jù)集通常是CIFAR-10或者類似的小尺寸圖片集圖片大小32x32共10個類別。這種規(guī)模的數(shù)據(jù)集網(wǎng)絡(luò)太深容易過擬合太淺又學(xué)不到足夠特征。我試過從3層卷積到10層卷積的各種配置最后發(fā)現(xiàn)對于32x32的輸入4到6個卷積層是比較合適的區(qū)間。為什么不是越深越好因為32x32的圖片經(jīng)過幾次池化之后空間維度降得很快。比如每經(jīng)過一次2x2池化長寬各減半32→16→8→4→2。如果做5次池化最后只剩1x1再深的卷積層已經(jīng)沒有空間信息可提取了。所以網(wǎng)絡(luò)深度要和輸入尺寸匹配不能盲目堆層數(shù)。另一個考慮是訓(xùn)練時間。這個作業(yè)通常要求在有限的計算資源下完成比如用免費GPU或者個人筆記本。太深的網(wǎng)絡(luò)訓(xùn)練一輪就要很久調(diào)參周期拉長不利于快速迭代。我一般建議先用一個輕量級的4層卷積網(wǎng)絡(luò)跑通流程確認數(shù)據(jù)加載、損失計算、反向傳播都沒問題再逐步加深或加寬。2.3 批歸一化和Dropout的引入時機批歸一化Batch Normalization和Dropout是兩個經(jīng)常被濫用的技術(shù)。批歸一化的作用是讓每一層的輸入分布更穩(wěn)定加速訓(xùn)練收斂Dropout則是隨機丟棄一部分神經(jīng)元防止過擬合。但在作業(yè)場景下這兩個東西不是越多越好。我的經(jīng)驗是批歸一化可以加在每一個卷積層之后、激活函數(shù)之前基本上沒有副作用還能讓你用更大的學(xué)習(xí)率。Dropout則要謹慎一般只在全連接層之前加一個丟棄率設(shè)0.5左右。如果在卷積層后面也加Dropout容易導(dǎo)致欠擬合尤其是網(wǎng)絡(luò)本身就不深的時候。注意批歸一化在訓(xùn)練和測試時的行為不一樣。訓(xùn)練時用當(dāng)前batch的均值和方差測試時用訓(xùn)練階段累積的滑動平均值。如果你自己實現(xiàn)批歸一化別忘了切換模式否則測試準(zhǔn)確率會莫名其妙地低一截。3. 核心細節(jié)解析每一層到底在做什么3.1 卷積層特征提取的基本單元卷積層是CNN的核心但很多人對它的理解停留在“滑動窗口”這個層面。實際上卷積核的每個參數(shù)都是可學(xué)習(xí)的權(quán)重訓(xùn)練過程就是讓這些權(quán)重逐漸變成能夠檢測特定特征的濾波器。比如淺層的卷積核可能學(xué)會檢測邊緣、顏色斑塊深層的卷積核則能檢測更復(fù)雜的紋理和形狀。在實現(xiàn)時有幾個參數(shù)需要你手動指定卷積核大小、步長、填充。對于3x3的卷積核步長通常設(shè)為1填充設(shè)為1這樣輸入輸出的空間尺寸不變。為什么用3x3而不是5x5或7x7因為兩個3x3卷積層的感受野等于一個5x5卷積層但參數(shù)更少2×918 vs 25非線性更強多了一次激活函數(shù)。所以現(xiàn)代CNN基本都用3x3作為默認選擇。代碼實現(xiàn)上如果用PyTorch一行就能搞定nn.Conv2d(in_channels3, out_channels64, kernel_size3, stride1, padding1)這里的in_channels是輸入通道數(shù)第一層是3RGB后續(xù)層等于上一層的輸出通道數(shù)。out_channels是卷積核的數(shù)量也就是輸出特征圖的通道數(shù)。這個值通常從32或64開始每經(jīng)過一次池化翻倍因為空間尺寸減半了通道數(shù)翻倍可以保持信息容量大致不變。3.2 池化層降維與平移不變性池化層的作用是降低空間維度減少計算量同時提供一定程度的平移不變性。最常用的是2x2最大池化步長為2這樣每次操作后長寬各減半。最大池化取的是每個2x2區(qū)域的最大值相當(dāng)于保留了最顯著的特征。為什么用最大池化而不是平均池化因為最大池化保留了區(qū)域內(nèi)的最強響應(yīng)對于紋理和邊緣特征更敏感。平均池化會平滑掉這些細節(jié)在淺層網(wǎng)絡(luò)中效果不如最大池化。當(dāng)然在某些深層網(wǎng)絡(luò)中平均池化也有它的用武之地但作業(yè)場景下用最大池化基本不會錯。池化層沒有可學(xué)習(xí)的參數(shù)所以不會增加模型復(fù)雜度。但要注意池化會丟失空間信息如果圖片本身很小比如32x32池化次數(shù)不宜過多。我一般做3到4次池化把32x32降到4x4或2x2然后接全連接層。3.3 激活函數(shù)ReLU為什么成了默認選擇激活函數(shù)給網(wǎng)絡(luò)引入非線性沒有它再多層疊加也等價于一層線性變換。早期用Sigmoid和Tanh但它們有個致命問題當(dāng)輸入很大或很小時梯度接近零導(dǎo)致反向傳播時梯度消失深層網(wǎng)絡(luò)訓(xùn)練不動。ReLURectified Linear Unit解決了這個問題它在正區(qū)間梯度恒為1負區(qū)間梯度為0計算簡單收斂快。但ReLU也有缺點負區(qū)間的梯度為零可能導(dǎo)致某些神經(jīng)元“死亡”永遠不被激活。為了解決這個問題后續(xù)出現(xiàn)了Leaky ReLU、ELU等變體。不過在作業(yè)場景下標(biāo)準(zhǔn)ReLU已經(jīng)足夠好了沒必要追求最新的激活函數(shù)。我試過把ReLU換成Leaky ReLU準(zhǔn)確率提升不到0.5%但代碼復(fù)雜度增加了不劃算。實操心得如果你發(fā)現(xiàn)訓(xùn)練過程中損失一直不下降先檢查激活函數(shù)是不是用錯了。我見過有人把Sigmoid用在深層卷積網(wǎng)絡(luò)里結(jié)果訓(xùn)練了20輪損失還在2.3左右徘徊換成ReLU后第三輪就降到1.5以下。3.4 全連接層與輸出層從特征到類別經(jīng)過幾次卷積和池化之后特征圖的空間尺寸已經(jīng)很小了比如4x4通道數(shù)可能達到256或512。這時候需要把特征圖展平成一維向量接入全連接層。展平操作就是把每個通道的每個位置的值按順序排成一列長度等于通道數(shù)×高×寬。全連接層通常只有一到兩層第一層的神經(jīng)元數(shù)量可以是512或1024最后一層的神經(jīng)元數(shù)量等于類別數(shù)比如10。最后一層后面接Softmax函數(shù)把輸出轉(zhuǎn)換成概率分布。損失函數(shù)用交叉熵損失它衡量的是預(yù)測概率分布和真實標(biāo)簽的差異。這里有個細節(jié)Softmax和交叉熵損失在數(shù)學(xué)上可以合并PyTorch的CrossEntropyLoss就包含了Softmax所以你在網(wǎng)絡(luò)最后一層不需要手動加Softmax直接輸出logits就行。如果加了Softmax再用CrossEntropyLoss相當(dāng)于做了兩次Softmax會導(dǎo)致梯度計算錯誤訓(xùn)練效果大打折扣。4. 實操過程從數(shù)據(jù)加載到模型訓(xùn)練4.1 數(shù)據(jù)預(yù)處理與增強策略數(shù)據(jù)預(yù)處理是很多人容易忽略的一步但它對最終準(zhǔn)確率的影響可能比網(wǎng)絡(luò)結(jié)構(gòu)還大。對于彩色圖片最基本的預(yù)處理是歸一化把像素值從0-255縮放到0-1再減去均值、除以標(biāo)準(zhǔn)差。均值和標(biāo)準(zhǔn)差可以按通道計算比如RGB三個通道分別算。這樣做的目的是讓輸入分布更接近標(biāo)準(zhǔn)正態(tài)分布加速訓(xùn)練收斂。數(shù)據(jù)增強是防止過擬合的利器。對于圖像分類任務(wù)常用的增強手段包括隨機水平翻轉(zhuǎn)、隨機裁剪、顏色抖動等。但要注意增強手段要和任務(wù)匹配。比如水平翻轉(zhuǎn)對于貓狗分類是合理的因為貓翻轉(zhuǎn)后還是貓但對于數(shù)字識別翻轉(zhuǎn)后的數(shù)字可能就不是合法數(shù)字了這時候就不能用水平翻轉(zhuǎn)。在作業(yè)場景下我建議至少加上隨機水平翻轉(zhuǎn)和隨機裁剪。隨機裁剪可以從圖片中隨機截取一個區(qū)域再縮放到原尺寸相當(dāng)于讓網(wǎng)絡(luò)看到不同視角的同一物體。顏色抖動可以輕微調(diào)整亮度、對比度、飽和度讓網(wǎng)絡(luò)對光照變化更魯棒。transform_train transforms.Compose([ transforms.RandomHorizontalFlip(), transforms.RandomCrop(32, padding4), transforms.ToTensor(), transforms.Normalize(mean[0.491, 0.482, 0.447], std[0.247, 0.243, 0.261]) ])這里的mean和std是CIFAR-10數(shù)據(jù)集的統(tǒng)計值如果你用的是其他數(shù)據(jù)集需要自己算一下。計算方法是把所有訓(xùn)練圖片的像素值加起來求平均再求標(biāo)準(zhǔn)差。4.2 網(wǎng)絡(luò)結(jié)構(gòu)的具體搭建基于前面的分析我給出一個在作業(yè)場景下表現(xiàn)穩(wěn)定的網(wǎng)絡(luò)結(jié)構(gòu)。這個結(jié)構(gòu)有4個卷積塊每個塊包含兩個3x3卷積層、批歸一化、ReLU激活然后接一個2x2最大池化。通道數(shù)從64開始每次池化后翻倍最后接一個全連接層輸出10個類別。class CNN(nn.Module): def __init__(self, num_classes10): super(CNN, self).__init__() self.block1 nn.Sequential( nn.Conv2d(3, 64, 3, padding1), nn.BatchNorm2d(64), nn.ReLU(), nn.Conv2d(64, 64, 3, padding1), nn.BatchNorm2d(64), nn.ReLU(), nn.MaxPool2d(2) ) self.block2 nn.Sequential( nn.Conv2d(64, 128, 3, padding1), nn.BatchNorm2d(128), nn.ReLU(), nn.Conv2d(128, 128, 3, padding1), nn.BatchNorm2d(128), nn.ReLU(), nn.MaxPool2d(2) ) self.block3 nn.Sequential( nn.Conv2d(128, 256, 3, padding1), nn.BatchNorm2d(256), nn.ReLU(), nn.Conv2d(256, 256, 3, padding1), nn.BatchNorm2d(256), nn.ReLU(), nn.MaxPool2d(2) ) self.block4 nn.Sequential( nn.Conv2d(256, 512, 3, padding1), nn.BatchNorm2d(512), nn.ReLU(), nn.Conv2d(512, 512, 3, padding1), nn.BatchNorm2d(512), nn.ReLU(), nn.MaxPool2d(2) ) self.classifier nn.Sequential( nn.Dropout(0.5), nn.Linear(512 * 2 * 2, num_classes) ) def forward(self, x): x self.block1(x) x self.block2(x) x self.block3(x) x self.block4(x) x x.view(x.size(0), -1) x self.classifier(x) return x這個網(wǎng)絡(luò)大約有1100萬個參數(shù)在CIFAR-10上訓(xùn)練50輪左右可以達到90%以上的測試準(zhǔn)確率。如果你覺得訓(xùn)練太慢可以把每個塊的第二個卷積層去掉參數(shù)量減半準(zhǔn)確率大概降2到3個百分點。4.3 訓(xùn)練循環(huán)與超參數(shù)選擇訓(xùn)練循環(huán)的代碼框架很固定前向傳播、計算損失、反向傳播、更新參數(shù)。但超參數(shù)的選擇需要一些經(jīng)驗。我給出一個經(jīng)過驗證的配置超參數(shù)推薦值說明優(yōu)化器SGD Momentum動量0.9比Adam泛化更好初始學(xué)習(xí)率0.1配合批歸一化使用學(xué)習(xí)率調(diào)度Cosine Annealing從0.1余弦衰減到0批大小128顯存不夠可以降到64訓(xùn)練輪數(shù)50通常30輪后準(zhǔn)確率趨于穩(wěn)定權(quán)重衰減5e-4防止過擬合為什么用SGD而不是AdamAdam收斂快但最終泛化性能往往不如調(diào)好學(xué)習(xí)率的SGD。在作業(yè)場景下SGD配合余弦退火學(xué)習(xí)率調(diào)度能穩(wěn)定達到較高的準(zhǔn)確率。學(xué)習(xí)率從0.1開始是因為批歸一化讓網(wǎng)絡(luò)對學(xué)習(xí)率不那么敏感0.1是一個比較激進的起點配合余弦退火可以在后期精細調(diào)整。optimizer optim.SGD(model.parameters(), lr0.1, momentum0.9, weight_decay5e-4) scheduler optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max50) criterion nn.CrossEntropyLoss()訓(xùn)練過程中要監(jiān)控訓(xùn)練損失和驗證準(zhǔn)確率。如果訓(xùn)練損失持續(xù)下降但驗證準(zhǔn)確率停滯甚至下降說明過擬合了可以增加Dropout率或加強數(shù)據(jù)增強。如果訓(xùn)練損失都不下降說明欠擬合需要增加網(wǎng)絡(luò)容量或提高學(xué)習(xí)率。實操心得我習(xí)慣每訓(xùn)練5輪就保存一次模型權(quán)重同時記錄驗證準(zhǔn)確率。這樣即使訓(xùn)練中途出現(xiàn)意外中斷也能從最近的檢查點恢復(fù)。另外驗證集不要用測試集代替否則你調(diào)參調(diào)到最后測試集就被“偷看”了最終評估結(jié)果不可信。4.4 學(xué)習(xí)率調(diào)度的具體實現(xiàn)與效果對比學(xué)習(xí)率調(diào)度是提升最終準(zhǔn)確率的關(guān)鍵技巧但很多人只是設(shè)一個固定學(xué)習(xí)率從頭跑到尾。我做過對比實驗固定學(xué)習(xí)率0.01訓(xùn)練50輪測試準(zhǔn)確率約87%用余弦退火從0.1降到0測試準(zhǔn)確率約92%。差距非常明顯。余弦退火的原理是讓學(xué)習(xí)率按照余弦曲線從初始值平滑降到零。前期學(xué)習(xí)率大快速探索參數(shù)空間后期學(xué)習(xí)率小精細調(diào)整。實現(xiàn)起來很簡單PyTorch的CosineAnnealingLR就是干這個的。你只需要在每個epoch結(jié)束后調(diào)用scheduler.step()。還有一種更激進的策略叫熱重啟Warm Restarts就是讓學(xué)習(xí)率周期性回升再下降。這種方法在某些任務(wù)上能跳出局部最優(yōu)但在作業(yè)場景下不一定比余弦退火好而且調(diào)參更麻煩。我的建議是先用余弦退火穩(wěn)定可靠。5. 常見問題與排查技巧實錄5.1 訓(xùn)練不收斂的排查路徑訓(xùn)練不收斂是新手最常遇到的問題表現(xiàn)是損失值居高不下或者震蕩劇烈。排查思路應(yīng)該從數(shù)據(jù)到模型逐層檢查。先看數(shù)據(jù)有沒有問題圖片是否正常加載、標(biāo)簽是否對應(yīng)正確、歸一化是否做了。我見過有人把標(biāo)簽和圖片搞混了訓(xùn)練了半天準(zhǔn)確率一直在10%左右隨機猜測水平。如果數(shù)據(jù)沒問題再看模型初始化。PyTorch默認用Kaiming初始化一般不需要改。但如果你自己寫了初始化邏輯檢查一下有沒有把權(quán)重初始化成全零或者過大的值。全零初始化會導(dǎo)致所有神經(jīng)元輸出相同反向傳播時梯度也相同網(wǎng)絡(luò)永遠學(xué)不到東西。最后檢查學(xué)習(xí)率。學(xué)習(xí)率太大會導(dǎo)致?lián)p失震蕩甚至發(fā)散太小則收斂極慢??梢宰鲆粋€簡單的學(xué)習(xí)率掃描從1e-5到1每個量級跑幾個batch看哪個學(xué)習(xí)率下?lián)p失下降最快。這個技巧叫LR Range Test能幫你快速找到合適的學(xué)習(xí)率區(qū)間。5.2 過擬合與欠擬合的判斷與應(yīng)對過擬合和欠擬合是兩種相反的問題判斷方法很簡單看訓(xùn)練準(zhǔn)確率和驗證準(zhǔn)確率的差距。如果訓(xùn)練準(zhǔn)確率很高比如99%但驗證準(zhǔn)確率低很多比如80%就是過擬合如果兩者都低比如60%就是欠擬合。過擬合的應(yīng)對手段包括增加數(shù)據(jù)增強、提高Dropout率、增加權(quán)重衰減、減少網(wǎng)絡(luò)參數(shù)量。我一般先加數(shù)據(jù)增強因為這是最根本的解決辦法——讓網(wǎng)絡(luò)看到更多樣的數(shù)據(jù)。如果增強已經(jīng)很強了還過擬合再考慮加Dropout或減參數(shù)量。欠擬合的應(yīng)對手段包括增加網(wǎng)絡(luò)深度或?qū)挾?、降低Dropout率、提高學(xué)習(xí)率、訓(xùn)練更長時間。有時候欠擬合是因為批歸一化沒加對位置比如加在了激活函數(shù)之后效果會打折扣。正確的順序是卷積→批歸一化→激活函數(shù)。5.3 顯存不足與訓(xùn)練速度優(yōu)化顯存不足是作業(yè)場景下的常見問題尤其是用免費GPU的時候。解決辦法有幾個減小批大小、用梯度累積模擬大批量、用混合精度訓(xùn)練、減少網(wǎng)絡(luò)參數(shù)量。批大小從128降到64顯存占用大概減半但訓(xùn)練速度會慢一些因為GPU并行度降低了。梯度累積是個好技巧用小的批大小跑多次前向傳播把梯度累加起來再一次性更新參數(shù)。這樣等效于大批量訓(xùn)練但顯存占用不變。實現(xiàn)起來就是在每次反向傳播后不立即更新參數(shù)而是等累積了N次之后再更新。accumulation_steps 4 for i, (inputs, labels) in enumerate(train_loader): outputs model(inputs) loss criterion(outputs, labels) / accumulation_steps loss.backward() if (i 1) % accumulation_steps 0: optimizer.step() optimizer.zero_grad()混合精度訓(xùn)練是另一個加速技巧用半精度浮點數(shù)float16做前向和反向傳播用單精度float32保存模型權(quán)重。PyTorch的amp模塊可以自動處理代碼改動很小訓(xùn)練速度能提升30%到50%。5.4 常見報錯速查表報錯信息可能原因解決方法RuntimeError: size mismatch全連接層輸入維度算錯打印展平后的維度調(diào)整Linear輸入CUDA out of memory顯存不足減小批大小用梯度累積Loss is nan學(xué)習(xí)率太大或數(shù)據(jù)有異常值降低學(xué)習(xí)率檢查數(shù)據(jù)歸一化Accuracy stuck at 10%標(biāo)簽錯亂或模型輸出未接Softmax檢查標(biāo)簽對應(yīng)關(guān)系確認損失函數(shù)Validation accuracy fluctuates批歸一化統(tǒng)計量不穩(wěn)定增大批大小或改用GroupNorm注意如果你在測試時發(fā)現(xiàn)準(zhǔn)確率比驗證時低很多檢查一下模型是否處于eval模式。PyTorch中要用model.eval()切換否則Dropout和批歸一化會繼續(xù)用訓(xùn)練模式導(dǎo)致結(jié)果不穩(wěn)定。6. 從作業(yè)到實戰(zhàn)可以繼續(xù)深挖的方向做完這份作業(yè)你其實已經(jīng)掌握了CNN的基本功。但如果想繼續(xù)深入有幾個方向值得探索。第一個是殘差連接Residual Connection它讓網(wǎng)絡(luò)可以做得非常深而不退化ResNet就是靠這個在ImageNet上拿了冠軍。實現(xiàn)起來就是在卷積塊之間加一條跳躍連接把輸入直接加到輸出上。第二個是注意力機制比如SE模塊Squeeze-and-Excitation它讓網(wǎng)絡(luò)自動學(xué)習(xí)每個通道的重要性權(quán)重。加在卷積塊后面參數(shù)量增加很少但準(zhǔn)確率能提升1到2個百分點。第三個是遷移學(xué)習(xí)用在大數(shù)據(jù)集上預(yù)訓(xùn)練好的模型在自己的小數(shù)據(jù)集上微調(diào)。這在數(shù)據(jù)量不足時特別有效通常只需要訓(xùn)練最后幾層就能達到不錯的效果。我自己在做完這個作業(yè)之后花了一個周末把ResNet-18在CIFAR-10上復(fù)現(xiàn)了一遍準(zhǔn)確率從92%提升到95%。過程并不復(fù)雜主要是把基本的卷積塊換成殘差塊再調(diào)整一下學(xué)習(xí)率調(diào)度。如果你也想試試建議先從小的殘差塊開始比如兩個3x3卷積加一個跳躍連接跑通了再加深。最后分享一個我踩過的坑有一次我為了追求更高的準(zhǔn)確率把網(wǎng)絡(luò)加深到10層結(jié)果訓(xùn)練了100輪準(zhǔn)確率反而下降了。后來發(fā)現(xiàn)是梯度消失導(dǎo)致的加了殘差連接才解決。所以網(wǎng)絡(luò)深度不是越深越好關(guān)鍵是要有讓梯度順暢傳播的機制。這個教訓(xùn)讓我明白結(jié)構(gòu)設(shè)計比盲目堆層數(shù)重要得多。