Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >YOLOv8优化策略:Adam该换了!斯坦福最新Sophia优化器,比Adam快2倍 | 2023.5月斯坦福最新成果

YOLOv8优化策略:Adam该换了!斯坦福最新Sophia优化器,比Adam快2倍 | 2023.5月斯坦福最新成果

原创
作者头像
AI小怪兽
发布于 2023-11-03 00:49:34
发布于 2023-11-03 00:49:34
2.2K00
代码可运行
举报
文章被收录于专栏:YOLO大作战YOLO大作战
运行总次数:0
代码可运行

1.Sophia优化器介绍

斯坦福2023.5月发表的最新研究成果,他们提出了「一种叫Sophia的优化器,相比Adam,它在LLM上能够快2倍,可以大幅降低训练成本」

论文:https://arxiv.org/pdf/2305.14342.pdf

本文介绍了一种新的模型预训练优化器:Sophia(Second-order Clipped Stochastic Optimization),这是一种轻量级二阶优化器,它使用Hessian对角线的廉价随机估计作为预调节器,并通过限幅机制来控制最坏情况下的更新大小。在GPT-2等预训练语言模型上,Sophia以比Adam少了50%的步骤,且实现了相同的预训练损失。

作者表示 Adam 对于异构曲率(heterogeneous curvatures)的适应性不足。另一方面,vanilla Newton 方法在凸函数中具有最优的 pre-conditioner,但对于负曲率和 Hessian 的快速变化容易受到影响。基于这些见解,该研究设计了一种新的优化器 Sophia,它比 Adam 更适应异构曲率,比 Newton 方法更能抵抗非凸性和 Hessian 的快速变化,并且还使用了成本较低的 pre-conditioner。

研究引入了两个对角 Hessian 估计器,它们的内存和运行时间成本都与计算梯度相似。估计器分别为 Hutchinson 无偏估计器以及 GNB( Gauss-Newton-Bartlett ) 估计器。伪代码如下所示:

比较 wall-clock 时间与计算量。表 1 比较了每一个 step 的总计算量 (TFLOPs) 和 A100 GPU 上的 wall-clock 时间。本文报告了每个 step 的平均时间,Hessian 计算花费的时间的总计算。较小的批量大小,即每 10 个 step 以计算对角 Hessian 估计,Hessian 计算占总计算量的 6%,与 AdamW 相比,整体 wall-clock 时间开销小于 5%。在内存使用方面,优化器 m 和 h 两个状态,这导致了与 AdamW 相同的内存开销。

2.Sophia引入到yolov8

2.1 修改ultralytics/yolo/engine/trainer.py

核心代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

import torch
from torch.optim.optimizer import Optimizer
import math
from torch import Tensor
from typing import List, Optional


class Sophia(Optimizer):
    def __init__(self, model, input_data, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0, k=10,
                 estimator="Hutchinson", rho=1):
        self.model = model
        self.input_data = input_data
        defaults = dict(lr=lr, betas=betas, eps=eps, weight_decay=weight_decay, k=k, estimator=estimator, rho=rho)
        super(Sophia, self).__init__(params, defaults)

    def step(self, closure=None):
        loss = None
        if closure is not None:
            loss = closure()

        for group in self.param_groups:
            for p in group["params"]:
                if p.grad is None:
                    continue
                grad = p.grad.data
                if grad.is_sparse:
                    raise RuntimeError("Sophia does not support sparse gradients")

                state = self.state[p]

                # state init
                if len(state) == 0:
                    state['step'] = 0
                    state['m'] = torch.zeros_like(p.data)
                    state['h'] = torch.zeros_like(p.data)

                m, h = state['m'], state['h']
                beta1, beta2 = group['betas']
                state['step'] += 1

                if group['weight_decay'] != 0:
                    grad = grad.add(group["weight_decay"], p.data)

                # update biased first moment estimate
                m.mul_(beta1).add_(1 - beta1, grad)

                # update hessian estimate
                if state['step'] % group['k'] == 1:
                    if group['estimator'] == "Hutchinson":
                        hessian_estimate = self.hutchinson(p, grad)
                    elif group['estimator'] == "Gauss-Newton-Bartlett":
                        hessian_estimate = self.gauss_newton_bartlett(p, grad)
                    else:
                        raise ValueError("Invalid estimator choice")
                    h.mul_(beta2).add_(1 - beta2, hessian_estimate)

                # update params
                p.data.add_(-group['lr'] * group['weight_decay'], p.data)
                p.data.addcdiv_(-group['lr'], m, h.add(group['eps']).clamp(max=group['rho']))

        return loss

    def hutchinson(self, p, grad):
        u = torch.randn_like(grad)
        grad_dot_u = torch.sum(grad * u)
        hessian_vector_product = torch.autograd.grad(grad_dot_u, p, retain_graph=True)[0]
        return u * hessian_vector_product

    def gauss_newton_bartlett(self, p, grad):
        B = len(self.input_data)
        logits = [self.model(xb) for xb in self.input_data]
        y_hats = [torch.softmax(logit, dim=0) for logit in logits]
        g_hat = \
        torch.autograd.grad(sum([self.loss_function(logit, y_hat) for logit, y_hat in zip(logits, y_hats)]) / B, p,
                            retain_graph=True)[0]
        return B * g_hat * g_hat


