优化器
Ascend
GPU
CPU
模型开发
概述
优化器在模型训练过程中,用于计算和更新网络参数,合适的优化器可以有效减少训练时间,提高最终模型性能。最基本的优化器是梯度下降(SGD),在此基础上,很多其他的优化器进行了改进,以实现目标函数能更快速更有效地收敛到全局最优点。
mindspore.nn.optim
是MindSpore框架中实现各种优化算法的模块,包含常用的优化器、学习率等,接口具备较好的通用性,可以将以后更新、更复杂的方法集成到模块里。mindspore.nn.optim
为模型提供常用的优化器,如mindspore.nn.SGD
、mindspore.nn.Adam
、mindspore.nn.Ftrl
、mindspore.nn.LazyAdam
、mindspore.nn.Momentum
、mindspore.nn.RMSProp
、mindspore.nn.LARS
、mindspore.nn.ProximalAadagrad
和mindspore.nn.Lamb
等。同时mindspore.nn
提供了动态学习率的模块,分为dynamic_lr
和learning_rate_schedule
,学习率的灵活设置可以有效支撑目标函数的收敛和模型的训练。
使用mindspore.nn.optim
时,我们需要构建一个Optimizer实例。这个实例能够保持当前参数状态并基于计算得到的梯度进行参数更新。为了构建一个Optimizer,要指定需要优化的网络权重(必须是Parameter实例)的iterable,然后设置Optimizer的参数选项,比如学习率,权重衰减等。
以下内容分别从权重学习率、权重衰减、参数分组、混合精度等方面的配置分别进行详细介绍。
权重配置
在构建Optimizer实例时,通过params
配置模型网络中要训练和更新的权重。params
必须配置,常见的配置方法有以下两种。
使用Cell的网络权重获取函数
Parameter
类中包含了一个requires_grad
的布尔型的类属性,表征了模型网络中的权重是否需要梯度来进行更新(详情可参考:https://gitee.com/mindspore/mindspore/blob/r1.6/mindspore/python/mindspore/common/parameter.py )。其中大部分权重的requires_grad
的默认值都为True;少数默认为False,例如BatchNormalize中的moving_mean
和moving_variance
。用户可以根据需要,自行对requires_grad
的值进行修改。
MindSpore提供了get_parameters
方法来获取模型网络中所有权重,该方法返回了Parameter
类型的网络权重;trainable_params
方法本质是一个filter,过滤了requires grad=True
的Parameter
。用户在构建优化器时,可以通过配置params
为net.trainable_params()
来指定需要优化和更新的权重。
代码样例如下:
[1]:
import numpy as np
import mindspore.ops as ops
from mindspore import nn, Model, Tensor, Parameter
class Net(nn.Cell):
def __init__(self):
super(Net, self).__init__()
self.matmul = ops.MatMul()
self.conv = nn.Conv2d(1, 6, 5, pad_mode="valid")
self.param = Parameter(Tensor(np.array([1.0], np.float32)))
def construct(self, x):
x = self.conv(x)
x = x * self.param
out = self.matmul(x, x)
return out
net = Net()
optim = nn.Adam(params=net.trainable_params())
自定义筛选
用户也可以设定筛选条件,在使用get_parameters
获取到网络全部参数后,通过限定参数名字等方法,自定义filter来决定哪些参数需要更新。例如下面的例子,训练过程中将只对非卷积参数进行更新:
[2]:
from mindspore import nn
params_all = net.get_parameters()
no_conv_params = list(filter(lambda x: 'conv' not in x.name, params_all))
optim = nn.Adam(params=no_conv_params, learning_rate=0.1, weight_decay=0.0)
学习率
学习率作为机器学习及深度学习中常见的超参,对目标函数能否收敛到局部最小值及何时收敛到最小值有重要作用。学习率过大容易导致目标函数波动较大,难以收敛到最优值,太小则会导致收敛过程耗时长,除了基本的固定值设置,很多动态学习率的设置方法也在深度网络的训练中取得了显著的效果。
固定学习率
使用固定学习率时,优化器传入的learning_rate
为浮点类型或标量Tensor。
以Momentum为例,固定学习率为0.01,用法如下:
[3]:
from mindspore import nn
net = Net()
optim = nn.Momentum(params=net.trainable_params(), learning_rate=0.01, momentum=0.9)
loss = nn.SoftmaxCrossEntropyWithLogits()
model = Model(net, loss_fn=loss, optimizer=optim)
动态学习率
模块提供了动态学习率的两种不同的实现方式,dynamic_lr
和learning_rate_schedule
:
dynamic_lr
: 预生成长度为total_step
的学习率列表,将列表传入优化器中使用, 训练过程中, 第i步使用第i个学习率的值作为当前step的学习率,其中,total_step
的设置值不能小于训练的总步数;learning_rate_schedule
: 优化器学习率指定一个LearningRateSchedule的Cell实例,学习率会和训练网络一起组成计算图,在执行过程中,根据step计算出当前学习率。
预生成学习率列表
本模块中的动态学习率为函数,配合优化器使用时,调用函数并将结果列表lr
传递给优化器。在训练过程中,优化器将 lr[current_step]
作为当前学习率。
mindspore.nn.dynamic_lr
支持以下不同的实现方式:
piecewise_constant_lr
:基于得到分段不变的学习速率。exponential_decay_lr
:基于指数衰减函数计算学习率。natural_exp_decay_lr
:基于自然指数衰减函数计算学习率。inverse_decay_lr
:基于反时间衰减函数计算学习速率。cosine_decay_lr
:基于余弦衰减函数计算学习率。polynomial_decay_lr
:基于多项式衰减函数计算学习率。warmup_lr
:提高学习率。
以piecewise_constant_lr
为例:
[4]:
from mindspore import nn
milestone = [2, 5, 10]
learning_rates = [0.1, 0.05, 0.01]
lr = nn.dynamic_lr.piecewise_constant_lr(milestone, learning_rates)
print(lr)
net = Net()
optim = nn.SGD(net.trainable_params(), learning_rate=lr)
[0.1, 0.1, 0.05, 0.05, 0.05, 0.01, 0.01, 0.01, 0.01, 0.01]
定义学习率计算图
本模块中的动态学习率为LearningRateSchedule
的子类,配合优化器使用时,将对应的LearningRateSchedule
子类的实例传递给优化器。在训练过程中,优化器调用以current_step
为输入的实例,以获得当前的学习率。
mindspore.nn.learning_rate_schedule
模块包含以下实现方式:
ExponentialDecayLR
:基于指数衰减函数计算学习率。NaturalExpDecayLR
:基于自然指数衰减函数计算学习率。InverseDecayLR
:基于反时间衰减函数计算学习速率。CosineDecayLR
:基于余弦衰减函数计算学习率。PolynomialDecayLR
:基于多项式衰减函数计算学习率。WarmUpLR
:提高学习率。
以ExponentialDecayLR
为例:
ExponentialDecayLR仅支持GPU和Ascend后端。
[5]:
from mindspore import nn
from mindspore import Tensor
polynomial_decay_lr = nn.learning_rate_schedule.PolynomialDecayLR(learning_rate=0.1,
end_learning_rate=0.01,
decay_steps=4,
power=0.5)
net = Net()
optim = nn.SGD(net.trainable_params(), learning_rate=polynomial_decay_lr)
权重衰减
一般情况下,weight_decay取值范围为[0, 1),实现对(BatchNorm以外的)参数使用权重衰减的策略,以避免模型过拟合问题;weight_decay的默认值为0.0,此时不使用权重衰减策略。
[6]:
net = Net()
optimizer = nn.Momentum(net.trainable_params(), learning_rate=0.01, momentum=0.9, weight_decay=0.9)
参数分组
优化器也支持为不同参数单独设置选项,此时不直接传入变量,而是传入一个字典的列表,每个字典定义一个参数组别的设置值,key可以为“params”,“lr”,“weight_decay”,”grad_centralizaiton”,value为对应的设定值。params
必须配置,其余参数可以选择配置,未配置的参数项,将采用定义优化器时设置的参数值。
分组时,学习率可以使用固定学习率,也可以使用dynamic_lr和learningrate_schedule动态学习率。
当前MindSpore除个别优化器外(例如AdaFactor,FTRL),均支持对学习率进行分组,详情参考各优化器的说明。
例如下面的例子:
conv_params组别的参数,使用固定学习率0.01,
weight_decay
为字典传入的数值0.01;no_conv_params组别使用
learning_rate_schedule
的动态学习率PolynomialDecayLR
,weight_decay
使用优化器配置的值0.0;group_params还提供了
order_params
配置项;一般情况下order_params
无需配置。group_params根据分组情况会改变parameter和梯度的计算顺序。如果使用自动并行策略,并通过set_all_reduce_fusion_split_indices
配置了梯度更新的切分点,group_params引起的顺序变化会影响梯度广播的并行效果,此时可以通过order_params
指定预期的参数顺序,例如指定为net.trainable_params()
,使参数顺序与网络中定义权重的原始顺序保持一致。
[7]:
from mindspore import nn
net = Net()
# Use parameter groups and set different values
conv_params = list(filter(lambda x: 'conv' in x.name, net.trainable_params()))
no_conv_params = list(filter(lambda x: 'conv' not in x.name, net.trainable_params()))
fix_lr = 0.01
polynomial_decay_lr = nn.learning_rate_schedule.PolynomialDecayLR(learning_rate=0.1,
end_learning_rate=0.01,
decay_steps=4,
power=0.5)
group_params = [{'params': conv_params, 'weight_decay': 0.01, 'lr': fix_lr},
{'params': no_conv_params, 'lr': polynomial_decay_lr},
{'order_params': net.trainable_params()}]
optim = nn.Momentum(group_params, learning_rate=0.1, momentum=0.9, weight_decay=0.0)
混合精度
深度神经网络存在使用混合精度训练的场景,这种方法通过混合使用单精度和半精度数据格式来加速网络训练,同时保持了单精度训练所能达到的网络精度。混合精度训练能够加速计算过程,减少内存使用和存取,并使得在特定的硬件上可以训练更大的模型或batch size。
在混合精度训练过程中,会使用float16类型来替代float32类型存储数据,但由于float16类型数据比float32类型数据范围小很多,所以当某些参数(例如梯度)在训练过程中变得很小时,就会发生数据下溢。为避免半精度float16类型数据下溢,MindSpore提供了FixedLossScaleManager
和DynamicLossScaleManager
方法。其主要思想是计算loss时,将loss扩大一定的倍数,由于链式法则的存在,梯度也会相应扩大,然后在优化器更新权重时再缩小相应的倍数,从而避免了数据下溢的情况又不影响计算结果。
一般情况下优化器不需要与LossScale
功能配合使用,但使用FixedLossScaleManager
,并且drop_overflow_update
为False时,优化器需设置loss_scale
的值,且loss_scale
值与FixedLossScaleManager
的相同,具体用法详见:https://www.mindspore.cn/docs/programming_guide/zh-CN/r1.6/lossscale.html。