深度卷积神经网络量化技术
1. 引言:为什么需要量化?
随着深度学习技术的普及,将神经网络部署到手机、IoT设备等边缘计算平台上的需求日益增长。然而,边缘设备通常面临计算能力有限、内存受限以及功耗约束的问题。此外,从云端向设备传输大型模型也会消耗大量能源和网络带宽。因此,研究人员一直在寻找有效的方法来优化模型,以实现更小的体积、更快的推理速度和更低的功耗。解决这一问题的方法多种多样。有些研究者选择从零设计高效模型,如MobileNet、ShuffleNet等;另一些则通过对现有模型应用量化、剪枝和压缩技术来减小模型规模;还有一些研究者专注于开发针对低精度计算的高效内核和硬件加速器,如GEMMLOWP、Intel MKL-DNN等。
在这些方法中,量化技术(Quantization)因其简单高效而受到广泛关注。所谓量化,就是减少模型权重和激活值的精度需求,例如将32位浮点数(float32)转换为8位整数(int8)。这种简单的转换带来了多方面的优势:量化技术最大的优点在于其广泛适用性——几乎不需要改变模型架构,就能在多种场景下实现优化。在许多情况下,开发者可以从现有的浮点模型出发,快速量化得到定点模型,几乎不损失准确率,无需等待新硬件开发。模型体积方面,8位量化可将模型大小减少4倍,这也意味着更快的模型更新下载速度。量化同时降低了中间计算结果所需的工作内存和缓存空间,使处理器能更好地利用缓存。计算效率上,大多数处理器对8位数据的处理速度本身就比32位浮点数据更快。在功耗方面,传输8位数据比32位浮点数据能效高4倍,考虑到在许多深度架构中,内存访问可能主导功耗消耗,数据移动量的减少能显著影响功耗表现。
综合这些因素,量化通常能实现2-3倍的推理速度提升。如果使用针对低精度向量运算优化的处理器或硬件加速器,性能和功耗改善可能更加显著。
2. 量化器的数学基础与设计
2.1 仿射均匀量化器
量化本质上是将连续的浮点值映射到离散的整数值。考虑一个浮点变量,其范围为(xmin, xmax),我们希望将其量化到范围(0, Nlevels-1),其中Nlevels=256(对应8位精度)。为此,需要确定两个关键参数:缩放因子(Scale, Δ)和零点(Zero-point, z)。缩放因子决定了量化步长,即每个量化级别对应的浮点值范围;而零点参数则确保浮点值0被精确量化为整数值,这一点非常重要,因为如卷积操作中的零填充等常见操作不应引入额外的量化误差。数学上,量化过程可表示为:
x i n t = r o u n d ( x Δ + z ) x_{int} = round\left(\frac{x}{\Delta} + z\right) xint=round(Δx+z)
x Q = c l a m p ( 0 , N l e v e l s − 1 , x i n t ) x_Q = clamp(0, N_{levels}-1, x_{int}) xQ=clamp(0,Nlevels−1,xint)
其中clamp函数用于确保量化值在指定范围内:
c l a m p ( a , b , x ) = { a 如果 x ≤ a x 如果 a ≤ x ≤ b b 如果 x ≥ b clamp(a, b, x) = \begin{cases} a & \text{如果 } x \leq a \\ x & \text{如果 } a \leq x \leq b \\ b & \text{如果 } x \geq b \end{cases} clamp(a,b,x)=⎩ ⎨ ⎧axb如果 x≤a如果 a≤x≤b如果 x≥b
反量化(将整数值转回浮点值)则通过以下公式实现:
x f l o a t = ( x Q − z ) Δ x_{float} = (x_Q - z) \Delta xfloat=(xQ−z)Δ
虽然仿射均匀量化器能够以8位精度存储权重和激活值,但零点参数会引入额外的计算开销。以卷积运算为例,量化后的计算可以展开为:
y ( k , l , n ) = conv ( w Q ( k , l , m ; n ) , x Q ( k , l , m ) ) − z w ∑ k , l , m x Q ( k , l , m ) − z x ∑ k , l , m w Q ( k , l , m ; n ) + z x z w y(k,l,n) = \text{conv}(w_Q(k,l,m;n), x_Q(k,l,m)) - z_w\sum_{k,l,m}x_Q(k,l,m) - z_x\sum_{k,l,m}w_Q(k,l,m;n) + z_xz_w y(k,l,n)=conv(wQ(k,l,m;n),xQ(k,l,m))−zwk,l,m∑xQ(k,l,m)−zxk,l,m∑wQ(k,l,m;n)+zxzw
这个公式表明,简单地在卷积前添加零点偏移会因操作数变宽(从8位变为16/32位)而使吞吐量下降2-4倍。更优的实现需要特别优化卷积核,以高效处理这些额外项。
2.2 对称均匀量化器
为了简化计算,可以使用对称量化器——将零点限制为0。这种情况下,量化操作简化为:
x i n t = r o u n d ( x Δ ) x_{int} = round\left(\frac{x}{\Delta}\right) xint=round(Δx)
对于有符号整数,限制范围为:
x Q = c l a m p ( − N l e v e l s / 2 , N l e v e l s / 2 − 1 , x i n t ) x_Q = clamp(-N_{levels}/2, N_{levels}/2-1, x_{int}) xQ=clamp(−Nlevels/2,Nlevels/2−1,xint)
对于无符号整数,限制范围为:
x Q = c l a m p ( 0 , N l e v e l s − 1 , x i n t ) x_Q = clamp(0, N_{levels}-1, x_{int}) xQ=clamp(0,Nlevels−1,xint)
为了更好地支持SIMD(单指令多数据)实现,可以进一步限制权重的范围。例如,有符号整数的clamp可调整为:
x Q = c l a m p ( − ( N l e v e l s / 2 − 1 ) , N l e v e l s / 2 − 1 , x i n t ) x_Q = clamp(-(N_{levels}/2-1), N_{levels}/2-1, x_{int}) xQ=clamp(−(Nlevels/2−1),Nlevels/2−1,xint)
反量化操作则变得更加简单:
x o u t = x Q Δ x_{out} = x_Q\Delta xout=xQΔ
2.3 随机量化器与反向传播中的量化建模
随机量化器通过在量化前添加均匀分布的随机噪声,然后进行舍入来模拟量化过程:
x i n t = r o u n d ( x + ϵ Δ + z ) , ϵ ∼ U n i f ( − 1 2 , 1 2 ) x_{int} = round\left(\frac{x + \epsilon}{\Delta} + z\right), \epsilon \sim Unif\left(-\frac{1}{2}, \frac{1}{2}\right) xint=round(Δx+ϵ+z),ϵ∼Unif(−21,21)
从期望值看,随机量化器等同于浮点权重的直接传递(加上范围限制),这使其在计算梯度时表现良好。不过在实际推理中,由于硬件通常不支持随机量化,我们一般不采用这种方法。在量化感知训练中,为了在反向传播中建模量化效应,我们使用"仿真量化"操作,它由量化和反量化两步组成:
x o u t = S i m Q u a n t ( x ) = Δ ⋅ c l a m p ( 0 , N l e v e l s − 1 , r o u n d ( x Δ ) − z ) x_{out} = SimQuant(x) = \Delta \cdot clamp\left(0, N_{levels}-1, round\left(\frac{x}{\Delta}\right) - z\right) xout=SimQuant(x)=Δ⋅clamp(0,Nlevels−1,round(Δx)−z)
由于量化函数在大多数点处导数为零,需要采用近似方法。一种实践中效果良好的方法是将量化器建模为限幅函数:
x o u t = c l a m p ( x m i n , x m a x , x ) x_{out} = clamp(x_{min}, x_{max}, x) xout=clamp(xmin,xmax,x)
反向传播采用"直通估计器"(straight through estimator),只在输入在特定范围内时传递梯度:
δ o u t = δ i n ⋅ I x ∈ [ x m i n , x m a x ] \delta_{out} = \delta_{in} \cdot \mathbb{I}_{x \in [x_{min}, x_{max}]} δout=δin⋅Ix∈[xmin,xmax]
其中 δ i n \delta_{in} δin是损失相对于量化器输出的梯度。
2.4 量化器参数确定与量化粒度
量化器参数(缩放因子和零点)的确定可以采用多种策略。TensorRT采用最小化原始分布与量化分布之间的KL散度;而本文采用更简单的方法:对于权重,直接使用实际最小值和最大值;对于激活值,使用跨批次的最小值和最大值的移动平均。量化还可以在不同粒度上进行。最简单的是"每层量化"(per-layer quantization),即对整个张量使用单一量化器。更精细的方法是"每通道量化"(per-channel quantization),即为每个卷积核单独设定量化参数。实验表明,每通道量化能提供更高的准确率,尤其是在使用批量归一化的网络中。不过,对激活值一般仍采用每层量化,以保持卷积和矩阵乘法核心计算的高效实现。
3. 训练后量化与量化感知训练
3.1 训练后量化——简单而有效
训练后量化是最简单的量化方法,它不需要重新训练模型,只对已训练好的浮点模型进行量化。这种方法分为两种主要策略:仅量化权重和同时量化权重与激活值。仅量化权重的方法极其简单,甚至不需要任何验证数据,只需将模型权重从32位浮点转换为8位整数。这种方法适合只关注减小模型存储大小,而不在意推理速度的场景,因为推理过程仍在浮点精度下进行。如果同时量化权重和激活值,则需要一些校准数据来确定激活值的动态范围。通常,约100个小批次的数据足以使激活范围的估计值收敛。这种量化方法能在减小模型大小的同时,显著提高推理速度。
实验表明,对于各种卷积神经网络架构,训练后量化能取得良好效果。特别是采用每通道量化方法时,量化模型的准确率能接近原始浮点模型。激活值也可以量化到8位精度,几乎不损失准确率,这部分得益于批量归一化和ReLU6等技术对激活值动态范围的限制。同时不同网络对量化的敏感度也不同:参数较多的网络(如ResNet和Inception-v3)通常对量化更加鲁棒,而参数较少的网络(如MobileNet系列)对量化更敏感。值得注意的是,当按层量化权重时,特别是对MobileNet架构,可能会出现显著的准确率下降,这主要是由批量归一化引起的——它导致同一层内不同卷积核的动态范围差异极大。每通道量化通过为每个卷积核单独量化,有效解决了这一问题。
3.2 量化感知训练——更高的准确率
量化感知训练在训练过程中模拟量化效应,能够比训练后量化提供更高的准确率。具体做法是在训练图中插入仿真量化操作,使网络适应量化带来的信息损失。在量化感知训练中,前向传播和反向传播都使用量化后的权重和激活值,但权重的更新仍在浮点精度下进行。这确保了微小的梯度能够累积并最终对量化值产生影响,而不会因为量化的粗粒度而丢失。更新后的浮点权重随即被量化,用于下一轮的前向和反向传播。
实现量化感知训练时,需要注意几个关键点:操作转换、批量归一化处理,以及低精度网络的特殊考量。
操作转换涉及将常规操作(如加法、连接等)调整为能处理量化值的形式。例如,对于两个量化值的加法,需要考虑它们可能有不同的缩放因子,因此需要先进行适当的重新缩放。同样,为了准确模拟推理时的操作融合(如加法后接ReLU),需要确保在训练时不在这些操作之间插入额外的量化操作。
批量归一化是量化感知训练中的一大挑战。常规训练中,批量归一化使用批次统计信息,而推理时使用长期统计信息,这种差异会导致量化权重出现批次间抖动,降低量化模型的准确率。为解决这一问题,训练工具实现了特殊的批量归一化处理策略:首先,在量化前根据长期统计数据对权重进行校正,消除批次间抖动;然后,在训练初期,通过额外的修正使输出与常规批量归一化相同;最后,在训练足够长时间后,将批量归一化参数冻结,使网络适应推理时使用的固定参数。
实验表明,量化感知训练能显著提高量化模型的准确率,即使是简单的每层量化也能取得接近浮点模型的性能。在8位精度下,训练后量化已经能提供接近浮点的准确率,而量化感知训练的优势在4位等更低精度量化时更为明显。研究发现,在4位精度下,每通道量化的优势更加突出;通过量化感知训练,即使在4位精度下,也能获得与8位量化相差不到5%的准确率。在实验中还发现,激活值量化至4位比权重量化至4位导致的准确率损失更严重。这可能是因为激活值量化引入的是随机误差(因为激活模式会随图像变化),而权重量化引入的是确定性误差,网络更容易学习补偿确定性的量化扭曲。
4. 量化训练的最佳实践与模型设计考量
通过大量实验,研究者总结了量化训练的一些最佳实践,帮助开发者获得更高质量的量化模型。
首先,随机量化并不比确定性量化提供更好的结果。理论上,随机量化可以帮助网络学习对量化噪声的鲁棒性,但由于推理时量化是确定性的,这种训练与推理的不匹配反而导致性能下降。实验证明,确定性量化在训练中表现更好。其次,从预训练的浮点模型开始量化通常比从头训练量化模型效果更好。这符合知识蒸馏的思路——先训练一个具有更多自由度的模型,然后将其知识"蒸馏"到参数受限的小模型中。实验结果显示,从浮点检查点微调得到的量化模型准确率始终高于从头训练的量化模型。
批量归一化的处理对量化模型的准确率有重大影响。实验比较了多种批量归一化量化方法,结果表明,使用校正因子并在适当时机冻结批量归一化参数的方法能提供最佳准确率,明显减少评估准确率的抖动。
在训练技巧上,需要注意权重的指数移动平均(EMA)在量化训练中可能产生负面影响。在浮点训练中,EMA通常能提高模型准确率,但在量化训练中,由于浮点权重往往收敛到量化决策边界附近,即使浮点权重的微小差异也可能导致量化后的权重大不相同,从而影响性能。实验显示,经过足够训练后,EMA权重的表现可能不如瞬时权重。模型架构方面,研究还发现一些有助于量化的设计原则。例如,不限制激活值范围往往能获得更好结果——使用普通ReLU而非ReLU6,让训练自行确定最佳激活范围,然后再量化,通常比预先限制固定范围效果更好。在模型结构选择上,存在明显的模型大小与可量化性之间的权衡。过度参数化的模型通常对量化更为鲁棒,因为它们有更多冗余来补偿量化引入的误差。即使在单一架构内,也可以在特征图数量和量化精度之间权衡——更多特征图允许使用更低位宽的权重,而依然保持相似的准确率。通过将权重精度从8位降至4位,研究表明可以在几乎不损失准确率的情况下,进一步减少25%的模型大小。
5. 性能评估与硬件推荐
5.1 实际设备上的性能测量
为评估量化对实际推理速度的影响,研究者在Google Pixel 2手机的单个大核上测量了浮点和量化模型的运行时间,同时使用Android NN-API在高通DSP上进行了测试。结果令人鼓舞:量化推理相比浮点推理速度提升了2-3倍,而在使用高通DSP(配备HVX向量扩展)时,速度提升接近10倍。
这些实测数据证实了量化技术在实际部署中的巨大价值,尤其是当硬件支持低精度计算时,性能提升更为显著。量化不仅加速了推理,还降低了功耗,这对电池供电的移动设备尤为重要。
5.2 神经网络加速器设计建议
积极的算子融合是第一个重要优化点。在单次传递中执行尽可能多的操作可以大幅降低内存访问成本,提高速度并降低功耗。紧接着是压缩内存访问——通过支持权重和激活值的即时解压缩,可以优化内存带宽利用。最简单的方法是支持低精度存储,同时保持计算精度。在计算精度方面,建议硬件加速器支持多种精度的算术运算,特别是4位、8位和16位。虽然分类任务通常可以在4-8位精度下取得良好结果,但像超分辨率和HDR图像处理等回归应用可能需要更高精度。理想情况下,加速器应允许每层选择不同位宽,因为网络中不同层对精度的敏感度各不相同。最后,支持权重的每通道量化至关重要。这不仅能提高量化模型的准确率,还简化了模型部署流程——开发者可以直接部署量化模型,无需针对特定硬件进行微调。每通道量化还为更低精度计算铺平了道路,进一步提高性能和效率。
6. 结论
在模型量化策略上,建议使用对称每通道权重量化作为训练后量化的起点。如果准确率有明显下降,可以进一步进行量化感知训练。实验表明,量化感知训练可以显著缩小与浮点准确率的差距,在4位精度下仍能保持与8位量化相差不到5%的准确率。在性能方面,8位量化推理在CPU上通常能提供2-3倍的速度提升,在优化的处理器上速度提升可接近10倍。模型大小也能显著减小——使用均匀量化可在不损失准确率的情况下将模型压缩4倍,而使用非均匀量化技术(如K-means)可实现更高压缩率。量化感知训练技术中,关键是确保量化推理与训练的前向传递匹配。批量归一化需要特殊处理才能获得最佳准确率。实验还表明,训练中的随机量化不如确定性量化表现好;权重的指数移动平均在量化训练中需谨慎使用,可能导致性能下降。从模型架构角度看,模型大小与量化后准确率之间存在明显权衡——更大模型通常对量化更加鲁棒。在相同架构下,可以权衡特征图数量与量化精度,使用更多特征图允许采用更低位宽而保持准确率。训练时不限制激活范围,并在训练后量化它们,往往比使用ReLU6等预设范围效果更好。