0
点赞
收藏
分享

微信扫一扫

ResNet迁移学习——pytorch

巧乐兹_d41f 2022-03-11 阅读 157

发现了一篇比较漂亮的resnet代码,借鉴,学习。原文

迁移学习

迁移学习的两种方式

  1. 微调。从线上下载以训练完毕的模型,利用本地数据集进行参数的微调,更新的是所有参数
  2. 用作特征提取器。外加一层全连接,只训练全连接部分的参数

1. 训练阶段

值得参考的tricks有:

  1. 训练过程中保存最优模型的参数,在每一个epochs中,若验证的准确率有所提升则更新参数
  2. 每个epoch中都包含训练和验证两个阶段。注意写法
# train
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

def train_model(model, criterion, optimizer, scheduler, num_epochs=25):   # 括号中的参数是模型,损失函数标准,优化器,学习速率衰减方式,训练次数

    best_model_wts = copy.deepcopy(model.state_dict())    # 先深拷贝一份当前模型的参数(wts=weights),后面迭代过程中若遇到更优模型则替换
    best_acc = 0.0                                        # 最佳正确率,用于判断是否替换best_model_wts

    for epoch in range(num_epochs):      # 开启每一个epoch
        print('Epoch {}/{}'.format(epoch + 1, num_epochs))

        for phase in ['train', 'val']:   # 每个epoch中都包含训练与验证两个阶段
            if phase == 'train':      
                model.train()            
            else:                       
                model.eval()            
                # 与train不同的是,test过程中没有batch-normalization与dropout,因此要区别对待。 
                # batchnorm是针对minibatch的,测试过程中每个样本单独测试,不存在minibatch

            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'):   # torch.set_grad_enabled(False/True)是上下文管理器,用于确定是否对with下的所有语句设计的参数求导,如果设置为false则新节点不可求导
                    outputs = model(inputs)            # 网络模型的前向传播,就是为了从输入得到输出
                    _, preds = torch.max(outputs, 1)   # 在维度1(行方向)查找最大值
                    loss = criterion(outputs, labels)  # 输出结果与label相比较

                    if phase == 'train':
                        loss.backward()     # 误差反向传播,计算每个w与b的更新值
                        optimizer.step()    # 将这些更新值施加到模型上

                # train, val都一样
                running_loss += loss.item() * inputs.size(0)         # 计算当前epoch过程中,所有batch的损失和
                running_corrects += torch.sum(preds == labels.data)  # 判断正确的样本数 
            if phase == 'train':    # 完成本次epoch所有样本的训练与验证之后,就对学习速率进行修正
                scheduler.step()     # 在训练过程中,要根据损失的情况修正学习速率

            epoch_loss = running_loss / dataset_sizes[phase]               # 当前epoch的损失值是loss总和除以样本数
            epoch_acc = running_corrects.double() / dataset_sizes[phase]   # 当前epoch的正确率

            print('{} Loss: {:.4f} Acc: {:.4f}'.format(         # 输出train/test,损失、正确率
                phase, epoch_loss, epoch_acc))

            # deep copy the model
            if phase == 'val' and epoch_acc > best_acc:                # 如果是val阶段,并且当前epoch的acc比best acc大
                best_acc = epoch_acc                                    # 就替换best acc为当前epoch的acc
                best_model_wts = copy.deepcopy(model.state_dict())      # 将best_model替换为当前模型
        
    print('Best val Acc: {:4f}'.format(best_acc)) 

    # 将最佳模型的相关参数加载到model中
    model.load_state_dict(best_model_wts)                        
    return model

2. 迁移学习部分

对模型所有层的所有参数都进行目标域的训练

model_ft = models.resnet18(pretrained=True)    # 加载预训练参数
num_ftrs = model_ft.fc.in_features             # 全连接层的输入的特征数

class_dim = 12
# 修改下载的model的全连接层的输入和输出维度
model_ft.fc = nn.Linear(num_ftrs, class_dim)           # 利用线性映射将原来的num_ftrs转换为总特征数
                                               # 将最后一个全连接由(512, 1000)改为(512, 12)   因为原网络是在1000类的ImageNet数据集上训练的
model_ft = model_ft.to(device)                 # 设置计算采用的设备,GPU还是CPU

criterion = nn.CrossEntropyLoss()              # 交叉熵损失函数

# 优化器,对加载的模型中所有参数都进行优化
optimizer_ft = torch.optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9)    # 选用 较小的学习率

# 学习率每step_size个周期 降低 gamma倍
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)   



model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler,num_epochs=25)    # 模型训练

举报

相关推荐

0 条评论