73·深度学习入门进阶

迁移学习与预训练模型

deep-learningtransfer-learning

第73课:迁移学习与预训练模型

学习目标

  1. 理解迁移学习的核心概念,知道为什么迁移学习能提高训练效率和模型性能
  2. 掌握使用预训练模型进行特征提取和模型微调的方法
  3. 学会在PyTorch和Keras中实现迁移学习,完成图像分类任务
  4. 了解不同场景下如何选择合适的预训练模型和微调策略

核心概念

什么是迁移学习?

想象一下,你已经学会了骑自行车,现在要学习骑摩托车。你不需要从零开始学习平衡和转向,因为自行车经验可以直接迁移过来。迁移学习就是这个道理——把在源任务(如ImageNet分类)上学到的知识,应用到新任务(如医学图像识别)上。

预训练模型的作用

预训练模型(如ResNet、VGG、BERT)已经在大型数据集上训练好,学到了丰富的特征提取能力:

  • 底层特征:边缘、纹理、颜色
  • 中层特征:形状、局部模式
  • 高层特征:物体部件、语义概念

迁移学习的两种策略

  1. 特征提取:冻结预训练模型,只训练新添加的分类层
  2. 微调:解冻部分或全部预训练层,用小学习率继续训练

代码示例

PyTorch实现迁移学习

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import models, transforms
from torch.utils.data import DataLoader
import torchvision.datasets as datasets

# 1. 数据预处理(与预训练模型训练时保持一致)
train_transform = transforms.Compose([
    transforms.RandomResizedCrop(224),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406],
                        std=[0.229, 0.224, 0.225])
])

# 2. 加载预训练模型(以ResNet18为例)
model = models.resnet18(pretrained=True)

# 3. 冻结所有层(特征提取模式)
for param in model.parameters():
    param.requires_grad = False

# 4. 替换最后的分类层(根据新任务调整输出类别数)
num_classes = 10  # 假设是10分类任务
model.fc = nn.Sequential(
    nn.Linear(model.fc.in_features, 256),
    nn.ReLU(),
    nn.Dropout(0.3),
    nn.Linear(256, num_classes)
)

# 5. 只优化新添加的参数
optimizer = optim.Adam(model.fc.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()

print(f"模型参数总数: {sum(p.numel() for p in model.parameters()):,}")
print(f"可训练参数: {sum(p.numel() for p in model.parameters() if p.requires_grad):,}")

Keras实现迁移学习

import tensorflow as tf
from tensorflow.keras.applications import ResNet50
from tensorflow.keras import layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator

# 1. 加载预训练模型(不包含顶部分类层)
base_model = ResNet50(weights='imagenet', include_top=False, input_shape=(224, 224, 3))

# 2. 冻结基础模型
base_model.trainable = False

# 3. 添加自定义分类层
model = models.Sequential([
    base_model,
    layers.GlobalAveragePooling2D(),
    layers.Dense(512, activation='relu'),
    layers.Dropout(0.5),
    layers.Dense(num_classes, activation='softmax')
])

# 4. 编译模型
model.compile(
    optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
    loss='categorical_crossentropy',
    metrics=['accuracy']
)

model.summary()
print(f"可训练参数: {sum([tf.keras.backend.count_params(w) for w in model.trainable_weights]):,}")

实践练习

练习1:基础迁移学习

要求

  1. 使用PyTorch的vgg16预训练模型
  2. 修改分类层,使其适配CIFAR-10数据集(10个类别)
  3. 只训练分类层,观察训练过程

预期输出

  • 显示模型结构中可训练参数的数量
  • 训练5个epoch后的准确率应明显高于随机猜测

练习2:微调策略实验

要求

  1. 在练习1的基础上,解冻模型的最后2个卷积块
  2. 比较以下两种策略的效果:
    • 策略A:只训练分类层
    • 策略B:微调最后2个卷积块 + 分类层
  3. 使用更小的学习率(如0.0001)进行微调

预期输出

  • 两种策略在测试集上的准确率对比
  • 微调后模型的参数量变化

练习3:自定义数据集应用

要求

  1. 假设你有100张猫狗图片(50猫+50狗)
  2. 使用迁移学习构建二分类模型
  3. 分别测试以下两种方案:
    • 方案A:完全从头训练
    • 方案B:使用预训练ResNet18 + 微调

预期输出

  • 两种方案的验证准确率对比
  • 分析迁移学习在小数据集上的优势

常见错误

1. 忽略输入预处理

错误:直接用原始图片输入预训练模型

# ❌ 错误:预训练模型期望特定的输入格式
train_transform = transforms.ToTensor()  # 缺少归一化

# ✅ 正确:使用与预训练时相同的预处理
train_transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406],
                        std=[0.229, 0.224, 0.225])
])

2. 学习率设置不当

错误:微调时使用过大的学习率

# ❌ 错误:微调时学习率太大,破坏已学到的特征
optimizer = optim.Adam(model.parameters(), lr=0.1)

# ✅ 正确:微调时使用更小的学习率
optimizer = optim.Adam([
    {'params': model.conv1.parameters(), 'lr': 0.0001},
    {'params': model.fc.parameters(), 'lr': 0.001}
])

3. 冻结层策略错误

错误:尝试冻结BatchNorm层但发现行为异常

# ❌ 错误:BatchNorm层即使冻结也会更新统计量
for param in model.bn1.parameters():
    param.requires_grad = False

# ✅ 正确:将模型设置为评估模式(但只影响BN和Dropout)
model.eval()  # 或 model.train(False)

4. 数据量不足导致过拟合

错误:小数据集上微调所有层

# ❌ 错误:1000张图片解冻所有层,容易过拟合
for param in model.parameters():
    param.requires_grad = True

# ✅ 正确:小数据集建议只微调最后1-2个块
for name, param in model.named_parameters():
    if "layer4" in name:  # 只解冻ResNet的layer4
        param.requires_grad = True

小结

关键要点回顾

  1. 迁移学习的核心:利用预训练模型的特征提取能力,加速新任务的学习
  2. 预训练模型选择:根据任务相似性选择合适的模型(图像→CNN,文本→Transformer)
  3. 两种主要策略
    • 特征提取:冻结预训练层,只训练分类层(适合小数据集)
    • 微调:解冻部分预训练层,用小学习率继续训练(需要更多数据)
  4. 实现步骤
    • 加载预训练模型
    • 冻结相应层
    • 修改输出层
    • 选择合适的优化策略
  5. 实践技巧
    • 保持输入预处理一致
    • 学习率要分层设置
    • 数据量小时优先使用特征提取
    • 验证时保持BN层正确行为

迁移学习的优势

  • 训练更快:只需训练少量参数
  • 数据需求少:小数据集也能获得不错效果
  • 性能更好:预训练特征通常比随机初始化好
  • 应用广泛:计算机视觉、自然语言处理、语音识别等

迁移学习是深度学习中非常实用的技术,掌握它能让你在实际项目中快速构建高性能模型。下一课我们将学习GPU训练与分布式训练,进一步提升训练效率!

练习编辑器

rust
Loading...

继续学习

完成本课后,建议继续学习下一课「GPU 训练与分布式训练基础」 以巩固所学知识。