同一模型,却得到不同结果
机器上的智能传感器需要把少量信号转换为分数。在开发电脑上结果看起来正确,但移植到微控制器后,有些数值相差一个整数单位。这是无害噪声还是错误?答案取决于这个整数代表什么。接近阈值时,数值表示中的一步就可能改变决策。在修改神经网络之前,应先理解把宽位数累加结果转换为较小输出格式的步骤。
本文研究重新量化:先重新缩放,再舍入并限制到可表示范围。我们推导一个三输入神经元,用精确分数执行计算,并为中点、负值和饱和值构造反例。目标不是证明 INT8 不可靠,而是明确两种实现必须共享的数值约定。我们没有使用开发板、测量延迟或评估完整网络;代码是教学参考,不是商业运行时的逐位精确模拟器。
整数是刻度上的标签
INT8 是有符号八位整数,采用二进制补码时可表示 −128 到 127。在量化网络中,该整数未必等于变量的数学值,还需要正缩放系数 s 和零点 z。s 表示一步的大小,z 表示哪个整数对应真实的零。这类似于给移动过原点的尺子刻度编号:只知道编号,无法确定长度。这个类比仅解释转换,不解释网络怎样工作。
q 是存储的整数,r 是重建值。本例中所有信号均已归一化且无量纲,不是在直接转换伏特或温度。s = 0.1、z = 10 时,q = 13 表示 r = 0.3;q = 10 表示零;q = 0 则表示 −1。因此为图像添加零边界时,数学零应编码为 z,而不一定是数值为零的字节。
先完成整个计算,再谈舍入
这个神经元将三个乘积相加,再加一个偏置,即平移结果的常数。整数输入为 [13, 9, 12],sₓ = 0.1、zₓ = 10,对应 [0.3, −0.1, 0.2]。整数权重为 [2, −3, 1],s𝑤 = 0.2、z𝑤 = 0,对应 [0.4, −0.6, 0.2]。真实偏置为 0.02。所选数值都能在各自网格上精确表示,因此可以单独研究最后的舍入,而不混入先前输入量化的误差。
实数计算为 0.3 × 0.4 + (−0.1) × (−0.6) + 0.2 × 0.2 + 0.02 = 0.24。要从整数得到相同值,首先减去输入零点。乘积变成 3 × 2、(−1) × (−3)、2 × 1,即 6、3、2。总和的一单位代表 sₓs𝑤 = 0.02,偏置也必须使用同一尺度:bq = 0.02/0.02 = 1。因此累加器 A = 6 + 3 + 2 + 1 = 12,重建结果仍是 12 × 0.02 = 0.24。
该关系假设权重零点为零,偏置能以乘积尺度精确表示;否则偏置也需要明确的舍入过程。A 是整数,典型流程使用 INT32 累加,以获得比单个 INT8 操作数更大的范围。本文假设累加没有溢出。足够的位宽是必要条件,但不能解决后续重新缩放的问题。
从累加值到输出字节
输出采用 sᵧ = 0.08、zᵧ = −7,因此使用另一把尺子。要把 A 转换为输出刻度,就用其真实值除以 sᵧ。比值 M = sₓs𝑤/sᵧ = 1/4 完成尺度转换。定义 R 为就近舍入,恰好处于中点时远离零取整;clamp 则把值限制到 −128 与 127 之间。我们的运算约定按如下顺序执行:
A = 12 时,MA = 3 已经是整数。加上 −7 后存储 qy = −4。这个负字节代表正结果:ŷ = 0.08 × (−4 + 7) = 0.24。因此编码的符号不等于变量的符号。直接以输出尺度加偏置通常也不等价;这里的偏置属于转换之前的累加器尺度。
处在两个数之间:需要完整规则
现在只把 A 改为 10。舍入前 MA = 2.5,整数 2 与 3 距离相同。我们的规则选 3,而中点取偶数规则选 2,因为它是偶数。加上 zᵧ 后分别得到 −4 和 −5,重建值为 0.24 与 0.16。重新量化前的值是 0.20;两者误差大小均为 0.04,但结果并不相同。
假设决策条件是 ŷ ≥ 0.20,第一种结果会触发,第二种不会。我们没有在数据集上测量准确率损失;这个反例只是证明一个编码单位的差异也可能重要。不存在适合所有网络的普遍最优舍入规则。兼容性问题在于验证与设备使用了不同规则,或忽略决策所需的容差。
顺序也很重要。按我们的规则,R(2.5) − 7 = −4,而 R(2.5 − 7) = R(−4.5) = −5。把加零点移到舍入之前,看似只是实数代数中的无害变形,却改变了中点相对于零的位置。因此公式必须说明在哪一步舍入。若运行时采用其他约定,对比参考就应复现那个约定;本文不声称所有运行时都使用 R。
负数、截断与偏差
去掉小数部分并不总是就近舍入。A = −11 时,MA = −2.75,最近整数是 −3,向零截断得到 −2,向负无穷取整则得到 −3。A = 11 时,向零截断与向负无穷取整都得到 2,而最近整数是 3。并非每种规则的偏差都对称。表中列出加上 zᵧ = −7 后的最终编码;若要解释为真实数值,仍需减去 zᵧ 再乘 0.08。
| A | MA | R:中点远离零 | 中点取偶数 | 向零截断 | 向负无穷 |
|---|---|---|---|---|---|
| -11 | −2.75 | -10 | -10 | -9 | -10 |
| -10 | −2.5 | -10 | -9 | -9 | -10 |
| 0 | 0 | -7 | -7 | -7 | -7 |
| 10 | 2.5 | -4 | -5 | -5 | -5 |
| 11 | 2.75 | -4 | -4 | -5 | -5 |
图中遍历 −16 到 16 的全部整数 A,不发生饱和,绘出重建误差 ŷ − y。点间连线只是辅助阅读,不代表对连续输入进行过测试。向零截断把数值拉向真实零;向负无穷取整只产生非正误差。在这个对称网格上,R 和向零截断的平均误差都是零,但单个误差并不相同,也不能据此证明实际非对称数据上不存在偏差。