class SophiaG(Optimizer):
    def __init__(self, params, lr=1e-4, betas=(0.965, 0.99), rho=0.04,
                 weight_decay=1e-1, *, maximize: bool = False,
                 capturable: bool = False):
        if not 0.0 <= lr:
            raise ValueError("Invalid learning rate: {}".format(lr))
        if not 0.0 <= betas[0] < 1.0:
            raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0]))
        if not 0.0 <= betas[1] < 1.0:
            raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1]))
        if not 0.0 <= rho:
            raise ValueError("Invalid rho parameter at index 1: {}".format(rho))
        if not 0.0 <= weight_decay:
            raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
        defaults = dict(lr=lr, betas=betas, rho=rho,
                        weight_decay=weight_decay,
                        maximize=maximize, capturable=capturable)
        super(SophiaG, self).__init__(params, defaults)

    def __setstate__(self, state):
        super().__setstate__(state)
        for group in self.param_groups:
            group.setdefault('maximize', False)
            group.setdefault('capturable', False)
        state_values = list(self.state.values())
        step_is_tensor = (len(state_values) != 0) and torch.is_tensor(state_values[0]['step'])
        if not step_is_tensor:
            for s in state_values:
                s['step'] = torch.tensor(float(s['step']))

    @torch.no_grad()
    def update_hessian(self):
        for group in self.param_groups:
            beta1, beta2 = group['betas']
            for p in group['params']:
                if p.grad is None:
                    continue
                state = self.state[p]

                if len(state) == 0:
                    state['step'] = torch.zeros((1,), dtype=torch.float, device=p.device) \
                        if self.defaults['capturable'] else torch.tensor(0.)
                    state['exp_avg'] = torch.zeros_like(p, memory_format=torch.preserve_format)
                    state['hessian'] = torch.zeros_like(p, memory_format=torch.preserve_format)

                if 'hessian' not in state.keys():
                    state['hessian'] = torch.zeros_like(p, memory_format=torch.preserve_format)

                state['hessian'].mul_(beta2).addcmul_(p.grad, p.grad, value=1 - beta2)

    @torch.no_grad()
    def step(self, closure=None, bs=5120):
        loss = None
        if closure is not None:
            with torch.enable_grad():
                loss = closure()

        for group in self.param_groups:
            params_with_grad = []
            grads = []
            exp_avgs = []
            state_steps = []
            hessian = []
            beta1, beta2 = group['betas']

            for p in group['params']:
                if p.grad is None:
                    continue
                params_with_grad.append(p)

                if p.grad.is_sparse:
                    raise RuntimeError('Hero does not support sparse gradients')
                grads.append(p.grad)
                state = self.state[p]
                # State initialization
                if len(state) == 0:
                    state['step'] = torch.zeros((1,), dtype=torch.float, device=p.device) \
                        if self.defaults['capturable'] else torch.tensor(0.)
                    state['exp_avg'] = torch.zeros_like(p, memory_format=torch.preserve_format)
                    state['hessian'] = torch.zeros_like(p, memory_format=torch.preserve_format)

                if 'hessian' not in state.keys():
                    state['hessian'] = torch.zeros_like(p, memory_format=torch.preserve_format)

                exp_avgs.append(state['exp_avg'])
                state_steps.append(state['step'])
                hessian.append(state['hessian'])

                if self.defaults['capturable']:
                    bs = torch.ones((1,), dtype=torch.float, device=p.device) * bs

            sophiag(params_with_grad,
                    grads,
                    exp_avgs,
                    hessian,
                    state_steps,
                    bs=bs,
                    beta1=beta1,
                    beta2=beta2,
                    rho=group['rho'],
                    lr=group['lr'],
                    weight_decay=group['weight_decay'],
                    maximize=group['maximize'],
                    capturable=group['capturable'])

        return loss


