迁移学习

本文介绍了PyTorch中迁移学习的两种方式——微调和特征提取,详细阐述了它们的原理及操作步骤。通过辅助函数展示了如何训练和验证模型,以及如何设置模型参数的`.requires_grad`属性来控制参数更新。内容涵盖模型初始化、数据处理、损失函数、优化器的选择,以及最佳模型的保存。
部署运行你感兴趣的模型镜像

迁移学习(微调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 2.5

PyTorch 2.5

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值