一、Mixed Precision Training [2017] [粗读]

论文:《Mixed Precision Training》

论文是关于混合精度训练的,考虑到混合精度训练的思想已经广为人知,因此这里仅做粗读。

  1. Introduction:略(参考原始论文)。

  2. 相关工作:略(参考原始论文)。

1.1 方法

  1. 我们介绍了 training with FP16 的关键技术,同时仍然匹配 FP32 training sessionmodel accuracysingle-precision master weights and updatesloss-scalingaccumulating FP16 products into FP32

    单精度浮点数为 float32、双精度浮点数为 float64

1.1.1 FP32 Master Copy Of Weights

  1. 在混合精度训练中,权重、activations 以及梯度以 FP16 格式存储。为了匹配 FP32 网络的准确率,我们维护权重的一份 FP32 master copy ,并在 optimizer step 中用 weight gradient 来更新它。在每次迭代中,使用 master weightsFP16 copy 进行正向传播和反向传播,将 FP32 training 所需的存储和带宽减半。Figure 1 说明了这种混合精度训练过程。

  2. 虽然 FP32 master weights 的需求并非普遍存在,但有两个可能的原因解释为什么许多网络需要它:

    • 一个解释是 updatesweight gradients 乘以学习率)变得太小而无法用 FP16 表示。FP16 中任何 magnitude 小于 224 的值都会变成零。如 Figure 2b 所示,约 5%weight gradient valuesexponents 小于 -24 。在 optimizer 中,这些小的梯度与学习率相乘后会变成零,并对模型准确率产生不利影响。使用 single-precision copy 进行更新可以克服这个问题并恢复准确率。

      即,weight updates 绝对很小,导致 updates 变成零。

    • 另一个解释是 weight valueweight update 的比率非常大。在这种情况下,即使 weight update 可以用 FP16 来表示,当加法操作将 weight update 右移从而将 binary pointweights 对齐时, weight update 仍然可能变成零。当 normalized weight valuemagnitudeweight updatemagnitude 大至少 2048 倍时,这种情况可能发生。由于FP1610-bit 尾数,implicit bit 必须右移 11 位或更多位才可能创建零(在某些情况下,rounding 可以恢复该值)。在比率大于 2048 的情况下,implicit bit 将右移 12 位或更多位。这将导致 weight update 变成无法恢复的零。更大的比率将对 de-normalized 数字产生这种效应。同样,这种效应可以通过在 FP32 中计算 weight update 来抵消。

      即,weight updates 相对 weight 很小,导致训练效率很低(即,每个 step 中,权重的变化幅度非常小)。

    为了说明权重的 FP32 master copy 的必要性,我们使用了 Mandarin 语音模型在约 800 小时的语音数据上训练 20 epochs 。如Figure 2a 所示,当在 FP16 forward and backward passes 之后更新 FP32 master copy of weights 时,我们匹配了 FP32 training 结果;而更新 FP16 weights 则导致 80% 的相对 accuracy loss

  3. 尽管与 single precision training 相比,维护额外的权重副本将权重的内存需求增加了 50% ,但对整体内存使用的影响要小得多。 对于训练,内存消耗主要由 activations 引起,这是由于较大的 batch size 、以及保存每层的 activations 从而在反向传播中重用。 由于 activations 也以半精度格式存储,所以整体内存消耗约减半。

    作者这里并未给出数据来说明。

1.1.2 Loss Scaling

  1. FP16 exponentnormalized value exponents 范围有偏地居中到 [-14,15] ,而实际梯度值往往以小 magnitudesnegative exponents )为主。例如,考虑 Figure 3,显示了在 FP32 训练 Multibox SSD 检测器网络过程中收集的所有层的 activation gradient values 的直方图。注意,FP16 可表示范围的大部分未被使用,而许多值低于最小可表示范围并变为零。 scaling up 梯度将使它们占用更多的可表示范围,并保留一些值(如果不 scaling up,这些值将会因为零而丢失)。

    不缩放梯度时,该特定网络会发散;但将其缩放 8 倍(对 exponents 增加 3 )足以匹配 FP32 training 达到的准确率。这表明,在 magnitude 上小于 227activation gradient values 对此模型的训练无关,但 [227,224) 范围内 magnitudeactivation gradient values 很重要。

    由于学习率通常很小,因此 [227,224) 范围内的activation gradient values 乘以学习率仍然可能小于 227 。但是,Weight Update 采用 FP32,因此不会变为零。

  2. gradient values 移至 FP16 可表示范围的一种有效方法是:在开始反向传播之前,缩放在前向传播中计算的 loss value 。通过链式法则,反向传播将确保所有梯度值被缩放相同的量。这在反向传播期间不需要额外的操作,并使相关的梯度值不会变成零。

    在权重更新之前,必须 unscale 权重梯度,从而保持 weight update magnitudesFP32 training 相同。最简单的方法是:在反向传播结束之后、但在梯度裁剪或任何其他与梯度相关的计算之前进行 unscale ,从而确保不需要调整任何超参数(如梯度裁剪阈值、weight decay 等)。

    虽然可以通过缩小学习率来间接地缩放梯度,但是梯度裁剪阈值、weight decay 等等方法的超参数也需要相应地缩放。

  3. loss scaling factor 有几种选择。最简单的一种是选择一个常量的 scaling factor 。我们用 832Kscaling factor 训练了各种网络(许多网络不需要 scaling factor )。可以经验性地选择一个常量 scaling factor ,或者如果有梯度统计信息,可以直接选择一个 scaling factor 使其与 maximum absolute gradient value 的乘积低于 65,504FP16 中可表示的最大值)。

    只要不在反向传播期间导致数值溢出,选择一个大的 scaling factor 就没有坏处。数值溢出会导致 weight gradients 中出现无穷大和 NaN ,在权重更新后会不可逆地破坏权重。请注意,数值溢出可以通过检查计算到的 weight gradients (例如在 unscale weight gradient values 的时候)来有效检测。一种解决方法是:在检测到数值溢出时跳过 weight update ,直接进入下一次迭代。

