权重量化方案的选择与算子命中定位

在 **** 的 ****(8 卡,单卡 64GB HBM,卡间互联 392 GB/s)上用 vLLM 起一个 OpenAI 兼容服务,把模型换成 W4A16 量化版。权重文件明显变小,启动日志里权重占用那一行随之下降,服务正常启动、请求正常返回。改动只落在量化方案这一项,精度抽测却出现偏差超阈值:同一批题目的答案与 BF16 版本对不上。另一组配置精度几乎没掉,吞吐却与量化前持平、尾延迟更长,量化没有产生收益。这两类现象要在同一处解释:先确认量化方案的实际位宽与格式,再确认量化 kernel 是否命中目标算子路径,最后把收益与代价分开记录。

方案标记的含义:权重位宽与激活位宽

一个量化标记由两段组成,W 后的数字是权重位宽,A 后的数字是激活位宽。W8A8 指权重与激活都量化到 8 bit;W4A16 指权重压到 4 bit、激活保留 16 bit,即 weight-only 量化。INT8 与 FP8 描述低位的具体格式:前者是整数格式,后者是浮点格式,FP8 内部还分 e4m3 与 e5m2 两种位分配,e4m3 阶码短、尾数长,利于精度,e5m2 动态范围更大。

标记 权重 激活 量化对象 常见来源
W8A8 8 bit(整数或浮点) 8 bit(整数或浮点) 权重与激活 训练后量化,激活需校准
W8A16 8 bit 16 bit 仅权重 GPTQ / AWQ
W4A16 4 bit 16 bit 仅权重 GPTQ / AWQ
W4A8 4 bit 8 bit 权重与激活 训练后量化
INT8 8 bit 整数 视方案 视方案 训练后量化
FP8 8 bit 浮点(e4m3 / e5m2) 视方案 视方案 原生低精度 / 训练后量化

量化产物的命名习惯里,位宽标记是文件名的一部分,例如 w8a16_gptq、w4a16-awq、w8a8c8、w4a16c8。名称可能带 W、A 之外的第三段,读方案时以完整名称为准,不能只看前两段。GPTQ、AWQ 属于 weight-only,作用面在权重一侧;SmoothQuant 一类方法在量化前对激活做等价缩放,把离群值从激活转移到权重,用来降低 W8A8 的激活量化误差。

校准:静态量化与动态量化的取舍

权重的数值范围在转换后固定,可以离线量化;激活的范围随输入变化,需要一组数据来估计,这一步即校准。两种做法的取舍如下:

做法 校准集 精度 额外步骤
动态量化 不需要 损失较大 无
静态量化 需要少量代表性数据 损失更小 需准备并维护校准集

静态量化用校准集预先算出激活的量化参数,精度损失更小,代价是校准集必须与线上分布接近;校准集偏了,激活误差就偏大,权重一侧的误差反而可控。校准方法本身也是一项选项(如 min_max 与 kl_divergence),换方法等价于换一组量化参数,需重新走一遍精度对照。只量化权重的 W4A16、W8A16 不需要激活校准,省去这一步,代价是激活仍按 16 bit 参与计算,收益只落在权重一侧。

收益与代价的来源

收益来自两处。权重占用按下式估算:

权重占用 ≈ 参数量 × 每参数字节数 ÷ tensor_parallel_size
每参数字节数:FP16 / BF16 = 2,INT8 / FP8 = 1,INT4 = 0.5

以 7B 级模型按上式估算(非实测):FP16 约 14 GB,INT8 约 7 GB,INT4 约 3.5 GB。decode 阶段每生成一个 token 要把参与该步的权重读一遍,当这一阶段受显存带宽约束时,每步读取的字节数随方案下降,带宽压力随之下降。这是量化在低并发、访存受限场景下的主要收益来源。

代价来自两处。一是量化误差:低位格式能表示的数值集合更小,权重与激活被截断,误差随位宽下降而上升。二是反量化开销:低位权重在参与计算前要还原到高位,若量化 kernel 未把这一步融合进矩阵乘,反量化会引入额外的指令与访存,抵消量化节省的一部分带宽。

