:從理論到PyTorch完整訓練流程)
從書桌抽屜里翻出那本已經(jīng)翻得卷了邊的深度學習教材時我習慣性地在目錄頁寫下了一個編號deeplearningbook_007。這是我給自己布置的第七個讀書實踐任務——圖像分類。而這篇博文要聊的是這個編號的后半段也就是 007-2把卷積神經(jīng)網(wǎng)絡CNN從理論書里搬進真實代碼用一套完整流程訓練出一個能用的分類模型。很多朋友讀 CNN 章節(jié)時的狀態(tài)我太熟悉了卷積是滑動窗口池化是降采樣反向傳播公式也能手推一遍可真要打開編輯器寫第一個模型光標停在第一行就不知道往哪兒落了。007-2 就是為這個尷尬期準備的。我會把從數(shù)據(jù)準備、模型搭建、訓練配置到調(diào)優(yōu)排錯的整個過程完整走一遍并且說清楚每個設計決策背后的原因。這篇內(nèi)容適合兩類人一類是剛讀完 CNN 理論、正準備動手實踐的新手另一類是已經(jīng)能跑通基礎模型、但總覺得哪里沒搞透的進階學習者。1. 項目定位007-2 到底要解決什么問題1.1 從讀書到動手為什么需要第二部分理論學習和實際落地之間有一條深深的溝這條溝不是靠再讀一遍書能填平的。我在 007-1 里做的事是手動實現(xiàn)卷積運算、推導感受野計算公式、逐層統(tǒng)計參數(shù)量這些工作幫我建立了對 CNN 底層機制的直觀理解。但到了真要訓練一個能分類圖片的模型時我發(fā)現(xiàn)自己仍然不知道從哪兒開始數(shù)據(jù)怎么加載損失函數(shù)選哪個學習率定多少007-2 就是用來填這條溝的。它能解決三個具體問題第一把書上抽象的概念映射到框架里的真實 API比如把卷積層對應到 Conv2d把批歸一化對應到 BatchNorm2d讓知識真正落地第二把數(shù)據(jù)預處理、模型設計、訓練、評估這條鏈路完整地走通一遍以后遇到任何圖像任務都可以復用這一套方法論第三把訓練過程中那些書上不會寫的問題暴露出來——過擬合、不收斂、顯存不夠——這些問題只有親手踩一遍才知道怎么處理。我自己的體會是這種先手動實現(xiàn)底層、再用框架快速搭建的兩段式學習法特別適合深度學習。手動實現(xiàn)讓你理解原理框架搭建讓你專注解決問題本身兩者互補缺一個都會讓能力樹長歪。1.2 數(shù)據(jù)與任務設定007-2 選的任務很樸素對一組公開的花卉圖片做分類。一共 5 個類別每類大概 600 張圖片圖片尺寸不太統(tǒng)一大部分在 200x200 到 500x500 像素之間。選這個任務有三個原因5 分類問題是展示 CNN 能力的合理規(guī)模既不會像千分類那樣需要龐大訓練資源和很長時間又能清楚展現(xiàn)特征提取、分類決策的完整過程。花卉圖片在顏色、紋理、形狀上差異明顯非常便于觀察模型到底學到了什么特征也方便用熱力圖做可視化分析。數(shù)據(jù)量不大即使用 CPU 訓練簡單模型也能在合理時間內(nèi)跑完一個 epoch適合反復調(diào)試。任務目標我定得很明確在測試集上達到 90% 以上的準確率同時記錄清楚訓練過程中 loss 和 accuracy 的變化曲線能夠解釋每一次調(diào)參操作帶來的影響。這個目標比單純跑通代碼高一個層次——不只是讓模型動起來而是要建立改動到結(jié)果的因果鏈條這種能力在真正的工作中比背下來幾個網(wǎng)絡結(jié)構(gòu)值錢得多。注意我這里用花卉數(shù)據(jù)集舉例是因為它類別清晰、容易上手。如果你有更貼合自己業(yè)務的數(shù)據(jù)直接把路徑和標簽替換掉即可整套流程完全通用。2. 核心組件拆解CNN 里的每一個設計決策2.1 卷積核大小與通道數(shù)的選擇依據(jù)搭建 CNN 時第一個要拍板的是卷積核大小。我見過不少新手直接照搬經(jīng)典網(wǎng)絡的 3x3 卷積卻不清楚這個尺寸為什么流行。答案有兩層從感受野的角度看兩個 3x3 卷積堆疊等效于一個 5x5 卷積的感受野但參數(shù)更少。計算一下就知道兩個 3x3 卷積的參數(shù)是 2 x 3 x 3 18 個而一個 5x5 卷積是 5 x 5 25 個后者多出近 40%。更關鍵的是兩個 3x3 中間夾了一次非線性激活網(wǎng)絡表達能力反而更強。從工程角度看3x3 是眾多硬件和深度學習框架深度優(yōu)化的尺寸計算效率高。但 3x3 不是唯一答案。輸入圖片很小的場景比如常見的小圖數(shù)據(jù)集那種 32x32 規(guī)格第一層直接用 5x5 或 7x7 卷積可以更快擴大感受野減少網(wǎng)絡層數(shù)輸入圖片很大時比如 500x500 以上第一層用大卷積核配合步長 2 做快速降采樣也很常見可以省掉一層池化。通道數(shù)的設計我習慣用一個從窄到寬再收窄的規(guī)律來理解。淺層用較少通道比如 32、64提取邊緣、顏色等基礎特征中層逐漸加寬128、256提取紋理、局部形狀等組合特征最后通過全局池化或全連接層把特征映射到類別空間。每經(jīng)過一個降采樣層通道數(shù)翻倍這是一個經(jīng)典的經(jīng)驗法則特征圖尺寸減半通道數(shù)就加倍既控制參數(shù)量又保證信息不丟失。007-2 里的具體配置是輸入圖片統(tǒng)一縮放到 128x128像素值歸一化到 [0,1]。卷積塊 1Conv2d(3, 32, kernel_size3, padding1) BatchNorm ReLU MaxPool2d(2)卷積塊 2Conv2d(32, 64, kernel_size3, padding1) BatchNorm ReLU MaxPool2d(2)卷積塊 3Conv2d(64, 128, kernel_size3, padding1) BatchNorm ReLU MaxPool2d(2)分類頭GlobalAvgPool Dense(128, 5)這個結(jié)構(gòu)總參數(shù)量不到 50 萬非常輕量但在小型數(shù)據(jù)集上足以跑出不錯的準確率。設計它時我刻意避開了那些動輒上千萬參數(shù)的經(jīng)典網(wǎng)絡因為對數(shù)據(jù)和算力都有限的學習項目來說理解架構(gòu)設計的思路比復現(xiàn)一個大模型更有價值。2.2 池化層的存在價值與替代方案很多初學者把池化看作簡單的圖片縮小操作這其實低估了它。池化層有兩個核心作用降維壓縮特征圖尺寸減少后續(xù)層計算量同時成倍擴大下一層的感受野。平移不變性池化讓模型對輸入的小幅平移、局部變化不那么敏感這對圖像分類很有利因為在真實圖片里目標和鏡頭相對位置總在變。不過我想說清楚一件事池化不是卷積網(wǎng)絡里必不可少的組件。我在 007-1 手動實現(xiàn)時發(fā)現(xiàn)帶步長的卷積完全可以完成降采樣任務所以現(xiàn)代網(wǎng)絡里出現(xiàn)了兩種派別。經(jīng)典派是卷積分支后用池化下采樣結(jié)構(gòu)直白容易調(diào)試現(xiàn)代派用 stride2 的卷積替代池化信息保留更完整殘差網(wǎng)絡里就是這么干的。007-2 中考慮到調(diào)試方便我選擇了經(jīng)典的 MaxPool2d。MaxPool 和 AvgPool 的選擇也有講究。MaxPool 保留區(qū)域內(nèi)最強的響應適合中間層提取邊緣紋理這類強特征AvgPool 對區(qū)域整體求平均適合最后做全局特征聚合。我自己的經(jīng)驗是中間層用 MaxPool最后一層用 GlobalAvgPool 替代 Flatten 加 Dense后者的優(yōu)勢非常明顯——它會強制讓每個通道對應一種語義特征同時參數(shù)量幾乎降為原來的十分之一還天然帶有正則化效果。這個改動在 007-2 里讓驗證準確率穩(wěn)定提升了將近 2 個百分點而且模型對輸入尺寸的變化也更魯棒了。2.3 激活函數(shù)與歸一化的組合拳激活函數(shù)的選擇直接決定訓練過程的穩(wěn)定性。ReLU 是目前默認選項但我在實踐里遇到過一個非常典型的問題學習率稍微調(diào)大一點網(wǎng)絡里就有一批神經(jīng)元的輸出永遠變成 0梯度也永遠為 0模型徹底死掉一部分表達能力。這個現(xiàn)象叫 Dead ReLU本質(zhì)是負輸入?yún)^(qū)間沒有梯度通路一旦神經(jīng)元落入負區(qū)就再也回不來。應對 Dead ReLU 的常用手段有三個換用 LeakyReLU 或 ELU給負半軸一點斜率讓梯度有機會回傳。用合理的權重初始化比如 He 初始化讓卷積輸出的分布不過于集中避免大量神經(jīng)元一開始就落入負區(qū)??刂瞥跏紝W習率不要為了追求快速收斂而設得太大。007-2 里我最終用了 ReLU 加 He 初始化初始學習率設為 0.001配合余弦退火調(diào)度。這里要特別強調(diào)批歸一化BatchNormalization的放置位置它應當放在卷積層之后、激活函數(shù)之前而不是很多人以為的激活之后。原因是 BN 的作用是穩(wěn)定卷積輸出的分布讓激活函數(shù)的輸入落在合適區(qū)間如果先過了非線性激活分布已經(jīng)被打亂BN 的效果會大打折扣訓練時還會出現(xiàn)奇怪的震蕩。注意BN 在小 batch size 下效果會明顯變差。如果顯存只允許 batch size 設為 8 甚至更小我建議改用 GroupNorm或者干脆去掉 BN、靠調(diào)整學習率來維持穩(wěn)定。不要盲目迷信 BN它也是有適用條件的。3. 實操全流程從數(shù)據(jù)到模型的完整落地3.1 數(shù)據(jù)準備數(shù)據(jù)集劃分與預處理細節(jié)寫模型代碼之前數(shù)據(jù)準備環(huán)節(jié)直接決定后續(xù)調(diào)試的體驗。我把處理流程分成四步每一步都有明確的坑要避。第一步是劃分數(shù)據(jù)集。按照 8:1:1 的比例切成訓練集、驗證集、測試集并且用分層抽樣保證每個類別的樣本比例在三個集合里一致。這一步用訓練測試分割工具加 stratify 參數(shù)就能輕松實現(xiàn)。為什么單獨留測試集因為驗證集在調(diào)參過程中被反復使用模型已經(jīng)間接見過它的信息了如果拿驗證集當最終成績會高估模型的真實泛化能力。測試集只能最后用一次這是鐵律。第二步是統(tǒng)一尺寸。我選的是 128x128。這個尺寸的確定背后有考量太小比如 64x64會丟失花瓣紋理等關鍵細節(jié)太大比如 256x256計算量會翻好幾倍。128 對這個小數(shù)據(jù)集來說是精度和速度的平衡點訓練一輪的時間也剛好在可接受范圍內(nèi)。第三步是歸一化。把像素值從 [0,255] 縮放到 [0,1]。這個操作看起來簡單很多人卻會漏掉。不歸一化的話輸入數(shù)值范圍過大會讓損失函數(shù)對權重初始化極其敏感訓練初期非常容易出現(xiàn) loss 震蕩甚至直接發(fā)散。歸一化之后模型對初始化的容忍度會高很多。第四步是數(shù)據(jù)增強。訓練集上我做了幾種隨機變換水平翻轉(zhuǎn)、隨機旋轉(zhuǎn) 15 度、隨機裁剪到 112x112 再縮放回 128x128、調(diào)整亮度和對比度。驗證集和測試集只做縮放和歸一化絕不做增強。增強的目的是讓模型看到更多變體相當于免費擴充數(shù)據(jù)集對防止過擬合有立竿見影的效果。這里踩過一個坑增強到底在歸一化之前還是之后做我的結(jié)論是先做幾何變換翻轉(zhuǎn)、旋轉(zhuǎn)、裁剪再做歸一化。因為幾何變換涉及像素插值先歸一化再插值會產(chǎn)生細微數(shù)值偏差雖然通常不影響最終精度但養(yǎng)成熟練的先后順序能省掉很多不必要的疑惑。3.2 模型搭建用框架寫出一個可訓練的 CNN下面就是 007-2 里實際使用的模型代碼結(jié)構(gòu)非常簡潔核心邏輯全部在這。import torch import torch.nn as nn class SimpleCNN(nn.Module): def __init__(self, num_classes5): super().__init__() self.features nn.Sequential( nn.Conv2d(3, 32, kernel_size3, padding1), nn.BatchNorm2d(32), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), nn.Conv2d(32, 64, kernel_size3, padding1), nn.BatchNorm2d(64), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), nn.Conv2d(64, 128, kernel_size3, padding1), nn.BatchNorm2d(128), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), ) self.classifier nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(128, num_classes) ) def forward(self, x): return self.classifier(self.features(x))我解釋一下這個模型里的幾個關鍵設計點AdaptiveAvgPool2d(1) 是個很省心的操作。無論前面特征圖的尺寸是多大它都會輸出 1x1 的向量省去了手動計算全連接層輸入維度的麻煩。如果以后想把輸入分辨率從 128 改成 96模型代碼完全不用動。BatchNorm 的位置嚴格按照卷積之后、激活之前排列這也是前面反復強調(diào)過的請務必養(yǎng)成這個習慣。最后一層沒有加 Softmax。這是個非常典型的看著對其實是多余的操作??蚣芾锏慕徊骒負p失函數(shù)內(nèi)部已經(jīng)包含了 Softmax 和 log 運算如果在前向里提前做了 Softmax損失函數(shù)會再次計算導致數(shù)值不穩(wěn)定甚至梯度錯誤。模型參數(shù)量用sum(p.numel() for p in model.parameters())可以查出來我這個結(jié)構(gòu)大概在 43 萬左右。對 3000 張級別的小數(shù)據(jù)集來說這個規(guī)模很合適容量夠用又不會因為參數(shù)過多而輕松過擬合。3.3 訓練配置優(yōu)化器、學習率與訓練循環(huán)模型搭好之后訓練配置是另一塊容易出問題的地方。007-2 的配置如下優(yōu)化器AdamW初始學習率 0.001權重衰減設為 0.0001。損失函數(shù)交叉熵損失。批次大小32。訓練輪數(shù)50。學習率調(diào)度余弦退火總周期 50 輪。為什么選 AdamW 而不選經(jīng)典的 SGD 加動量這兩個的取舍值得說清楚。SGD 加動量在超參數(shù)調(diào)好后往往能收斂到更好的結(jié)果但它對學習率太敏感手動調(diào)整的成本很高而且訓練過程的穩(wěn)定性差一些。AdamW 是 Adam 的改進版把權重衰減從梯度更新里分離出來既保留了 Adam 快速收斂的優(yōu)點又緩解了 L2 正則與 Adam 相互作用導致的過擬合問題。對于 007-2 這種需要快速驗證想法、反復迭代的場景AdamW 是更省心的選擇。訓練循環(huán)的骨架長這樣for epoch in range(epochs): model.train() train_loss 0.0 correct 0 total 0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() train_loss loss.item() * images.size(0) _, preds torch.max(outputs, 1) correct (preds labels).sum().item() total labels.size(0) model.eval() val_loss 0.0 val_correct 0 val_total 0 with torch.no_grad(): for images, labels in val_loader: images, labels images.to(device), labels.to(device) outputs model(images) loss criterion(outputs, labels) val_loss loss.item() * images.size(0) _, preds torch.max(outputs, 1) val_correct (preds labels).sum().item() val_total labels.size(0) scheduler.step() train_acc 100.0 * correct / total val_acc 100.0 * val_correct / val_total print(fEpoch {epoch1:02d} | fTrain Loss {train_loss / total:.4f} | Train Acc {train_acc:.2f}% | fVal Loss {val_loss / val_total:.4f} | Val Acc {val_acc:.2f}%)這段代碼有兩個細節(jié)是新手的常見盲區(qū)model.train() 和 model.eval() 必須成對出現(xiàn)。train 模式下批歸一化層會統(tǒng)計當前 batch 的均值和方差eval 模式下它才使用訓練階段積累的全局統(tǒng)計量。如果漏了 model.eval()驗證集的 loss 會忽高忽低甚至出現(xiàn)驗證準確率像是隨機猜測的假象之前的調(diào)試工作全都白費。驗證階段必須用 torch.no_grad() 包起來。這個作用有兩個節(jié)省顯存因為不再保存中間計算圖加快驗證速度因為不需要計算梯度。我還故意把驗證集的 loss 除以樣本總數(shù)而不是 batch 數(shù)這樣得到的是每個樣本的平均損失和訓練集對比起來才公平。3.4 評估與結(jié)果分析訓練 50 輪之后我拿到的曲線大致是這樣的訓練準確率從初始的 60% 左右快速爬升到第 10 輪已經(jīng)接近 92%驗證準確率在第 20 輪左右達到峰值大概 91%之后訓練準確率還在緩慢上升驗證準確率則穩(wěn)定在 90% 到 92% 之間沒有明顯掉頭。最終測試集準確率 91.6%。這個結(jié)果告訴我兩件事模型沒有明顯過擬合泛化能力符合預期訓練曲線的走勢整體健康沒有大幅震蕩。如果訓練準確率 97%、驗證準確率只有 88%說明過擬合已經(jīng)出現(xiàn)了需要加數(shù)據(jù)增強、加正則化或者降低模型容量。除準確率外我還多算了一步混淆矩陣。分析后發(fā)現(xiàn)模型最容易把玫瑰和月季搞混這兩類在視覺上確實高度相似。這種細顆粒度的分析在真實業(yè)務里非常有用——如果分類錯誤的代價很高你就知道該優(yōu)先收集哪些易混淆類別的數(shù)據(jù)或者給損失函數(shù)里的稀有類別加上權重。4. 訓練調(diào)優(yōu)的實戰(zhàn)心得與踩坑記錄4.1 過擬合的判斷與應對策略判斷過擬合我從來不只看 loss 曲線而是盯住訓練準確率和驗證準確率的差距。差距超過 5 個百分點就要開始警惕超過 10 個點基本可以確診。007-2 的模型差距始終控制在 3 個點以內(nèi)所以沒有太擔心。如果確實過擬合了我的應對策略按優(yōu)先級排序第一優(yōu)先加強數(shù)據(jù)增強。比如隨機旋轉(zhuǎn)從 10 度加到 20 度或者引入隨機遮擋。這個手段成本最低效果卻常常最好因為它的本質(zhì)是擴大訓練集的有效多樣性。第二優(yōu)先在全連接層前加一個 Dropout比率 0.3 左右。我見過不少模型靠這一招把驗證準確率拉回 2 到 3 個點。第三優(yōu)先降低模型容量。比如去掉 128 通道的那個卷積塊讓模型沒有能力去背下訓練集。第四優(yōu)先調(diào)大權重衰減從 0.0001 試到 0.0005。這里想特別提醒一句數(shù)據(jù)增強不是越狠越好。我貪心過一次把翻轉(zhuǎn)、旋轉(zhuǎn) 30 度、隨機裁剪、顏色抖動、隨機遮擋全部疊加驗證準確率反而掉了。原因很直白增強過猛會讓訓練樣本的分布偏離真實數(shù)據(jù)分布模型學到的特征被增強痕跡帶偏了。正確做法是每次只加一種增強觀察驗證集曲線變化再決定要不要繼續(xù)加。4.2 損失不下降的排查清單訓練中損失卡著不掉是讓新手最崩潰的場景。這里分享一份我反復用到的排查清單按順序檢查能省掉大量瞎試的時間檢查數(shù)據(jù)預處理歸一化做了沒有標簽是不是從 0 開始連續(xù)編號有沒有混入損壞的圖片文件檢查損失函數(shù)分類任務用的是交叉熵損失而不是均方誤差前者才適合離散類別。檢查學習率太小了損失下降會慢得讓你懷疑人生太大了初期會震蕩然后卡住。我建議做一次學習率掃描實驗快速找到合適區(qū)間。檢查模型輸出單獨跑一個 batch 的前向確認輸出形狀是 (batch_size, num_classes)別搞成 (batch_size,) 或者 (num_classes,)。檢查梯度每隔幾個 step 打印參數(shù)梯度的范數(shù)如果始終為 0說明梯度根本沒有回傳問題大概率出在權重初始化、激活函數(shù)或者模型結(jié)構(gòu)上。我印象最深的一次排錯經(jīng)歷是這樣的某個自定義數(shù)據(jù)類里忘了歸一化結(jié)果損失一直停在 2.3 附近模型輸出的概率幾乎是均勻分布怎么看都不對。排查了一下午最后才發(fā)現(xiàn)是數(shù)據(jù)值太大數(shù)值計算把梯度搞崩了。所以檢查數(shù)據(jù)預處理這條一定要放在第一位它不是走過場。4.3 顯存優(yōu)化與訓練速度提升技巧如果顯存不夠我的處理順序是這樣的調(diào)小 batch size同時把學習率也相應調(diào)低一點因為小 batch 的梯度噪聲更大維持原來的學習率容易震蕩。開啟混合精度訓練AMP讓部分計算自動切換到半精度顯存占用通常能直接降三分之一左右。檢查代碼里有沒有多余的顯存占用比如驗證階段忘了開 torch.no_grad()或者訓練循環(huán)里保留了用不到的中間張量。以上都不夠就降低輸入分辨率從 128 降到 96。分辨率降低對準確率的影響往往小于 1 個百分點但顯存節(jié)省非常顯著。訓練速度方面一個容易被忽視的細節(jié)是數(shù)據(jù)加載的并行度。我剛開始把加載器參數(shù)留成默認值 0結(jié)果每個 epoch 的數(shù)據(jù)加載時間比模型訓練時間還長。改成并行數(shù)為 4 并且打開內(nèi)存釘住之后整個訓練時間縮短了將近一半。如果你的機器核心多還可以繼續(xù)調(diào)大但要注意不同操作系統(tǒng)下并行加載可能和主程序進程相關遇到奇怪報錯就調(diào)回 0 試試。4.4 常見問題速查表現(xiàn)象可能原因排查方向損失一直不降數(shù)據(jù)未歸一化 / 學習率不當 / 標簽錯位檢查預處理、損失曲線、梯度范數(shù)訓練準確率高、驗證準確率低過擬合增強數(shù)據(jù)增強、加 Dropout、降低容量驗證損失波動大忘了 eval 模式 / 批次太小 / 批歸一化失效切換模型模式、增加批次、考慮分組歸一化顯存溢出批次過大 / 未開混合精度 / 多余中間變量調(diào)小批次、開 AMP、檢查代碼某類別準確率特別低類別不均衡 / 類別特征相似計算類別權重、收集更多該類數(shù)據(jù)訓練時間過長加載并行度為零 / 未用 GPU / 分辨率過高開多進程加載、升級硬件、降低分辨率這張表是我自己調(diào)試時反復翻看的總結(jié)里面很多現(xiàn)象其實是同一個根因。你可以把表存下來遇到問題時按行去對照比從零開始猜要高效得多。5. 從 007-2 到下一個實踐的擴展思路5.1 把單模型變成可解釋的系統(tǒng)007-2 跑通一個 91% 準確率的模型之后我做的第一件事并不是拼命刷準確率而是回頭研究模型到底學到了什么。用類激活映射CAM生成熱力圖觀察分類向日葵時模型關注的是花盤的哪個區(qū)域、分類玫瑰時又聚焦在哪些結(jié)構(gòu)上。這個步驟讓我對 CNN 的理解又上了一個臺階它不是簡單記住模板而是在提取顏色、紋理、形狀的組合特征。如果你也在做類似的圖像分類項目我強烈建議把可視化當成必做項它會幫你快速驗證模型學到的特征是否合理也能在結(jié)果異常時提供最直觀的線索。5.2 后續(xù)可以嘗試的三個方向遷移學習把預訓練模型拿來做特征提取只訓練自己數(shù)據(jù)的分類頭。在數(shù)據(jù)量只有一兩千張的小數(shù)據(jù)集上遷移學習往往能把準確率從 90% 直接拉到 96% 以上省時省力是性價比極高的升級路徑。目標檢測把這張圖是什么擴展為圖里有什么、在哪里用檢測框架把每朵花的位置和類別都框出來。這個方向邏輯遞進非常自然也是從學術練習走向?qū)嶋H應用的常見一步。模型壓縮用剪枝、量化等手段把模型從幾十萬參數(shù)壓到幾萬參數(shù)在準確率下降可接受的前提下?lián)Q一個更輕量、更適合部署的版本。對真實產(chǎn)品落地來說這一步幾乎是必經(jīng)之路。5.3 一個值得養(yǎng)成的習慣實驗記錄最后分享一個這段時間讓我受益非常大的習慣給每次實驗做記錄。我會把每次改動的內(nèi)容、最終準確率、訓練耗時、顯存占用全部寫進一個簡單的表格里。這個習慣在調(diào)參階段的價值最明顯——你不用反復嘗試已經(jīng)試過的配置也能清晰地知道到底哪個改動帶來了真實的提升。記錄模板通常包含這幾項實驗編號與日期、模型結(jié)構(gòu)改動點、數(shù)據(jù)增強策略、優(yōu)化器與學習率等超參數(shù)、最終的訓練與驗證與測試準確率、訓練耗時與顯存占用、備注這次實驗想驗證什么假設。有了這個表你會發(fā)現(xiàn)憑感覺調(diào)參和有依據(jù)地調(diào)參差距巨大后者得到的模型更穩(wěn)定也更容易復現(xiàn)。007-2 只是整個深度學習讀書實踐系列里的一個節(jié)點但這個記錄習慣會陪你走完后面所有項目。個人經(jīng)驗角度再說一句深度學習教材里的每個模塊只有親手放到一個具體項目里跑通一遍才能真正體會到設計的精妙和那些藏在代碼縫隙里的坑。如果你現(xiàn)在正卡在理論階段看不進去別再硬啃找一個像 007-2 這樣的小項目動手跑起來等回頭再看書里那些公式你會體驗到一種完全不同的豁然開朗。