8位优化器
有状态优化器会随着时间推移维护梯度统计信息,例如过去梯度值的指数平滑和(带动量的 SGD)或平方和(Adam)。与普通的随机梯度下降相比,此状态可用于加速优化,但会使用原本可能分配给模型参数的内存。结果,这限制了实际上可以训练的模型的最大大小。现在,让我们看看可以使用 8 位优化器训练的最大模型。
bitsandbytes 优化器使用 8 位统计信息,同时保持使用 32 位优化器状态的性能水平。
为了克服由此产生的计算、量化和稳定性挑战,8 位优化器具有三个组成部分
- 分块量化:将输入张量划分为较小的块,这些块独立量化,隔离异常值并将误差更均匀地分布到所有位上。每个块在内核之间并行处理,从而实现更快的优化和高精度量化。
- 动态量化:以高精度量化小值和大值。
- 稳定嵌入层:提高具有词嵌入的模型在优化过程中的稳定性。
使用这些组件,使用 8 位状态执行优化器更新非常简单。在执行更新之前,将 8 位优化器状态解量化为 32 位,然后将状态量化回 8 位以进行存储。
8 位到 32 位的转换在寄存器中逐元素进行,这意味着不需要缓慢复制到 GPU 内存或额外的临时内存来执行量化和解量化。对于 GPU 来说,这使得 8 位优化器比普通的 32 位优化器快得多。
稳定嵌入层
稳定嵌入层提高了 NLP 任务中标准词嵌入层的训练稳定性。它解决了非均匀输入分布的挑战,并减轻了极端梯度变化。这意味着稳定嵌入层可以支持更积极的量化策略,而不会影响训练稳定性,并且可以帮助实现稳定的训练结果,这对于处理多样化和复杂语言数据的模型尤其重要。
稳定嵌入层有三个特性
- 初始化:利用 Xavier 均匀初始化来保持一致的方差,降低出现大梯度的可能性。
- 归一化:在添加位置嵌入之前加入层归一化,有助于输出稳定性。
- 优化器状态:专门为此层使用 32 位优化器状态以增强稳定性,而模型的其余部分可以使用标准的 16 位精度。
分页优化器
分页优化器构建在 CUDA 的统一内存功能之上。统一内存提供了一个 GPU 和 CPU 可以轻松访问的单个内存空间。虽然 PyTorch 不支持此功能,但它已添加到 bitsandbytes 中。
分页优化器的工作方式类似于普通的 CPU 分页,这意味着它**仅在 GPU 内存不足时才会激活**。发生这种情况时,内存将逐页从 GPU 传输到 CPU。内存被映射,这意味着页面在 CPU 上预先分配,但不会自动更新。仅当访问内存或启动交换操作时,才会更新页面。
统一内存功能的效率低于常规的异步内存传输,并且通常无法获得完整的 PCIe 内存带宽利用率。如果你进行手动预取,传输速度可能会很高,但仍然只有 PCIe 内存带宽的一半或更低(在 16x 通道 PCIe 3.0 上测试)。
这意味着性能在很大程度上取决于特定的用例。例如,如果你在每个前向-反向-优化器循环中逐出 1 GB 的内存,那么在最佳情况下,你可以预期 PCIe 带宽的大约 50%。因此,对于具有 16x 通道的 PCIe 3.0,1 GB 的运行速度为 16 GB/s,这相当于每个优化器步骤的开销为1/(16*0.5) = 1/8 = 125ms
。给定 PCIe 接口、通道和每次迭代中逐出的内存,可以为特定用例估计其他开销。
与 CPU 卸载相比,如果所有内存都适合设备,则分页优化器没有开销;只有当部分内存需要被驱逐时,才会产生一些开销。对于卸载,您通常会卸载模型的固定部分,并且需要在每次遍历模型时都进行卸载和加载所有这些内存(有时对于前向和后向传递都需要进行两次)。
< > 在 GitHub 上更新