量化 kernel 与算子路径的兼容性

量化配置能被解析、服务能启动,不等于量化算子真的被命中。量化后精度尚可而收益不成立甚至变差,多半是目标量化 kernel 没有命中,退回了一条未融合的通用实现。回退不报错,只在启动日志里留一行后端选择结果,判断方式与通用的静默回退一致:比对 target_backend 与 resolved_backend,两者不一致即发生回退;这两个是本笔记自设的记录字段,真实凭证是启动日志里那一行后端选择(vLLM 形如 Using <backend> backend ...)。

两类兼容约束最容易撞上。

触发条件

负载 / 配置形态 现象
低并发、decode 受带宽约束 量化收益最容易兑现
高并发、算力已打满 带宽不再是瓶颈,量化收益收窄
校准集与线上分布不一致 权重误差可控,激活误差偏大,精度抽测偏差超阈值
量化方案只覆盖 dense、未覆盖 MoE 权重占用下降,吞吐无变化
KV dtype 与量化方案叠加、后端不兼容 attention 路径回退,收益不成立

现象

三类结论可分开记录:量化后精度跌落过大、量化后收益不成立、量化配置不可落地。三者的处置方向不同,混写成「量化效果一般」会丢掉定位线索。

定位步骤

  1. 固定校准集。记录 calibration_source,同一组校准数据用于量化前后对照;校准集与线上分布不一致时,先换一组再判断精度偏差。
  2. 逐层比对。取若干层,比对量化前后同一输入的中间结果与权重张量,定位误差是均匀分布还是集中在少数层。
  3. 确认算子命中。比对 target_backend 与 resolved_backend,确认量化 kernel 真的落在目标算子路径上;未命中时先修 guard(KV dtype、block size、shape),再谈收益。
  4. 收益与精度分开记录。按 throughput_before / throughput_after 与 accuracy_before / accuracy_after 两组数分别统计,量化流程走通不等于量化收益成立。
# 示意:一条带量化方案的启动命令(参数名取自框架,取值按本机规格填)
vllm serve <quantized-model-path> \
  --tensor-parallel-size 8 \
  --quantization <scheme> \
  --kv-cache-dtype auto \
  --attention-backend FLASH_ATTN \
  --enforce-eager

处置

方向 动作 代价 / 前提
精度偏差超阈值 提高位宽(W4A16 → W8A16 → W8A8),或改用 FP8 占用上升,收益收窄
激活误差偏大 换校准集,或改用 SmoothQuant 一类先做等价缩放的方法 需重做校准
收益不成立 确认量化 kernel 命中;修 guard(KV dtype、block size) 未命中时调参无用
MoE 无收益 确认方案是否覆盖专家层 覆盖不到时该路径维持未量化

记录字段

# 一条量化记录(字段名示意)
hardware_sku:              # 卡型(8 卡,单卡 64GB HBM,互联 392 GB/s)
base_stack_version:        # 基础软件栈 / 运行时版本,写 <pinned-version>
model_name:
model_format:
quant_scheme:              # W8A8 / W4A16 / W8A16 / INT8 / FP8 ...
calibration_source:        # 校准集来源与规模
weight_dtype:              # 量化后权重 dtype
activation_dtype:          # 量化后激活 dtype
accuracy_before:           # 量化前精度(同一评测任务)
accuracy_after:            # 量化后精度
accuracy_loss_threshold:   # 精度损失阈值,超过即判量化失败
throughput_before:         # 量化前吞吐
throughput_after:          # 量化后吞吐
operator_path:
  target_backend:          # 目标算子路径
  resolved_backend:        # 实际命中路径
  fallback_observed:       # 是否回退到未量化实现
status:                    # 流程走通 / 收益成立 / 收益不成立 / 精度不达标

小结

量化方案的选择由三段信息决定:W 与 A 两段给出权重与激活的位宽,收益来自权重字节数与带宽压力的下降,代价来自量化误差与反量化开销。量化配置能被解析不等于量化 kernel 命中目标算子路径,收益不成立时先比对 target_backend 与 resolved_backend。精度偏差超阈值时先固定校准集、逐层比对,再决定是否提高位宽。