1.1.3 算术精度

  1. 在很大程度上,神经网络的算术分为三类:向量点积、reductions 、以及 point-wise 操作。这些算术类别在 reduced precision 算术方面受益于不同的处理。

    • 为了保持模型准确率,我们发现一些网络要求 FP16 vector dot-product 从而累积 partial productsFP32 值,然后在写入内存前将其转换为 FP16 。如果不这样累积到 FP32 ,一些 FP16 模型的准确率无法匹配 baseline 模型。而先前的 GPU 只支持 FP16 multiply-add 操作,而 NVIDIA Volta GPU 引入了 Tensor Core ,可以将 FP16 的 输入矩阵相乘并累积乘积结果到 FP16 outputFP32 output

    • 大型的 reductions(如向量元素之和)应在 FP32 中进行。这种 reductions 主要出现在 batch-normalization layers (累积统计信息)和 softmax layers 。在我们的实现中,这两种 layer 类型仍从内存读取和写入 FP16 张量,而在 FP32 中执行算术运算。这并没有减慢训练过程,因为这些 layers 受内存带宽限制,而对于算术速度不敏感。

    • point-wise 操作(如非线性激活函数、element-wise 矩阵乘法)受内存带宽限制。由于算术精度不影响这些操作的速度,可以使用 FP16FP32

1.2 实验

  1. 略(参考原始论文)。

二、Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference[2017] [粗读]

论文:《Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference》

论文是关于量化 CNN 模型的,这里仅做粗读,仅关注核心算法。

  1. Introduction:略(参考原始论文)。

  2. 相关工作:略(参考原始论文)。

2.1 Quantized Inference

2.1.1 量化方案

  1. 在这一节中,我们描述了通用的量化方案,也就是两种类型数值之间的对应关系: bit representation of values(这个整数记为 q ,表示"quantized value")、以及它们作为数学的实数的解释(对应的实数记为 r ,表示 "real value")。我们的量化方案在推理时只使用整数算术实现,在训练时使用浮点算术实现,两种 implementations 高度对应。我们首先数学上严格定义量化方案,然后分别采用该方案用于 integer-arithmetic inferencefloating-point training

    floating-point training 类似于量化感知训练 Quantization Aware Training,即在训练阶段就应用 “模拟” 量化操作,从而降低 integer-arithmetic inference 的量化误差。

  2. 我们量化方案的一个基本要求是,它允许只使用整数算术操作对 quantized values 进行所有算术运算的有效实现(我们避免需要 lookup tables 的实现,因为它们往往比在 SIMD 硬件上进行纯算术运算表现更差)。这相当于要求量化方案是整数 q 到实数 r 的仿射映射,即形式为:

    (1)r=S(qZ)

    其中:SZ 为某些常量。

    • 常数 S (表示 "scale" )是一个任意的正实数。它通常以软件中的浮点数形式表示,类似于实数 r 。本文后续内容描述了避免在推理工作中表示这种浮点数的方法。

    • 常数 Z (表示"zero-point" )与 quantized value q 的类型相同,事实上是对应于实数 0quantized value q 。这自动满足以下要求:实数 r=0 可以被 quantized value 精确地表示。这一要求的动机是,高效实现的神经网络算子通常需要对数组进行 zero-padding

    这个公式是我们的量化方案,常数 S 和常数 Z 是我们的量化参数(quantization parameters)。我们的量化方案对每个 activations array内的所有值、以及 weights array 内的所有值使用单组量化参数,即:不同的 array 使用独立的量化参数。

    对于 8-bit 量化,q 被量化为 8-bit 整数(对于 B-bit 量化,q 被量化为 B-bit 整数)。一些数组(通常是 bias vectors )被量化为 32-bit 整数,参见后续内容。

  3. 到目前为止的讨论被总结在以下的 quantized buffer 数据结构中,神经网络中的每个 activations arrayweights array 都有这样一个 buffer 实例。我们使用 C++ 语法是因为它可以明确地表达类型:

2.1.2 仅整数算术的矩阵乘法

  1. 我们现在考虑如何仅使用整数算术进行推理,也就是如何使用方程 r=S(qZ) 将实数运算翻译成 quantized-values 运算,以及后者如何设计为仅包含整数算术,即使 scaling 参数 S 是浮点数不是整数。

  2. 考虑两个 N×N 的实数矩阵 R1R2 的乘法,其矩阵乘积表示为 R3=R1R2 。我们将每个矩阵 Rαα=1,2,3)的项表示为 rα(i,j),其中 1i,jN。对应的 quantized 矩阵为 Qa,每一项表示为 qa(i,j)。对应的量化参数为 (Sα,Zα)。则方程 r=S(qZ) 则变为:

    (2)rα(i,j)=Sα(qα(i,j)Zα)

    从矩阵乘法的定义,我们有:

    (3)S3(q3(i,k)Z3)=j=1N[S1(q1(i,j)Z1)S2(q2(j,k)Z2)]

    这可以重写为:

    (4)q3(i,k)=Z3+Mj=1N[(q1(i,j)Z1)(q2(j,k)Z2)]

    其中乘数 M 定义为:

    (5)M=S1S2S3
  3. q3(i,k) 的方程中,唯一的非整数是乘数 M 。作为仅依赖于 quantization scales S1S2S3 的常数,它可以离线计算。我们经验性地发现它总是在区间 (0,1) 内,因此可以表示为归一化的形式:

    (6)M=2nM0

    其中:其中 M0 在区间 [0.5,1) 内,n 为非负整数。

    • 归一化的乘数 M0 现在很适合表示为定点乘数(例如根据硬件能力,采用 int16int32 )。例如,如果使用 int32 的整数乘法,表示 M0 的整数是最接近 231×M0int32 值。由于 M0>0.5 ,这个值总是至少 230 ,因此总是有至少 30-bit 的相对精度。因此,乘以 M0 的乘法可以实现为定点乘法(fixed-point multiplication)。

    • 同时,乘以 2n 的乘法可以实现为有效的移位运算(bit-shift ),尽管需要具有正确的四舍五入(round-to-nearest )行为,我们在附录 B 中讨论这个问题。

2.1.3 高效处理 ZeroPoint

  1. 为了在不执行 2N3 次减法、并且不将乘法操作数展开成 16-bit 整数的情况下,有效地实现公式 q3(i,k) 的求值,我们首先注意到通过在公式 q3(i,k) 中分配乘法,我们可以重写为:

    (7)q3(i,k)=Z3+M(NZ1Z2Z1a¯2kZ2a¯1(i)+j=1Nq1(i,j)q2(j,k))

    其中:

    (8)a¯2(k)=j=1Nq2(j,k),a¯1(i)=j=1Nq1(i,j)

    每个 a¯2(k)a¯1(i) 只需要 N 次加法来计算,所以总共只需要 2N2 次加法。

    上式的剩余成本几乎全部集中在核心的整数矩阵乘法累加上:

    (9)j=1Nq1(i,j)q2(j,k)

    这需要 2N3 次算术运算。

    的确,q3(i,k) 的新公式中的所有其他运算都是 O(N2),其中 O 中的常数很小。因此,展开成上述新公式的形式、以及分解为计算 a¯2(k)a¯1(i) ,使得对于任意 zero-points 都能以很小的开销处理,除了 N 非常小的情况;问题简化为相同的 core integer matrix multiplication accumulation,因为我们在任何其他 zero-points-free 方案中都必须计算它。

    这仅仅改善了减法操作,而没有改善乘法操作。

    • 原始方法中,对于 Q3 ,一共需要 2N3 的减法操作;但是新的方法中,计算 {a¯2(k)}k=1N 需要 N2 次加法操作。然后 a¯2 可以在不同的 q3(,k) 中共享。

    • 但是,对于乘法操作,二者之间并未改善。

