量化点积里最容易写漏的一项是看起来无关紧要的常数项。设实数近似为xsx*(qx-zx)、wsw*(qw-zw)沿长度 K 做点积展开后最后一项是K*zx*zw。没有 K只在某些零点恰好为零的例子里看不出问题。对称量化通常让零点为零从而减少修正项这不意味着所有 INT8 文件都采用对称约定。动态量化还涉及按 token、按通道还是按张量求尺度不能把一种选择写成通用定义。SmoothQuant 论文讨论的是通过等价缩放处理激活离群值它并未允许实现忽略量化参数。对称量化围绕零点分桶尺度决定重建间隔。图右的 tail 指分布尾部的离群值它与下文向量循环不足一个宽度的尾部是两种不同概念。def expanded_dot(qx,qw,zx,zw): if len(qx)!len(qw): raise ValueError(length mismatch) klen(qx) return (sum(x*w for x,w in zip(qx,qw)) -zx*sum(qw)-zw*sum(qx)k*zx*zw) qx[-2,0,3,7,1]; qw[5,-1,2,4,0] zx,zw2,-3 directsum((x-zx)*(w-zw) for x,w in zip(qx,qw)) assert expanded_dot(qx,qw,zx,zw)direct assert expanded_dot([],[],zx,zw)0 print(zero-point correction: nonzero offsets and empty dot passed)尾部和溢出同样属于量化实现向量循环每次处理 8 或 16 个元素剩余元素仍然存在。补零要补的是量化意义上的实数零在非对称编码下填充码通常应是零点直接填整数字节零未必等价。累加类型也不能只看输入是 INT8。极端情况下K 个乘积的绝对和会随 K 增长。需要根据范围证明 INT32 是否足够或者分段扩大累加。Python 的整数不会溢出这正适合做参考但不能据此声称 C INT32 内核安全。动态求尺度时整行全零要单独处理。尺度可以约定为 1并令量化值为零含 NaN、Inf 的输入应明确拒绝或走专门策略。舍入方式与饱和范围也要成为契约的一部分。把展开式按一组反例检查非零零点是最有区分力的输入。若 zx 或 zw 为零最后的常数项会消失漏 K 的错误也会隐藏。上面的测试让两个零点都非零再比较直接去零点相乘和展开求和。这比随机挑一组大矩阵更容易说明每一项修正来自哪里。对分组或按通道尺度展开式需要在共享同一组参数的范围内应用。若一个点积跨过两个尺度不同的组就不能先把全部整数乘积合成一个总数最后只乘一对尺度。应分别恢复各组贡献再累加或者采用经过证明的等价组织方式。尺度索引与零点索引应一起走过组边界。量化范围也不是越宽越好。对称约定常选择围绕零的范围以便正负两侧规则一致是否使用最小的那个负码字需要由格式明确说明。不能在打包阶段使用一种范围反量化阶段又假定另一种。相同字节在两套约定下可能只差少量数值模型输出却会持续偏移。校验时可以分三层先验证整数去零点的代数再比较带尺度的浮点重建最后评估真实模型。每一层都保留明确误差和输入。若第一层失败继续调校准数据没有意义若前两层通过而模型质量下降才应进一步检查裁剪、尺度粒度和模型敏感层。输出重标定是下一条独立边界。整数累加恢复成浮点后若还要再量化到低精度就会有新的尺度、舍入和饱和。不能因为输入量化已经验证过就省掉输出端的检查也不能把两次舍入的误差只记成一次。保留浮点观察点有助于把每一步误差拆开解释。点积修正通过只说明整数代数对上了。误差还来自裁剪、尺度估计和模型对离群值的敏感度。需要真实数据集、固定 tokenizer 和评估配置才能讨论困惑度或任务分数。这里没有加载 Llama、没有测 GPU因此不报告精度无损或吞吐倍数。
阅读完成 · 觉得有帮助?