文章目录
- 优化器(Optimizer)
- 1、优化器概念
- 2、`PyTorch`中的优化器基类:`Optimizer`
- (1)参数组概念
- (2)基类属性
- (3)基类方法
- 3、`Pytorch`中的十种优化器
- (1)学习率(learning rate)
- (2)动量(momentum)
- (3)优化器——` torch.optim.SGD`
- (4)`Pytorch`中其他九种优化器
- <1>`torch.optim.ASGD`
- <2> `torch.optim.Rprop
- <3>`torch.optim.Adagrad`
- <4> `torch.optim.Adadelta`
- <5> `torch.optim.RMSprop`
- <6> `torch.optim.Adam`
- <7> `torch.optim.Adamax`
- <8> `torch.optim.SparseAdam`
- <9> `torch.optim.LBFGS`
优化器(Optimizer)
1、优化器概念
- 机器学习模块中,数据读取,构建模型,得到损失函数后,就需要构造优化器,最优化的过程依赖的算法称为优化器
- 深度学习优化器的两个核心是梯度与学习率,前者决定参数更新的方向后者决定参数更新程度
- 深度学习优化器之所以采用梯度是因为,对于高维的函数其更高阶导的计算复杂度大,应用到深度学习的优化中不实际
- 深度学习的优化器有许多种类,大致可以分为两类:
- 一类为优化过程中,学习率不受梯度影响,全程不变或者按照一定的learning schedule随时间变化,这类包括最常见的SGD(随机梯度下降法),带Momentum的SGD,带Nesterov的SGD
- 一类是优化过程中,学习率随着梯度自适应的改变,并尽可能去消除给定的全局学习率的影响,常见的有Adagrad ,Adadelta, RMSprop, Adam等
2、PyTorch
中的优化器基类:Optimizer
PyTorch
中的所有优化器均是Optimizer
的子类,因此应当首先了解Optimizer
这个基类
(1)参数组概念
-
Optimizer
对参数的管理是基于组的概念,可以为每一组参数配置特定的lr,momentum,weight_decay 等等 - 参数组在
Optimizer
中表现为一个list(self.param_groups)
,其中每个元素是一个字典,表示一个参数及其相应配置,在字典中中包含'params'
、'weight_decay'
、'lr'
、'momentum'
等字段
(2)基类属性
def __init__(self, params, defaults):
torch._C._log_api_usage_once("python.optimizer")
self.defaults = defaults
if isinstance(params, torch.Tensor):
raise TypeError("params argument given to the optimizer should be "
"an iterable of Tensors or dicts, but got " +
torch.typename(params))
self.state = defaultdict(dict)
self.param_groups = []
param_groups = list(params)
if len(param_groups) == 0:
raise ValueError("optimizer got an empty parameter list")
if not isinstance(param_groups[0], dict):
param_groups = [{'params': param_groups}]
for param_group in param_groups:
self.add_param_group(param_group)
从__init__()
函数中可以看出,一共有三个属性:
-
defaults
:优化器超参数 -
state
:参数缓存,如momentum的缓存 -
param_groups
:管理的参数组
(3)基类方法
-
zero_grad
:清空所管理参数的梯度,这是由于PyTorch
中张量梯度不自动清零这个特性导致在每一次更新前需要进行此操作
def zero_grad(self):
r"""Clears the gradients of all optimized :class:`torch.Tensor` s."""
for group in self.param_groups:
for p in group['params']:
if p.grad is not None:
p.grad.detach_()
p.grad.zero_()
-
step(closure)
:执行一步权值更新, 其中可传入参数closure
(一个闭包)。如,当采用 LBFGS 优化方法时,需要多次计算,因此需要传入一个闭包去允许它们重新计算 loss
def step(self, closure):
r"""Performs a single optimization step (parameter update).
Arguments:
closure (callable): A closure that reevaluates the model and
returns the loss. Optional for most optimizers.
.. note::
Unless otherwise specified, this function should not modify the
``.grad`` field of the parameters.
"""
raise NotImplementedError
-
add_param_group()
:给optimizer
管理的参数组中增加一组参数,可为该组参数定制 lr, momentum, weight_decay等,在finetune常用
def add_param_group(self, param_group):
r"""Add a param group to the :class:`Optimizer` s `param_groups`.
This can be useful when fine tuning a pre-trained network as frozen layers can be made
trainable and added to the :class:`Optimizer` as training progresses.
Arguments:
param_group (dict): Specifies what Tensors should be optimized along with group
specific optimization options.
"""
assert isinstance(param_group, dict), "param group must be a dict"
params = param_group['params']
if isinstance(params, torch.Tensor):
param_group['params'] = [params]
elif isinstance(params, set):
raise TypeError('optimizer parameters need to be organized in ordered collections, but the ordering of tensors in sets will change between runs. Please use a list instead.')
else:
param_group['params'] = list(params)
for param in param_group['params']:
if not isinstance(param, torch.Tensor):
raise TypeError("optimizer can only optimize Tensors, "
"but one of the params is " + torch.typename(param))
if not param.is_leaf:
raise ValueError("can't optimize a non-leaf Tensor")
for name, default in self.defaults.items():
if default is required and name not in param_group:
raise ValueError("parameter group didn't specify a value of required optimization parameter " + name)
else:
param_group.setdefault(name, default)
param_set = set()
for group in self.param_groups:
param_set.update(set(group['params']))
if not param_set.isdisjoint(set(param_group['params'])):
raise ValueError("some parameters appear in more than one parameter group")
self.param_groups.append(param_group)
-
state_dict()
:获取模型当前的参数,以一个有序字典形式返回。 这个有序字典中,key
是各层参数名,value
就是参数
def state_dict(self):
r"""Returns the state of the optimizer as a :class:`dict`.
It contains two entries:
* state - a dict holding current optimization state. Its content
differs between optimizer classes.
* param_groups - a dict containing all parameter groups
"""
# Save ids instead of Tensors
def pack_group(group):
packed = {k: v for k, v in group.items() if k != 'params'}
packed['params'] = [id(p) for p in group['params']]
return packed
param_groups = [pack_group(g) for g in self.param_groups]
# Remap state to use ids as keys
packed_state = {(id(k) if isinstance(k, torch.Tensor) else k): v
for k, v in self.state.items()}
return {
'state': packed_state,
'param_groups': param_groups,
}
-
load_state_dict()
:将state_dict
中的参数加载到当前网络,常用于finetune
def load_state_dict(self, state_dict):
r"""Loads the optimizer state.
Arguments:
state_dict (dict): optimizer state. Should be an object returned
from a call to :meth:`state_dict`.
"""
# deepcopy, to be consistent with module API
state_dict = deepcopy(state_dict)
# Validate the state_dict
groups = self.param_groups
saved_groups = state_dict['param_groups']
if len(groups) != len(saved_groups):
raise ValueError("loaded state dict has a different number of "
"parameter groups")
param_lens = (len(g['params']) for g in groups)
saved_lens = (len(g['params']) for g in saved_groups)
if any(p_len != s_len for p_len, s_len in zip(param_lens, saved_lens)):
raise ValueError("loaded state dict contains a parameter group "
"that doesn't match the size of optimizer's group")
# Update the state
id_map = {old_id: p for old_id, p in
zip(chain(*(g['params'] for g in saved_groups)),
chain(*(g['params'] for g in groups)))}
def cast(param, value):
r"""Make a deep copy of value, casting all tensors to device of param."""
if isinstance(value, torch.Tensor):
# Floating-point types are a bit special here. They are the only ones
# that are assumed to always match the type of params.
if param.is_floating_point():
value = value.to(param.dtype)
value = value.to(param.device)
return value
elif isinstance(value, dict):
return {k: cast(param, v) for k, v in value.items()}
elif isinstance(value, container_abcs.Iterable):
return type(value)(cast(param, v) for v in value)
else:
return value
# Copy state assigned to params (and cast tensors to appropriate types).
# State that is not assigned to params is copied as is (needed for
# backward compatibility).
state = defaultdict(dict)
for k, v in state_dict['state'].items():
if k in id_map:
param = id_map[k]
state[param] = cast(param, v)
else:
state[k] = v
# Update parameter groups, setting their 'params' value
def update_group(group, new_group):
new_group['params'] = group['params']
return new_group
param_groups = [
update_group(g, ng) for g, ng in zip(groups, saved_groups)]
self.__setstate__({'state': state, 'param_groups': param_groups})
3、Pytorch
中的十种优化器
(1)学习率(learning rate)
梯度下降公式为:
如下图所示,对于函数,,如果采用梯度下降公式,
x | y | g(y) |
x0=2 | y0=16 | g(y0)=16 |
x1=2-16=-14 | y1=784 | g(y1)=-112 |
x2=-14+112=98 | y2=38416 | g(y2)=784 |
… | … | … |
可以看出并没有逐渐收敛到,因此就引出了学习率,控制更新的步伐,此时公式就变为:
例如,我们将学习率设置为,则得到如下的图像:
可以看出控制更新步伐后,就能逐渐向移动,最终收敛到,那么学习率的大小对算法有什么影响呢?
从上面这幅图中可以看出,学习率越小,收敛的速度往往越慢,但是学习率大并不意味着收敛速度就一定更快,例如上面的函数,如果初始点为,则显然,当学习率时,根据公式,一次迭代就能收敛到,所以学习率不能太大,也不能太小
(2)动量(momentum)
- 动量一词来源于物理学,用于刻画物体的运动状态,这里引入动量,主要是为了结合当前梯度与上一次更新信息,用于当前更新,从而加快算法的收敛速度
- 首先需要了解指数加权平均的概念:指数加权平均通常用于时间序列中求取平均值的方法,如果用表示当前时刻的参数值,表示当前时刻参数的平均值,则有公式:
通过上面的递推关系,我们可以得到:
由于是一个小于1的数,因此,从上式可以看出离越远,权重越小,同时权重是呈指数下降的趋势 - 超参数:是一个超参数,如下图所示,不同的,衰减的速度不同,可以理解为记忆周期,即越大,记忆周期越长,通常设置为0.9,表示的物理含义是——“关注最近10个时间段()的变化”
-
Pytorch
随机梯度下降+动量:加上动量后,更新公式变化为:
其中,为momentum系数 - 实例:
左右两幅图中,左边的图设置学习率为0.3和0.1,均没有加momentum系数,而右边对学习率为0.1的情况加上了momentum系数,设置m=0.9
,可以看出有一个振荡收敛的过程
(3)优化器——torch.optim.SGD
-
PyTorch
中最常用且最实用的优化器为SGD
优化器,深度学习中90%的任务都可以用SGD
进行优化
optim.SGD(params,
lr=<object object>,
momentum=0,
dampening=0,
weight_decay=0,
nesterov=False)
- 参数:
-
params
:管理的参数组 -
lr
:初始学习率 -
momentum
:动量系数 -
weight_decay
:L2正则化系数 -
nesterov
:是否采用NAG
(4)Pytorch
中其他九种优化器
<1>torch.optim.ASGD
optim.ASGD(params,
lr=0.01,
lambd=0.0001,
alpha=0.75,
t0=1000000.0,
weight_decay=0)
- 功能:ASGD也称为SAG,均表示随机平均梯度下降(Averaged Stochastic Gradient Descent),简单地说ASGD就是用空间换时间的一种SGD
- 参数:
-
params
:管理的参数组 -
lr
:初始学习率 -
lambd
:衰减项 -
alpha
:power for eta update -
t0
:point at which to start averaging -
weight_decay
:权值衰减系数,也就是 L2 正则项的系数
<2> `torch.optim.Rprop
torch.optim.Rprop(params,
lr=0.01,
etas=(0.5, 1.2),
step_sizes=(1e-06, 50))
- 功能:实现 Rprop 优化方法(弹性反向传播),适用于 full-batch,不适用于 mini-batch
<3>torch.optim.Adagrad
torch.optim.Adagrad(params,
lr=0.01,
lr_decay=0,
weight_decay=0,
initial _accumulator_value=0)
- 功能:实现 Adagrad 优化方法(Adaptive Gradient),Adagrad 是一种自适应优化方法,是自适应的为各个参数分配不同的学习率。这个学习率的变化,会受到梯度的大小和迭代次数的影响。梯度越大,学习率越小;梯度越小,学习率越大。缺点是训练后期,学习率过小, 因为 Adagrad 累加之前所有的梯度平方作为分母
<4> torch.optim.Adadelta
torch.optim.Adadelta(params,
lr=1.0,
rho=0.9,
eps=1e- 06,
weight_decay=0)
- 功能:实现 Adadelta 优化方法。Adadelta 是 Adagrad 的改进。Adadelta 分母中采用距离当前时间点比较近的累计项,这可以避免在训练后期,学习率过小的问题
<5> torch.optim.RMSprop
torch.optim.RMSprop(params,
lr=0.01,
alpha=0.99,
eps=1e- 08,
weight_decay=0,
momentum=0,
centered=False)
- 功能:实现 RMSprop 优化方法(Hinton 提出),RMS 是均方根(root meam square)的意 思。RMSprop 和 Adadelta 一样,也是对 Adagrad 的一种改进。RMSprop 采用均方根作为分 母,可缓解 Adagrad 学习率下降较快的问题,并且引入均方根,可以减少摆动
<6> torch.optim.Adam
torch.optim.Adam(params,
lr=0.001,
betas=(0.9, 0.999),
eps=1e- 08,
weight_decay=0,
amsgrad=False)
- 功能:实现 Adam(Adaptive Moment Estimation))优化方法。Adam 是一种自适应学习率的优化方法,Adam 利用梯度的一阶矩估计和二阶矩估计动态的调整学习率, Adam 是结合了 Momentum 和 RMSprop,并进行了偏差修正
<7> torch.optim.Adamax
torch.optim.Adamax(params,
lr=0.002,
betas=(0.9, 0.999),
eps=1e- 08,
weight_decay=0)
- 功能:实现 Adamax 优化方法。Adamax 是对 Adam 增加了一个学习率上限的概念,所以也称之为 Adamax
<8> torch.optim.SparseAdam
torch.optim.SparseAdam(params,
lr=0.001,
betas=(0.9, 0.999),
eps=1e-08)
- 功能:针对稀疏张量的一种“阉割版”Adam 优化方法
<9> torch.optim.LBFGS
torch.optim.LBFGS(params,
lr=1,
max_iter=20,
max_eval=None,
tolerance_ grad=1e-05,
tolerance_change=1e-09,
history_size=100,
line_search_fn=None)
- 功能:实现 L-BFGS(Limited-memory Broyden–Fletcher–Goldfarb–Shanno)优化方法。 L-BFGS 属于拟牛顿算法。L-BFGS 是对 BFGS 的改进,特点就是节省内存