实现能够在训练过程中手动更改学习率

Posted wanghui-garcia

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现能够在训练过程中手动更改学习率相关的知识,希望对你有一定的参考价值。

深度学习框架PyTorch一书的学习-第六章-实战指南pytorch Debug —交互式调试工具Pdb (ipdb是增强版的pdb)-1-在pytorch中使用pytorch实现性别检测三篇文章的基础上写的这篇文章

之前我们使用的是:

exp_lr_scheduler = optim.lr_scheduler.StepLR(optimizer_conv, step_size=3, gamma=0.1)

去自动递减学习率,但是这种方法还是十分死板的,希望实现能够手动根据收敛地效果去更改学习率的大小。所以在这里就是用了ipdb调试工具

1)

首先我们会使用argparse去实现命令行解析,实现通过设置--debugFile命令,然后通过生成和删除指定文件夹去进入调试状态:

def getArgs():
#设置解析
    parser = argparse.ArgumentParser()
    parser.add_argument(--debugFile, nargs=?, default=None, type=str)
    args = parser.parse_args()
    return vars(args) #用vars()内建函数化为字典  

 

2)然后在train_model()函数中添加:

                        # 进入debug模式
                        # print(args : , args[debugFile])
                        if os.path.exists(args[debugFile]):
                            import ipdb;
                            ipdb.set_trace()

实现当读取到该指定的文件夹后进入调试状态

 

3)

整个代码如下:

技术图片
# coding:utf8
from torchvision import datasets, models
from torch import nn, optim
from torchvision import transforms as T
from torch.utils import data

import os
import copy
import time
import torch
import argparse

#首先进行数据的处理
data_dir = ./data
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")


#转换图片数据
normalize = T.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225])
data_transforms ={
    train: T.Compose([
        T.RandomResizedCrop(224),#从图片中心截取
                T.RandomHorizontalFlip(),#随机水平翻转给定的PIL.Image,翻转概率为0.5  
        T.ToTensor(),#转成Tensor格式,大小范围为[0,1]
        normalize
    ]),

    val: T.Compose([
        T.Resize(256),#重新设定大小 
        T.CenterCrop(224),
        T.ToTensor(),
        normalize
    ]),
}

#加载图片
#man的label为0, woman的label为1
image_datasets = {x : datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in [train, val]}

#得到train和val中的数据量
dataset_sizes = {x : len(image_datasets[x].imgs) for x in [train, val]}
dataloaders = {x : data.DataLoader(image_datasets[x], batch_size=4, shuffle=True,num_workers=4) for x in [train, val]}



#然后选择使用的模型
model_conv = models.resnet18(pretrained=True)
#冻结参数,不训练卷积层网络
#for param in model_conv.parameters():
#    param.requires_grad = False

#提取fc全连接层中固定的参数,后面的训练只对全连接层的参数进行优化
fc_features = model_conv.fc.in_features
#修改类别为2,即man和woman
model_conv.fc = nn.Linear(fc_features, 2)
model_conv.to(device)
#定义使用的损失函数为交叉熵代价函数
criterion = nn.CrossEntropyLoss()
#定义使用的优化器
#optimizer_conv = optim.SGD(model_conv.fc.parameters(), lr=0.0001, momentum=0.9)
#optimizer_conv = optim.SGD(model_conv.parameters(), lr=0.0001, momentum=0.9) 
optimizer_conv = optim.Adam(model_conv.parameters(), lr=0.0001, betas=(0.9, 0.99))
#设置自动递减的学习率,等间隔调整学习率,即在7个step时,将学习率调整为 lr*gamma
# exp_lr_scheduler = optim.lr_scheduler.StepLR(optimizer_conv, step_size=3, gamma=0.1)
#exp_lr_scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer_conv, mode=min, verbose=True)