2.1.4 典型 fused layer 的实现

  1. 我们继续前面的讨论,但现在显式地定义所有涉及的量(quantities )的数据类型,并修改 quantized 的矩阵乘法,从而直接将 bias-additionactivation function evaluation 合并到其中。这样将整个 layer 融合成单个操作而不仅是一个优化。由于我们必须在推理代码中重现训练中使用的相同算术,推理代码中的 fused operators 的粒度(接受 8-bit quantized input 并产生 8-bit quantized output )必须与训练图中 "fake quantization" operatorsplacement 相匹配。

    对于 ARMx86 CPU 架构,我们使用 gemmlowp 库(《gemmlowp: a small self-contained low-precision gemm library》),其 GemmWithOutputPipeline 入口点支持我们现在描述的 fused operations

    General Matrix Multiplication: GEMM :通用矩阵乘法。

  2. 我们将 Q1 矩阵设为权重、Q2 矩阵设为 activations 。权重和 activations 均为 uint8 类型(我们也可以等效地选择 int8 ,适当修改 zero-points )。uint8 值的乘积的累加需要 32-bit accumulator ,我们为 accumulator 选择带符号类型,原因很快就会变清楚。 j=1Nq1(i,j)q2(j,k) 中的求和因此具有如下形式:

    (10)int32=unit8 × unit8 

    为了得到 quantized bias-addition (将 int32 bias 添加到 int32 accumulator 上),bias-vector 被量化为:

    • 使用 int32 作为其量化数据类型。

    • 使用 0 作为其 quantization zero-point Zbias

    • quantization scale Sbiasaccumulators 的相同,即 weights scaleinput activations scale 的乘积:

      (11)Sbias=S1S2,Zbias=0

    尽管 bias-vectors 被量化为 32-bit 值,但它们只占神经网络参数的很小一部分。此外,对 bias vectors 使用更高精度满足了一个真正的需求:由于每个 bias-vector entry 被添加到许多 output activations 中,bias-vector 中的任何量化误差往往表现为一个 overall bias (即,均值不为零的误差项),这必须避免以保持端到端的神经网络准确性。

    即,对于任何样本 Xbias-vector 都添加到它的 output activation 上,因此 bias-vector 对所有样本都产生了影响。

  3. int32 accumulator 的最终值,还剩下三件事要做:

    • scale downfinal scale,这个 final scale8-bit output activations 所使用。

    • cast downunit8

    • 应用 activation function 生成 final 8-bit output activation

    下采样对应于乘数 M。如前所述,它实现为由归一化的乘数 M0 的定点乘法、以及 rounding bit-shift 组成。之后,我们执行 castuint8 ,饱和到 [0,255] 范围。

    我们仅仅关注 clamps 类型的激活函数,例如 ReLUReLU6 。数学函数在附录 A.1 中讨论,目前我们不会将它们融合到这种层中。因此,我们 fused activation functions 唯一需要做的就是进一步将 uint8 值截断到 [0,255] 的某个子区间,然后存储 final uint8 output activation。实际上,quantized training process (参考下一节)倾向于学习使用整个 output uint8 区间 [0,255] ,以致 activation function 不再起任何作用,其效果被归因于截断到区间 [0, 255] (由于 castunit8 所隐含的)。

2.2 Training with simulated quantization

在大模型时代,通常很少采用 simulated quantization 的训练,而是直接训练一个大模型,然后再进行 post-training 量化。

  1. 训练 quantized networks 的常见方法是:用浮点进行训练,然后量化所得到的权重(有时伴随额外 post-quantization training 从而进行微调)。我们发现,这种方法对具有相当表达能力的大型模型效果足够好,但对小型模型会导致明显的准确率下降。simple post-training quantization 的常见失败模式包括:

    • 1) :不同 output channels 的权重范围差异很大(大于 100 倍)。注意:前面章节中规定,同一层的所有通道必须量化到相同分辨率;这会导致权重范围较小的通道中,权重的相对误差更大。

    • 2)outlier weight values,这些异常值使得量化后所有的非异常值的权重降低了精确性。

  2. 我们提出了一种在训练的前向传播中模拟量化效应(quantization effects )的方法。反向传播仍然照常进行,所有权重和 bias 以浮点存储,以便可以轻易地略微调整。但是,前向传播模拟 quantized inference (因为 quantized inference 发生在推理引擎中),通过如下方式:以浮点算法实现我们前面介绍的量化方案中的 rounding behavior

    • 权重在与 input 执行卷积之前被量化。如果 layer 使用 batch normalization ,则在量化之前,batch normalization 参数被 “折叠”到权重中。

    • activations 在这样的时点上被量化:在推理期间 activations 被量化的点。例如:在 activation function 应用于卷积层或全连接层输出之后、或在像 ResNet 中那样多个层的输出相加或拼接的 bypass connection 之后。

    对于每一层,quantizationquantization levels 数量、以及 clamping range 的参数化,并通过 point-wise 地应用下面定义的量化函数 q 来执行:

    (12)clamp(r;a,b)=min(max(r,a),b),abs(a,b,n)=ban1q(r;a,b,n)=clamp(r;a,b)as(a,b,n)×s(a,b,n)+a

    其中:r 是要被量化的实值数;[a;b] 是量化范围;nquantization levels 数量; 表示四舍五入到最近的整数。

    在我们的实验中,n 对所有层固定,例如 8-bit 量化时 n=28=256

    即,在原始的神经网络中插入这些特定的 fake quantization layer

