资讯动态

135M 权重实测:三值量化误差 60%,int8 只有 3.7%,差距在尺度

发布时间:2026/10/8 18:46:59 来源:尧图企业网站定制
把一份现成的 fp16 权重直接压成三值-1 / 0 / 1重构误差是 60%而 int8 分组量化只有 0.8%。两个数字放在一起看容易得出三值不靠谱的结论但这个比较其实不公平官方那批 1.58 bit 模型是从预训练就在用三值权重后处理直接压和它不是一个场景。下面这份数据是给想在自己权重上试三值的人看的——误差打哪来、把尺度定细一点能挽回多少、存下来的体积差几倍。1.58 bit 这个数字的来历三值权重的取值只有三种均匀分布下的信息熵是 log2(3) ≈ 1.585 bit这就是1.58 bit的来源真正落盘时按 2 bit 打包所以宣传里的 1.58 是信息量下限不是文件大小。论文《The Era of 1-bit LLMs: All Large Language Models are in 1.58 Bits》给的量化式很朴素先把权重矩阵按平均绝对值缩放再四舍五入到 {-1, 0, 1}即 W̃ RoundClip(W / (γε), -1, 1)γ 取权重矩阵的平均绝对值。换句话说三值量化除了权重本身还要保存一个尺度。官方推理框架 microsoft/BitNet 在仓库里给了加速数字x86 CPU 上相比同尺寸 fp16 模型快 2.37 到 6.17 倍、能耗降 71.9% 到 82.2%ARM 上是 1.37 到 5.07 倍、能耗降 55.4% 到 70.0%100B 的三值模型能在单颗 CPU 上跑到 5 到 7 token/s。这些数字的前提都是模型本身就是三值训练的不是把 fp16 模型压出来的。先解决读权重这一步我测的对象是 HuggingFaceTB 的 SmolLM2-135M单文件 safetensors134.5M 参数BF16。safetensors 的格式简单到不需要装任何库开头 8 字节是 JSON 头长度接下来是头的 JSON里面每个张量带 dtype、shape 和一对 data_offsets数据区紧跟头之后。唯一要自己处理的是 BF16——numpy 没有这个 dtype把 uint16 左移 16 位再按 float32 解读即可。importjsonimportnumpyasnp pathmodel.safetensorswithopen(path,rb)asf:nint.from_bytes(f.read(8),little)# 头 8 字节 JSON 头长度hdrjson.loads(f.read(n))base8n# 数据区起点infohdr[model.layers.0.self_attn.q_proj.weight]f.seek(baseinfo[data_offsets][0])rawf.read(info[data_offsets][1]-info[data_offsets][0])unp.frombuffer(raw,dtypenp.uint16).astype(np.uint32)16# BF16 - FP32wu.view(np.float32).reshape(info[shape])print(w.shape,w.dtype,float(np.abs(w).max()))运行结果safetensors 头部条目: 272 | 张量 dtype: [BF16] | 元数据: [format] 张量数: 272 | 参数总量: 134515008 (134.5 M) 文件大小: 256.6 MiB 头部大小: 30528 字节六种量化方案的真实误差四个张量各跑一遍embed_tokens49152×576、第 0 层的 q_proj、第 15 层的 down_proj 和 gate_proj。误差口径是相对 Frobenius 范数 ‖W−Q‖/‖W‖越低越好。方案每权重位数embed_tokensq_projdown_projgate_proj平均三值·整张量尺度20.61500.66750.55780.57250.6032三值·按输出行尺度20.60250.60810.54100.53260.5710三值·分组 6420.59760.60300.53610.52810.5662三值·分组 1620.57770.58400.52070.51360.5490int8·整张量尺度80.03360.04210.03800.03600.0374int8·分组 12880.01040.00830.00740.00710.0083三值的误差在 55% 到 67% 之间int8 是 0.7% 到 4.2%差两个量级。尺度定得越细三值确实越好但收益不大从整张量换到按输出行平均误差只从 0.6032 降到 0.5710。顺手统计了量化后的码字分布这个能验证 1.58 bit 是不是虚的张量尺度方式归零比例饱和到 ±1 的比例embed_tokens整张量33.8%66.2%embed_tokens按输出行33.2%66.8%q_proj整张量42.5%57.5%q_proj按输出行39.9%60.1%归零比例在 33% 到 43%接近均匀三值分布的 1/3所以按 2 bit 打包没有浪费多少熵口径的 1.58 bit 是实的。换成输出误差看再算体积账重构误差只说明权重被改了真正影响结果的是矩阵乘。让 X 是随机激活Y XWᵀ再插一组离群激活随机挑千分之一通道放大 30 倍模拟大模型里常见的大通道张量方案普通激活带离群激活q_proj三值·整张量0.66910.6037q_proj三值·分组 160.58440.5408q_projint8·整张量0.04230.0474down_proj三值·整张量0.55700.5461down_proj三值·分组 160.52050.5133down_projint8·整张量0.03810.0371离群激活把 int8 整张量的输出误差从 0.0423 推到 0.0474涨了 12%三值几乎不动不是它抗离群是它本来就已经错了 60%离群那点扰动占不上比重。想抗离群方向是 int8 里把大通道单独拎出来算跟三值无关。体积这块容易被忽略尺度也要存而且是 fp16 存。按 134515008 个参数算存法权重位数尺度额外位数合计体积fp16160.00016.000257 MiBint8·整张量80.0008.000128 MiBint8·分组 12880.1258.125130 MiB三值·整张量20.0002.00032 MiB三值·按输出行20.0282.02833 MiB三值·分组 6420.2502.25036 MiB三值·分组 1621.0003.00048 MiB三值分组 16 把误差从 0.6032 压到 0.5490相对改善 9%代价是每组一个 fp16 尺度每权重从 2.0 bit 涨到 3.0 bit体积从 32 MiB 变 48 MiB。分组 64 是折中0.5662、2.25 bit、36 MiB。我怎么用它先摆结论三值不是后训练量化方案。现成 fp16 模型直接压三值误差 60%这不是无损官方的无损前提是训练时就用三值权重——想走这条路准备的是重训预算不是量化脚本。只想过省内存又不想重训我会直接上 int8 分组 128误差 0.83%、体积 130 MiB差不多是 fp16 的一半代价几乎可以忽略。真要压到三分之一以下才轮到三值出场。尺度粒度要先算账再动手。按输出行定尺度只多 0.028 bit/权重误差降 3.2 个百分点接近白拿分组 16 的重构误差比按行好 4%但要付 1 bit/权重体积多 45%。我自己的判断是分组 64 到头再细不划算。踩到的坑有两个。一是 embed_tokens 是这批张量里误差最高的0.6150分组 16 之后 0.5777而且它的量化噪声走的是整行查表的路子直接进残差真要用三值我会单独给它更细的分组或者干脆留着不量化。二是 BF16 的头里还有个__metadata__键它没有 dtype 字段我第一次遍历头部时直接 KeyError顺手把它跳过就行。至于量化耗时2 核机器上 embed_tokens 的 2831 万权重三值 0.90 秒、int8 0.94 秒——量化本身从来不是瓶颈。要落地的话就三件事从自己模型里抽 q_proj 和 down_proj 各一个张量跑一遍整张量 absmean 和按行 absmean误差降不到 5% 就别上分组只求省内存先上 int8 分组 128算体积时把尺度位数一起算进去分组 16 是三值权重的 3 bit不是 2 bit。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