先定位到量化基础的哪一层
推理量化基础定义 、granularity、weight-only 与 W8A8;本页只解释几类方法如何选择/变换这些表示,不把品牌名当统一格式。
| 方法 | 主要对象 | 核心思想 | 推理时仍需什么 |
|---|---|---|---|
| GPTQ | 低比特 weight-only,常见 INT4 | 用 calibration/Hessian 近似按列/块量化并补偿后续权重误差 | packed weights、group scales/zeros、匹配 GEMM/GEMV kernel |
| AWQ | 低比特 weight-only | 用 activation 统计识别 salient weight channels,并搜索缩放使它们更耐量化 | 缩放后的 packed checkpoint + 支持对应 layout 的 kernel |
| SmoothQuant | W8A8 activation+weight | 等价通道缩放,把 activation outlier 难度迁移到 weights | activation quantization、INT8 kernel、校准 scale 与模型变换 |
| FP8 | 权重/activation/KV 的低精度浮点格式族 | E4M3/E5M2 等 exponent/mantissa tradeoff,配合 tensor/channel scaling 与更高精度累加 | GPU/backend 格式支持、scale recipe、amax history/校准、accumulator |
SmoothQuant:用等价缩放迁移 outlier
线性层:
对 hidden/input channel 选择正 scale :
则未量化时:
SmoothQuant 常由 calibration activation max 与 weight max 选择:
控制把量化难度从 activation 移到 weight 的程度。它不是“让误差消失”;变换后权重某些 channels 变大,W8A8 的两边误差与 kernel scale granularity共同决定结果。Scale 可 fold 到前一 norm/weight 或保存在转换后的 checkpoint,必须避免部署时重复应用。
本页构造 ,令 activation channel 0/1 分别含 40×/10× outliers。 后未量化输出最大差仅浮点级 1.907e-06;per-tensor W8A8 fake quant MAE 从 0.453008 降至 0.029458。这是教学反例,不能推广成真实模型精度保证。
GPTQ:逐步量化并用二阶近似补偿
给 calibration inputs ,常见目标是最小化层输出重构误差:
其曲率近似与 相关。GPTQ 类算法按列/块量化某部分权重后,利用 inverse-Hessian 信息更新尚未量化权重,补偿已引入误差。核心不是简单 round(W/s),而是顺序、分组与误差传播。
本页不实现完整 GPTQ:稳定 Cholesky/inverse、damping、block updates、act-order、packing 都会使示例超过一个可靠术语实验。生产正确性应验证转换 checkpoint 的 layer outputs/perplexity 与 loader/kernel schema,而不是只打印权重 MSE。
成本主要在离线 calibration/quantization;推理 payload 可接近 INT4+metadata,但真实速度依赖 low-bit kernel。小 batch decode 更可能受权重带宽收益,prefill 大 GEMM 则受 dequant/Tensor Core path 影响。
AWQ:activation-aware 的 salient channel 保护
仅看每个 weight channel 的平均 quantization error,可能把业务重要性判断错。若 calibration activation 第 通道幅值/能量很大,小权重误差也会被放大到输出:
粗略 channel salience 可用 加权:
AWQ 使用 activation 分布寻找少量 salient weights/channels,并通过 per-channel scaling 降低低比特误差,而不是把这些 weights 永久保留为 FP16 的唯一策略。具体搜索、clipping、grouping 以实现为准。
实验里仅看权重误差时最差 input channel 是 2;乘 calibration activation energy 后最差变为 outlier channel 0。这个结果只证明“activation-aware 排序可能不同”,不是 AWQ 完整复现。
FP8:格式/scale recipe,不是 INT8 的别名
FP8 保留 sign、exponent、mantissa,常见 E4M3 与 E5M2 在动态范围/精度间取舍。与整数 affine quantization 不同,FP8 值本身有浮点编码,但生产仍常使用外部 scale:
- E4M3 通常精度较高、范围较窄;E5M2 范围更大、精度更低,具体 finite/NaN 语义依标准/硬件格式。
- GEMM 通常以更高精度累加,输出/epilogue dtype 另定。
- Dynamic scaling 要计算/更新 amax,增加 reduction/state;static scale 依赖 calibration 覆盖分布。
- GPU generation/backend 若无 native FP8 path,可能 cast/fallback,文件更小但算子不快。
FP8 weights、W8A8 activations 与 FP8 KV cache 是不同路径;不能从模型“FP8”标签推断所有 tensors 都是 8-bit 或统一 scale。
可运行的等价缩放与 salience 实验
import platform,torch
torch.manual_seed(239)
DEVICE=torch.device("cuda" if torch.cuda.is_available() else "cpu")
def fake_quant_per_tensor(x,qmax=127):
scale=x.abs().amax().clamp_min(1e-8)/qmax
return torch.round(x/scale).clamp(-qmax,qmax)*scale
def main():
tokens,hidden,output=64,8,6
x=torch.randn(tokens,hidden,device=DEVICE); x[:,0]*=40; x[:,1]*=10
weight=torch.randn(hidden,output,device=DEVICE); weight[0]*=.08; weight[1]*=.2
reference=x@weight
activation_max=x.abs().amax(0).clamp_min(1e-8)
weight_max=weight.abs().amax(1).clamp_min(1e-8)
alpha=.5; smooth_scale=activation_max.pow(alpha)/weight_max.pow(1-alpha)
x_smooth=x/smooth_scale; weight_smooth=weight*smooth_scale[:,None]
torch.testing.assert_close(x_smooth@weight_smooth,reference,rtol=1e-5,atol=1e-5)
baseline=fake_quant_per_tensor(x)@fake_quant_per_tensor(weight)
smoothed=fake_quant_per_tensor(x_smooth)@fake_quant_per_tensor(weight_smooth)
baseline_mae=(baseline-reference).abs().mean()
smoothed_mae=(smoothed-reference).abs().mean()
assert smoothed_mae<baseline_mae
calibration=torch.randn(tokens,hidden,device=DEVICE); calibration[:,0]*=20
importance=calibration.square().mean(0)
step=weight.abs().amax()/7
rounded=torch.round(weight/step).clamp(-7,7)*step
quant_error=(rounded-weight).square()
unweighted=quant_error.mean(1)
weighted=(quant_error*importance[:,None]).mean(1)
assert int(weighted.argmax())!=int(unweighted.argmax())
print(f"python={platform.python_version()} torch={torch.__version__}")
print(f"device={DEVICE} X={tuple(x.shape)} W={tuple(weight.shape)} alpha={alpha}")
print(f"smooth_equivalence_max_abs_diff={(x_smooth@weight_smooth-reference).abs().max():.3e}")
print(f"baseline_W8A8_fake_quant_MAE={baseline_mae:.6f}")
print(f"smoothed_W8A8_fake_quant_MAE={smoothed_mae:.6f}")
print(f"activation_range_before_max={activation_max.max():.3f} after_max={x_smooth.abs().amax(0).max():.3f}")
print(f"unweighted_worst_input_channel={int(unweighted.argmax())} activation_weighted_worst_channel={int(weighted.argmax())}")
print(f"importance={importance.tolist()}")
if __name__=="__main__":
with torch.inference_mode(): main()
完整文件位于 examples/quantization_methods.py。实际 activation 最大范围从 97.099 降到 3.531;W8A8 fake-quant MAE 从 0.453008 到 0.029458;unweighted/activation-weighted 最差 channels 分别为 2/0。
脚本用 per-tensor fake INT8,既没有 GPTQ Hessian update、AWQ scale search,也没有 native FP8 tensor/kernel。它提供的是方法动机的可检查数学证据,而非算法性能或模型精度复现。
生产验收与失败场景
方法级量化至少核对:
- checkpoint schema:bit width、packing、group size、scale/zero shape、transpose/order;
- loader 后实际 GPU allocated 与 weight dtype/layout,防止展开;
- profiler 中真实 kernel/backend,而非 config 名;
- layer-wise output error、perplexity/任务/长上下文、不同 prompt 分布;
- prefill/decode 分开性能、batch/shape sweep、compile/graph compatibility;
- TP/EP shards 与 quant groups/scale ownership一致;
- 未量化 layers(embedding/norm/lm head)与 workspace 计入容量。
常见失败:calibration 太窄;SmoothQuant scale 应用两次/漏应用;GPTQ/AWQ checkpoint 与 runtime packing variant 不匹配;FP8 scale overflow/underflow 或 amax history跨请求错误;低比特 kernel只支持特定 GPU;weight-only decode 加速但 prefill fallback 变慢。
Backend Dispatch给出 profiler/fallback 证据层;模型加载负责 schema、TP slicing 与不展开;KV 量化是独立 cache 表示,不能把 weight method 名直接套到 K/V。