代数上等价的两步,却产生不同舍入
设备可能通过整数乘法和位移实现分数乘数。理解风险并不需要模拟某个处理器。另取 M = 3/8,明确比较两种算法:第一种只在末尾舍入;第二种先计算 3A/4 并舍入,再除以二并再次舍入。没有舍入时,3/8 与 (3/4)/2 相同。但 A = 1 时,第一种得到 R(0.375) = 0,第二种得到 R(R(0.75)/2) = R(0.5) = 1。
这不是在指责某个库存在缺陷,而是构造了两种不同的数值约定。有些实现会有意采用不同且经过验证的中间步骤。比较结果时,需要知道乘数、移位量、中间位宽及每一步的舍入规则。只检查最终实数乘数 M 不够。附件计算了 A 从 −16 到 16 的两种算法结果,也包含负值。
能够界定多大的误差?
如果只有一次就近舍入、乘数精确且没有饱和,R(MA) 与 MA 的距离不超过半个整数单位。乘以输出尺度,得到重建值的局部误差界:
这只证明了该步骤的性质,不是相对于原始浮点模型的误差界,因为上游权重、输入、偏置可能已经近似。它也不保证多层网络的最终决策。后续运算可能放大半步误差;若分数接近阈值,这点误差就足以使其越界。分析整个网络需要完整运算链和有代表性的数据。
若设备使用近似乘数 M̂,且没有其他中间舍入,由三角不等式可分离两项:|ŷ − y| ≤ sᵧ(1/2 + |A|·|M̂ − M|),仍以无截幅为前提。第一项来自最终舍入,第二项是乘数误差被累加器放大。因此常数的相对误差小,并不意味着可以忽略 A 的范围。该界不包含溢出和其他隐藏舍入。
饱和会破坏半步误差保证
保持原尺度,把 A 增至 600。真实值为 12,MA = 150,限制前编码为 150 − 7 = 143。INT8 无法容纳它,clamp 返回 127。重建值为 0.08 × (127 + 7) = 10.72,误差 −1.28。这不是舍入误差,而是到达表示边界。0.04 的保证不适用,因为无饱和假设已被破坏。
增大 sᵧ 以扩展范围,也会增大相邻值的间距。因此应结合预期用途的数据,同时选择范围与分辨率。激活函数还可能进一步缩小区间:ReLU 会把负值置零,其零边界编码是 zᵧ,本例为 −7,不一定是 0。数值饱和与激活在概念上不同,即使内核将它们融合执行。
从文献走向设备验证
Jacob 等人在 2018 年工作中描述了仿射量化、偏置尺度与整数流水线,我们阅读了第 2、4 节。其 MobileNet 对比针对 Snapdragon 硬件与明确协议,不能移植为微控制器性能结论。gemmlowp 文档解释累加器转换,LiteRT 规范区分算子类型、范围与零点。这些来源帮助理解机制;本文小例子与其基准测试分开,也不认证与特定版本兼容。
Python 中使用 Fraction,在计算过程中精确保留 1/10、1/5 和 2/25。即使把 0.1 写成浮点数,也可能引入二进制近似,而这里不需要这个额外变量。away 函数通过整数算术对绝对值就近取整,再恢复符号;requant 依次执行乘数、舍入、零点与饱和。断言检查完整例子、中点、双重舍入,以及整个网格上的半步误差界。数据确定,不需要随机种子。
L 个输入的点积需要 O(L) 次操作;转换 N 个现有累加器需要 O(N) 个步骤。脚本内存随保存行数增长,但这不是固件峰值 RAM 测量。Python 使用任意精度整数,嵌入式实现必须明确中间乘积位宽、合法移位、饱和及类型转换。逐行翻译教学代码,并不能证明编译器会保留所有性质。
建议的设备验证从共享参考向量开始:真实零、负数、中点、饱和边界以及不同尺度的通道。记录转换后模型、转换器版本、运行时、内核、编译器与选项。若不要求逐位一致,就明确逐算子容差,并单独评估对决策的影响。确认正确性后,再按明确硬件协议测量平均与分位延迟、峰值 RAM、功率及每次推理能量。这些测试尚未执行,本文计算没有产生任何此类测量。
答案:检查字节代表的含义
正确的 INT32 累加并不能独自保证两个系统产生等价的 INT8 输出。必须保留尺度与零点,明确中点规则,遵守运算顺序,并区分舍入与饱和。本例中同一个累加器 10 因规则不同而重建为 0.24 或 0.16;A = 600 时,格式边界导致更大误差。将 AI 部署到小型设备时,首先应核查模型究竟执行了什么数值运算,然后再问它运行多快。
来源与可复现性
Google gemmlowp, Building a quantization paradigm from first principles.
Google LiteRT, 8-bit quantization specification.
from fractions import Fraction
from experiment import requant, run
print(requant(10), requant(10, rounder=round))
print(requant(600))
print(next(r for r in run()['double_rounding'] if r['a'] == 1))
代码、数据与说明 · JSON. 教学计算使用 Python 3.14.0,图使用 Matplotlib 3.11.2。分析由 AI 辅助,不声称经过同行评审或人工审核。原创 ImageGen 封面仅作示意,不记录 EL-AI 人员、场所或实际安装。来源查阅于 2026 年 10 月 4 日。

