主动改变函数,先声明近似目标
前面的缓存要求与参考模型表示同一个函数,量化则把部分实数权重替换为有限网格上的近似值。它的验收不能只写“程序能跑”或“输出形状没变”。本章先在标准库中定义一个确定的舍入映射,再在冻结模型副本上观察 logits、分布和损失变化。项目没有实现整数矩阵乘法,没有将整数打包后保存,也没有减少实际浮点参数存储。它是用于隔离误差来源的伪量化实验,不能据其推导真实量化内核的速度。
令位宽为 k,使用对称整数集合 ,其中 。这故意不用有符号整数最负的一个编码,以便正负对称。给一组有限权重 ,峰值为 ,若 a 非零则尺度 ,整数 是 的最近整数并限制在范围内,恢复值为 。全零组约定尺度一、整数全零,避免除零。Python round 与 PyTorch round 对精确半整数采用偶数方向,本项目两端遵守同一约定。
在不发生超范围截断时,最近舍入意味着 ,乘上正尺度得到 。峰值定义的尺度让本组原始有限值恰好落在范围内,因此训练后静态权重可满足该界。若尺度来自校准范围而未来激活越界,clip 引入额外误差,这个半步长界就不再覆盖全部输入。必须在推导处写清前提,不能用权重固定范围的界承诺任意激活值都安全。
完整算例一:舍入与离群值
对一组权重零、一半、一,使用三位对称范围负三到三,尺度为三分之一。零映射为零,一映射为三并精确恢复;一半除以尺度为一点五,按偶数规则舍入到二,恢复为三分之二,误差六分之一,恰好达到半步长。此处最大误差界是实数近似界,而二进制浮点实现还会有微小表示误差,测试应允许机器精度量级的容差。位宽越小步长越大,这是局部误差变化,不直接等于全模型损失变化。
再看八位组零点一、零点二、一百,峰值一百,尺度约零点七八七。前两个数除以尺度都不到半,因而同时舍入为零;大值一百却能精确恢复。最大绝对误差仅零点二,若只看一百的相对误差会误以为全部很好。小权重可能对应关键方向,不能因为它绝对值小就断言不重要。将不相同范围的输出通道分开定尺度,可以避免某一通道的大值把所有通道的分辨率拖低,但同一通道内的离群值仍存在。
def quantize(xs,bits):
qmax=2**(bits-1)-1
scale=max(map(abs,xs))/qmax if any(xs) else 1.
qs=[max(-qmax,min(qmax,round(x/scale))) for x in xs]
ys=[q*scale for q in qs]
assert max(abs(x-y) for x,y in zip(xs,ys))<=scale/2+1e-12
return scale,qs,ys
s,q,y=quantize([0.,.5,1.],3)
assert q==[0,2,3]
assert quantize([.1,.2,100.],8)[1]==[0,0,127]
assert quantize([0.,0.],4)==(1.,[0,0],[0.,0.])
print(s,y)从权重误差推到线性输出误差
设线性层某个输出为 ,输入固定、偏置不量化,则输出误差是 。由三角不等式得 。若该输出行使用共同尺度 ,且各项无截断,进一步有 。输入幅度会放大权重误差;经过多层非线性、归一化和残差后,局部界不能不加条件地直接相加为紧的端到端界。
例如某输出通道尺度零点零二,输入为三、负四,则界为零点零一乘七,即零点零七。若两项权重误差分别零点零零五、负零点零一,实际输出误差为零点零一五加零点零四,即零点零五五,低于该界。这个完整小例子同时说明符号可以同向累积,也可以抵消;平均权重误差接近零不能保证某特定输入的输出误差小。校准数据应覆盖真实输入范围,而本实验的随机词元只验证当前固定数值函数。
完整算例二:四位和八位的实际模型对照
项目逐输出通道量化所有 Linear 的权重,包括 Q、K、V、O、前馈和词表头;嵌入、归一化、偏置保持原样。恢复值仍写回双精度模型副本。固定二十四词元、批量二的相邻预测共有四十六个有效位置。八位最大 logit 差约零点零零三五九五,平均正向 KL 约 ,四十六位置 argmax 全相同。四位最大差约零点零五三三四四,平均 KL 约零点零零零一一六三,argmax 一致率四十除以四十六,约百分之八十六点九六。
同次实验参考负对数似然为三点四六二一四七;八位约三点四六二零二二,四位约三点四五八四九五。四位在这串随机目标上的损失反而略低,不表示模型更懂语言,也不表示更低位宽一般更好。随机初始化没有训练语义,随机目标还可能偶然偏爱任意扰动。应将这些数字解释为近似函数的可复跑差异,若要评价语言质量,需要冻结训练好的检查点、独立真实文档、相同词表与评分协议,并报告不确定性。
from lab import make_model,tokens,quant_report,state_hash
model=make_model();before=state_hash(model)
for bits in [8,4]:
report=quant_report(model,tokens(24,2),bits)
assert report['ledger']['actual_storage_unchanged']
print(bits,report['maximum_logit_error'],report['argmax_agreement'])
assert state_hash(model)==before不能把理想位数当作实际节省
本实验共近似二万五千五百六十八个 Linear 权重,有六百零七个输出通道尺度。若真按八位打包,理想权重载荷二万五千五百六十八字节;若四位紧密打包则一万二千七百八十四字节。假设每个尺度以四字节保存,还需二千四百二十八字节,另外未量化参数、对齐和容器都没有包含。当前代码没有打包:实际权重仍是每个八字节,尺度只用于生成恢复值。故报告 ideal_packed_bytes 是条件账本,actual_storage_unchanged 才描述本实现事实。
真实低位内核必须处理整数累加、尺度应用、可能的零点、分组与对齐,并证明没有溢出或错误广播。权重量化、激活量化、KV 量化又是不同位置的改变;本项目仅覆盖第一类的舍入恢复。SmoothQuant 和 GPTQ 分别提供更复杂的校准或补偿机制,属于原论文讨论的独立方法,本课程未实现它们,也不把简单逐行舍入冠以其名称。引用论文是说明深入方向和机制出处,不是借用它们在其他模型与硬件上的实验成绩。
第一类验证是局部数学性质:全零恢复、整数范围、无截断半步长界以及局部线性误差界。第二类是端到端函数检查:原模型哈希不变、候选确实改变、同输入的分布差异、目标损失与决策变化。局部界通过不能替代端到端检查;argmax 一致也不能证明概率校准相同。下一章将建立这几类证据如何共同组成验收门槛,并用更小的决策间隔反例说明“看起来一样”仍然不足。
对称映射还隐含了零点为零的语义。若整数乘法想直接代替浮点乘法,在只有权重量化且输入浮点时,需要将每个输出通道的累加结果按相应尺度恢复;若输入也量化,尺度变成输入尺度与权重尺度的乘积。非零零点则要求在累加中校正偏移项。不同算子与硬件对这些步骤的融合能力不同,因此理论载荷下降并不自动产生低延迟。当前实验刻意在前向前恢复浮点值,使误差研究不混入内核差异,代价是没有真实存储和计算节省。
还可从通道轴检查错误实现:PyTorch Linear 的 weight 形状为输出宽乘输入宽,所以每输出通道应沿输入轴求峰值,尺度形状为输出宽乘一。若沿输出轴求峰值,得到的是每输入通道尺度,仍然能广播运行,却变成另一个算法。应以一个各行范围明显不同的小矩阵检查,例如第一行峰值一、第二行峰值一百,确认两行步长不同。测试形状成功并不足以证明粒度正确;这类轴语义应随公式写进代码审查。
练习与项目增量
TRY IT YOURSELF
基础:尺度和误差界
四位对称量化一组峰值1.4的权重,qmax、尺度与无截断最大舍入误差界各是多少?
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
进阶:局部线性界
某行尺度0.04,输入(2,−3,5)。只量化权重时输出误差的保守界是什么?
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
挑战:存储与能力两重错误
同学从本项目四位载荷12784字节且NLL下降,写“已实现小四倍、质量更好的中文模型”。逐项改写。
提示 1
提示 2
对照参考解答
本章增量是 symmetric、pseudoquant 与 quant_report,交付中保留八位和四位的完整层级误差账本。没有提交一个以“量化”名义实际仍等价的空操作,也没有把浮点恢复方案的速度当成整数核优势。后继章节沿用同一模型哈希和相同输入,分别讨论正确性门槛与性能统计。