如何解决混合精度训练大模型的局限性问题

news2024/11/28 12:48:06

混合精度已经成为训练大型深度学习模型的必要条件,但也带来了许多挑战。将模型参数和梯度转换为较低精度数据类型(如FP16)可以加快训练速度,但也会带来数值稳定性的问题。使用进行FP16 训练梯度更容易溢出或不足,导致优化器计算不精确,以及产生累加器超出数据类型范围的等问题。

在这篇文章中,我们将讨论混合精确训练的数值稳定性问题。为了处理数值上的不稳定性,大型训练工作经常会被搁置数天,会导致项目的延期。所以我们可以引入Tensor Collection Hook来监控训练期间的梯度条件,这样可以更好地理解模型的内部状态,更快地识别数值不稳定性。

在早期训练阶段了解模型的内部状态可以判断模型在后期训练中是否容易出现不稳定是非常好的办法,如果能够在训练的头几个小时就能识别出梯度不稳定性,可以帮助我们提升很大的效率。所以本文提供了一系列值得关注的警告,以及数值不稳定性的补救措施。

混合精度训练

随着深度学习继续向更大的基础模型发展。像GPT和T5这样的大型语言模型现在主导着NLP,在CV中对比模型(如CLIP)的泛化效果优于传统的监督模型。特别是CLIP的学习文本嵌入意味着它可以执行超过过去CV模型能力的零样本和少样本推理,训练这些模型都是一个挑战。

这些大型的模型通常涉及深度transformers网络,包括视觉和文本,并且包含数十亿个参数。GPT3有1750亿个参数,CLIP则是在数百tb的图像上进行训练的。模型和数据的大小意味着模型需要在大型GPU集群上进行数周甚至数月的训练。为了加速训练减少所需gpu的数量,模型通常以混合精度进行训练。

混合精确训练将一些训练操作放在FP16中,而不是FP32。在FP16中进行的操作需要更少的内存,并且在现代gpu上可以比FP32的处理速度快8倍。尽管在FP16中训练的大多数模型精度较低,但由于过度的参数化它们没有显示出任何的性能下降。

随着英伟达在Volta架构中引入Tensor Cores,低精度浮点加速训练更加快速。因为深度学习模型有很多参数,任何一个参数的确切值通常都不重要。通过用16位而不是32位来表示数字,可以一次性在Tensor Core寄存器中拟合更多参数,增加每个操作的并行性。

但FP16的训练是存在挑战性的。因为FP16不能表示绝对值大于65,504或小于5.96e-8的数字。深度学习框架例如如PyTorch带有内置工具来处理FP16的限制(梯度缩放和自动混合精度)。但即使进行了这些安全检查,由于参数或梯度超出可用范围而导致大型训练工作失败的情况也很常见。深度学习的一些组件在FP32中发挥得很好,但是例如BN通常需要非常细粒度的调整,在FP16的限制下会导致数值不稳定,或者不能产生足够的精度使模型正确收敛。这意味着模型并不能盲目地转换为FP16。

所以深度学习框架使用自动混合精度(AMP),它通过一个预先定义的FP16训练安全操作列表。AMP只转换模型中被认为安全的部分,同时将需要更高精度的操作保留在FP32中。另外在混合精度训练中模型中通过给一些接近于零梯度(低于FP16的最小范围)的损失乘以一定数值来获得更大的梯度,然后在应用优化器更新模型权重时将按比例向下调整来解决梯度过小的问题,这种方法被称为梯度缩放。

下面是PyTorch中一个典型的AMP训练循环示例。

梯度缩放器scaler会将损失乘以一个可变的量。如果在梯度中观察到nan,则将倍数降低一半,直到nan消失,然后在没有出现nan的情况下,默认每2000步逐渐增加倍数。这样会保持梯度在FP16范围内,同时也防止梯度变为零。

训练不稳定的案例

尽管框架都尽了最大的努力,但PyTorch和TensorFlow中内置的工具都不能阻止在FP16中出现的数值不稳定情况。

在HuggingFace的T5实现中,即使在训练之后模型变体也会产生INF值。在非常深的T5模型中,注意力值会在层上累积,最终达到FP16范围之外,这会导致值无穷大,比如在BN层中出现nan。他们是通过将INF值改为在FP16的最大值解决了这个问题,并且发现这对推断的影响可以忽略不计。

