迁移学习实战:预训练模型、特征提取与微调全流程

迁移学习实战:什么是迁移学习、预训练模型生态(ImageNet/HuggingFace)、冻结与微调策略、特征提取 vs 微调的选择、学习率分层、适配层设计、CNN 与 Transformer 迁移、完整微调代码流水线。

引言

从零训练一个图像分类/文本模型要几千万样本和大量 GPU,而现实中你的数据可能只有几千条。**迁移学习(Transfer Learning)**把「别人在大数据上学到的通用表示」拿过来,用你的小数据做适配——这是 CV 与 NLP 的默认起手式。本文讲清:什么是迁移学习、预训练模型从哪来、冻结与微调的取舍、以及一套可复用的 PyTorch 微调流水线。

前置:/ml-neural-networks-basics/(PyTorch 基础)、/ml-cv-basics/(CNN 图像基础)、/ml-deep-learning-advanced/(训练技巧)。


目录


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 微调文档

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ml」更多文章

  1. 集成学习实战:Bagging、随机森林、梯度提升与 Stacking
  2. 计算机视觉入门实战:图像处理与 CNN 图像分类
  3. 深度学习进阶:CNN、RNN、正则化与训练调参实战