一、迁移学习中的常见技巧:微调(fine-tuning)
1.1 概念
- 将在大数据集上训练得到的weights作为特定任务(小数据集)的初始化权重,重新训练该网络(根据需要,修改全连接层输出);至于训练的方式可以是:
1.微调所有层; 2.固定网络前面几层权重,只微调网络的后面几层,这样做有两个原因:A. 避免因数据量小造成过拟合现象;B.CNN前几层的特征中包含更多的一般特征(比如,边缘信息,色彩信息等),这对许多任务来说是非常通用的,但是CNN后面几层的特征学习注重高层特征,也就是语义特征,这是针对于数据集而言的,不同的数据集后面几层学习的语义特征也是完全不同的;
1.2 步骤
- 在源数据集上训练神经网络模型或将已经在大数据集上训练好的模型保存的模型,即源模型;
- 创建新的神经网络模型,即目标模型。这将复制源模型上的所有模型设计(即模型层数设计)及其参数(输出层除外)。假定模型参数包含从源数据集中学到的知识,这些知识也将适用于目标数据集;
- 想目标模型中添加输出层,其输出类别数目是目标数据集中的类别数, 然后随机初始化该层的模型参数;
- 在目标数据集上训练目标模型,输出层从头开始训练,其他所有层的参数将根据源模型的参数进行微调。
1.3 训练
- 源数据集远复杂于目标数据,通常微调效果更好;
- 通常使用更小的学习率和更少的数据迭代;
1.4 实现
from d2l import torch as d2l
from torch import nn
import torchvision
import torch
import os
%matplotlib inline
"""
我们使用的热狗数据集来源于网络。
该数据集包含1400张热狗的“正类”图像,以及包含尽可能多的其他食物的“负类”图像。
含着两个类别的1000张图片用于训练,其余的则用于测试。
"""
d2l.DATA_HUB['hotdog'] = (d2l.DATA_URL + 'hotdog.zip',
'fba480ffa8aa7e0febbb511d181409f899b9baa5')
data_dir = d2l.download_extract('hotdog')
print(data_dir)
train_imgs=torchvision.datasets.ImageFolder(os.path.join(data_dir,'train'))
test_imgs=torchvision.datasets.ImageFolder(os.path.join(data_dir,'test'))
hotdogs=[train_imgs[i][0] for i in range(8)]
not_hotdogs=[train_imgs[-i-1][0] for i in range(8)]
d2l.show_images(hotdogs+not_hotdogs,2,8,scale=1.4)
"""
在训练期间,我们首先从图像中裁切随机大小和随机长宽比的区域,然后将该区域缩放为\(224*224\)输入图像。
在测试过程中,我们将图像的高度和宽度都缩放到256像素,然后裁剪中央\(224*224\)区域作为输入。
此外,对于RGB(红、绿和蓝)颜色通道,我们分别标准化每个通道。
具体而言,该通道的每个值减去该通道的平均值,然后将结果除以该通道的标准差。
"""
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),
torchvision.transforms.CenterCrop(224),
torchvision.transforms.ToTensor(),
normalize])
pretrained_net=torchvision.models.resnet18(pretrained=True)
"""
预训练的源模型实例包含许多特征层和一个输出层fc(全连接层)。
此划分的主要目的是促进对除输出层以外所有层的模型参数进行微调。
下面给出了源模型的成员变量fc。
"""
pretrained_net.fc
finetune_net=torchvision.models.resnet18(pretrained=True)
finetune_net.fc=nn.Linear(finetune_net.fc.in_features,2)
nn.init.xavier_uniform_(finetune_net.fc.weight)
tensor([[ 0.0378, 0.0630, -0.0080, ..., -0.0220, -0.0511, 0.0959],
[ 0.0556, 0.0227, -0.0262, ..., -0.1059, -0.0171, 0.0051]],
requires_grad=True)
def train_fine_tuning(net, learning_rate, batch_size=128, num_epochs=5,
param_group=True):
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)
devices = d2l.try_all_gpus()
loss = nn.CrossEntropyLoss(reduction="none")
if param_group:
params_1x = [param for name, param in net.named_parameters()
if name not in ["fc.weight", "fc.bias"]]
trainer = torch.optim.SGD([{'params': params_1x},
{'params': net.fc.parameters(),
'lr': learning_rate * 10}],
lr=learning_rate, weight_decay=0.001)
else:
trainer = torch.optim.SGD(net.parameters(), 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)
scratch_net = torchvision.models.resnet18()
scratch_net.fc = nn.Linear(scratch_net.fc.in_features, 2)
train_fine_tuning(scratch_net, 5e-4, param_group=False)
|