# 训练模型
# 参数说明:
# model:待训练的模型
# criterion:评价函数
# optimizer:优化器
# scheduler:学习率
# num_epochs:表示实现完整训练的次数,一个epoch表示一整個训练周期
# def train_model(model, criterion, optimizer, scheduler, args, num_epochs=20):
def train_model(model, criterion, optimizer, args, num_epochs=20):
    # 定义训练开始时间
    since = time.time()
    #用于保存最优的权重
    best_model_wts = copy.deepcopy(model.state_dict())
    #最优精度值
    best_train_acc = 0.0
    best_val_acc = 0.0
    best_iteration = 0

    # # meters,统计指标:平滑处理之后的损失,还有混淆矩阵
    # loss_meter = meter.AverageValueMeter()#能够计算所有数的平均值和标准差,用来统计一个epoch中损失的平均值
    # confusion_matrix = meter.ConfusionMeter(2)#用来统计分类问题中的分类情况,是一个比准确率更详细的统计指标

    # 对整个数据集进行num_epochs次训练
    for epoch in range(num_epochs):
        print(Epoch {}/{}.format(epoch, num_epochs - 1))
        print(- * 10)
        
        #用于存储train acc还没有与val acc比较之前的值
        temp = 0
        # Each epoch has a training and validation phase
        # 每轮训练训练包含`train`和`val`的数据
        for phase in [train, val]:
            if phase == train:
                # 学习率步进
                # scheduler.step()
                # 设置模型的模式为训练模式(因为在预测模式下,采用了`Dropout`方法的模型会关闭部分神经元)
                model.train()  # Set model to training mode
            else:
            # 预测模式
                model.eval()   # Set model to evaluate mode

            running_loss = 0.0
            running_corrects = 0

            # Iterate over data.
            # 遍历数据,这里的`dataloaders`近似于一个迭代器,每一次迭代都生成一批`inputs`和`labels`数据,
            # 一批有四个图片,一共有dataset_sizes[train]/4或dataset_sizes[val]/4批
            # 这里循环几次就看有几批数据
            for inputs, labels in dataloaders[phase]:
                inputs = inputs.to(device)   # 当前批次的训练输入 
                labels = labels.to(device)  # 当前批次的标签输入
                # print(input : , inputs)
                # print(labels : , labels)

                # 将梯度参数归0
                optimizer.zero_grad()

                # 前向计算
                # track history if only in train
                with torch.set_grad_enabled(phase == train):
                    # 相应输入对应的输出
                    outputs = model(inputs)
                    # print(outputs : , outputs)
                    # 取输出的最大值作为预测值preds,dim=1,得到每行中的最大值的位置索引,用来判别其为0或1
                    _, preds = torch.max(outputs, 1)
                    # print(preds : , preds)
                    # 计算预测的输出与实际的标签之间的误差
                    loss = criterion(outputs, labels)
                    # backward + optimize only if in training phase
                    if phase == train:
                    # 对误差进行反向传播
                        loss.backward()
                        #scheduler.step(loss) #当使用的学习率递减函数为optim.lr_scheduler.ReduceLROnPlateau时,使用在这里
                        # 执行优化器对梯度进行优化
                        optimizer.step()

                        # loss_meter.add(loss.item())
                        # confusion_matrix.add(outputs.detach(), labels.detach()) 
                        
                        # 进入debug模式
                        # print(args : , args[debugFile])
                        if os.path.exists(args[debugFile]):
                            import ipdb;
                            ipdb.set_trace()

                # statistics
                # 计算`running_loss`和`running_corrects`
                #loss.item()得到的是此时损失loss的值
                #inputs.size(0)得到的是一批图片的数量,这里为4
                #两者相乘得到的是4张图片的总损失
                #叠加得到所有数据的损失
                running_loss += loss.item() * inputs.size(0)
                #torch.sum(preds == labels.data)判断得到的结果中有几个正确,running_corrects得到四个中正确的个数
                #叠加得到所有数据中判断成功的个数
                running_corrects += torch.sum(preds == labels.data)

        # 当前轮的损失,除以所有数据量个数得到平均loss值
            epoch_loss = running_loss / dataset_sizes[phase]
            # 当前轮的精度,除以所有数据量个数得到平均准确度
            epoch_acc = running_corrects.double() / dataset_sizes[phase]

            print({} Loss: {:.4f} Acc: {:.4f}.format(phase, epoch_loss, epoch_acc))

            # deep copy the model
            # 对模型进行深度复制         
            if phase == train and epoch_acc > best_train_acc:
                temp = epoch_acc
            if phase ==val and epoch_acc > 0 and epoch_acc < temp:
                best_train_acc = temp
                best_val_acc = epoch_acc
                best_iteration = epoch
                best_model_wts = copy.deepcopy(model.state_dict())

    # 计算训练所需要的总时间
    time_elapsed = time.time() - since
    print(Training complete in {:.0f}m {:.0f}s.format(time_elapsed // 60, time_elapsed % 60))
    print(Best epoch: {:4f}.format(best_iteration))  
    print(Best train Acc: {:4f}.format(best_train_acc)) 
    print(Best val Acc: {:4f}.format(best_val_acc))

    # load best model weights
    # 加载模型的最优权重
    model.load_state_dict(best_model_wts)
    return model

def getArgs():
#设置解析
    parser = argparse.ArgumentParser()
    parser.add_argument(--debugFile, nargs=?, default=None, type=str)
    args = parser.parse_args()
    return vars(args) #用vars()内建函数化为字典  

if __name__ == __main__:
    args_dist = getArgs()
    print(args_dist)
    # model_train = train_model(model_conv, criterion, optimizer_conv, exp_lr_scheduler, args_dist)
    model_train = train_model(model_conv, criterion, optimizer_conv, args_dist)
    torch.save(model_train, GenderTest1_18.pkl)
View Code

 

4)

然后运行:

(deeplearning) userdeMBP:resnet18 user$ python train.py --debugFile=./debug
{debugFile: ./debug}
Epoch 0/19
----------
train Loss: 0.7313 Acc: 0.6000
val Loss: 0.6133 Acc: 0.5500
Epoch 1/19
----------
train Loss: 0.3051 Acc: 0.9500
val Loss: 0.5630 Acc: 0.7000
Epoch 2/19
----------
train Loss: 0.1872 Acc: 0.9000
val Loss: 0.8300 Acc: 0.6500
Epoch 3/19
----------
train Loss: 0.3791 Acc: 0.8500
val Loss: 1.1445 Acc: 0.6000
Epoch 4/19
----------
train Loss: 0.4880 Acc: 0.8000
val Loss: 0.5832 Acc: 0.7000
Epoch 5/19
----------

这时候在当前文件夹下生成一个名为debug的文件夹,就会进入调试模式:

Epoch 5/19
----------
--Call--
> /anaconda3/envs/deeplearning/lib/python3.6/site-packages/torch/autograd/grad_mode.py(129)__exit__()
    128 
--> 129     def __exit__(self, *args):
    130         torch.set_grad_enabled(self.prev)

ipdb> u   #进入上一条命令                                                                    
> /Users/user/pytorch/gender_test_work/resnet18/train.py(151)train_model()
    150                             import ipdb;
--> 151                             ipdb.set_trace()
    152 

ipdb> for group in optimizer.param_groups: group[lr] #查看当前的学习率值                         
0.0001
ipdb> for group in optimizer.param_groups: group[lr]=0.01  #更改为新的学习率值

然后这时候删除debug文件夹,在调试中运行c命令继续向下运行:

ipdb> c                                                                         
train Loss: 1.0321 Acc: 0.7500
val Loss: 8590.6042 Acc: 0.6000
Epoch 6/19
----------
train Loss: 2.5962 Acc: 0.4000
val Loss: 23884.8344 Acc: 0.5000
Epoch 7/19
----------
train Loss: 1.0793 Acc: 0.5500
val Loss: 65956.7039 Acc: 0.5000
Epoch 8/19
----------
train Loss: 1.6199 Acc: 0.4500
val Loss: 16973.9813 Acc: 0.5000
Epoch 9/19
----------
train Loss: 1.4478 Acc: 0.3500
val Loss: 1580.6444 Acc: 0.5000
Epoch 10/19
----------

然后再生成debug文件夹进入调试命令,查看此时的学习率,可见此时的学习率果然为调后的0.01:

Epoch 10/19
----------
> /Users/user/pytorch/gender_test_work/resnet18/train.py(151)train_model()
    150                             import ipdb;
--> 151                             ipdb.set_trace()
    152 

ipdb> for group in optimizer.param_groups: group[lr]                          
0.01
ipdb>  

上面的训练结果什么的不要太在意,只是为了演示随便跑的

 

5)

中间有出现一个问题:

SyntaxError: non-default argument follows default argument

这种错误原因是将没有默认值的参数在定义时放在了有默认值的参数的后面

如:

def train_model(model, criterion, optimizer, scheduler, num_epochs=200, args_dist):

应该写成:

def train_model(model, criterion, optimizer, scheduler, args_dist, num_epochs=200):

 

以上是关于实现能够在训练过程中手动更改学习率的主要内容,如果未能解决你的问题,请参考以下文章

PyTorch:如何在任何给定时刻更改优化器的学习率(无 LR 计划)

在训练的过程中降低学习率

Pytorch不同层设置不同学习率

Pytorch不同层设置不同学习率

Adam优化器

在深度学习模型训练中修改学习率