← 返回总目录

Day 117 — 迁移学习 (Transfer Learning)

数据分析与AI/ML 270 行 3 个可运行脚本

Day 117 — 迁移学习 (Transfer Learning)

🎯 今日目标:掌握迁移学习的核心思想,学会使用预训练模型进行微调,实现自定义图片分类


一、什么是迁移学习?

1.1 核心思想

迁移学习(Transfer Learning)是指将在大规模数据集上训练好的模型,迁移到目标任务上使用。

为什么需要迁移学习?

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% │     │
    └─────────────┘

迁移学习:复用浅层和中层,微调深层和分类头

八、思考题

  1. 为什么迁移学习有效? 从特征复用的角度思考,预训练模型的浅层和深层分别学到了什么?

  2. 学习率的选择:在微调预训练模型时,为什么底层的学习率要小于顶层?如果所有层用相同的学习率会怎样?

  3. 数据增强策略:当目标任务的数据量很小时,除了迁移学习,还有哪些数据增强策略可以提升效果?

  4. 模型选择:在资源受限的移动端设备上部署图像分类模型,你会选择 ResNet-18、EfficientNet-B0 还是 MobileNet?为什么?

  5. 负迁移(Negative Transfer):在什么情况下迁移学习可能比从头训练效果更差?如何避免?


参考资料