VGG16 遷移學習怎麼凍結卷積層只訓練全連接層?
VGG16 連題組第四題:在實務應用中,我們常使用遷移學習(transfer learning)技巧,即載入預訓練模型(如 VGG16),凍結部分層的參數,只針對特定任務重新訓練最後幾層,這種做法可節省訓練時間並提升模型效能。假設你要對 VGG16 進行遷移學習(transfer learning),希望凍結卷積層的參數,只訓練最後全連接層(classifier)。下列哪段程式碼寫法正確?
動物園影像辨識專案的工程師想用 VGG16 預訓練模型做遷移學習,計畫凍結卷積層(model.features)讓它不更新,只重新訓練後面的全連接層(model.classifier)來適應動物分類任務。題目給了四段程式碼,每段都用不同方式嘗試實現「凍結 features,只訓練 classifier」,但只有一段是正確的。
問你:哪一段程式碼正確地「凍結卷積層、只讓全連接層更新」?
一句話總結
遷移學習凍結卷積層的正確方法是選項 B:對 model.features.parameters() 設定 requires_grad = False,只凍結 features 部分,然後換掉 classifier[6]——這樣 features 不更新,新的 classifier[6] 預設 requires_grad = True,會正常訓練。
先感受問題:只讓「特定員工」去上新課
動物園工程師有一個 VGG16 預訓練模型,已經在 ImageNet 上學過 1,000 種物件的特徵。現在他只需要分類 10 種動物,不需要從頭訓練 1.38 億個參數。
策略:把卷積層(features)的「學習模式」關掉——它已經學得很好了,凍結保留;換掉最後一個全連接層,改成輸出 10 類;然後只訓練全連接層(classifier)。
在 PyTorch 裡,「學習模式」的開關是 requires_grad:True 表示要更新這個參數,False 表示凍結不動。關鍵是要精準地「只把 features 的 requires_grad 設為 False」,而不是整個模型、也不是 classifier。
凍結錯對象,遷移學習會有什麼後果?
- 凍結全部(選項 A 的問題):把所有參數都設為 requires_grad = False,然後換掉 classifier[6]。換後的新層預設 requires_grad = True,但原本的 classifier[0-5] 已被凍結。這樣只有新換的最後一層在訓練,其他全連接層的權重都不更新,效果比選項 B 差。
- 凍結 classifier(選項 C):把正確應該訓練的那些層(classifier)全部凍結,反而讓 features 繼續更新,這完全相反:破壞了預訓練的特徵提取器,新任務也沒學到。
- 對 model 設屬性(選項 D):model.requires_grad = False 設的是 Python 物件屬性,不是 PyTorch 的 requires_grad 機制。PyTorch 的梯度控制是針對各個 Parameter 的,這行程式碼實際上不會凍結任何參數,是無效操作。
- requires_grad 是參數層級的設定:凍結必須針對每個 nn.Parameter,要用 .parameters() 迭代每個參數,不能直接對模型物件設定。
- 新換的層預設 requires_grad = True:`model.classifier[6] = nn.Linear(4096, 10)` 建立的新層,其參數預設是可訓練的,只要不對它設 False 就會自動更新。
精準凍結 features,讓 classifier 繼續學習
動物園工程師的正確做法(選項 B):
import torch
import torchvision.models as models
model = models.vgg16(pretrained=True)
# Step 1:只凍結 features(卷積部分)
for param in model.features.parameters():
param.requires_grad = False
# Step 2:換掉最後一層,輸出改為 10 類(動物)
model.classifier[6] = torch.nn.Linear(4096, 10)
# 新建的 Linear 層預設 requires_grad = True,會自動訓練
執行後的結果:features 裡的 13 層 Conv2d 全部凍結(約 14.7M 參數不更新);classifier 裡的 Linear-33、Linear-36 以及新的 classifier[6] 都是可訓練的(約 123.6M 參數會更新,其中主要是前兩個全連接層)。
這就是選項 B 講的:for param in model.features.parameters(): param.requires_grad = False。
技術版:PyTorch 遷移學習的 requires_grad 機制
中級考試大概率會考程式碼跟公式,所以這部分你還是要學。但如果現在學起來很痛苦,可以先跳過,等讀完其他題目回頭再來。
想像 VGG16 是一個有 16 個部門的大公司。前 13 個部門(卷積層,features)是資深員工,他們已經在大公司學了 10 年,現在要換到動物園工作,這些技能(邊緣偵測、紋理分析)還是通用的,不需要重新訓練,讓他們「凍結在原本的知識狀態」。
後 3 個部門(全連接層,classifier)是分類決策部門,原本在分辨 1000 種物件,現在要改成辨識 10 種動物,這些人需要重新培訓(更新參數)。
PyTorch 的 requires_grad = False 就像在每個資深員工的胸牌上貼「免培訓」,梯度下降算法看到這個標籤就跳過不更新。
| 白話說法 | 程式碼 |
|---|---|
| 凍結卷積層(features) | for p in model.features.parameters(): p.requires_grad = False |
| 換掉最後分類層 | model.classifier[6] = nn.Linear(4096, 10) |
| 確認哪些參數需要訓練 | params = [p for p in model.parameters() if p.requires_grad] |
| 只把可訓練參數傳給優化器 | optimizer = torch.optim.Adam(params, lr=1e-4) |
| 凍結全部(選項 A 的錯誤) | for p in model.parameters(): p.requires_grad = False |
- requires_grad
- PyTorch 張量的屬性,True 表示需要計算梯度(會被反向傳播更新),False 表示凍結不動
- model.features
- VGG16 的卷積部分(nn.Sequential),包含 13 個 Conv2d 和相關 ReLU、MaxPool
- model.classifier
- VGG16 的全連接部分(nn.Sequential),classifier[6] 是最後一層輸出層
- model.parameters()
- 迭代模型所有參數(包含 features 和 classifier)
- model.features.parameters()
- 只迭代 features 部分的參數,不含 classifier
選項 A 的問題: model.parameters() 包含所有 features + classifier 的參數 都設 False 後,只有新換的 classifier[6] 可訓練 classifier[0-5] 被凍結 → 訓練不充分 選項 B(正確): model.features.parameters() 只包含 13 層 Conv2d 的參數 設 False 後,classifier[0-5] 和新的 classifier[6] 仍可訓練 符合「凍結卷積層、只訓練全連接層」的需求 選項 C 的問題: model.classifier.parameters() 只包含全連接層的參數 設 False → 全連接層被凍結,features 卻繼續更新 → 完全反了 選項 D 的問題: model.requires_grad = False 是設 Python 物件屬性,不是 nn.Parameter PyTorch 的反向傳播不看這個屬性,沒有效果
- requires_grad = False 和 torch.no_grad() 有什麼差異?各自用在什麼場景?
- 選項 A 和選項 B 有什麼差異?兩者訓練的參數數量各是多少?
- 遷移學習為什麼通常凍結卷積層而不是全連接層?
- 如果只想訓練最後兩層全連接層,程式碼應該怎麼改?
- 把 optimizer 只傳入 requires_grad=True 的參數,有什麼好處?
為什麼其他選項是錯的
字面在說什麼:凍結模型全部參數,再換最後一層。
為什麼不對:model.parameters() 包含 features 和 classifier 的所有參數。全部凍結後,只有新換的 classifier[6](建立時預設 requires_grad=True)是可訓練的,classifier[0] 到 classifier[5](前兩個全連接層)都被凍結了。題目要求「只訓練 classifier」,但 A 選項實際上只訓練了最後一層,效果遠不如同時訓練整個 classifier 的選項 B。
誰會選錯:認為「凍結全部再換最後一層」等同於「只訓練最後一層」,沒注意到「只訓練全連接層」應該是指整個 classifier 模組,不只是最後一層。
字面在說什麼:凍結全連接層(classifier)的參數,再換最後一層。
為什麼不對:這完全把需求搞反了。凍結 classifier 讓全連接層無法更新,反而讓 features(卷積層)繼續接受梯度更新。題目說「凍結卷積層」,結果凍結的是全連接層。訓練出來的模型會破壞預訓練的特徵提取能力,分類效果很差。
誰會選錯:讀題時漏掉「凍結卷積層」這個關鍵,看到 classifier 就覺得「最後幾層才需要訓練」,倒過來操作了。
字面在說什麼:直接對 model 物件設定 requires_grad 屬性來凍結參數。
為什麼不對:model 是 nn.Module 的子類,model.requires_grad = False 只是在這個 Python 物件上新增一個名為 requires_grad 的屬性,對 PyTorch 的梯度計算完全沒有影響。PyTorch 的凍結機制必須針對每個 nn.Parameter 物件設定其 requires_grad 屬性,只有對 parameter.requires_grad = False 才有效。這是典型的「語法看起來合理但語意完全錯誤」。
誰會選錯:不熟悉 PyTorch 的 Parameter 機制,以為對 Module 物件設定 requires_grad 就能控制所有參數的梯度,混淆了 Module 和 Parameter 的區別。
同個考點下次怎麼變形
直覺:如果只想凍結 VGG16 前 7 層卷積(features 的前半段),只訓練後半段卷積加全連接層,程式碼怎麼寫?
答案:`for i, layer in enumerate(model.features): if i < 14: for p in layer.parameters(): p.requires_grad = False`。features 模組的索引 0-13 對應前 7 組操作(Conv+ReLU 各兩個),索引 14 之後繼續訓練。實際上常見的策略是只凍結前幾個 Block,讓後幾個 Block 和全連接層一起 fine-tune。
直覺:怎麼驗證選項 B 執行後,features 確實凍結了?
答案:`for name, param in model.named_parameters(): print(name, param.requires_grad)`。features 的參數應顯示 False,classifier 的參數應顯示 True(除了舊有的 classifier[6] 已被替換)。或用 `sum(p.numel() for p in model.parameters() if p.requires_grad)` 確認可訓練參數數量 ≈ 123.6M(不含 features 的 14.7M)。
直覺:凍結 features 後,優化器應該怎麼設定才能節省記憶體和計算量?
答案:只把可訓練參數傳給優化器:`optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=1e-4)`。如果把凍結的參數也傳給優化器,反向傳播時仍然不會更新這些參數,但 Adam 仍會為它們儲存 m/v,浪費記憶體。正確做法是只傳需要更新的參數。
直覺:requires_grad = False 和 with torch.no_grad() 有什麼差異?
答案:requires_grad = False 是「這個參數不需要梯度,訓練時跳過」,是持久設定;torch.no_grad() 是一個 context manager,關閉當前程式碼塊內所有張量的梯度計算,常用於推論時節省記憶體(不儲存計算圖)。前者控制「某些參數是否更新」,後者控制「整個前向傳播是否計算梯度」。
直覺:ResNet-50 的遷移學習,要凍結所有卷積層,程式碼怎麼寫?
答案:ResNet-50 的結構不同,卷積層分散在多個 layer 中。常見做法是凍結除最後一層 fc 以外的所有參數:`for name, param in model.named_parameters(): if 'fc' not in name: param.requires_grad = False`,然後換 fc:`model.fc = nn.Linear(2048, num_classes)`。ResNet 只有一個全連接層,不像 VGG16 有 features/classifier 的清晰分組。
想再往下看,這 5 個
- 遷移學習(Transfer Learning)本題核心技術,利用預訓練模型的特徵提取能力,降低在小資料集上訓練的難度。
- 微調(Fine-Tuning)遷移學習的主要實作方式,凍結預訓練層後只更新特定層,讓模型適應新任務。
- 卷積神經網路(CNN)VGG16 連題組的架構基礎,理解 features/classifier 的模組分工是正確凍結的前提。
- 梯度下降(Gradient Descent)requires_grad 機制的底層原理,凍結就是讓反向傳播跳過不計算該參數的梯度。
- 特徵提取(Feature Extraction)遷移學習凍結卷積層的理論依據,預訓練的卷積層已學會通用的低階特徵,不需要重新訓練。