另一个常见问题是ADAM优化器的限制。作为一个小更新,ADAM使用梯度的第一和第二矩的移动平均来适应模型中每个参数的学习率。

这里Beta1 和 Beta2 是每个时刻的移动平均参数,通常分别设置为 .9 和 .999。用 beta 参数除以步数的幂消除了更新中的初始偏差。在更新步骤中,向二阶矩参数添加一个小的 epsilon 以避免被零除产生错误。epsilon 的典型默认值是 1e-8。但 FP16 的最小值为 5.96e-8。这意味着如果二阶矩太小,更新将除以零。所以在 PyTorch 中为了训练不会发散,更新将跳过该步骤的更改。但问题仍然存在尤其是在 Beta2=.999 的情况下,任何小于 5.96e-8 的梯度都可能会在较长时间内停止参数的权重更新,优化器会进入不稳定状态。

ADAM的优点是通过使用这两个矩,可以调整每个参数的学习率。对于较慢的学习参数,可以加快学习速度,而对于快速学习参数,可以减慢学习速度。但如果对多个步骤的梯度计算为零,即使是很小的正值也会导致模型在学习率有时间向下调整之前发散。

另外PyTorch目前还一个问题,在使用混合精度时自动将epsilon更改为1e-7,这可以帮助防止梯度移回正值时发散。但是这样做会带来一个新的问题,当我们知道梯度在相同的范围内时,增加ε会降低了优化器适应学习率的能力。所以盲目的增加epsilon也不能解决由于零梯度而导致训练停滞的情况。

CLIP训练中的梯度缩放

为了进一步证明训练中可能出现的不稳定性,我们在CLIP图像模型上构建了一系列实验。CLIP是一种基于对比学习的模型,它通过视觉转换器和描述这些图像的文本嵌入同时学习图像。对比组件试图在每批数据中将图像匹配回原始描述。由于损失是在批次中计算的,在较大批次上的训练已被证明能提供更好的结果。

CLIP同时训练两个transformers模型,一个类似GPT的语言模型和一个ViT图像模型。两种模型的深度都为梯度增长创造了超越FP16限制的机会。OpenClip(arxiv 2212.07143)实现描述了使用FP16时的训练不稳定性。

Tensor Collection Hook

为了更好地理解训练期间的内部模型状态,我们开发了一个Tensor Collection Hook (TCH)。TCH可以包装一个模型,并定期收集关于权重、梯度、损失、输入、输出和优化器状态的摘要信息。

例如,在这个实验中,我们要找到和记录训练过程中的梯度条件。比如可能想每隔10步从每一层收集梯度范数、最小值、最大值、绝对值、平均值和标准差,并在 TensorBoard 中可视化结果。

然后可以用out_dir作为–logdir输入启动TensorBoard。

实验

为了重现CLIP中的训练不稳定性,用于OpenCLIP训练Laion 50亿图像数据集的一个子集。我们用TCH包装模型,定期保存模型梯度、权重和优化器时刻的状态,这样就可以观察到不稳定发生时模型内部发生了什么。

从vvi - h -14变体开始,OpenCLIP作者描述了在训练期间存在稳定性问题。从预训练的检查点开始,将学习率提高到1-e4,与CLIP训练后半段的学习率相似。在训练进行到300步时,有意连续引入10个难度较大的训练批次。

损失会随着学习率的增加而增加,这是可预期的。当在第300步引入难度较大的情况时,损失会有一个小的,但不是很大的增加。该模型发现难度较大的情况,但没有更新这些步骤中的大部分权重,因为nan出现在梯度中(在第二个图中显示为三角形)。通过这组难度较大的情况后,梯度降为零。

PyTorch梯度缩放

这里发生了什么?为什么梯度是零?问题就出在PyTorch的梯度缩放。梯度缩放是混合精度训练中的一个重要工具。因为在具有数百万或数十亿个参数的模型中,任何一个参数的梯度都很小,并且通常低于FP16的最小范围。

当混合精确训练刚刚提出时,深度学习的科学家发现他们的模型在训练早期通常会按照预期进行训练,但最终会出现分歧。随着训练的进行梯度趋于变小,一些下溢的 FP16 变为零,使训练变得不稳定。