2.2.1 学习 quantization ranges

  1. weight quantizationactivation quantizationquantization ranges 的处理不同:

    • 对于权重,基本思想是:简单地设置 a=min(W);b=max(W),其中 W 为权重矩阵。我们对此稍作调整,使得权重一旦被量化为 int8 值,取值范围仅仅在 [-127, 127] 并且不会取值为 -128 ,因为这 enable 了一个重要的 optimization opportunity (更多细节见附录 B )。

    • 对于 activationsranges 取决于网络输入。为了估计 ranges ,我们在训练期间收集 activations 上的 [a; b] 范围,然后通过指数移动平均(exponential moving averages: EMA )聚合它们,平滑参数接近 1 ,以便 observed ranges在数千步训练中平滑。鉴于在 ranges 快速变化时,EMA 更新 activation ranges有明显延迟,我们发现在训练开始时完全禁用 activation quantization 很有用(例如前 50K 步到 2M 步)。这使网络进入更稳定状态,其中 activation quantization ranges 不会排除 values 的重要部分。

      这类似于 Batch Normalization 的思想。在训练中自动更新 activation ranges

    在两种情况下,都会调整 [a; b] 的边界,以便值 0.0 在量化后可以精确表示为整数 z(a,b,n)。因此,学到的 quantization parameters 映射到方程 r=S(qZ) 中的 scale Szero-point Z

    (13)S=s(a,b,n),Z=z(a,b,n)
  2. 下面我们假设神经网络的计算被捕获为 TensorFlow graph ,然后描述 simulated quantization 。典型的工作流程在 Algorithm 1 中描述。通过 fusing and removing operationsinference graph 的优化,超出了本文范围。用于 graph modifications 的源码(插入 fake quantization 操作、创建和优化 inference graph)、以及 low bit inference engine 已在 《Tensorflow quantized training support》 中通过 TensorFlow contributions 开源。

    公式 12 为:

    (14)clamp(r;a,b)=min(max(r,a),b),abs(a,b,n)=ban1q(r;a,b,n)=clamp(r;a,b)as(a,b,n)×s(a,b,n)+a

  3. Figure 1.1 a and b 说明了一个简单卷积层量化前后的 TensorFlow graphFigure C.3 中,更复杂的带 bypass connection 的卷积的说明,可以在 Figure C.4 中找到。

    请注意,bias 没有被量化,因为在推理过程中它们表示为 32-bit 整数,范围和精度远高于 8-bit weights and activations。此外,用于,bias 的量化参数从权重和激活的量化参数推导出来。

    使用《Tensorflow quantized training support》 的典型 TensorFlow 代码示例如下:

2.2.2 Batch normalization folding

  1. 对于使用 batch normalization 的模型,存在额外复杂性:training graphbatch normalization是单独的操作块,而 inference graphbatch normalization 参数 “折叠” 到卷积层或全连接层的权重和 bias 中,从而提高效率。为了准确模拟量化效应,我们需要模拟这种折叠,并在权重被 batch normalization parameters 缩放后再来量化权重。我们采用以下方法:

    (15)wfold=γwEMA(σB2)+ϵ

    其中:

    • γbatch normalizationscale 参数。

    • EMA(σB2)convolution resultsbatch 内方差的移动平均估计。

    • ϵ 是一个小常数用于数值稳定性。

    折叠后,batch-normalized convolutional layer 简化为 Figure 1.1a 中所示的简单卷积层,具有被折叠的权重 wfold 、以及相应的被折叠的 bias 。因此,相同的配方也适用于 Figure 1.1b 。附录中可以查阅 batch-normalized convolutional layerFigure C.5 )、相应的 inference graphFigure C.6 )、batch-norm folding 后的训练图( Figure C.7 )以及 folding and quantization 后的训练图( Figure C.8 )。

2.3 实验

  1. 略(参考原始论文)。

三、LLM.int8()[2022]

