【PyTorch实战演练】自调整学习率实例应用(附代码)

简介: 【PyTorch实战演练】自调整学习率实例应用(附代码)

0. 前言

按照国际惯例,首先声明:本文只是我自己学习的理解,虽然参考了他人的宝贵见解及成果,但是内容可能存在不准确的地方。如果发现文中错误,希望批评指正,共同进步。


本文介绍深度学习训练中经常能使用到的实用技巧——自调整学习率,并基于PyTorch框架通过实例进行使用,最后对比同样条件下以自调整学习率和固定学习率在模型训练上的表现。


在深度学习模型训练过程中,经常会出现损失值不收敛的头疼情况,令人怀疑自己设计的网络模型存在不合理或者错误之处,但是导致这种情况的真正原因往往非常简单——就是学习率的设定不合理。



这就导致在深度学习模型训练的初期,可能需要“摸索”一个比较合适的学习率,在后期逐渐细化的过程可能还需要尝试其他的学习率,进行“分段训练”。学习率的这种“摸索”费时费力,因此自调整学习率的方法应运而生。


1. 自调整学习率的常用方法


自调整学习率是一种通用的方法,通过在训练期间动态地更新学习率以使模型更好地收敛,提高模型的精确度和稳定性。在PyTorch框架 torch.optim.lr_scheduler 中集成了大量的自调整学习率的方法:


lr_scheduler.LambdaLR

lr_scheduler.MultiplicativeLR

lr_scheduler.StepLR

lr_scheduler.MultiStepLR

lr_scheduler.ConstantLR

lr_scheduler.LinearLR

lr_scheduler.ExponentialLR

lr_scheduler.PolynomialLR

lr_scheduler.CosineAnnealingLR

lr_scheduler.ChainedScheduler

lr_scheduler.SequentialLR

lr_scheduler.ReduceLROnPlateau

lr_scheduler.CyclicLR

lr_scheduler.OneCycleLR

lr_scheduler.CosineAnnealingWarmRestarts


这里具体方法虽然很多,但是每种自动调整学习率的策略都比较简单,本文仅介绍以下两种常见的方法,其余可以查看PyTorch官网说明


1.1  ExponentialLR 指数衰减方法

这种方法的学习率为:

这里有两个参数:

  • :初始学习率
  • :衰减指数,取值范围(0,1),一般来说取值要非常接近1(>0.95,甚至要>0.99),否则在动辄几百几千的迭代次数epoch条件下学习率很快会衰减到0


PyTorch调用代码为:

torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma, last_epoch)


  • optimizer:训练该模型的优化算法
  • gamma:上面的衰减指数
  • last_epoch:不用理会,默认-1即可


1.2 CosineAnnealingLR 余弦退火方法

首先我要吐槽下不知道是哪个大聪明给起的这个名字,我学过《机械加工原理与工艺》,但我不明白这个算法和退火有什么关系?名字非常唬人,方法并不复杂。

这个方法就是让学习率按余弦曲线进行震荡,其震荡范围为[0, lr_initial],震荡周期为T_max:

PyTorch调用代码为:


torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max, last_epoch)


其参数同上说明不再赘述。

1.3 ChainedScheduler 链式方法

这个方法说白了就是多种自调节学习率方法进行组合使用,例如以下:

scheduler1 = ConstantLR(self.opt, factor=0.1, total_iters=2)
scheduler2 = ExponentialLR(self.opt, gamma=0.9)
scheduler = ChainedScheduler([scheduler1, scheduler2])


最后再强调一下,无论使用哪种方法,在coding时别忘了每个epoch学习后加上 scheduler.step() ,这样才能更新学习率。


2. 实例说明

本文目的是验证自调节学习率的作用,选用一个简单的“平方网络”实例,即期望输出为输入值的平方。


训练输入数据x_train,输出数据y_train分别为:

x_train = torch.tensor([0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1],dtype=torch.float32).unsqueeze(-1)
y_train = torch.tensor([0.01,0.04,0.09,0.16,0.25,0.36,0.49,0.64,0.81,1],dtype=torch.float32).unsqueeze(-1)