为了解决梯度下溢,早期的技术只是简单地将损失乘以一个固定的量,计算更大的梯度,然后将权重更新调整为相同的固定量(在混合精确训练期间,权重仍然存储在FP32中)。但有时这个固定的量仍然不够。而较新的技术,如PyTorch的梯度缩放,从一个较大的乘数开始,通常是65536。但是由于这可能很高,导致大的梯度会溢出FP16值,所以梯度缩放器监视将溢出的nan梯度。如果观察到nan,则在这一步跳过权重更新将乘数减半,然后继续下一步。这一直持续到在梯度中没有观察到nan。如果在2000步中梯度缩放器没有检测到nan,它将尝试使乘数加倍。

在上面的例子中,梯度缩放器完全按照预期工作。我们向它传递一组比预期损失更大的情况,这会产生更大的梯度导致溢出。但问题是现在的乘数很低,较小的梯度正在下降到零,梯度缩放器不监视零梯度只监视nan。

上面的例子最初看起来可能有些故意的成分,因为我们有意将困难的例子分组。但是经过数天的训练,在大批量的情况下,产生nan的异常情况的概率肯定会增加。所以遇到足够多的nan将梯度推至零的几率是非常大。其实即使不引入困难的样本,也经常会发现在几千个训练步骤后,梯度始终为零。

产生梯度下溢的模型

为了进一步探索问题何时发生,何时不发生,将CLIP与通常在混合精度下训练的较小CV模型YOLOV5进行了比较。在这两种情况下的训练过程中跟踪了每一层中零梯度的频率。

在前9000步的训练中,CLIP中5-20%的层显示梯度下溢,而Yolo中的层仅显示偶尔下溢。CLIP中的下溢率也随着时间的推移而增加,使得训练不太稳定。

使用梯度缩放并不能解决这个问题,因为CLIP范围内的梯度幅度远远大于YOLO范围内的梯度幅度。在CLIP的情况下,当梯度缩放器将较大的梯度移到FP16的最大值附近时,最小的梯度仍然低于最小值。

如何解决解CLIP中的梯度不稳定性

在某些情况下,调整梯度缩放器的参数可以帮助防止下溢。在CLIP的情况下,可以尝试修改以一个更大的乘数开始,并缩短增加间隔。

但是我们发现乘数会立即下降以防止溢出,并迫使小梯度回到零。

改进缩放比例的一种解决方案是使其更适应参数范围。比如论文 Adaptive Loss Scaling for Mixed Precision Training 建议按层而不是整个模型执行损失缩放,这样可以防止下溢。而我们的实验表明需要一种更具适应性的方法。由于 CLIP 层内的梯度仍然覆盖整个 FP16 范围,缩放需要适应每个单独的参数以确保训练稳定性。但是这种详细的缩放需要大量内存会减少了训练的批大小。

较新的硬件提供了更有效的解决方案。比如BFloat16 (BF16) 是另一种 16 位数据类型,它以精度换取更大的范围。FP16 处理 5.96e-8 到 65,504,而BF16 可以处理 1.17e-38 到 3.39e38,与 FP32 的范围相同。但是 BF16 的精度低于 FP16,会导致某些模型不收敛。但对于大型的transformers模型,BF16 并未显示会降低收敛性。

我们运行相同的测试,插入一批困难的观察结果,在 BF16 中,当引入困难的情况时,梯度会出现尖峰,然后返回到常规训练,因为梯度缩放由于范围增加而从未在梯度中观察到 NaN。

对比FP16和BF16的CLIP,我们发现BF16中只有偶尔的梯度下溢。

在PyTorch 1.12及更高版本中,可以通过对AMP的一个小更改来启动BF16。

如果需要更高的精度,可以试试Tensorfloat32 (TF32)数据类型。TF32由英伟达在安培GPU中引入,是一个19位浮点数,增加了BF16的额外范围位,同时保留了FP16的精度。与FP16和BF16不同,它被设计成直接取代FP32,而不是在混合精度下启用。要在PyTorch中启用TF32,在训练开始时添加两行。

这里需要注意的是:在PyTorch 1.11之前,TF32在支持该数据类型的gpu上默认启用。从PyTorch 1.11开始,它必须手动启用。TF32的训练速度比BF16和FP16慢,理论FLOPS只有FP16的一半,但仍然比FP32的训练速度快得多。

