Ptorch中的Optimizer

本文详细介绍了Ptorch中的Optimizer,包括神经网络参数、优化器的构造、各种优化算法如Adadelta、AdaGrad、Adam等的实现,以及学习率调整策略如LambdaLR、StepLR等。通过深入理解这些内容,读者可以更好地掌握如何在Ptorch中训练深度学习模型。

Ptorch中的Optimizer

神经网路的参数

对于一个神经网络,我们学习网络中的参数。首先,假设将给定的神经网络所有可学习的参数被分为 N N N组: S 0 , S 1 , … , S N − 1 S_0, S_1, … ,S_{N-1} S0,S1,,SN1,每一组都有各自的不同配置,例如不同的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,,DN1]

  • 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_groupsstateparam_groups用于保存变量,另外一个state保存每个变量的状态信息。例如在带momentum的SGD中,每个变量需要保存一个velocity来计算最终的梯度。这个velocity就保存在成员属性state中。 两个属性都是字典。

序列化

首先是和 pickle 相关的特殊函数。__getstate____setstate__dumpload相关:

def __getstate__(self):
    return {
   
   
        'state': self.state,
        'param_groups': self.param_groups,
    }

def __setstate__(self, state):
    self.__dict__.update(state)

这个两个函数定义了如何对一个类进行 picklingunpickling

  • dump 的时候,通过__getstate__返回一个字典,如果没有定义__getstate__这个函数,那么默认使用的是self.__dict__
  • 同样,load 的时候,通过__setstate__来重构这个类的实例。

另外, 我们需要保存一个优化的当前状态,state_dictload_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=1nJi(θ)/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=τ=1tgτ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

### PyTorch 中优化器的相关信息 #### 优化器的作用与基本概念 在机器学习模型训练过程中,优化器用于更新网络权重以最小化损失函数。通过调整这些参数,可以提高模型性能并加速收敛过程。 #### 创建自定义权重张量 为了展示如何创建带有梯度属性的张量,在下面的例子中定义了一个`geneWeight()` 函数用来生成随机初始化且具有可求导特性的二维矩阵[^1]: ```python import torch def geneWeight(): weight = torch.randn((2,2), requires_grad=True) weight.grad = torch.ones((2,2)) return weight ``` #### 初始化多个参数组 当需要管理不同子集下的变量时(比如冻结某些层),可以通过向 `add_param_group()` 方法传递字典形式的新参数列表实现多组配置的支持。此操作允许为每组设置独立的学习率和其他超参选项,如下所示: ```python torch.manual_seed(0) a = geneWeight() b = geneWeight() optimizer = torch.optim.SGD([a], lr=0.1) optimizer.add_param_group({'params': [b], 'weight_decay': 0.005}) ``` #### 执行单步优化迭代 调用 `.step()` 可触发一次完整的反向传播计算,并依据选定算法自动完成权值修正;而`.zero_grad()` 则负责清除当前累积的所有梯度信息以便下一轮循环正常运作: ```python optimizer.step() # 更新参数 optimizer.zero_grad() # 清除已存梯度 ``` #### 构建参与训练的参数集合 对于更复杂的场景而言,可能只希望部分特定组件参与到实际更新流程当中。此时可通过遍历整个模块结构筛选符合条件者加入到待处理队列之中,如代码片段所描述那样[^2]: ```python param_to_optim = [] for param in model.parameters(): if not param.requires_grad: continue param_to_optim.append(param) optimizer = torch.optim.SGD( param_to_optim, lr=0.001, momentum=0.9, weight_decay=1e-4 ) ``` #### 常见优化器种类简介 PyTorch 提供了多种内置优化策略供开发者选用,其中包括但不限于: - **SGD (Stochastic Gradient Descent)**: 随机梯度下降法是最基础也是最常用的优化方式之一; - **Adam**: 自适应矩估计结合了一阶动量和二阶动量的优点,通常能带来更快更好的效果; - **RMSprop**: 根均方误差传播法则特别适合于非稳态环境下的在线/增量式学习任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值