论文:《LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale》

  1. 大型 pretrained 语言模型在 NLP 中被广泛采用,但需要大量内存进行推理。对于超过 6.7B 参数的大型 transformer 语言模型,前馈层(feed-forward layer )和注意力投影层(attention projection layer)及其矩阵乘法操作占据了 95% 的参数和 65-85% 的所有计算(《High performance natural language processing》)。减小参数大小的一种方法是将其量化为更少的比特并使用低位精度(low-bit-precision )的矩阵乘法。考虑到这一目标,人们已经开发了 transformer8-bit 量化方法。虽然这些方法减少了内存使用,但它们降低了性能,通常需要在训练后进一步 tuning quantization,并且只在少于 350M 参数的模型上进行了研究。低于 350M 参数时性能无损的量化理解得不够好,十亿参数级的量化仍是一个开放性挑战。

    在本文中,我们提出了第一个不降低性能的十亿参数级 Int8 量化过程。我们的过程使得加载一个具有 16-bit32-bit 参数的 175B 参数的 transformer ,将前馈层和注意力投影层转换为 8-bit,并立即用于推理而不降低任何性能。我们通过解决两个关键挑战实现了这一结果:

    • 大于 1B 参数时需要更高的量化精度(quantization precision )。

    • 需要明确表示稀疏的但系统性的大幅值的异常值特征(sparse but systematic large magnitude outlier features),在所有的 transformer 层(从 6.7B 参数规模开始)中,这些特征一旦出现就会破坏量化精度。一旦这些异常值特征出现,C4 evaluation perplexity 以及 zero-shot accuracy 就会反映出这种精度损失(loss of precision ),如 Figure 1 所示。

    我们证明,通过我们方法的第一部分向量化量化(vector-wise quantization),可以在高达 2.7B 参数的规模下保持性能。对于向量化量化,矩阵乘法可以看作是行向量序列和列向量序列的独立内积序列。因此,我们可以为每个内积使用单独的量化归一化常数(quantization normalization constant)来提高量化精度。我们可以通过在执行下一个操作之前用列归一化常数和行归一化常数的外积对矩阵乘法的输出进行反归一化(denormalizing)来恢复矩阵乘法的输出。

    为了在不降低性能的情况下扩展到超过 6.7B 的参数,理解推理过程中隐状态(隐状态就是 activationfeature dimensions中的极端异常值的出现至关重要。为此,我们提供了一个新的描述性分析,该分析显示:

    • 在将 transformer 扩展到 6B 参数时,所有 transformer 层中大约 25% 首先出现了幅度高达其他维度 20 倍的大特征(large features ),然后这些大特征逐渐传播到其他层。

    • 在大约 6.7B 参数时,发生了一个phase shift ,所有 transformer 层、以及 75% 的所有sequence dimensions都受到极端幅度的特征(extreme magnitude features)的影响。这些异常值高度地系统化(systematic):在 6.7B 规模下,每个序列有 150,000 个异常值,但它们集中在整个 transformer 中的仅 6feature dimensions中。

      将这些异常feature dimensions设置为零会使 top-1 attention softmax 概率质量降低 20% 以上,并使 validation perplexity 退化 600-1000% ,尽管它们只占所有输入特征的约 0.1% 。相比之下,删除相同数量的随机特征会最大降低 0.3% 的概率质量,并退化约 0.1% 的困惑度(perplexity)。

    给定一个文本序列,sequence 维度表示 token id 的维度,feature 表示 embeddingactivation 的维度。上述描述的含义是:

    • 6B 模型,有 25% 的层(l=1,,L)出现了异常值。

    • 6.7B 模型,所有的层出现了异常值;沿着 token id 的方向,有 75% 的位置出现了异常值( s=1,,S);沿着 embedding/activation 的方向,有 6 个位置出现了异常值(i=1,,d)。

    写成矩阵的形式:

    (16)H1=[h1,1(1)h1,2(1)h1,d(1)h2,1(1)h2,2(1)h2,d(1)hS,1(1)hS,2(1)hS,d(1)],,HL=[h1,1(L)h1,2(L)h1,d(L)h2,1(L)h2,2(L)h2,d(L)hS,1(L)hS,2(L)hS,d(L)]RS×d

    为了支持这样极端异常值的有效量化,我们开发了混合精度分解(mixed-precision decomposition),即我们方法的第二部分。 我们对异常feature dimensions执行 16-bit 矩阵乘法、对其余 99.9% 的维度执行 8-bit 矩阵乘法。我们将向量化量化、以及混合精度分解的组合命名为 LLM.int8() 。 我们证明,通过使用 LLM.int8() ,我们可以在具有多达 175B 参数的 LLM 上进行推理,而不降低任何性能。我们的方法不仅为这些异常值对模型性能的影响提供了新的见解,而且还首次使非常大的模型(例如 OPT-175B/BLOOM )能够在具有消费级 GPU 的单服务器上使用。虽然我们的工作重点是在不降低性能的情况下使大型语言模型可访问,但我们在附录 D 中还展示了我们为大型模型(如 BLOOM-176B )维持端到端 inference runtime 性能,并为大于或等于 6.7B 参数的 GPT-3 模型提供了适度的矩阵乘法速度提升。我们开源了我们的软件,并发布了 Hugging Face Transformers 集成,使我们的方法可用于 Hugging Face Models 托管的所有具有线性层的模型。

    可以看到,LLM.int8() 仅在较大的模型(模型规模大于 6.7B )时才会加速推断;在更小的模型时,推断速度反而更慢(相比较于原始的 FP16 )。

  2. 相关工作:下面列出了 quantization data types 、以及 quantization of transformers 有密切相关的工作。附录 B 提供了有关 quantization of convolutional networks 的更多相关工作。

    • 8-bit Data Types:我们的工作研究围绕 Int8 数据类型的量化技术,因为它目前是 GPU 支持的唯一 8-bit 数据类型。其他常见的数据类型是定点 8-bit 数据类型或浮点 8-bit 数据类型(floating point 8-bit: FP8 )。

      这些数据类型通常有一个符号位( sign bit) 、以及指数位( exponent bit )和小数位( fraction bit) 的不同组合。例如,这种数据类型的一个常见变体有 5 bits 指数、 2 bits 小数,并使用 zeropoint scaling 或者 no scaling constants 。由于这些数据类型对 fraction 只有 2 bits,所以对大的幅值(large magnitude )有很大误差,但可以为小的幅值提供高精度。

      《F8net: Fixed-point 8-bit only multiplication for network quantization》 提供了出色的分析,关于对具有特定标准差的输入,在何时某些 fixed point exponent/fraction bit widths 是最佳的。我们认为 FP8 数据类型与 Int8 数据类型相比提供了优异的性能,但当前 GPUTPU 都不支持此数据类型。

    • 语言模型中的异常值特征(Outlier Features ):语言模型中大幅值异常值特征(large magnitude outlier features)的研究已经存在。

      • 先前的工作证明了 transformer 中异常值出现与 layer normalization 以及 token frequency distribution 的理论关系(《Representation degeneration problem in training natural language generation models》)。

      • 类似地,《Bert busters: Outlier dimensions that disrupt transformers》BERT 模型族中异常值的出现归因于 LayerNorm

      • 《Outliers dimensions that disrupt transformers are driven by frequency》 在经验上表明异常值的出现与训练分布中 tokens 的频率有关。

      我们通过展示自回归模型的规模与这些异常值特征的 emergent properties 之间的关系,以及适当建模异常值对有效量化(effective quantization )的重要性,从而来进一步扩展这项工作。

    • 十亿规模 transformer 的量化:与我们的工作相比,人们平行开发了两种方法:

      • nuQmm《nuqmm: Quantized matmul for efficient inference of large-scale generative language models》)、以及 ZeroQuant《Zeroquant: Efficient and affordable post-training quantization for large-scale transformers》)。两者使用相同的量化方案:分组向量化量化(group-wise quantization),其量化归一化常数粒度比向量量化更细。这种方案提供了更高的量化精度,但也需要自定义的 CUDA kernels

        • nuQmmZeroQuant 旨在加速推理和减少内存占用,而我们则关注在 8-bit 内存占用下保留预测性能。

        • nuQmmZeroQuant 评估的最大模型分别是 2.7B20B 参数的 transformerZeroQuant 实现了 20B 模型的 8-bit 量化,同时保持性能没有衰退。我们展示了我们的方法允许高达 176B 参数的模型进行 zero-degradation quantizationnuQmmZeroQuant 表明,更细粒度的量化可以是大型模型量化的有效手段。这些方法与 LLM.int8() 是互补的。

      • 另一个平行工作是 GLM-130B ,它使用我们工作的见解实现 zero-degradation 8-bit quantization《Glm-130b: An open bilingual pre-trained model》)。GLM-130B8-bit weight storage 进行完整的16-bit 精度的矩阵乘法。

        相比之下,LLM.int8() 采用 8-bit 的矩阵乘法。

