Day 117 — 迁移学习 (Transfer Learning)
🎯 今日目标:掌握迁移学习的核心思想,学会使用预训练模型进行微调,实现自定义图片分类
一、什么是迁移学习?
1.1 核心思想
迁移学习(Transfer Learning)是指将在大规模数据集上训练好的模型,迁移到目标任务上使用。
为什么需要迁移学习?
- 从头训练一个深度学习模型需要海量数据和大量计算资源
- 真实场景中,我们往往只有少量标注数据
- 预训练模型已经在 ImageNet(1400万张图片,1000个类别)上学到了通用的视觉特征
- 我们可以复用这些通用特征,只需要针对目标任务微调
1.2 类比理解
人类学习 vs 迁移学习
─────────────────────────────────────
人类:学了英语 → 学法语更容易(语言知识迁移)
模型:在 ImageNet 训练 → 识别医学影像更容易(视觉特征迁移)
关键点:底层特征(边缘、纹理)是通用的
二、预训练模型
2.1 常见预训练模型
| 模型 | 参数量 | Top-1 准确率 | 特点 | 适用场景 |
|---|---|---|---|---|
| ResNet-18 | 11.7M | 69.8% | 轻量级,残差连接 | 快速实验、移动端 |
| ResNet-50 | 25.6M | 76.1% | 平衡性能与效率 | 通用任务首选 |
| ResNet-152 | 60.2M | 78.3% | 深层网络 | 高精度要求 |
| VGG-16 | 138M | 71.6% | 结构简单,参数多 | 特征提取、风格迁移 |
| EfficientNet-B0 | 5.3M | 77.1% | 高效,可缩放 | 资源受限场景 |
| EfficientNet-B4 | 19M | 82.9% | 高效+高精度 | 通用高精度任务 |
2.2 PyTorch 中使用预训练模型
python
import torchvision.models as models
# 加载预训练的 ResNet-50(权重在 ImageNet 上预训练)
model = models.resnet50(weights='IMAGENET1K_V2')
# 查看模型结构
print(model)
2.3 模型结构解析(以 ResNet-50 为例)
ResNet-50 结构
═══════════════════════════════════════════════════════
Input (224×224×3)
│
├── conv1 (7×7, 64) + BN + ReLU + MaxPool
│
├── layer1 (3 blocks) ──── 256 channels
│ └── BasicBlock: conv → BN → ReLU → conv → BN + shortcut
│
├── layer2 (4 blocks) ──── 512 channels
│
├── layer3 (6 blocks) ──── 1024 channels
│
├── layer4 (3 blocks) ──── 2048 channels
│
├── AdaptiveAvgPool2d (1×1)
│
└── fc (2048 → 1000) ← ImageNet 的 1000 类
═══════════════════════════════════════════════════════
三、迁移学习的两种核心策略
3.1 策略对比
策略一:特征提取(Feature Extraction)
═══════════════════════════════════════════
┌─────────────────────────┐
│ 冻结预训练模型的卷积层 │ ← 不更新权重
│ 只训练最后的分类层 │ ← 只更新这一部分
└─────────────────────────┘
优点:训练快、数据需求少
缺点:特征可能不匹配
策略二:微调(Fine-tuning)
═══════════════════════════════════════════
┌─────────────────────────┐
│ 解冻部分/全部预训练层 │ ← 用小学习率更新
│ 用更小的学习率训练 │ ← 避免破坏已有特征
│ 同时训练新的分类层 │
└─────────────────────────┘
优点:效果更好、适应性更强
缺点:需要更多数据、训练更慢
3.2 如何选择?
| 场景 | 推荐策略 | 原因 |
|---|---|---|
| 数据量 < 1000 张 | 特征提取 | 数据太少,微调容易过拟合 |
| 数据量 1000~10000 张 | 微调(冻结前几层) | 平衡效果与稳定性 |
| 数据量 > 10000 张 | 全量微调 | 数据充足,可以充分训练 |
| 目标任务与源任务相似 | 特征提取 | 通用特征已经很有效 |
| 目标任务与源任务差异大 | 全量微调 | 需要调整更多层的特征 |
四、微调(Fine-tuning)详解
4.1 微调的关键技巧
python
# 技巧 1:分层学习率(Layer-wise Learning Rate)
# 底层特征更通用 → 学习率更小
# 高层特征更任务相关 → 学习率更大
# 技巧 2:渐进式解冻(Gradual Unfreezing)
# 先训练分类头 → 冻结 → 解冻最后几层 → 逐步解冻更多层
# 技巧 3:差分学习率(Discriminative Learning Rates)
# 不同层使用不同的学习率
4.2 微调流程图
微调流程
═══════════════════════════════════════════════════════════
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 加载预训练模型 │ → │ 修改分类头 │ → │ 冻结卷积层 │
│ (ImageNet) │ │ (适配任务) │ │ │
└──────────────┘ └──────────────┘ └──────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 训练分类头 │ → │ 解冻部分层 │ → │ 全量微调 │
│ (冻结卷积层) │ │ (小学习率) │ │ (更小学习率) │
└──────────────┘ └──────────────┘ └──────────────┘
五、API 速查表
5.1 PyTorch 迁移学习核心 API
python
# 1. 加载预训练模型
import torchvision.models as models
model = models.resnet50(weights='IMAGENET1K_V2')
# 2. 冻结所有参数
for param in model.parameters():
param.requires_grad = False
# 3. 修改分类头(替换最后的全连接层)
model.fc = nn.Linear(model.fc.in_features, num_classes)
# 4. 只训练分类头的参数
trainable_params = filter(lambda p: p.requires_grad, model.parameters())
# 5. 差分学习率
optimizer = Adam([
{'params': model.layer4.parameters(), 'lr': 1e-4},
{'params': model.fc.parameters(), 'lr': 1e-3},
])
# 6. 解冻指定层
for param in model.layer4.parameters():
param.requires_grad = True
5.2 常用预训练权重
python
# PyTorch 支持的权重
weights = {
'IMAGENET1K_V1': '标准权重',
'IMAGENET1K_V2': '改进权重(推荐)',
'IMAGENET1K_V1_OLD': '旧版权重',
}
# 加载方式
model = models.resnet50(weights='IMAGENET1K_V2')
model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2)
六、实战:自定义图片分类
6.1 任务描述
使用 ResNet-50 进行迁移学习,对自定义数据集进行分类。我们使用 torchvision.datasets 自带的数据集作为演示。
6.2 完整代码
见 code/ 目录下的代码文件。
七、图解
7.1 迁移学习的特征层级
深度网络的特征层级
═══════════════════════════════════════════════════════════
输入图像: 🐱 猫的照片
Layer 1-2 (浅层): 检测边缘、颜色、纹理
┌─────────────┐
│ ─ │ ╱ │ ╲ │ → 边缘检测器
└─────────────┘
Layer 3-4 (中层): 检测局部形状、纹理组合
┌─────────────┐
│ ◯ │ △ │ □ │ → 形状检测器
└─────────────┘
Layer 5+ (深层): 检测高级语义特征
┌─────────────┐
│ 👁 │ 👂 │ 🐾 │ → 语义特征
└─────────────┘
分类头: 最终分类决策
┌─────────────┐
│ 猫 95% │ │ → 输出概率
│ 狗 3% │ │
└─────────────┘
迁移学习:复用浅层和中层,微调深层和分类头
八、思考题
-
为什么迁移学习有效? 从特征复用的角度思考,预训练模型的浅层和深层分别学到了什么?
-
学习率的选择:在微调预训练模型时,为什么底层的学习率要小于顶层?如果所有层用相同的学习率会怎样?
-
数据增强策略:当目标任务的数据量很小时,除了迁移学习,还有哪些数据增强策略可以提升效果?
-
模型选择:在资源受限的移动端设备上部署图像分类模型,你会选择 ResNet-18、EfficientNet-B0 还是 MobileNet?为什么?
-
负迁移(Negative Transfer):在什么情况下迁移学习可能比从头训练效果更差?如何避免?