如果你用亚马逊的AWS:BF16和TF32在P4d、P4de、G5、Trn1和DL1实例上是可用的。

在问题发生之前解决问题

上面的例子说明了如何识别和修复FP16范围内的限制。但这些问题往往在训练后期才会出现。在训练早期,模型会产生更高的损失并对异常值不太敏感,就像在OpenCLIP训练中发生的那样,在问题出现之前可能需要几天的时间,这回浪费了昂贵的计算时间。

FP16和BF16都有优点和缺点。FP16的限制会导致不稳定和失速训练。但BF16提供的精度较低,收敛性也可能较差。所以我们肯定希望在训练早期识别易受FP16不稳定性影响的模型,这样我们就可以在不稳定性发生之前做出明智的决定。所以再次对比那些表现出和没有表现出后续训练不稳定性的模型,可以发现两个趋势。

在FP16中训练的YOLO模型和在BF16中训练的CLIP模型都显示出梯度下溢率一般小于1%,并且随着时间的推移是稳定的。

在FP16中训练的CLIP模型在训练的前1000步中下溢率为5-10%,并随着时间的推移呈现上升趋势。

所以通过使用TCH来跟踪梯度下溢率,能够在训练的前4-6小时内识别出更高梯度不稳定性的趋势。当观察到这种趋势时可以切换到BF16。

总结

混合精确训练是训练现有大型基础模型的重要组成部分,但需要特别注意数值稳定性。了解模型的内部状态对于诊断模型何时遇到混合精度数据类型的限制非常重要。通过用一个TCH包装模型,可以跟踪参数或梯度是否接近数值极限,并在不稳定发生之前执行训练更改,从而可能减少不成功的训练运行天数。

https://avoid.overfit.cn/post/eb6b99ec76e3464b8f13d70db588b1f6

作者:Ben Snyder

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/331464.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【王道数据结构】第六章(下) | 图的应用

目录 一、最小生成树 二、最短路径 三、有向⽆环图描述表达式 四、拓扑排序 五、关键路径 一、最小生成树 1、最小生成树的概念 对于一个带权连通无向图G (V,E),生成树不,每棵树的权(即树中所有边上的权值之和)也可能不同。设R为G的所…

【2023】Prometheus-接入Alertmanager并实现邮件告警通知

目录1.使用二进制方式安装Alertmanager2.Alertmanager配置3.alert接入prometheus4.创建告警配置文件(在prometheus服务器)5.测试告警1.使用二进制方式安装Alertmanager 下载安装包 wget https://github.com/prometheus/alertmanager/releases/download…

Python pip工具使用

一、pip工具 1、pip简介 pip 是一个通用的 Python包管理工具。提供了对 Python 包的查找、下载、安装、卸载的功能,便于我们对 Python的资源包进行管理。 在安装 Python时,会自动下载并且安装 pip。 (1)查看是否安装 pip 查看…

C/C++ :程序环境和预处理(上)

目录 程序的编译链接过程 1.编译过程中的预处理阶段 2.编译过程中的正式编译阶段 3.编译过程中的汇编阶段 4.链接过程 程序的编译链接过程 一个程序的源码文件要经过复杂的编译链接过程才能被转换为可执行的机器指令(二进制指令) 编译链接过程概述: 编译过程&…

java顺序存储二叉树应用实例

八大排序算法中的堆排序,就会使用到顺序存储二叉树。 1.线索化二叉树 1.1先看一个问题 将数列 {1, 3, 6, 8, 10, 14 } 构建成一颗二叉树. n17 问题分析: 当我们对上面的二叉树进行中序遍历时,数列为 {8, 3, 10, 1, 6, 14 } 但是 6, 8, 10, 14 这几个…

windows装双系统,添加ubuntu

1、查看分区 此电脑右键---管理----磁盘管理----选有空闲位置的硬盘右键----压缩卷 就会出现空闲的卷 2 制作U盘,U盘初始是空的 下载rufus win10系统怎么查看磁盘分区形式 【百科全说】 (bkqs.com.cn) 双击打开----如下配置 出现这个提示,照做 …

内存数据库Apache Derby、H2

