原文链接:https://www.yuque.com/yahei/hey-yahei/quantization-post_training
欢迎转载&引用,但烦请注明出处~
按照是否需要训练划分,量化通常可以分为从头训练(train from scratch)、重训练(retrain)、后训练(post-training)三种,本文主要介绍几种后训练量化的方案,并以线性均匀分布的定点量化为例。
**后训练量化指的是,对预训练后的网络选择合适的量化操作和校准操作,以实现量化损失的最小化,该过程不需要训练,通常不直接更新权重原始数值而是选用合适的量化参数。**当然也有一些利用校准数据集适当更新的权重原始数值的技术出现,这类优化方法有点“训练”的意味,但通常所需的“训练”样本非常少,前传(甚至有的需要局部反传)的次数小于训练时一个epoch的所需次数。
常见的优化目标
通常特征图、权重都可以转换成向量的形式来进行优化,所以可以不失一般性地假设我们的优化对象就是向量。
误差函数
误差函数通常就是距离函数,常见的距离函数有三种——
- L2距离,用于衡量两向量在空间位置上的差异
$$J_{l2} = ||a - b||^2_F = \sqrt{\sum_i (a_i - b_i)^2}\ < b r / > 很 多 情 况 下 我 们 并 不 关 注 准 确 的 L 2 距 离 , 只 需 要 比 较 相 对 大 小 , 所 以 很 多 时 候 L 2 距 离 都 指 的 是 平 方 和 误 差 , 也 即 < b r / > <br />很多情况下我们并不关注准确的L2距离,只需要比较相对大小,所以很多时候L2距离都指的是平方和误差,也即<br /> <br/>很多情况下我们并不关注准确的L2距离,只需要比较相对大小,所以很多时候L2距离都指的是平方和误差,也即<br/>J_{l2}=\sum_i (a_i - b_i)^2\$$
后文如不特殊说明,L2距离均指的是平方和误差。
L2距离也可以有其他变种,比如加权版本的L2距离——
$$J_{l2}’ = \sum_i |a_i|(a_i - b_i)^2 \$$
从裁剪的经验上看,我们往往认为绝对值较大的权重会比较重要,那么很自然地想到我们可以为L2距离进行加权,增加大权重在量化后误差在整体误差上的重要性。从实践上看,加权版本的L2距离在较高比特的时候似乎没有明显的优势,甚至可能带来更差的结果;但在较低比特量化的应用场景中,似乎能带来更好的结果(相对明显的提升)。
L2距离是一种相对直观的选择,也是最常用的选择。除此之外,L2距离有一些很好的特性,比如它很方便求导,可以很容易推导出它的迭代公式,进行交替优化。
- **KL距离,用于衡量两向量在分布上的差异
**为向量 a a a和 b b b统计直方图(注意bin数要一致),然后进行归一化操作,使所有bin上的数值相加后为1,也即得到概率分布图 H ( a ) H^{(a)} H(a)和 H ( b ) H^{(b)} H(b),那么向量 a a a和 b b b的KL距离为
$$J_{kl}=\sum_i H^{(a)}i log \frac{H{(a)}_i}{H{(b)}i} \ < b r / > K L 其 实 不 是 严 格 的 “ 距 离 ” , 它 没 有 对 称 性 ( 也 即 <br />KL其实不是严格的“距离”,它没有对称性(也即 <br/>KL其实不是严格的“距离”,它没有对称性(也即J{kl}(a, b) \neq J{kl}(b, a)$$),对应地产生了一些KL的变种,如JS距离、推土机距离,也可以用作误差函数;
衡量分布差异的误差函数对数据分布比较敏感,通常需要较大的数据量来保证收集的分布与真实分布足够接近,而L2距离和余弦距离则没有这样的要求。量化的实际应用最早算是英伟达的TensorRT推动起来的,其校准策略就是最小化KL距离,因此多数校准策略也都模仿它采用了KL距离。
- 余弦距离,用于衡量两向量在方向上的差异
$$J_{cos} = \frac{a \cdot b}{||a|| \ ||b||} = \frac{\sum_i a_i b_i}{\sqrt{\sum_i a_i^2} \sqrt{\sum_i b_i^2}}\$$
余弦距离作为误差函数我没用过,确实也有些论文(比如EasyQuant)利用余弦距离作为目标进行优化,但与L2距离和KL距离不同,余弦距离取值范围为[-1, 1]
,越接近1表示误差越小,所以优化时是最大化余弦距离而非最小化。
优化对象
- 直接最小化输入/权重的量化误差
把输入和权重拉伸成向量 v v v,量化得到KaTeX parse error: Expected '}', got 'EOF' at end of input: \bar{v,反量化后得到 v ~ \tilde{v} v~,也即
$$\bar{v}=Quantize(v, \alpha, \beta)=Clamp\left(Round(\frac{v}{\alpha} - \beta)\right)\ < b r / > <br /> <br/>\tilde{v}=Dequantize(\bar{v}, \alpha, \beta)=\alpha (\bar{v}+\beta) \ < b r / > 其 中 , <br />其中, <br/>其中,\alpha 和 和 和\beta 分 别 是 线 性 均 匀 分 布 的 定 点 量 化 中 的 两 个 量 化 参 数 , 分 别 用 于 缩 放 和 偏 移 ; < b r / > 为 了 方 便 讨 论 , 我 们 令 分别是线性均匀分布的定点量化中的两个量化参数,分别用于缩放和偏移;<br />为了方便讨论,我们令 分别是线性均匀分布的定点量化中的两个量化参数,分别用于缩放和偏移;<br/>为了方便讨论,我们令\beta=0 , 那 么 < b r / > ,那么<br /> ,那么<br/>\bar{v}=Clamp\left(Round(\frac{v}{\alpha})\right)\ < b r / > <br /> <br/>\tilde{v}=\alpha \bar{v}\ < b r / > 那 么 可 以 定 义 一 个 误 差 函 数 <br />那么可以定义一个误差函数 <br/>那么可以定义一个误差函数J(v, \tilde{v}) , ∗ ∗ 优 化 和 校 准 过 程 通 常 就 是 寻 找 一 个 合 适 的 量 化 参 数 ,**优化和校准过程通常就是寻找一个合适的量化参数 ,∗∗优化和校准过程通常就是寻找一个合适的量化参数\alpha , 使 得 误 差 ,使得误差 ,使得误差J$$最小化。** - 最小化输出误差
记输入和权重为 x x x和 w w w,量化后为 x ˉ \bar{x} xˉ和 w ˉ \bar{w} wˉ,反量化后为 x ~ \tilde{x} x~和 w ~ \tilde{w} w~,卷积输出为 y = w ∗ x y=w*x y=w∗x;
直观上看,最小化输出误差相比于直接最小化一个向量的量化误差更加直接,也更加有效,但实践上会发现可能并非如此,有时候会出现精度反而下降的情况,这可能是对小样本校准数据集过拟合的结果。此外,为了得到 y ~ \tilde{y} y~,优化和校准过程中每一次迭代都需要一次完整的前向传播(具体次数取决于迭代方法的收敛速度,或搜索方法的粒度),这意味着该方法的计算代价将远高于直接前一种方法。
我们可以直接将优化目标定为最小化输出误差,也即最小化 J ( y , y ~ ) J(y, \tilde{y}) J(y,y~),这里的KaTeX parse error: Expected '}', got 'EOF' at end of input: \tilde{ 可以是- y ~ = w ~ ∗ x \tilde{y}=\tilde{w}*x y~=w~∗x:保持全精度输入,只搜索确定 w w w的量化参数
- y ~ = w ∗ x ~ \tilde{y}=w * \tilde{x} y~=w∗x~:保持全精度权重,只搜索确定 x x x的量化参数
- y ~ = w ~ ∗ x ~ \tilde{y}=\tilde{w} * \tilde{x} y~=w~∗x~:同时搜索确定 w w w和 x x x的量化参数;或量化输入/权重其中一个,搜索确定另一个的量化参数
优化方法
- 交替优化(Alternating Optimization)
参考论文:《Fixed-point feedforward deep neural network design using weights +1, 0, and −1 (2014)》
假定其他参数确定且与当前参数无关,推导出待优化参数在当前情况下的最优解,然后固定该参数,继续推导下一个参数的最优解,反复迭代、交替优化,直到收敛为止(参数本身收敛,也即更新幅度很小;或者目标收敛,比如误差不再减小)。本质上是一种贪心搜索,由于实际场景中误差函数不是理想、光滑的凸函数,所以通常得不到全局最优解。
该方法需要求解迭代更新的公式,在前述的三种误差函数中,L2的迭代公式推导上最为方便,以下便用L2误差进行举例——(如果迭代更新公式难以推导,也可采用梯度下降法)
- 以最小化向量的量化误差为例
- 初始化 α \alpha α
通常用朴素的最大最小值确定初始的 α \alpha α - 假设 α \alpha α确定,并对向量 v v v进行量化
v ˉ = Q u a n t i z e ( v , α ) \bar{v} = Quantize(v, \alpha) vˉ=Quantize(v,α) - 假设 v ˉ \bar{v} vˉ确定,推导出能使 J l 2 ( v , v ~ ) J_{l2}(v, \tilde{v}) Jl2(v,v~)最小化的 α \alpha α
J l 2 ( v , v ~ ) = J l 2 ( v , α v ˉ ) = ∣ ∣ v − α v ˉ ∣ ∣ 2 = v T v − 2 α v T v ˉ + α 2 v ˉ T v ˉ J_{l2}(v, \tilde{v}) = J_{l2}(v, \alpha \bar{v}) = ||v - \alpha \bar{v}||^2 = v^T v - 2 \alpha v^T \bar{v} + \alpha^2 \bar{v}^T \bar{v} Jl2(v,v~)=Jl2(v,αvˉ)=∣∣v−αvˉ∣∣2=vTv−2αvTvˉ+α2vˉTvˉ
∂ J l 2 ∂ α = − 2 v T v ˉ + 2 α v ˉ T v ˉ = 0 \frac{\partial J_{l2}}{\partial \alpha} = -2 v^T \bar{v} + 2 \alpha \bar{v}^T \bar{v} = 0 ∂α∂Jl2=−2vTvˉ+2αvˉTvˉ=0
(这里假定了KaTeX parse error: Expected '}', got 'EOF' at end of input: \bar{v与 α \alpha α无关;而且因为 J l 2 J_{l2} J
- 初始化 α \alpha α