大模型量化到底在做什么?——从浮点数原理到 Qwen FP8 落地的完整拆解

技术

大模型量化到底在做什么?——从浮点数原理到 Qwen FP8 落地的完整拆解

如果你部署过大模型,第一个拦路虎通常不是”算不算得动”,而是放不放得下、搬不搬得快。一个 27B 参数的模型,权重以 BF16 存储,每个参数占 2 Byte,光是权重本体就要 27B × 2 ≈ 54 GB 显存——这还没算 KV Cache、激活值、运行时碎片。一张 80GB 的卡,一个模型就吃掉大半,batch 稍大一点、上下文稍长一点,直接 OOM。

量化的本质,就是在这个残酷算术面前,给大模型找一条活路。


一、量化的核心矛盾:用精度换生存空间

量化解决的是一个非常工程化的矛盾:

  • 降低权重常驻显存:用更少的 bit 存参数;
  • 减少显存带宽压力:搬运 8-bit 数据天生比搬运 16-bit 快;
  • 在精度可接受的前提下,让同一张卡扛更大的 batch、更长的上下文、更大的模型。

它不是为了把模型文件”压缩小一点”给人看,而是大模型部署里绕不开的工程手段——把一部分数值精度让出来,换取真实的显存预算和推理吞吐。


二、一切的起点:浮点数在计算机里怎么存的

要理解量化,得先理解浮点数的三段结构。任何浮点数本质上就是二进制版的科学记数法,由三部分组成:

value = (-1)^sign × 2^(exponent − bias) × 1.fraction

  • 符号位(sign):正负而已,1 bit 搞定。
  • 指数位(exponent):决定动态范围——这个数能表示多大、多小的量级。
  • 尾数位(fraction / mantissa):决定有效精度——小数点后面能保留几位有效数字。

指数之所以要加一个偏移量(bias,如 8 位指数的 bias=127),是因为真实指数可正可负,而硬件更喜欢把指数当无符号整数来存和比较。整体向右平移 bias,负指数、零、正指数就都能塞进同一个无符号字段里了。

尾数的巧妙之处在于hidden bit(隐藏位):规格化后的二进制浮点数总是 1.xxxxx × 2^n 的形式,那个前面的 1. 几乎是固定的,所以硬件干脆不存,只存 xxxxx 部分,读的时候再补回来——省了 1 bit,却是最值钱的 1 bit。

这一点至关重要: 当你砍指数位,主要损失的是动态范围(更容易溢出/下溢);当你砍尾数位,主要损失的是精度(数值变”糙”)。两类损失性质不同,这也是 BF16 和 FP16 虽然同为 16-bit,工程含义却天差地别的原因。


三、FP32 / FP16 / BF16 / FP8 到底差在哪

把它们摆在一起,差异一目了然:

格式 位宽 符号/指数/尾数 1B 参数占用 核心特点
FP32 32 bit 1 / 8 / 23 ~4 GB 范围大、精度高、最稳——训练基准
FP16 16 bit 1 / 5 / 10 ~2 GB 精度还行,但指数位少,容易溢出
BF16 16 bit 1 / 8 / 7 ~2 GB 指数位与 FP32 同款,范围稳,精度粗——训练默认
FP8-E4M3 8 bit 1 / 4 / 3 ~1 GB 精度略好、范围较小,适合前向权重/激活
FP8-E5M2 8 bit 1 / 5 / 2 ~1 GB 范围更大、精度更低,适合梯度等”范围优先”场景

一句话总结 BF16 为什么赢下训练时代:它用 16 bit 偷到了接近 FP32 的动态范围。 对大模型而言,数值”不崩掉、不下溢”往往比小数点后很多位更重要。

FP8 则是把压缩再推一层: 单个数压到 8 bit,权重存储和带宽压力直接再减半。代价也同样明确——指数和尾数都更少了,动态范围和精度更脆弱,所以 FP8 高度依赖缩放因子(scale)、量化粒度和混合精度设计,绝不可能粗暴地”把所有东西一刀切到 8 bit”。


四、量化到底在做什么:不是改 dtype,是重建数值表达

