Appearance
微调
一、什么是微调
微调(fine-tuning)是迁移学习的一种常见技巧,就是把在一个大数据集(比如 ImageNet)上训练好的模型(源模型),拿来在一个小的目标数据集上进行调整,让模型能适应新的任务。
比如我们想识别图片里的椅子,但是我们只有几千张椅子的图片,直接训练模型很容易过拟合,而且准确率不高。这时候我们可以用在 ImageNet 上训练好的 ResNet 模型,这个模型已经学会了很多通用的图像特征,比如边缘、纹理、形状这些,这些特征对识别椅子也有用。我们只需要把这个模型的输出层改一下,变成识别椅子的类别,然后用我们的椅子数据集稍微调整一下模型的参数,就能得到一个准确率不错的模型了。
二、微调的步骤
微调的步骤很简单,一共四步:
先在源数据集(比如 ImageNet)上训练好一个模型,这个模型就是源模型。
创建一个新的目标模型,复制源模型的所有层和参数,但是不复制输出层,因为源模型的输出层是针对源数据集的类别,和我们的目标任务不一样。
给目标模型加一个新的输出层,输出层的类别数是目标数据集的类别数,然后随机初始化这个输出层的参数。
用目标数据集训练目标模型,输出层从头开始训练,其他层的参数用小的学习率进行微调。
三、实战:热狗识别
我们用热狗识别的例子来看看怎么用 PyTorch 做微调。
1. 获取数据集
我们用的热狗数据集有 1400 张热狗的图片,还有 1400 张其他食物的图片,其中 1000 张用来训练,400 张用来测试。
首先下载数据集:
python
import torch
import torchvision
from d2l import torch as d2l
d2l.DATA_HUB['hotdog'] = (d2l.DATA_URL + 'hotdog.zip',
'fba480ffa8aa7e0febbb511d181409f899b9baa5')
data_dir = d2l.download_extract('hotdog')然后读取训练和测试数据集:
python
train_imgs = torchvision.datasets.ImageFolder(os.path.join(data_dir, 'train'))
test_imgs = torchvision.datasets.ImageFolder(os.path.join(data_dir, 'test'))
2. 数据预处理
训练的时候,我们需要对图片做一些增强,比如随机裁剪、随机翻转,然后把图片转换成模型需要的格式,再做标准化:
python
# 标准化的均值和标准差,用的是ImageNet的统计值
normalize = torchvision.transforms.Normalize(
[0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
# 训练集的预处理
train_augs = torchvision.transforms.Compose([
torchvision.transforms.RandomResizedCrop(224),
torchvision.transforms.RandomHorizontalFlip(),
torchvision.transforms.ToTensor(),
normalize])
# 测试集的预处理
test_augs = torchvision.transforms.Compose([
torchvision.transforms.Resize([256, 256]),
torchvision.transforms.CenterCrop(224),
torchvision.transforms.ToTensor(),
normalize])3. 定义模型
我们用在 ImageNet 上预训练好的 ResNet-18 作为源模型,然后把输出层改成 2 类(热狗和不是热狗):
python
# 加载预训练的ResNet-18
pretrained_net = torchvision.models.resnet18(pretrained=True)
# 查看源模型的输出层
print(pretrained_net.fc) # Linear(in_features=512, out_features=1000, bias=True)
# 创建目标模型,把输出层改成2类
finetune_net = torchvision.models.resnet18(pretrained=True)
finetune_net.fc = torch.nn.Linear(finetune_net.fc.in_features, 2)
# 初始化输出层的参数
torch.nn.init.xavier_uniform_(finetune_net.fc.weight)4. 训练模型
我们定义一个训练函数,训练的时候,输出层用大一点的学习率,其他层用小一点的学习率:
python
def train_fine_tuning(net, learning_rate, batch_size=128, num_epochs=5):
# 加载数据
train_iter = torch.utils.data.DataLoader(
torchvision.datasets.ImageFolder(os.path.join(data_dir, 'train'), transform=train_augs),
batch_size=batch_size, shuffle=True)
test_iter = torch.utils.data.DataLoader(
torchvision.datasets.ImageFolder(os.path.join(data_dir, 'test'), transform=test_augs),
batch_size=batch_size)
# 获取所有GPU
devices = d2l.try_all_gpus()
# 把模型放到GPU上
net = torch.nn.DataParallel(net, device_ids=devices)
# 损失函数
loss = torch.nn.CrossEntropyLoss(reduction="none")
# 优化器,输出层的学习率是其他层的10倍
params_1x = [param for name, param in net.named_parameters()
if name not in ["module.fc.weight", "module.fc.bias"]]
trainer = torch.optim.SGD([
{'params': params_1x},
{'params': net.module.fc.parameters(), 'lr': learning_rate * 10}
], lr=learning_rate, weight_decay=0.001)
# 训练
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)
# 开始训练,用小的学习率
train_fine_tuning(finetune_net, 5e-5)训练完成后,我们可以看到模型的测试准确率大概在 93% 左右,比从头训练的模型准确率高很多。
5. 对比:从头训练模型
我们也可以训练一个从头初始化的模型,看看对比效果:
python
# 从头初始化模型
scratch_net = torchvision.models.resnet18()
scratch_net.fc = torch.nn.Linear(scratch_net.fc.in_features, 2)
# 训练,用大一点的学习率
train_fine_tuning(scratch_net, 5e-4, param_group=False)从头训练的模型测试准确率大概在 84% 左右,比微调的模型低很多,说明微调确实能提升模型的性能。
四、小结
微调是迁移学习的一种方法,把在大数据集上训练好的模型拿来在小数据集上调整,能
提升模型的泛化能力,避免过拟合。微调的时候,我们复制源模型的所有层和参数,只替换输出层,然后用小的学习率微调其他层的参数,输出层用大一点的学习率从头训练。
微调的模型比从头训练的模型准确率高很多,因为源模型已经学会了很多通用的特征,这些特征对目标任务也有用。
(注:文档部分内容可能由 AI 生成) 源地址