概述 传统关系型数据库涉及大量的工作,如果想在Java应用程序里使用MySQL数据库,至少需要如下步骤: 安装(可选:配置用户名密码)建表(要么从命令行进入,要么安装一个可视化工具&…

Java基础-网络编程

1. 网络编程入门 1.1 网络编程概述 计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统…

Springboot扩展点之InstantiationAwareBeanPostProcessor

前言前面介绍了Springboot的扩展点之BeanPostProcessor,再来介绍另一个扩展点InstantiationAwareBeanPostProcessor就容易多了。因为InstantiationAwareBeanPostProcessor也属于Bean级的后置处理器,还继于BeanPostProcessor,因此Instantiatio…

vue-cli3创建Vue项目

文章目录前言一、使用vue-cli3创建项目1.检查当前vue的版本2.下载并安装Vue-cli33.使用命令行创建项目二、关于配置前言 本文讲解了如何使用vue-cli3创建属于自己的Vue项目,如果本文对你有所帮助请三连支持博主,你的支持是我更新的动力。 下面案例可供…

【C++】类与对象(上)

文章目录一、面向过程和面向对象初步认识二、类的引入三、类的定义四、类的访问限定符及封装①访问限定符②封装五、类的作用域六、类的实例化七、类对象模型①如何计算类对象大小②类对象的存储方式③结构体中内存对齐规则八、this指针①this指针的引出②this指针的特性一、面…

XCP实战系列介绍07-使用ASAP2 Editor生成A2l文件详解

本文框架 1.概述2. A2L文件编辑及生成2.1 新建项目工程2.2 加载elf文件2.3 A2L文件的项目属性配置2.4 DAQ事件的设定2.5 添加观察量2.6 添加标定量2.7 编译生成A2l1.概述 在前面一篇文章《看了就会的XCP协议介绍》中详细介绍了XCP的协议,在《XCP实战系列介绍01-测量与标定底层…

JavaScript 类继承

JavaScript 类继承 JavaScript 类继承使用 extends 关键字。 继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。 super() 方法用于调用父类的构造函数。 当创建一个类时,您不需要重新编写新的数据成员和成员函数&…

synchronized和ReentrantLock之间的区别

synchronized和ReentrantLock的区别 synchronized是一个关键字,是JVM内部实现的;ReentrantLock是标准库的一个类,是JVM外部基于Java实现的。synchronized在申请锁失败时会死等;ReentrantLock可以通过tryLock的方式等待一段时间就…

偏微分方程约束下的优化控制问题(PDE-constrained optimal control problems)

优化控制问题介绍 优化控制问题的数学形式 {min⁡(y(x),u(x))∈YUJ(y(x),u(x)),s.t. F(y(x),u(x))0in Ω,and u(x)∈Uad,\left\{\begin{aligned} &\min _{(y(\mathbf{x}), u(\mathbf{x})) \in Y \times U} J(y(\mathbf{x}), u(\mathbf{x}) ),\\ &\text { s.t. } \ \…

Python列表的元素比较

在用python处理多个列表元素时,需要处理的信息一般比较多且杂。这时运用Python列表元素比较的方法,就能快速处理列表信息,能更轻松访问每个元素。1 问题如何运用Python列表的元素比较解决问题。2 方法方法一 for循环,此情况是list…

Spring 事务【隔离级别与传播机制】

Spring 事务【隔离级别与传播机制】🍎一.事务隔离级别🍒1.1 事务特性回顾🍒1.2 事务的隔离级别(5种)🍒1.3 事务隔离级别的设置🍎二.Spring 事务传播机制🍒2.1 Spring 事务传播机制的作用🍒2.2 事…

描述性统计图表——箱线图

文章目录1、分位数1.1、分位数的概念1.2、特殊分位数1.3、分位数的计算2、箱线图2.1、概念2.2、作图步骤2.3、箱线图的作用2.3.1、定位异常值2.3.2、偏态和尾重2.3.3、数据的形状1、分位数 1.1、分位数的概念 分位数(Quantile),亦称分位点&a…

红黑树简析

一. 概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是…

java 自定义json解析注解 复杂json解析

java 自定义json解析注解 复杂json解析 工具类 目录java 自定义json解析注解 复杂json解析 工具类1.背景2、需求-各式各样的json一、一星难度json【json对象中不分层】二、二星难度json【json对象中出现层级】三、三星难度json【json对象中存在数组】四、四星难度json【json对象…