3.1 背景知识

  1. 在这项工作中,我们通过扩展 transformer 模型将量化技术推向了极限。我们对两个问题感兴趣:

    • 在何种模型规模下量化技术会失败,以及为何量化技术会失败?

    • 量化技术的失败与量化精度有何关系?

    为了回答这些问题,我们研究了高精度非对称量化(zeropoint quantization )和高精度对称量化(absolute maximum quantization )。虽然 zeropoint quantization 通过使用数据类型的 full bit-range 提供了高精度,但由于实际限制而很少使用它。absolute maximum quantization 是最常用的技术。

3.1.1 8-bit 数据类型和量化

  1. absmax 量化通过将输入乘以 sXf16 从而缩放到 8-bit 范围 [-127, 127] 中。因此,对于 FP16 数据类型的输入矩阵 Xf16RS×hInt8 absmax quantization 由以下公式给出:

    (17)Xi8=127maxi,j(|Xf16,i,j|)×Xf16=127||Xf16||×Xf16=sXf16×Xf16

    其中:

    • || 表示绝对值, 表示四舍五入到最近的整数。

    • Xf16,i,j 表示 Xf16 的第 i 行、j 列的元素。

    • sXf16=127||Xf16|| 为量化常数。

  2. zeropoint 量化通过如下的方式将 input distribution 移动到 full range [-127, 127] 中:首先用 normalized dynamic range ndX 来缩放,然后用 zeropoint zpX 来移动。通过这种仿射变换(affine transformation ),任何输入张量都将使用数据类型的所有比特,从而减少非对称分布(asymmetric distributions )的量化错误。例如,对于 ReLU 输出,在 absmax 量化中,所有 [-127, 0) 的值都未被使用;而在 zeropoint 量化中,完整范围 [-127,127] 都被使用。

    zeropoint 量化由以下等式给出:

    (18)ndXf16=2×127maxi,j(Xf16,i,j)mini,j(Xf16,i,j)zpXi16=ndXf16×maxi,j(Xf16,i,j)+mini,j(Xf16,i,j)2Xi8=ndXf16×Xf16+zpXi16

    推导过程:假设 a=max(x),b=min(x),axb 。我们希望把 x 投影到 [-127, 127] 之间。则可以采用仿射变换:

    (19)y=xb(ab)×(2×127)127=x×2×127ab127×a+bab

    然后根据 q=y 来进行量化。

    根据论文《Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference》zeropoint 量化的形式为:

    (20)x=1ndXf16(qzp)1ndXf16(yzp)

    因此:

    (21)ndXf16=2×127ab,zp=127×a+bab

    逆量化为:Xf16=1ndXf16×(Xi8zp)

    要在操作中使用 zeropoint 量化,我们同时传入张量 Xi8zeropoint zpXi8 到一个特殊指令,该指令在执行 16-bit 整数操作之前,将zpXi8 添加到 Xi8 的每个元素中。例如,要将两个 zeropoint quantized 数值 Ai8Bi8 (以及它们的零点 zpAi8zpBi8)相乘,我们计算:

    (22)Ci32=multiplyi16(AzpAi16,BzpBi16)=(Ai8zpAi8)×(Bi8zpBi8)

    如果没有 multiply_i16 指令,如在 GPUTPU 上,则需要展开:

    (23)Ci32=Ai8Bi8Ai8zpBi8Bi8zpAi8+zpAi8zpBi8

    注意:结果是 32-bit 整数。

    其中:Ai8Bi8Int8 精度计算,其余以 Int16/32 精度计算。因此,如果没有 multiply_i16 指令,zeropoint 量化可能比较慢。

    在上述两种情况下,输出以32-bit 整数 Ci32 累加。为了逆量化 Ci32 ,我们将其除以缩放常数 ndAf16ndBf16

    即:

    (24)AzpAf16×BzpBf16=1ndAf16×ndBf16×[(Ai8zpAi8)×(Bi8zpBi8)]=1ndAf16×ndBf16×Ci32
  3. 具有 16-bit 浮点输入和输出的 Int8 矩阵乘法:给定隐状态 Xf16RS×h 和权重 Wf16Rh×o,其中 Ssequence dimensionshfeature dimensionso 为输出维度,我们执行具有 16-bit 浮点输入和输出的 8-bit 矩阵乘法如下:

    (25)Xf16Wf16=Cf161cXf16cWf16×Ci32=sf16×Ci32=sf16×Xi8Wi8sf16×Q(Xf16)Q(Wf16)

    其中:

    • cXf16,cWf16tensor-wise 的量化常数:

      • 对于 absmax 量化,它们分别为 sXsW

      • 对于 zeropoint 量化,它们分别为 ndX,ndW

    • sf16=1cXf16cWf16 为新的量化常数,Q()absmax 量化或 zeropoint 量化。

