Ptorch中的Optimizer
文章目录
神经网路的参数
对于一个神经网络,我们学习网络中的参数。首先,假设将给定的神经网络所有可学习的参数被分为 N N N组: S 0 , S 1 , … , S N − 1 S_0, S_1, … ,S_{N-1} S0,S1,…,SN−1,每一组都有各自的不同配置,例如不同的learning rate等。 对于每一组 S n S_n Sn,我们可以用一个字典 D n D_n Dn来表示:
{
'params': Parameters, 'lr': learning_rate}
其中Parameters是一个列表(list), 它表示了这组里有哪些需要学习的参数。其他关键字定义了这组参数不同于别组的配置情况。通常,多组参数之间存在很多相同的设置,对于这些相同的不需要额外设置的配置,,我们用字典defaults
来表示。即,如果字典 D n D_n Dn里没有的定义,那么就复用defaults里的配置。
代码实现
在实现不用的优化器的之前,我们不妨先考虑一个优化器基类需要的一些特征。 我们先论基本的优化器的实现,然后从SGD的具体实现来进一步的了解相关的操作。
构造函数
首先是如何构造一个优化器实例。在实例化一个优化器时,我们应该需要两个参数:
-
Parameter Groups: [ D 0 , D 1 , … , D N − 1 ] [D_0, D_1, … ,D_{N-1}] [D0,D1,…,DN−1]
-
Defaults :默认的参数设置
特殊情况,当我们只有一组参数时,即Parameter Groups 只有 D 0 D_0 D0, 这时候, 我们希望只用给出简单化的初始化方式即:给出Parameters和Defaults。
在PyTorch中, 定义了所有优化器的基类Optimizer
:
Optimizer(params, defaults)
其中的参数为:
- params:可以是一个网络的所有Parameters, 也可以是上面提到的Parameter Groups
- defaults:定义默认的参数配置
当需要构造一个新的Optimizer的子类的时候,只需要在初始化的时候,将配置信息保存在defaults
里,例如:
class SGD(Optimizer):
def __init__(self, param. lr=required, momentum=0, dampening=0,
weight_decay=0, nesterov=False):
defaults = dict(lr=lr, momentum=momentum, dampening=dampening,
weight_decay=weight_decay, nesterov=nesterov)
if nesterov and (momentum <= 0 or dampening != 0):
raise ValueError("Nesterov momentum requires a momentum and zero dampening")
super(SGD, self).__init__(params, defaults)
下面给出一个构造一个多组变量的优化器的例子:
optim.SGD([{
'params': model.base.parameters()},
{
'params': model.classifier.parameters(), 'lr': 1e-3}],
lr=1e-2, momentum=0.9)
如果model需要在GPU上运行, 则需要在构造优化器之前调用
.cuda()
. 因为调用.cuda()
前后的Parameters是不同的.
属性和方法
一个优化器需要的属性有param_groups
和state
。param_groups
用于保存变量,另外一个state
保存每个变量的状态信息。例如在带momentum的SGD中,每个变量需要保存一个velocity来计算最终的梯度。这个velocity就保存在成员属性state中。 两个属性都是字典。
序列化
首先是和 pickle 相关的特殊函数。__getstate__
,__setstate__
和dump
,load
相关:
def __getstate__(self):
return {
'state': self.state,
'param_groups': self.param_groups,
}
def __setstate__(self, state):
self.__dict__.update(state)
这个两个函数定义了如何对一个类进行 pickling 和 unpickling 。
- 在 dump 的时候,通过
__getstate__
返回一个字典,如果没有定义__getstate__
这个函数,那么默认使用的是self.__dict__
。 - 同样,load 的时候,通过
__setstate__
来重构这个类的实例。
另外, 我们需要保存一个优化的当前状态,state_dict
和 load_state_dict
提供了返回和读取优化器状态的方法。由于实际内存中的 Parameter 不是我们真正关心的。我们只关心这些Parameter对应的一些状态信息。因此 state_dict
的返回中,将 Parameter 都用id来代替,来节省空间。
def state_dict(self):
# Save ids instead of Variables
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, Variable) else k): v
for k, v in self.state.items()}
return {
'state': packed_state,
'param_groups': param_groups,
}
同样在读取状态的时候,也不需要读取Parameter,因为真实的Parameter已经保存在当前实例的属性中,我们只需要回复每个Parameter对应的参数即可。
简单来说:
-
state_dict
将返回类属性{ 'state': packed_state, 'param_groups': param_groups}
-
load_state_dict
读取类属性。
更新参数
优化器通过方法step
来更新参数。有一些优化算法需要传入一个额外的closure。这个函数计算一轮迭代,并且返回loss。
optimizer.step()
:
for input, target in dataset:
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
optimizer.step()
optimizer.step(closure)
:
for input, target in dataset:
def closure():
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
return loss
optimizer.step(closure)
所有的优化器类
首先假设:
θ : = θ − η ∇ J ( θ ) = θ − η ∑ i = 1 n ∇ J i ( θ ) / n \theta:=\theta-\eta \nabla J(\theta)=\theta-\eta \sum _{i=1}^{n}\nabla J_{i}(\theta)/n θ:=θ−η∇J(θ)=θ−ηi=1∑n∇Ji(θ)/n
θ \theta θ, η \eta η 和 J ( θ ) J(\theta) J(θ) 分别为参数,学习率和损失函数。
Adatelta
class torch.optim.Adadelta(params,
lr=1.0,
rho=0.9,
eps=1e-06,
weight_decay=0)
It has been proposed in ADADELTA: An Adaptive Learning Rate Method.
Parameters:
- params (iterable) – 参数,同基类
- rho (float, optional) – coefficient used for computing a running average of squared gradients (默认: 0.9)
- eps (float, optional) – term added to the denominator to improve numerical stability (默认: 1e-6)
- lr (float, optional) – coefficient that scale delta before it is applied to the parameters (默认: 1.0)
- weight_decay(float, optional) – weight decay ( l 2 l_2 l2 范数) (默认: 0)
AdaGrad
class torch.optim.Adagrad(params,
lr=0.01,
lr_decay=0,
weight_decay=0)
Implements Adagrad algorithm.
Informally, this increases the learning rate for more sparse parameters and decreases the learning rate for less sparse ones. This strategy often improves convergence performance over standard stochastic gradient descent in settings where data is sparse and sparse parameters are more informative. Examples of such applications include natural language processing and image recognition.
It has been proposed in Adaptive Subgradient Methods for Online Learning and Stochastic Optimization.
G = ∑ τ = 1 t g τ g τ T G=\sum _{\tau =1}^{t}g_{\tau }g_{\tau }^{\mathsf {T}} G=τ=1∑tgτgτT
where g τ = ∇ J i ( θ ) g_{\tau}=\nabla J_{i}(\theta) gτ=∇Ji(θ) the gradient, at iteration τ \tau τ. The diagonal is given by
G j , j = ∑ τ = 1 t g τ , j 2 G_{j,j}=\sum _{\tau =1}^{t}g_{\tau ,j}^{2} Gj,j=τ=1∑