Quantization
[4] Xia L., Anthonissen M., Hochstenbach M. and Koren B. Improved stochastic rounding. 2020.
引
整理了一下量化中的基础概念和方法. 其实, 在计算机中, 我们所见之浮点数也只不过是真正数字的一个高精度量化 (e.g, FP32), 但是随着模型的逐步增加, 我们需要将模型进一步'精简'. 当然了, '精简'的法子有很多: 裁剪 (pruning), 蒸馏 (distillation), 包括这里讲到的量化 (quantization). 个人感觉, 相较于裁剪和蒸馏, 量化是一种即插即用的方法 (虽然会有一些追求极限精度的量化方法需要一些校准). 本文讨论如下的例子:
线性量化 (Linear Quantization)
对称量化
-
的对称量化过程如下:
- 确定 的(绝对值)范围: ;
- 确定 的范围: , 表示量化后的精度, 比如 Int8 时 .
- 量化:
-
如上图所示, 首先将 压缩到 之中, 然后再映射回 的量化后的精度中.
def quant(f: torch.Tensor, N: int = 8):
delta_f = f.abs().max()
delta_q = 2 ** (N - 1) - 1
return (f * delta_q / delta_f).round().to(torch.int), delta_f
- 利用上述代码可得:
- 反解的过程:
def dequant(q: torch.Tensor, delta_f: torch.Tensor, N: int = 8):
delta_q = 2 ** (N - 1) - 1
return (q * delta_f / detal_q).float()
- 得到:
非对称量化
-
的非对称量化过程如下:
- 确定 的(绝对值)范围: ;
- 确定 的范围: , 表示量化后的精度, 比如 UInt8 时 .
- 确定零点偏移量:
- 量化:
-
注意到:
def quant(f: torch.Tensor, N: int = 8):
offset = -f.min()
delta_f = f.max() + offset
delta_q = 2 ** N - 1
return ((f + offset) * delta_q / delta_f).round().to(torch.int), delta_f, offset
-
通过如上代码可得量化结果:
-
通过如下方式进行反解:
def dequant(q: torch.Tensor, delta_f: torch.Tensor, offset: torch.Tensor, N: int = 8):
delta_q = 2 ** N - 1
return (q * delta_f / delta_q).add(-offset).float()
- 得到:
非线性量化
-
其实, 一般的量化都可以用如下的过程统一表示:
- 将 normalize 到 区间内;
- 在该区间内设置 (或者 ) 个点, 记为:
- 寻找最近邻的作为量化结果:
-
之前的线性量化实际上就是:
和
-
而 round 就是一种自动的 nearest 的搜索方式.
-
当然了, 的分布不必像线性量化一样那样的均匀, 实际上这种分布往往不是最优的. 容易感觉到, 最优的分布应当在 中比较稠密的范围分布较多的量化点, 否则会造成大量的浪费. 当然了, 这种非线性量化的方式相较于线性量化有它的不足之处, 就是得根据数据'找'一个合适的分布, 这个'找'的过程往往是比较耗时的.
Logarithmic Quantization
- 对数量化的过程如下:
- 确定 的(绝对值)范围: (不一定是最大值);
- 确定 的范围: ;
- 量化:
- 反解的过程为:
Power-of-X
注: Power-of-X 根据 [3] 的确是这么定义的, 但是, 在有些里论文里, power-x 表示的是幂函数. 我说实话, 有那么点晕了.
-
Power-of-X 的特点就是 的分布是服从指数分布的:
其中 .
-
其量化过程为:
- 确定 的(绝对值)范围: ;
- 确定 的范围: , 表示量化后的精度, 比如 Int8 时 .
- 量化:
- 反解的过程为:
注: 这里 是为了将 保留给真正的 0 值.
def quant(f: torch.Tensor, alpha: float, N: int = 8):
sign, f = f.sign(), f.abs()
delta_f = f.max()
delta_q = 2 ** (N - 1) - 1
logalpha = math.log2(alpha)
return (f / delta_f).log2().div(logalpha).add(1).round().clip(0, delta_q).mul(sign).to(torch.int), delta_f
def dequant(q: torch.Tensor, alpha: float, delta_f: torch.Tensor, N: int = 8):
sign, q = q.sign(), q.abs()
return (alpha ** q.add(-1)).mul(delta_f).mul(sign)
-
如上图所示, , 不均匀性大大增加. 这个性质在有些时候会很有用, 因为很多数据的分布是形容高斯分布的, 此时采用不均匀的会更加高效.
-
需要声明的一点是, 的时候, 整体会显得更加均匀一点, 但是并不能等价线性量化, 注意到:
这意味着, 除了 之外, 所能表示的最小值会随着 增加逐步增大. 这其实是另一种极为严重的 '不均匀' 性质.
-
此外 是比较特殊的一种情况, 可以通过位操作加速.
Rounding
有些时候 Rounding 的方式也很重要, 毕竟它是误差的来源.
Deterministic rounding
Stochastic rounding
-
随机 rounding 形式如下:
其中 表示 round down 操作.
-
举个例子, 对于 , 它有 0.4 的概率为 有 的概率为 0. 对于 , 它有 的概率为 , 有 的概率为 .
-
一个比较好的性质是:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix