第73课:迁移学习与预训练模型
学习目标
- 理解迁移学习的核心概念,知道为什么迁移学习能提高训练效率和模型性能
- 掌握使用预训练模型进行特征提取和模型微调的方法
- 学会在PyTorch和Keras中实现迁移学习,完成图像分类任务
- 了解不同场景下如何选择合适的预训练模型和微调策略
核心概念
什么是迁移学习?
想象一下,你已经学会了骑自行车,现在要学习骑摩托车。你不需要从零开始学习平衡和转向,因为自行车经验可以直接迁移过来。迁移学习就是这个道理——把在源任务(如ImageNet分类)上学到的知识,应用到新任务(如医学图像识别)上。
预训练模型的作用
预训练模型(如ResNet、VGG、BERT)已经在大型数据集上训练好,学到了丰富的特征提取能力:
- 底层特征:边缘、纹理、颜色
- 中层特征:形状、局部模式
- 高层特征:物体部件、语义概念
迁移学习的两种策略
- 特征提取:冻结预训练模型,只训练新添加的分类层
- 微调:解冻部分或全部预训练层,用小学习率继续训练
代码示例
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:基础迁移学习
要求:
- 使用PyTorch的
vgg16预训练模型 - 修改分类层,使其适配CIFAR-10数据集(10个类别)
- 只训练分类层,观察训练过程
预期输出:
- 显示模型结构中可训练参数的数量
- 训练5个epoch后的准确率应明显高于随机猜测
练习2:微调策略实验
要求:
- 在练习1的基础上,解冻模型的最后2个卷积块
- 比较以下两种策略的效果:
- 策略A:只训练分类层
- 策略B:微调最后2个卷积块 + 分类层
- 使用更小的学习率(如0.0001)进行微调
预期输出:
- 两种策略在测试集上的准确率对比
- 微调后模型的参数量变化
练习3:自定义数据集应用
要求:
- 假设你有100张猫狗图片(50猫+50狗)
- 使用迁移学习构建二分类模型
- 分别测试以下两种方案:
- 方案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
小结
关键要点回顾
- 迁移学习的核心:利用预训练模型的特征提取能力,加速新任务的学习
- 预训练模型选择:根据任务相似性选择合适的模型(图像→CNN,文本→Transformer)
- 两种主要策略:
- 特征提取:冻结预训练层,只训练分类层(适合小数据集)
- 微调:解冻部分预训练层,用小学习率继续训练(需要更多数据)
- 实现步骤:
- 加载预训练模型
- 冻结相应层
- 修改输出层
- 选择合适的优化策略
- 实践技巧:
- 保持输入预处理一致
- 学习率要分层设置
- 数据量小时优先使用特征提取
- 验证时保持BN层正确行为
迁移学习的优势
- 训练更快:只需训练少量参数
- 数据需求少:小数据集也能获得不错效果
- 性能更好:预训练特征通常比随机初始化好
- 应用广泛:计算机视觉、自然语言处理、语音识别等
迁移学习是深度学习中非常实用的技术,掌握它能让你在实际项目中快速构建高性能模型。下一课我们将学习GPU训练与分布式训练,进一步提升训练效率!
练习编辑器
rust
Loading...