论文:
《Mixed Precision Training》
论文是关于混合精度训练的,考虑到混合精度训练的思想已经广为人知,因此这里仅做粗读。
Introduction
:略(参考原始论文)。
相关工作:略(参考原始论文)。
我们介绍了 training with FP16
的关键技术,同时仍然匹配 FP32 training session
的 model accuracy
:single-precision master weights and updates
、loss-scaling
、accumulating FP16 products into FP32
。
单精度浮点数为
float32
、双精度浮点数为float64
。
在混合精度训练中,权重、activations
以及梯度以 FP16
格式存储。为了匹配 FP32
网络的准确率,我们维护权重的一份 FP32 master copy
,并在 optimizer step
中用 weight gradient
来更新它。在每次迭代中,使用 master weights
的 FP16 copy
进行正向传播和反向传播,将 FP32 training
所需的存储和带宽减半。Figure 1
说明了这种混合精度训练过程。
虽然 FP32 master weights
的需求并非普遍存在,但有两个可能的原因解释为什么许多网络需要它:
一个解释是 updates
( weight gradients
乘以学习率)变得太小而无法用 FP16
表示。FP16
中任何 magnitude
小于 Figure 2b
所示,约 5%
的 weight gradient values
的 exponents
小于 -24
。在 optimizer
中,这些小的梯度与学习率相乘后会变成零,并对模型准确率产生不利影响。使用 single-precision copy
进行更新可以克服这个问题并恢复准确率。
即,
weight updates
绝对很小,导致updates
变成零。
另一个解释是 weight value
与 weight update
的比率非常大。在这种情况下,即使 weight update
可以用 FP16
来表示,当加法操作将 weight update
右移从而将 binary point
与 weights
对齐时, weight update
仍然可能变成零。当 normalized weight value
的 magnitude
比 weight update
的 magnitude
大至少 2048
倍时,这种情况可能发生。由于FP16
有 10-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
。
尽管与 single precision training
相比,维护额外的权重副本将权重的内存需求增加了 50%
,但对整体内存使用的影响要小得多。 对于训练,内存消耗主要由 activations
引起,这是由于较大的 batch size
、以及保存每层的 activations
从而在反向传播中重用。 由于 activations
也以半精度格式存储,所以整体内存消耗约减半。
作者这里并未给出数据来说明。
FP16 exponent
将 normalized value exponents
范围有偏地居中到 [-14,15]
,而实际梯度值往往以小 magnitudes
( negative exponents
)为主。例如,考虑 Figure 3
,显示了在 FP32
训练 Multibox SSD
检测器网络过程中收集的所有层的 activation gradient values
的直方图。注意,FP16
可表示范围的大部分未被使用,而许多值低于最小可表示范围并变为零。 scaling up
梯度将使它们占用更多的可表示范围,并保留一些值(如果不 scaling up
,这些值将会因为零而丢失)。
不缩放梯度时,该特定网络会发散;但将其缩放 8
倍(对 exponents
增加 3
)足以匹配 FP32 training
达到的准确率。这表明,在 magnitude
上小于 activation gradient values
对此模型的训练无关,但 magnitude
的 activation gradient values
很重要。
由于学习率通常很小,因此
范围内的 activation gradient values
乘以学习率仍然可能小于。但是, Weight Update
采用FP32
,因此不会变为零。
将 gradient values
移至 FP16
可表示范围的一种有效方法是:在开始反向传播之前,缩放在前向传播中计算的 loss value
。通过链式法则,反向传播将确保所有梯度值被缩放相同的量。这在反向传播期间不需要额外的操作,并使相关的梯度值不会变成零。
在权重更新之前,必须 unscale
权重梯度,从而保持 weight update magnitudes
与 FP32 training
相同。最简单的方法是:在反向传播结束之后、但在梯度裁剪或任何其他与梯度相关的计算之前进行 unscale
,从而确保不需要调整任何超参数(如梯度裁剪阈值、weight decay
等)。
虽然可以通过缩小学习率来间接地缩放梯度,但是梯度裁剪阈值、
weight decay
等等方法的超参数也需要相应地缩放。
loss scaling factor
有几种选择。最简单的一种是选择一个常量的 scaling factor
。我们用 8
到 32K
的 scaling factor
训练了各种网络(许多网络不需要 scaling factor
)。可以经验性地选择一个常量 scaling factor
,或者如果有梯度统计信息,可以直接选择一个 scaling factor
使其与 maximum absolute gradient value
的乘积低于 65,504
(FP16
中可表示的最大值)。
只要不在反向传播期间导致数值溢出,选择一个大的 scaling factor
就没有坏处。数值溢出会导致 weight gradients
中出现无穷大和 NaN
,在权重更新后会不可逆地破坏权重。请注意,数值溢出可以通过检查计算到的 weight gradients
(例如在 unscale weight gradient values
的时候)来有效检测。一种解决方法是:在检测到数值溢出时跳过 weight update
,直接进入下一次迭代。
在很大程度上,神经网络的算术分为三类:向量点积、reductions
、以及 point-wise
操作。这些算术类别在 reduced precision
算术方面受益于不同的处理。
为了保持模型准确率,我们发现一些网络要求 FP16 vector dot-product
从而累积 partial products
到 FP32
值,然后在写入内存前将其转换为 FP16
。如果不这样累积到 FP32
,一些 FP16
模型的准确率无法匹配 baseline
模型。而先前的 GPU
只支持 FP16 multiply-add
操作,而 NVIDIA Volta GPU
引入了 Tensor Core
,可以将 FP16
的 输入矩阵相乘并累积乘积结果到 FP16 output
或 FP32 output
。
大型的 reductions
(如向量元素之和)应在 FP32
中进行。这种 reductions
主要出现在 batch-normalization layers
(累积统计信息)和 softmax layers
。在我们的实现中,这两种 layer
类型仍从内存读取和写入 FP16
张量,而在 FP32
中执行算术运算。这并没有减慢训练过程,因为这些 layers
受内存带宽限制,而对于算术速度不敏感。
point-wise
操作(如非线性激活函数、element-wise
矩阵乘法)受内存带宽限制。由于算术精度不影响这些操作的速度,可以使用 FP16
或 FP32
。
略(参考原始论文)。
论文:
《Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference》
论文是关于量化 CNN
模型的,这里仅做粗读,仅关注核心算法。
Introduction
:略(参考原始论文)。
相关工作:略(参考原始论文)。
在这一节中,我们描述了通用的量化方案,也就是两种类型数值之间的对应关系: bit representation of values
(这个整数记为 "quantized value"
)、以及它们作为数学的实数的解释(对应的实数记为 "real value"
)。我们的量化方案在推理时只使用整数算术实现,在训练时使用浮点算术实现,两种 implementations
高度对应。我们首先数学上严格定义量化方案,然后分别采用该方案用于 integer-arithmetic inference
和 floating-point training
。
floating-point training
类似于量化感知训练Quantization Aware Training
,即在训练阶段就应用 “模拟” 量化操作,从而降低integer-arithmetic inference
的量化误差。
我们量化方案的一个基本要求是,它允许只使用整数算术操作对 quantized values
进行所有算术运算的有效实现(我们避免需要 lookup tables
的实现,因为它们往往比在 SIMD
硬件上进行纯算术运算表现更差)。这相当于要求量化方案是整数
其中:
常数 "scale"
)是一个任意的正实数。它通常以软件中的浮点数形式表示,类似于实数
常数 "zero-point"
)与 quantized value
quantized value
quantized value
精确地表示。这一要求的动机是,高效实现的神经网络算子通常需要对数组进行 zero-padding
。
这个公式是我们的量化方案,常数 quantization parameters
)。我们的量化方案对每个 activations array
内的所有值、以及 weights array
内的所有值使用单组量化参数,即:不同的 array
使用独立的量化参数。
对于 8-bit
量化,8-bit
整数(对于 B-bit
量化,B-bit
整数)。一些数组(通常是 bias vectors
)被量化为 32-bit
整数,参见后续内容。
到目前为止的讨论被总结在以下的 quantized buffer
数据结构中,神经网络中的每个 activations array
和 weights array
都有这样一个 buffer
实例。我们使用 C++
语法是因为它可以明确地表达类型:
xxxxxxxxxx
template<typename QType> // e.g. QType=uint8
struct QuantizedBuffer {
vector<QType> q; // the quantized values
float S; // the scale
QType Z; // the zero-point
};
我们现在考虑如何仅使用整数算术进行推理,也就是如何使用方程 quantized-values
运算,以及后者如何设计为仅包含整数算术,即使 scaling
参数
考虑两个 quantized
矩阵为
从矩阵乘法的定义,我们有:
这可以重写为:
其中乘数
quantization scales
其中:其中
归一化的乘数 int16
或 int32
)。例如,如果使用 int32
的整数乘法,表示 int32
值。由于 30-bit
的相对精度。因此,乘以 fixed-point multiplication
)。
同时,乘以 bit-shift
),尽管需要具有正确的四舍五入(round-to-nearest
)行为,我们在附录 B
中讨论这个问题。
为了在不执行 16-bit
整数的情况下,有效地实现公式
其中:
每个
上式的剩余成本几乎全部集中在核心的整数矩阵乘法累加上:
这需要
的确,zero-points
都能以很小的开销处理,除了 core integer matrix multiplication accumulation
,因为我们在任何其他 zero-points-free
方案中都必须计算它。
这仅仅改善了减法操作,而没有改善乘法操作。
原始方法中,对于
,一共需要 的减法操作;但是新的方法中,计算 需要 次加法操作。然后 可以在不同的 中共享。 但是,对于乘法操作,二者之间并未改善。
我们继续前面的讨论,但现在显式地定义所有涉及的量(quantities
)的数据类型,并修改 quantized
的矩阵乘法,从而直接将 bias-addition
和 activation function evaluation
合并到其中。这样将整个 layer
融合成单个操作而不仅是一个优化。由于我们必须在推理代码中重现训练中使用的相同算术,推理代码中的 fused operators
的粒度(接受 8-bit quantized input
并产生 8-bit quantized output
)必须与训练图中 "fake quantization" operators
的 placement
相匹配。
对于 ARM
和 x86 CPU
架构,我们使用 gemmlowp
库(《gemmlowp: a small self-contained low-precision gemm library》
),其 GemmWithOutputPipeline
入口点支持我们现在描述的 fused operations
。
General Matrix Multiplication: GEMM
:通用矩阵乘法。
我们将 activations
。权重和 activations
均为 uint8
类型(我们也可以等效地选择 int8
,适当修改 zero-points
)。uint8
值的乘积的累加需要 32-bit accumulator
,我们为 accumulator
选择带符号类型,原因很快就会变清楚。
为了得到 quantized bias-addition
(将 int32 bias
添加到 int32 accumulator
上),bias-vector
被量化为:
使用 int32
作为其量化数据类型。
使用 0
作为其 quantization zero-point
其 quantization scale
accumulators
的相同,即 weights scale
和 input activations scale
的乘积:
尽管 bias-vectors
被量化为 32-bit
值,但它们只占神经网络参数的很小一部分。此外,对 bias vectors
使用更高精度满足了一个真正的需求:由于每个 bias-vector entry
被添加到许多 output activations
中,bias-vector
中的任何量化误差往往表现为一个 overall bias
(即,均值不为零的误差项),这必须避免以保持端到端的神经网络准确性。
即,对于任何样本
, bias-vector
都添加到它的output activation
上,因此bias-vector
对所有样本都产生了影响。
对 int32 accumulator
的最终值,还剩下三件事要做:
scale down
到 final scale
,这个 final scale
被 8-bit output activations
所使用。
cast down
到 unit8
。
应用 activation function
生成 final 8-bit output activation
。
下采样对应于乘数 rounding bit-shift
组成。之后,我们执行 cast
到 uint8
,饱和到 [0,255]
范围。
我们仅仅关注 clamps
类型的激活函数,例如 ReLU
、ReLU6
。数学函数在附录 A.1
中讨论,目前我们不会将它们融合到这种层中。因此,我们 fused activation functions
唯一需要做的就是进一步将 uint8
值截断到 [0,255]
的某个子区间,然后存储 final uint8 output activation
。实际上,quantized training process
(参考下一节)倾向于学习使用整个 output uint8
区间 [0,255]
,以致 activation function
不再起任何作用,其效果被归因于截断到区间 [0, 255]
(由于 cast
到 unit8
所隐含的)。
在大模型时代,通常很少采用
simulated quantization
的训练,而是直接训练一个大模型,然后再进行post-training
量化。
训练 quantized networks
的常见方法是:用浮点进行训练,然后量化所得到的权重(有时伴随额外 post-quantization training
从而进行微调)。我们发现,这种方法对具有相当表达能力的大型模型效果足够好,但对小型模型会导致明显的准确率下降。simple post-training quantization
的常见失败模式包括:
1)
:不同 output channels
的权重范围差异很大(大于 100
倍)。注意:前面章节中规定,同一层的所有通道必须量化到相同分辨率;这会导致权重范围较小的通道中,权重的相对误差更大。
2)
:outlier weight values
,这些异常值使得量化后所有的非异常值的权重降低了精确性。
我们提出了一种在训练的前向传播中模拟量化效应(quantization effects
)的方法。反向传播仍然照常进行,所有权重和 bias
以浮点存储,以便可以轻易地略微调整。但是,前向传播模拟 quantized inference
(因为 quantized inference
发生在推理引擎中),通过如下方式:以浮点算法实现我们前面介绍的量化方案中的 rounding behavior
。
权重在与 input
执行卷积之前被量化。如果 layer
使用 batch normalization
,则在量化之前,batch normalization
参数被 “折叠”到权重中。
activations
在这样的时点上被量化:在推理期间 activations
被量化的点。例如:在 activation function
应用于卷积层或全连接层输出之后、或在像 ResNet
中那样多个层的输出相加或拼接的 bypass connection
之后。
对于每一层,quantization
由 quantization levels
数量、以及 clamping range
的参数化,并通过 point-wise
地应用下面定义的量化函数
其中:quantization levels
数量;
在我们的实验中,8-bit
量化时
即,在原始的神经网络中插入这些特定的
fake quantization layer
。
weight quantization
与 activation quantization
的 quantization ranges
的处理不同:
对于权重,基本思想是:简单地设置 int8
值,取值范围仅仅在 [-127, 127]
并且不会取值为 -128
,因为这 enable
了一个重要的 optimization opportunity
(更多细节见附录 B
)。
对于 activations
,ranges
取决于网络输入。为了估计 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
在量化后可以精确表示为整数 quantization parameters
映射到方程 scale
zero-point
下面我们假设神经网络的计算被捕获为 TensorFlow graph
,然后描述 simulated quantization
。典型的工作流程在 Algorithm 1
中描述。通过 fusing and removing operations
的inference graph
的优化,超出了本文范围。用于 graph modifications
的源码(插入 fake quantization
操作、创建和优化 inference graph
)、以及 low bit inference engine
已在 《Tensorflow quantized training support》
中通过 TensorFlow contributions
开源。
公式
12
为:
Figure 1.1 a and b
说明了一个简单卷积层量化前后的 TensorFlow graph
。Figure C.3
中,更复杂的带 bypass connection
的卷积的说明,可以在 Figure C.4
中找到。
请注意,bias
没有被量化,因为在推理过程中它们表示为 32-bit
整数,范围和精度远高于 8-bit weights and activations
。此外,用于,bias
的量化参数从权重和激活的量化参数推导出来。
使用《Tensorflow quantized training support》
的典型 TensorFlow
代码示例如下:
xxxxxxxxxx
from tf.contrib.quantize import quantize_graph as qg
g = tf.Graph()
with g.as_default():
output = ...
total_loss = ...
optimizer = ...
train_tensor = ...
if is_training:
quantized_graph = qg.create_training_graph(g)
else:
quantized_graph = qg.create_eval_graph(g)
# Train or evaluate quantized_graph.
对于使用 batch normalization
的模型,存在额外复杂性:training graph
中 batch normalization
是单独的操作块,而 inference graph
中 batch normalization
参数 “折叠” 到卷积层或全连接层的权重和 bias
中,从而提高效率。为了准确模拟量化效应,我们需要模拟这种折叠,并在权重被 batch normalization parameters
缩放后再来量化权重。我们采用以下方法:
其中:
batch normalization
的 scale
参数。
convolution results
的 batch
内方差的移动平均估计。
折叠后,batch-normalized convolutional layer
简化为 Figure 1.1a
中所示的简单卷积层,具有被折叠的权重 bias
。因此,相同的配方也适用于 Figure 1.1b
。附录中可以查阅 batch-normalized convolutional layer
( Figure C.5
)、相应的 inference graph
(Figure C.6
)、batch-norm folding
后的训练图( Figure C.7
)以及 folding and quantization
后的训练图( Figure C.8
)。
略(参考原始论文)。
论文:
《LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale》
大型 pretrained
语言模型在 NLP
中被广泛采用,但需要大量内存进行推理。对于超过 6.7B
参数的大型 transformer
语言模型,前馈层(feed-forward layer
)和注意力投影层(attention projection layer
)及其矩阵乘法操作占据了 95%
的参数和 65-85%
的所有计算(《High performance natural language processing》
)。减小参数大小的一种方法是将其量化为更少的比特并使用低位精度(low-bit-precision
)的矩阵乘法。考虑到这一目标,人们已经开发了 transformer
的 8-bit
量化方法。虽然这些方法减少了内存使用,但它们降低了性能,通常需要在训练后进一步 tuning quantization
,并且只在少于 350M
参数的模型上进行了研究。低于 350M
参数时性能无损的量化理解得不够好,十亿参数级的量化仍是一个开放性挑战。
在本文中,我们提出了第一个不降低性能的十亿参数级 Int8
量化过程。我们的过程使得加载一个具有 16-bit
或 32-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
的参数,理解推理过程中隐状态(隐状态就是 activation
)feature 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
中的仅 6
个feature dimensions
中。
将这些异常feature dimensions
设置为零会使 top-1 attention softmax
概率质量降低 20%
以上,并使 validation perplexity
退化 600-1000%
,尽管它们只占所有输入特征的约 0.1%
。相比之下,删除相同数量的随机特征会最大降低 0.3%
的概率质量,并退化约 0.1%
的困惑度(perplexity
)。
给定一个文本序列,
sequence
维度表示token id
的维度,feature
表示embedding
或activation
的维度。上述描述的含义是:
在
6B
模型,有25%
的层()出现了异常值。 在
6.7B
模型,所有的层出现了异常值;沿着token id
的方向,有75%
的位置出现了异常值();沿着 embedding/activation
的方向,有6
个位置出现了异常值()。 写成矩阵的形式:
为了支持这样极端异常值的有效量化,我们开发了混合精度分解(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
)。
相关工作:下面列出了 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
数据类型相比提供了优异的性能,但当前 GPU
和 TPU
都不支持此数据类型。
语言模型中的异常值特征(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
。
nuQmm
和 ZeroQuant
旨在加速推理和减少内存占用,而我们则关注在 8-bit
内存占用下保留预测性能。
nuQmm
和 ZeroQuant
评估的最大模型分别是 2.7B
和 20B
参数的 transformer
。ZeroQuant
实现了 20B
模型的 8-bit
量化,同时保持性能没有衰退。我们展示了我们的方法允许高达 176B
参数的模型进行 zero-degradation quantization
。nuQmm
和 ZeroQuant
表明,更细粒度的量化可以是大型模型量化的有效手段。这些方法与 LLM.int8()
是互补的。
另一个平行工作是 GLM-130B
,它使用我们工作的见解实现 zero-degradation 8-bit quantization
(《Glm-130b: An open bilingual pre-trained model》
)。GLM-130B
以 8-bit weight storage
进行完整的16-bit
精度的矩阵乘法。
相比之下,
LLM.int8()
采用8-bit
的矩阵乘法。
在这项工作中,我们通过扩展 transformer
模型将量化技术推向了极限。我们对两个问题感兴趣:
在何种模型规模下量化技术会失败,以及为何量化技术会失败?
量化技术的失败与量化精度有何关系?
为了回答这些问题,我们研究了高精度非对称量化(zeropoint quantization
)和高精度对称量化(absolute maximum quantization
)。虽然 zeropoint quantization
通过使用数据类型的 full bit-range
提供了高精度,但由于实际限制而很少使用它。absolute maximum quantization
是最常用的技术。
absmax
量化通过将输入乘以 8-bit
范围 [-127, 127]
中。因此,对于 FP16
数据类型的输入矩阵 Int8 absmax quantization
由以下公式给出:
其中:
zeropoint
量化通过如下的方式将 input distribution
移动到 full range [-127, 127]
中:首先用 normalized dynamic range
zeropoint
affine transformation
),任何输入张量都将使用数据类型的所有比特,从而减少非对称分布(asymmetric distributions
)的量化错误。例如,对于 ReLU
输出,在 absmax
量化中,所有 [-127, 0)
的值都未被使用;而在 zeropoint
量化中,完整范围 [-127,127]
都被使用。
zeropoint
量化由以下等式给出:
推导过程:假设
。我们希望把 投影到 [-127, 127]
之间。则可以采用仿射变换:然后根据
来进行量化。 根据论文
《Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference》
,zeropoint
量化的形式为:因此:
逆量化为:
。
要在操作中使用 zeropoint
量化,我们同时传入张量 zeropoint
16-bit
整数操作之前,将zeropoint quantized
数值
如果没有 multiply_i16
指令,如在 GPU
或 TPU
上,则需要展开:
注意:结果是
32-bit
整数。
其中:Int8
精度计算,其余以 Int16/32
精度计算。因此,如果没有 multiply_i16
指令,zeropoint
量化可能比较慢。
在上述两种情况下,输出以32-bit
整数
即:
具有 16-bit
浮点输入和输出的 Int8
矩阵乘法:给定隐状态 sequence dimensions
、feature dimensions
、16-bit
浮点输入和输出的 8-bit
矩阵乘法如下:
其中:
tensor-wise
的量化常数:
对于 absmax
量化,它们分别为
对于 zeropoint
量化,它们分别为
absmax
量化或 zeropoint
量化。
使用每个张量一个 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
方式之一,详细内容如下所述。
为了处理超过 6.7B
规模的所有 transformer layers
中出现的大幅值异常值特征(large magnitude outlier features
),仅仅向量化量化是不够的。为此,我们开发了混合精度分解(mixed-precision decomposition
),其中少量的大幅值的 feature dimensions
(large magnitude feature dimensions
),占比约 0.1%
以 16-bit
精度来表示,而其他 99.9%
的值以 8-bit
相乘。由于大多数元素仍以低精度来表示,与 16-bit
相比,我们仍保留约 50%
的内存减少。例如,对于 BLOOM-176B
,我们将模型的内存占用降低了 1.96
倍。
Figure 2
显示了向量化量化和混合精度分解。LLM.int8()
方法是 absmax vector-wise quantization
和混合精度分解的组合。
注意:
按列分解, 按行分解。为什么?因为异常值特征主要集中在 activation
(即,)的少部分的列。而 通常不包含异常值。 本文并没有理论指导,而是根据大模型的实验性分析从而得到的方法。
增加矩阵乘法的 scaling constants
的数量的一种方法是:将矩阵乘法视为独立内积的序列。给定隐状态 scaling constants
denormalize
每个内积结果。对于整个矩阵乘法,这相当于通过外积 denormalize
,其中