量化准确的描述不是”把 BF16 改成 INT8/FP8”,而是三步:

  1. 决定压谁——权重(最常规、可离线)、激活值(动态、需运行时 scale)、KV Cache(长上下文大户);
  2. 选格式 + 算 scale——把原始数值范围映射到低位格式能表达的空间;
  3. 推理时用低精度存储、高精度累加完成计算。

4.1 scale 与量化粒度是灵魂

对 INT 量化,核心公式是 x_q = round(x / s),还原时 x ≈ x_q × s,s 就是缩放因子。

粒度决定了误差的去向:

  • per-tensor:整个张量一个 scale,最简单但误差最大(分布不均匀时吃亏);
  • per-channel / per-group / per-block:分块各自算 scale,更精细,误差更小,但元数据更多、内核更复杂。

现代 FP8 实践普遍采用 block-wise 量化(如 128×128 分块),因为它介于”太粗”和”太碎”之间,既稳住误差,又能对齐高性能 kernel。

4.2 PTQ vs. QAT:你要的是部署还是极致

  • PTQ(Post-Training Quantization):训练完再量化,不需要重训底座模型,先把高精度权重扫描一遍、算 scale、存出量化检查点——部署工程师的日常。
  • QAT(Quantization-Aware Training):训练时就模拟量化误差,让模型提前适应低精度,效果更好更稳,但成本更高,更像训练工程。

大多数团队的正确顺序是:先跑高精度基线 → 试官方量化版/在线量化 → 只有在确有需要时,才做离线 PTQ 或 QAT。


五、Qwen3.5-27B-FP8:一个 FP8 落地范本

Qwen/Qwen3.5-27B-FP8 不是一个”全程 FP8 训练”的模型,而是训练完成后通过 PTQ 产出的推理检查点,官方写明采用 fine-grained FP8 quantization,block size = 128

关键信息解读:

  • 它不是粗粒度量化:整个权重矩阵不共用一个 scale,而是按 128×128 分 block,每个 block 配自己的 scale。以一个 [5120, 17408] 的 Linear 层为例,block 数约为 40 × 136,远比”一个 scale 管全场”精细。
  • weight_scale_inv 等元数据就是反量化线索:权重本体以 FP8 存(省显存、省带宽),推理时 kernel 用每块配套的 scale 把数值映射回合适空间,累加仍在更高精度(FP16/BF16/FP32)里做——所以 FP8 推理的真实图景是”低精度存储搬运 + 高精度关键计算”。
  • 混合精度保质量:像 embed_tokenslm_head、LayerNorm 这类对输出分布敏感的层,参数占比未必大,但影响直接,通常保留 BF16;真正被压的是那些参数量大、矩阵乘法密集、对轻微误差相对不敏感的 Linear 层——FP8 的收益主要来自这里。

六、实操路线:你该从哪条路入手

路线 何时选 要点
直接用官方量化版 官方有 FP8/GPTQ/AWQ/GGUF 时 最省心,先用 vLLM 起基线:vllm serve Qwen/Qwen3.5-27B-FP8
在线量化 只有 BF16 模型,想快速试 vllm serve ... --quantization fp8_per_block,部署时临时压,方便但非最优终态
离线 PTQ 长期部署、要可控可复现 用 llmcompressor 等工具离线跑一遍,产出量化检查点再上线;关键选择是 targets / scheme / ignore / 校准数据

文章给出的 llmcompressor 最小脚本也讲清了核心决策点:量化作用到 "Linear"(大头在这),scheme 选 FP8_BLOCKignore 放过 lm_headembed_tokens,再跑一次 sanity generation 验证没崩——整个流程几十行代码,但背后每一行都对应一个工程判断。


七、总结

量化不是玄学,也不是”把精度滑块往左拉一拉”——它是一次对数值表达方式的重新设计:选什么格式(FP8-E4M3 还是 E5M2?INT8 还是 INT4?)、scale 怎么算(per-tensor 还是 block-wise?)、哪些层保留高精度(混合精度策略)、推理 kernel 怎么对齐(低精度搬运 + 高精度累加)。

对绝大多数团队而言,最稳的策略始终是同一句话:先跑高精度基线建立参照,再用官方量化版或在线量化验证收益,最后只在必要时做离线 PTQ——不要迷信最低 bit,能在质量、显存、吞吐和硬件兼容之间拿到平衡,才是好的量化方案。

京ICP备2026025110号-1