验证输入数据x_val为:

x_val = torch.tensor([0.15,0.25,0.35,0.45,0.55,0.65,0.75,0.85,0.95],dtype=torch.float32).unsqueeze(-1)

网络模型使用5层全连接网络,对应这种简单问题足够用了:

class Linear(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.layers = torch.nn.Sequential(
            torch.nn.Linear(in_features=1, out_features=3),
            torch.nn.Sigmoid(),
            torch.nn.Linear(in_features=3,out_features=5),
            torch.nn.Sigmoid(),
            torch.nn.Linear(in_features=5, out_features=10),
            torch.nn.Sigmoid(),
            torch.nn.Linear(in_features=10,out_features=5),
            torch.nn.Sigmoid(),
            torch.nn.Linear(in_features=5, out_features=1),
            torch.nn.ReLU(),
        )


优化器选用Adam,训练组及验证组的损失函数都选用MSE均方差。


3. 结果说明

对于本文对比的三种方法,其训练参数设定如下表:

学习率调整方法 训练参数设定
指数衰减 迭代次数epoch=2000,初始学习率initial_lr=0.0005,衰减指数gamma=0.99995
余弦退火 迭代次数epoch=2000,初始学习率initial_lr=0.001(因为学习率均值为初始值的一半,公平起见给余弦退火方法初始学习率*2),震荡周期T_max=200
恒定学习率 学习率lr=0.0005

各种方法的训练过程如下图:


3.1 余弦退火法训练过程

3.2 指数衰减法训练过程

3.3 恒定学习率训练过程

3.4 结果解读
  1. 指数衰减方法的gamma真的要选择非常接近1才行!理由我上面也解释了,下面是gamma=0.999时的loss下降情况,可见其下降速度之慢;
  2. 本实例中,使用余弦退火方法训练的模型在验证组表现最好,验证组损失值val=0.0027,其次是指数衰减法(val=0.0037),最差是恒定学习率(val=0.0053),但这个结果仅限本实例中,并不是说哪种方法就比哪种方法好,在不同模型上可能要因地制宜选择合适的方法


  1. 目前这些所谓的“自调节学习率”我认为仅能算是“半自动调节”,因为仍有超参数需要事前设定(例如衰减指数gamma)。而选择哪种方法最好,以及这种方法的参数如何设定呢?可能还是需要做一定的“摸索”,但是相比恒定学习率肯定会节省很多时间!


4. 完整代码

import torch
import matplotlib.pyplot as plt
from tqdm import tqdm
import argparse
 
 
torch.manual_seed(25)
 
x_train = torch.tensor([0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1],dtype=torch.float32).unsqueeze(-1)
y_train = torch.tensor([0.01,0.04,0.09,0.16,0.25,0.36,0.49,0.64,0.81,1],dtype=torch.float32).unsqueeze(-1)
x_val = torch.tensor([0.15,0.25,0.35,0.45,0.55,0.65,0.75,0.85,0.95],dtype=torch.float32).unsqueeze(-1)
 
class Linear(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.layers = torch.nn.Sequential(
            torch.nn.Linear(in_features=1, out_features=3),
            torch.nn.Sigmoid(),
            torch.nn.Linear(in_features=3,out_features=5),
            torch.nn.Sigmoid(),
            torch.nn.Linear(in_features=5, out_features=10),
            torch.nn.Sigmoid(),
            torch.nn.Linear(in_features=10,out_features=5),
            torch.nn.Sigmoid(),
            torch.nn.Linear(in_features=5, out_features=1),
            torch.nn.ReLU(),
        )
 
    def forward(self,x):
        return self.layers(x)
 
 
criterion = torch.nn.MSELoss()
 
def train_with_CosinAnneal(epoch, initial_lr, T_max):
    linear1 = Linear()
    opt = torch.optim.Adam(linear1.parameters(), lr=initial_lr)
    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer=opt, T_max=T_max, last_epoch=-1)
    last_epoch_loss = 0
 
    for i in tqdm(range(epoch)):
        opt.zero_grad()
        total_loss = 0
        for j in range(len(x_train)):
            output = linear1(x_train[j])
            loss = criterion(output,y_train[j])
 
            total_loss = total_loss + loss.detach()
            loss.backward()
            opt.step()
            if i == epoch-1:
                last_epoch_loss = total_loss
        scheduler.step()
 
        cur_lr = scheduler.get_lr()  #查看学习率变化情况
        # plt.scatter(i, cur_lr, s=2, c='g')  
 
        plt.scatter(i, total_loss,s=2,c='r')
 
    val = 0
    for x in x_val:
        val = val + ((linear1(x) - x * x) / x * x)**2
 
    plt.title('CosinAnneal---val=%f---last_epoch_loss=%f'%(val,last_epoch_loss))
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.show()
 
 
def train_with_ExponentialLR(epoch, initial_lr, gamma):
    linear2 = Linear()
    opt = torch.optim.Adam(linear2.parameters(), lr=initial_lr)
    scheduler = torch.optim.lr_scheduler.ExponentialLR(optimizer=opt, gamma=gamma, last_epoch=-1)
    last_epoch_loss = 0
 
    for i in tqdm(range(epoch)):
        opt.zero_grad()
        total_loss = 0
        for j in range(len(x_train)):
            output = linear2(x_train[j])
            loss = criterion(output,y_train[j])
 
            total_loss = total_loss + loss.detach()
            loss.backward()
            opt.step()
            if i == epoch-1:
                last_epoch_loss = total_loss
 
        scheduler.step()
 
        cur_lr = scheduler.get_lr()
 
        # plt.scatter(i, cur_lr, s=2, c='g')
        plt.scatter(i, total_loss,s=2,c='g')
 
 
    val = 0
    for x in x_val:
        val = val + ((linear2(x) - x * x) / x * x)**2
 
    plt.title('ExponentialLR---val=%f---last_epoch_loss=%f'%(val,last_epoch_loss))
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.show()
 
def train_without_lr_scheduler(epoch, initial_lr):
    linear3 = Linear()
    opt = torch.optim.Adam(linear3.parameters(), lr=initial_lr)
    last_epoch_loss = 0
 
    for i in tqdm(range(epoch)):
        opt.zero_grad()
        total_loss = 0
        for j in range(len(x_train)):
            output = linear3(x_train[j])
            loss = criterion(output, y_train[j])
            total_loss = total_loss + loss.detach()
            loss.backward()
            opt.step()
            if i == epoch-1:
                last_epoch_loss = total_loss
 
        plt.scatter(i, total_loss, s=2, c='b')
 
    val = 0
    for x in x_val:
        val = val + ((linear3(x) - x * x) / x * x)**2
 
 
    plt.title('without_lr_scheduler---val=%f---last_epoch_loss=%f'%(val,last_epoch_loss))
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.show()
 
 
if __name__ == '__main__' :
    epoch = 2000
    initial_lr = 0.0005
    gamma = 0.99995
    T_max = 200
 
 
 
    Cosine_Anneal_args = [epoch,initial_lr*2,T_max]
    train_with_CosinAnneal(*Cosine_Anneal_args)
 
    # ExponentialLR_args = [epoch, initial_lr, gamma]
    # train_with_ExponentialLR(*ExponentialLR_args)
    #
    # without_scheduler_args = [epoch, initial_lr]
    # train_without_lr_scheduler(*without_scheduler_args)


相关文章
|
16天前
|
PyTorch Linux 算法框架/工具
pytorch学习一:Anaconda下载、安装、配置环境变量。anaconda创建多版本python环境。安装 pytorch。
这篇文章是关于如何使用Anaconda进行Python环境管理,包括下载、安装、配置环境变量、创建多版本Python环境、安装PyTorch以及使用Jupyter Notebook的详细指南。
145 1
pytorch学习一:Anaconda下载、安装、配置环境变量。anaconda创建多版本python环境。安装 pytorch。
|
2月前
|
机器学习/深度学习 存储 PyTorch
PyTorch自定义学习率调度器实现指南
本文将详细介绍如何通过扩展PyTorch的 ``` LRScheduler ``` 类来实现一个具有预热阶段的余弦衰减调度器。我们将分五个关键步骤来完成这个过程。
63 2
|
2月前
|
机器学习/深度学习 PyTorch 调度
在Pytorch中为不同层设置不同学习率来提升性能,优化深度学习模型
在深度学习中,学习率作为关键超参数对模型收敛速度和性能至关重要。传统方法采用统一学习率,但研究表明为不同层设置差异化学习率能显著提升性能。本文探讨了这一策略的理论基础及PyTorch实现方法,包括模型定义、参数分组、优化器配置及训练流程。通过示例展示了如何为ResNet18设置不同层的学习率,并介绍了渐进式解冻和层适应学习率等高级技巧,帮助研究者更好地优化模型训练。
97 4
在Pytorch中为不同层设置不同学习率来提升性能,优化深度学习模型
|
16天前
|
机器学习/深度学习 缓存 PyTorch
pytorch学习一(扩展篇):miniconda下载、安装、配置环境变量。miniconda创建多版本python环境。整理常用命令(亲测ok)
这篇文章是关于如何下载、安装和配置Miniconda,以及如何使用Miniconda创建和管理Python环境的详细指南。
232 0
pytorch学习一(扩展篇):miniconda下载、安装、配置环境变量。miniconda创建多版本python环境。整理常用命令(亲测ok)
|
3月前
|
机器学习/深度学习 PyTorch TensorFlow
TensorFlow和PyTorch的实际应用比较
TensorFlow和PyTorch的实际应用比较
|
2月前
|
机器学习/深度学习 数据挖掘 TensorFlow
解锁Python数据分析新技能,TensorFlow&PyTorch双引擎驱动深度学习实战盛宴
在数据驱动时代,Python凭借简洁的语法和强大的库支持,成为数据分析与机器学习的首选语言。Pandas和NumPy是Python数据分析的基础,前者提供高效的数据处理工具,后者则支持科学计算。TensorFlow与PyTorch作为深度学习领域的两大框架,助力数据科学家构建复杂神经网络,挖掘数据深层价值。通过Python打下的坚实基础,结合TensorFlow和PyTorch的强大功能,我们能在数据科学领域探索无限可能,解决复杂问题并推动科研进步。
53 0
|
3月前
|
机器学习/深度学习 PyTorch TensorFlow
【PyTorch】PyTorch深度学习框架实战(一):实现你的第一个DNN网络
【PyTorch】PyTorch深度学习框架实战(一):实现你的第一个DNN网络
148 1
|
3月前
|
机器学习/深度学习 数据采集 自然语言处理
PyTorch 在自然语言处理中的应用实践
【8月更文第29天】随着深度学习技术的发展,自然语言处理(NLP)领域取得了显著的进步。PyTorch 作为一款强大的深度学习框架,因其灵活性和易用性而被广泛采用。本文将介绍如何利用 PyTorch 构建文本分类模型,并以情感分析为例进行详细介绍。
35 0
|
4月前
|
机器学习/深度学习 数据挖掘 TensorFlow
解锁Python数据分析新技能,TensorFlow&PyTorch双引擎驱动深度学习实战盛宴
【7月更文挑战第31天】在数据驱动时代,Python凭借其简洁性与强大的库支持,成为数据分析与机器学习的首选语言。**数据分析基础**从Pandas和NumPy开始,Pandas简化了数据处理和清洗,NumPy支持高效的数学运算。例如,加载并清洗CSV数据、计算总销售额等。
56 2
|
4月前
|
机器学习/深度学习 人工智能 数据挖掘
从0到1构建AI帝国:PyTorch深度学习框架下的数据分析与实战秘籍
【7月更文挑战第30天】PyTorch以其灵活性和易用性成为深度学习的首选框架。
65 2