引言
从零训练一个图像分类/文本模型要几千万样本和大量 GPU,而现实中你的数据可能只有几千条。**迁移学习(Transfer Learning)**把「别人在大数据上学到的通用表示」拿过来,用你的小数据做适配——这是 CV 与 NLP 的默认起手式。本文讲清:什么是迁移学习、预训练模型从哪来、冻结与微调的取舍、以及一套可复用的 PyTorch 微调流水线。
前置:/ml-neural-networks-basics/(PyTorch 基础)、/ml-cv-basics/(CNN 图像基础)、/ml-deep-learning-advanced/(训练技巧)。
目录
- 1. 为什么从零训练不划算
- 2. 迁移学习的三种范式
- 3. 预训练模型生态与下载
- 4. 冻结 vs 微调:怎么选
- 5. 特征提取:把模型当特征器
- 6. 微调策略:分层学习率与逐步解冻
- 7. 适配层:换头与分类器设计
- 8. CNN 迁移实战:图像分类流水线
- 9. Transformer 迁移实战:NLP 微调
- 10. 速查表与一句话记忆
- 延伸阅读
1. 为什么从零训练不划算
1.1 数据与算力鸿沟
从零训练 ResNet-50 做猫狗分类:
需要 ImageNet 级别规模 + 多卡 GPU 数天
你的场景:
几千张图 + 单卡 → 效果还差
1.2 迁移学习的核心直觉
预训练模型在前置大任务上学到的通用特征(边缘→纹理→物体部件)可以复用到新任务——你的小数据只需要学「怎么在这些通用特征上做你的分类」。
记忆:迁移学习 = 复用大数据上学到的通用表示 + 小数据适配;解决「数据不够、算力不够」的起手式。
2. 迁移学习的三种范式
| 范式 | 做法 | 数据需求 |
|---|---|---|
| 特征提取 | 冻结骨干,只用输出特征训分类器 | 很少 |
| 全量微调 | 整网络继续训练 | 较多 |
| 增量微调 | 骨干低学习率,新层高学习率 | 中 |
范式选择核心权衡:冻结越多越省算力、越防过拟合;微调越多适配越强但越容易过拟合。
记忆:三种范式——纯特征提取、全量微调、增量微调;数据越多越可大胆微调,数据少就多冻结。
3. 预训练模型生态与下载
3.1 视觉生态(torchvision)
import torchvision.models as models
# ImageNet 预训练
resnet = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2)
densenet = models.densenet121(weights=models.DenseNet121_Weights.IMAGENET1K_V1)
vgg16 = models.vgg16(weights=models.VGG16_Weights.IMAGENET1K_V1)
3.2 文本生态(HuggingFace Transformers)
from transformers import AutoModel, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('bert-base-uncased')
bert = AutoModel.from_pretrained('bert-base-uncased')
记忆:视觉用 torchvision 的 ImageNet 权重一行下载;文本用 HuggingFace 的 AutoModel/AutoTokenizer 自动拉取。
4. 冻结 vs 微调:怎么选
4.1 决策表
| 你的数据量 | 与预训练任务相似度 | 建议 |
|---|---|---|
| 少 | 高 | 特征提取(全冻结) |
| 少 | 低 | 冻结前几层 + 微调高层 |
| 多 | 高 | 全量微调(低学习率) |
| 多 | 低 | 全量微调(稍高学习率) |
4.2 冻结代码
for param in model.parameters():
param.requires_grad = False # 全部冻结
# 只让新分类头可训练
for param in model.fc.parameters():
param.requires_grad = True
记忆:数据少或任务相似 → 多冻结;数据多或任务差异大 → 多微调;requires_grad=False 即冻结。
5. 特征提取:把模型当特征器
5.1 两步法
① 前向拿到特征(去掉分类头),② 用特征训一个简单分类器:
import torch
def extract_features(model, loader):
model.eval()
feats, labels = [], []
with torch.no_grad():
for x, y in loader:
feat = model(x) # 此时模型输出是特征向量
feats.append(feat); labels.append(y)
return torch.cat(feats), torch.cat(labels)
# 替换分类头为恒等(或取 avgpool 输出)
model.fc = torch.nn.Identity()
X_feat, y_feat = extract_features(model, train_loader)
# 用 sklearn 逻辑回归/线性 SVM 训分类
from sklearn.linear_model import LogisticRegression
clf = LogisticRegression(max_iter=1000).fit(X_feat.numpy(), y_feat.numpy())
5.2 优点
- 只需一次前向,极快
- 特征固定,无过拟合风险
- 换分类器无需重算特征
记忆:特征提取两步——冻结模型输出特征向量,再用逻辑回归/SVM 训分类器;一次前向、快且不易过拟合。
6. 微调策略:分层学习率与逐步解冻
6.1 分层学习率
底层特征通用、要小步慢走;新分类头从零学、可以大步:
optimizer = torch.optim.AdamW([
{'params': model.parameters(), 'lr': 1e-5}, # 骨干:小学习率
{'params': model.fc.parameters(), 'lr': 3e-4}, # 新头:大学习率
])
6.2 逐步解冻(Progressive Unfreezing)
先只训新头 → 稳定后解冻最后几层 → 逐段向底层解冻。适合任务差异较大的情况。
记忆:微调用分层学习率(骨干 1e-5、新头 3e-4);任务差异大时逐步解冻,稳定一段解冻一段。
7. 适配层:换头与分类器设计
7.1 替换分类头
# ResNet 默认 1000 类 → 换成你的 K 类
model.fc = torch.nn.Sequential(
torch.nn.Linear(2048, 256),
torch.nn.ReLU(),
torch.nn.Dropout(0.3),
torch.nn.Linear(256, K))
7.2 为什么不建议只换线性层
浅头表达力有限;加一层 ReLU + Dropout 能显著提升小数据效果。分类头设计:
| 你的数据量 | 头结构 |
|---|---|
| <1k | 单线性层 + 强正则 |
| 1k-10k | 一层隐层 + Dropout |
| >10k | 可深一点 + BatchNorm |
记忆:换头时加一层隐层+Dropout 比纯线性头效果好;头越简单数据越少越安全。
8. CNN 迁移实战:图像分类流水线
8.1 完整代码骨架
import torch, torchvision
from torchvision import datasets, transforms, models
import torch.nn as nn
# ① 预训练 + 换头
model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1)
model.fc = nn.Sequential(nn.Linear(512, 128), nn.ReLU(), nn.Dropout(0.3), nn.Linear(128, 2))
# ② 数据增强(小数据必备)
train_tf = transforms.Compose([
transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(),
transforms.ColorJitter(0.2, 0.2, 0.2),
transforms.ToTensor(), transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])])
# ③ 分层优化器
optimizer = torch.optim.AdamW([
{'params': model.parameters(), 'lr': 1e-5},
{'params': model.fc.parameters(), 'lr': 3e-4}])
# ④ 标准训练循环(省略)→ 早停 + 保存最佳
8.2 关键纪律
- 用预训练同款 Normalize 均值方差
- 验证集评估选最佳、测试集只用一次
- 小数据务必配数据增强
记忆:CNN 迁移四步——预训练换头、数据增强、分层优化器、早停存最佳;Normalize 必须跟预训练一致。
9. Transformer 迁移实战:NLP 微调
9.1 用 Trainer API 快速微调
from transformers import AutoModelForSequenceClassification, TrainingArguments, Trainer
model = AutoModelForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=2)
args = TrainingArguments('out', learning_rate=2e-5, num_train_epochs=3,
per_device_train_batch_size=8, evaluation_strategy='epoch')
trainer = Trainer(model=model, args=args, train_dataset=train_ds, eval_dataset=val_ds)
trainer.train()
9.2 NLP 微调要点
- BERT 类学习率典型 2e-5 ~ 5e-5(比 CNN 更小)
- 需按任务加 tokenizer 的 padding/truncation
- 短文本下小模型(distilbert)效果接近且快
记忆:NLP 用 Transformers 的 Trainer 几行微调;BERT 学习率 2e-5 量级,别照搬 CNN 的 1e-3。
10. 速查表与一句话记忆
| 环节 | 关键决策 |
|---|---|
| 范式 | 特征提取 / 全量 / 增量微调 |
| 数据量少 | 多冻结 + 简单头 |
| 数据量多 | 低学习率全量微调 |
| 优化器 | AdamW + 分层学习率 |
| 视觉 | torchvision 换 fc 头 |
| 文本 | Trainer + BERT 2e-5 |
| 正则 | 数据增强 / Dropout / 早停 |
一句话记忆:迁移学习 = 复用大数据预训练模型的通用表示,用小数据适配——数据少就冻结骨干只训新分类头(特征提取),数据多就低学习率全量微调;用分层学习率(骨干 1e-5、新头 3e-4)兼顾稳定与适配;视觉用 torchvision 换头、文本用 Transformers 的 Trainer 微调 BERT;小数据必配数据增强 + 早停,测试集只评估一次。
延伸阅读
- /ml-cv-basics/ — CNN 图像分类基础
- /ml-deep-learning-advanced/ — 正则化与训练调参
- /ml-neural-networks-basics/ — PyTorch 张量与训练循环
- /ml-nlp-basics/ — NLP 与 HuggingFace 生态
- /ml-model-deployment/ — 微调后模型部署
- [[ai-ml]] — 深度学习进阶专题
- HuggingFace 微调文档
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。