3.2 大规模的 Int8 矩阵乘法

  1. 使用每个张量一个 scaling constant (即, scaling constant )的量化方法的主要挑战是,单个异常值可以降低所有其他值的量化精度。因此,每个张量拥有多个 scaling constant 是可取的,比如 block-wise constants《8-bit optimizers via block-wise quantization》),这样异常值的影响就局限于每个 block 。我们通过使用向量化量化(vector-wise quantization )来改进 row-wise quantization《Fbgemm: Enabling high-performance low-precision deep learning inference》),其中row-wise quantization 是最常见的 blocking quantization 方式之一,详细内容如下所述。

  2. 为了处理超过 6.7B 规模的所有 transformer layers 中出现的大幅值异常值特征(large magnitude outlier features ),仅仅向量化量化是不够的。为此,我们开发了混合精度分解(mixed-precision decomposition),其中少量的大幅值的 feature dimensionslarge magnitude feature dimensions),占比约 0.1%16-bit 精度来表示,而其他 99.9% 的值以 8-bit 相乘。由于大多数元素仍以低精度来表示,与 16-bit 相比,我们仍保留约 50% 的内存减少。例如,对于 BLOOM-176B,我们将模型的内存占用降低了 1.96 倍。

  3. Figure 2 显示了向量化量化和混合精度分解。LLM.int8() 方法是 absmax vector-wise quantization 和混合精度分解的组合。

    注意:X 按列分解,W 按行分解。为什么?因为异常值特征主要集中在 activation (即,X)的少部分的列。而 W 通常不包含异常值。

    本文并没有理论指导,而是根据大模型的实验性分析从而得到的方法。

3.2.1 Vector-wise Quantization

  1. 增加矩阵乘法的 scaling constants 的数量的一种方法是:将矩阵乘法视为独立内积的序列。给定隐状态 Xf16RS×h 和权重 Wf16Rh×o,我们可以为 Xf16 的每一行分配不同的 scaling constants cXf16,并为 Wf16 的每一列分配不同的 cWf16。为了逆量化,我们通过 1/(cXf16cWf16)denormalize 每个内积结果。对于整个矩阵乘法,这相当于通过外积 cXf16cWf16denormalize ,其中 cXf16RS