def sophiag(params: List[Tensor],
            grads: List[Tensor],
            exp_avgs: List[Tensor],
            hessian: List[Tensor],
            state_steps: List[Tensor],
            capturable: bool = False,
            *,
            bs: int,
            beta1: float,
            beta2: float,
            rho: float,
            lr: float,
            weight_decay: float,
            maximize: bool):
    if not all(isinstance(t, torch.Tensor) for t in state_steps):
        raise RuntimeError("API has changed, `state_steps` argument must contain a list of singleton tensors")

    func = _single_tensor_sophiag
    #
    func(params,
         grads,
         exp_avgs,
         hessian,
         state_steps,
         bs=bs,
         beta1=beta1,
         beta2=beta2,
         rho=rho,
         lr=lr,
         weight_decay=weight_decay,
         maximize=maximize,
         capturable=capturable)


def _single_tensor_sophiag(params: List[Tensor],
                           grads: List[Tensor],
                           exp_avgs: List[Tensor],
                           hessian: List[Tensor],
                           state_steps: List[Tensor],
                           *,
                           bs: int,
                           beta1: float,
                           beta2: float,
                           rho: float,
                           lr: float,
                           weight_decay: float,
                           maximize: bool,
                           capturable: bool):
    for i, param in enumerate(params):
        grad = grads[i] if not maximize else -grads[i]
        exp_avg = exp_avgs[i]
        hess = hessian[i]
        step_t = state_steps[i]

        if capturable:
            assert param.is_cuda and step_t.is_cuda and bs.is_cuda

        if torch.is_complex(param):
            grad = torch.view_as_real(grad)
            exp_avg = torch.view_as_real(exp_avg)
            hess = torch.view_as_real(hess)
            param = torch.view_as_real(param)

        # update step
        step_t += 1

        # Perform stepweight decay
        param.mul_(1 - lr * weight_decay)

        # Decay the first and second moment running average coefficient
        exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1)

        if capturable:
            step = step_t
            step_size = lr
            step_size_neg = step_size.neg()

            ratio = (exp_avg.abs() / (rho * bs * hess + 1e-15)).clamp(None, 1)
            param.addcmul_(exp_avg.sign(), ratio, value=step_size_neg)
        else:
            step = step_t.item()
            step_size_neg = - lr

            ratio = (exp_avg.abs() / (rho * bs * hess + 1e-15)).clamp(None, 1)
            param.addcmul_(exp_avg.sign(), ratio, value=step_size_neg)

3.总结

训练稳定性。与 AdamW 和 Lion 相比,Sophia-H 在预训练中具有更好的稳定性。梯度裁剪 (by norm) 是语言模型预训练中的一项重要技术。在实践中,梯度裁剪触发的频率与训练的稳定性有关 —— 如果梯度被频繁裁剪,迭代可能处于非常不稳定的状态。图 7 (a) 比较了 GPT-2 (125M) 触发梯度裁剪的 step 比例。尽管所有方法都使用相同的裁剪阈值 1.0,但 Sophia-H 很少触发梯度裁剪,而 AdamW 和 Lion 在超过 10% 的 step 中触发梯度裁剪。

详见:

https://blog.csdn.net/m0_63774211/article/details/130912702

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
YOLOv8优化策略: 谷歌强势推出优化器Lion,内存更小、效率更高,秒杀Adam(W)
代码:automl/lion at master · google/automl · GitHub
AI小怪兽
2023/11/03
2.5K0
Google Brain新提出的优化器“Lion”,效果要比Adam(W)更好
与 AdamW 和各种自适应优化器需要同时保存一阶和二阶矩相比,Lion 只需要动量,将额外的内存占用减半。 这在训练大型模型和大Batch size时很有用。 例如,AdamW 需要至少 16 个 TPU V4 芯片来训练图像大小为 224、批量大小为 4,096 的 ViT-B/16,而 Lion 只需要8个。
致Great
2023/08/25
1.1K0
Google Brain新提出的优化器“Lion”,效果要比Adam(W)更好
神经网络调参技巧:warmup策略
有一些论文对warmup进行了讨论,使用 SGD 训练神经网络时,在初始使用较大学习率而后期改为较小学习率在各种任务场景下都是一种广为使用的做法,在实践中效果好且最近也有若干文章尝试对其进行了理论解释。例如《On Layer Normalization in the Transformer Architecture》等,论文中作者发现Post-LN Transformer在训练的初始阶段,输出层附近的期望梯度非常大,所以没有warm-up的话模型优化过程就会非常不稳定。
炼丹笔记
2022/04/06
1.2K0
神经网络调参技巧:warmup策略
手把手教你从零实现一个深度学习框架(附代码实现)
当前深度学习框架越来越成熟,对于使用者而言封装程度越来越高,好处就是现在可以非常快速地将这些框架作为工具使用,用非常少的代码就可以构建模型进行实验,坏处就是可能背后地实现都被隐藏起来了。在这篇文章里笔者将设计和实现一个、轻量级的(约 200 行)、易于扩展的深度学习框架 tinynn(基于 Python 和 Numpy 实现),希望对大家了解深度学习的基本组件、框架的设计和实现有一定的帮助。
一点人工一点智能
2022/12/27
1.5K0
手把手教你从零实现一个深度学习框架(附代码实现)
torch.optim
为了使用torch.optim,你必须构建一个优化对象,那将会保持现有的状态,并且基于计算的来更新参数。
狼啸风云
2020/06/12
1.6K0
“瘦身成功”的ALBERT,能取代BERT吗?
模型的创新点集中在了预训练过程,采用Masked LM和Next Sentence Prediction两种方法,分别捕捉词语和句子级别的表示。
量子位
2020/03/31
9700
“瘦身成功”的ALBERT,能取代BERT吗?
当前训练神经网络最快的方式:AdamW优化算法+超级收敛
Adam 优化器之旅可以说是过山车(roller-coaster)式的。该优化器于 2014 年推出,本质上是一个出于直觉的简单想法:既然我们明确地知道某些参数需要移动得更快、更远,那么为什么每个参数还要遵循相同的学习率?因为最近梯度的平方告诉我们每一个权重可以得到多少信号,所以我们可以除以这个,以确保即使是最迟钝的权重也有机会发光。Adam 接受了这个想法,在过程中加入了标准方法,就这样产生了 Adam 优化器(稍加调整以避免早期批次出现偏差)!
机器之心
2018/07/26
1.6K0
当前训练神经网络最快的方式:AdamW优化算法+超级收敛
【深度学习实验】网络优化与正则化(三):随机梯度下降的改进——Adam算法详解(Adam≈梯度方向优化Momentum+自适应学习率RMSprop)
  目前,研究人员通过大量实践总结了一些经验方法,以在神经网络的表示能力、复杂度、学习效率和泛化能力之间取得良好的平衡,从而得到良好的网络模型。本系列文章将从网络优化和网络正则化两个方面来介绍如下方法:
Qomolangma
2024/07/30
4370
【深度学习实验】网络优化与正则化(三):随机梯度下降的改进——Adam算法详解(Adam≈梯度方向优化Momentum+自适应学习率RMSprop)
神经网络中的优化方法
在传统的梯度下降优化算法中,如果碰到平缓区域,梯度值较小,参数优化变慢 ,遇到鞍点(是指在某些方向上梯度为零而在其他方向上梯度非零的点。),梯度为 0,参数无法优化,碰到局部最小值。实践中使用的小批量梯度下降法(mini-batch SGD)因其梯度估计的噪声性质,有时能够使模型脱离这些点。
@小森
2024/05/06
1260
神经网络中的优化方法
机器学习|从0开发大模型之模型预训练
继续写《从0开发大模型》系列文章,本文主要介绍预训练过程。 预训练是目的是让模型学习知识,需要将预处理的数据(《机器学习|从0开发大模型之数据预处理》)中生成的 pretrain_data.bin 文件的上下文全部学习到,那预训练怎么做呢?
用户1904552
2025/02/27
1920
机器学习|从0开发大模型之模型预训练
【知识】PyTorch中不同优化器的特点和使用
小锋学长生活大爆炸
2025/04/09
3470
动手学深度学习(八) 优化算法进阶
在 Section 11.4 中,我们提到,目标函数有关自变量的梯度代表了目标函数在自变量当前位置下降最快的方向。因此,梯度下降也叫作最陡下降(steepest descent)。在每次迭代中,梯度下降根据自变量当前位置,沿着当前位置的梯度更新自变量。然而,如果自变量的迭代方向仅仅取决于自变量当前位置,这可能会带来一些问题。对于noisy gradient,我们需要谨慎的选取学习率和batch size, 来控制梯度方差和收敛的结果。
致Great
2020/02/25
1.4K0
动手学深度学习(八) 优化算法进阶
【AI】从零构建深度学习框架过程学习
当前深度学习框架越来越成熟,对于使用者而言封装程度越来越高,好处就是现在可以非常快速地将这些框架作为工具使用,用非常少的代码就可以构建模型进行实验,坏处就是可能背后地实现都被隐藏起来了。在这篇文章里笔者将设计和实现一个、轻量级的(约 200 行)、易于扩展的深度学习框架 tinynn(基于 Python 和 Numpy 实现),希望对大家了解深度学习的基本组件、框架的设计和实现有一定的帮助。
Freedom123
2024/05/17
1770
[源码解析] PyTorch分布式优化器(3)---- 模型并行
本系列介绍分布式优化器,分为三篇文章,分别是基石篇,DP/DDP/Horovod 之中数据并行的优化器,PyTorch 分布式优化器,按照深度递进。本文介绍PyTorch 分布式优化器和PipeDream之中的优化器,主要涉及模型并行(流水线并行)。
罗西的思考
2021/12/10
1.5K0
[源码解析] PyTorch分布式优化器(3)---- 模型并行
【RL Base】强化学习:信赖域策略优化(TRPO)算法
在强化学习(RL)领域,如何稳定地优化策略是一个核心挑战。2015 年,由 John Schulman 等人提出的信赖域策略优化(Trust Region Policy Optimization, TRPO)算法为这一问题提供了优雅的解决方案。TRPO 通过限制策略更新的幅度,避免了策略更新过大导致的不稳定问题,是强化学习中经典的策略优化方法之一。
不去幼儿园
2024/12/03
3050
【RL Base】强化学习:信赖域策略优化(TRPO)算法
[源码解析] PyTorch分布式优化器(2)----数据并行优化器
本系列介绍分布式优化器,分为三篇文章,分别是基石篇,DP/DDP/Horovod 之中数据并行的优化器,PyTorch 分布式优化器,按照深度递进。
罗西的思考
2021/12/09
1.1K0
[源码解析] PyTorch分布式优化器(2)----数据并行优化器
torch.optim
torch.optim is a package implementing various optimization algorithms. Most commonly used methods are already supported, and the interface is general enough, so that more sophisticated ones can be also easily integrated in the future.
狼啸风云
2019/09/25
9030
PyTorch 2.2 中文官方教程(十)
有时,用户需要识别由代码更改导致的 PyTorch 操作符和 CUDA 内核的变化。为了支持这一需求,HTA 提供了一个追踪比较功能。该功能允许用户输入两组追踪文件,第一组可以被视为控制组,第二组可以被视为测试组,类似于 A/B 测试。TraceDiff类提供了比较追踪之间差异的函数以及可视化这些差异的功能。特别是,用户可以找到每个组中添加和删除的操作符和内核,以及每个操作符/内核的频率和操作符/内核所花费的累积时间。
ApacheCN_飞龙
2024/02/05
4330
PyTorch 2.2 中文官方教程(十)
点赞收藏:PyTorch常用代码段整理合集
PyTorch 将被安装在 anaconda3/lib/python3.7/site-packages/torch/目录下。
机器之心
2019/05/10
1.7K0
[源码解析] PyTorch分布式优化器(1)----基石篇
我们接下来通过几篇文章来看看分布式优化器。本系列分为三篇文章,分别是基石篇,DP/DDP/Horovod 之中数据并行的优化器,PyTorch 分布式优化器,按照深度递进。
罗西的思考
2021/12/09
2K0
[源码解析] PyTorch分布式优化器(1)----基石篇
推荐阅读
相关推荐
YOLOv8优化策略: 谷歌强势推出优化器Lion,内存更小、效率更高,秒杀Adam(W)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验