迁移学习(微调Torchvision模型)
两种类型:微调和特征提取
微调:从与训练模型开始,更新新任务的所有模型参数,实质上是重新训练整个模型。
特征提取:从预训练模型开始,仅更新从中导出预测的最终图层权重。使用预训练的CNN作为固定的特征提取器,并且仅改变输出层。
这两种迁移学习方法都遵循如下步骤:
- 初始化与训练模型
- 重组最后一层,使其具有与新数据集类别数相同的输出数
- 为优化算法定义想要在训练期间更新的参数
- 运行训练步骤
辅助函数
1. 模型训练和验证代码(可作为参考)
train_model函数处理给定模型的训练和验证。作为输入,它需要PyTorch模型、数据加载器字典、损失函数、优化器、用于训练和验 证epoch数,以及当模型是初始模型时的布尔标志。跟踪最佳性能的模型(从验证准确率方面),并在训练结束时返回性能最好的模型。
def train_model(model, dataloaders, criterion, optimizer, num_epochs=25, is_inception=False):
since = time.time()
val_acc_history = []
best_model_wts = copy.deepcopy(model.state_dict())
best_acc = 0.0
for epoch in range(num_epochs):
print('Epoch {}/{}'.format(epoch, num_epochs - 1))
print('-' * 10)
# 每个epoch都有一个训练和验证阶段
for phase in ['train', 'val']:
if phase == 'train':
model.train() # Set model to training mode
else:
model.eval() # Set model to evaluate mode
running_loss = 0.0
running_corrects = 0
# 迭代数据
for inputs, labels in dataloaders[phase]:
inputs = inputs.to(device)
labels = labels.to(device)
# 零参数梯度
optimizer.zero_grad()
# 前向
# 如果只在训练时则跟踪轨迹
with torch.set_grad_enabled(phase == 'train'):
# 获取模型输出并计算损失
# 开始的特殊情况,因为在训练中它有一个辅助输出。
# 在训练模式下,我们通过将最终输出和辅助输出相加来计算损耗
# 但在测试中我们只考虑最终输出。
if is_inception and phase == 'train':
# From https://discuss.pytorch.org/t/how-to-optimize-inception-model-with-auxiliary-classifiers/7958
outputs, aux_outputs = model(inputs)
loss1 = criterion(outputs, labels)
loss2 = criterion(aux_outputs, labels)
loss = loss1 + 0.4*loss2
else:
outputs = model(inputs)
loss = criterion(outputs, labels)
_, preds = torch.max(outputs, 1)
# backward + optimize only if in training phase
if phase == 'train':
loss.backward()
optimizer.step()
# 统计
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
epoch_loss = running_loss / len(dataloaders[phase].dataset)
epoch_acc = running_corrects.double() / len(dataloaders[phase].dataset)
print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))
# deep copy the model
if phase == 'val' and epoch_acc > best_acc:
best_acc = epoch_acc
best_model_wts = copy.deepcopy(model.state_dict())
if phase == 'val':
val_acc_history.append(epoch_acc)
print()
time_elapsed = time.time() - since
print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
print('Best val Acc: {:4f}'.format(best_acc))
# load best model weights
model.load_state_dict(best_model_wts)
return model, val_acc_history
2. 设置模型参数的.requires_grad属性
当进行特征提取时,此辅助函数将模型中参数的 .requires_grad 属性设置为False。
默认情况下,当加载一个预训练模型时,所有参数都是 .requires_grad = True,如果从头开始训练或微调,这种设置就没问题。 但是,如果要运行特征提取并且只想为新初始化的层计算梯度,那么希望所有其他参数不需要梯度变化。
def set_parameter_requires_grad(model, feature_extracting):
if feature_extracting:
for param in model.parameters():
param.requires_grad = False
知道输入尺寸,可以初始化数据转换、图像数据集和数据加载器。请注意,模型是使用硬编码标准化值进行 预先训练的
# 数据扩充和训练规范化
# 只需验证标准化
data_transforms = {
'train': transforms.Compose([
transforms.RandomResizedCrop(input_size),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
'val': transforms.Compose([
transforms.Resize(input_size),
transforms.CenterCrop(input_size),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
}
print("Initializing Datasets and Dataloaders...")
# 创建训练和验证数据集
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in ['train', 'val']}
# 创建训练和验证数据加载器
dataloaders_dict = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=batch_size, shuffle=True, num_workers=4) for x in ['train', 'val']}
# 检测我们是否有可用的GPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
微调和特征提取的最后一步是创建一个只更新所需参数的优化器。
# 将模型发送到GPU
model_ft = model_ft.to(device)
# 在此运行中收集要优化/更新的参数。
# 如果我们正在进行微调,我们将更新所有参数。
# 但如果我们正在进行特征提取方法,我们只会更新刚刚初始化的参数,即`requires_grad`的参数为True。
params_to_update = model_ft.parameters()
print("Params to learn:")
if feature_extract:
params_to_update = []
for name,param in model_ft.named_parameters():
if param.requires_grad == True:
params_to_update.append(param)
print("\t",name)
else:
for name,param in model_ft.named_parameters():
if param.requires_grad == True:
print("\t",name)
# 观察所有参数都在优化
optimizer_ft = optim.SGD(params_to_update, lr=0.001, momentum=0.9)
最后一步是为模型设置损失,然后对设定的epoch数运行训练和验证函数
参考链接:link
本文介绍了PyTorch中迁移学习的两种方式——微调和特征提取,详细阐述了它们的原理及操作步骤。通过辅助函数展示了如何训练和验证模型,以及如何设置模型参数的`.requires_grad`属性来控制参数更新。内容涵盖模型初始化、数据处理、损失函数、优化器的选择,以及最佳模型的保存。
1637

被折叠的 条评论
为什么被折叠?



