跳转至

量化全景:GPTQ / AWQ / SmoothQuant / FP8

更新日期:2026-04-15


一、量化的动机

精度 比特数 内存比 典型应用 精度损失
FP32 32 训练 baseline 0
BF16 / FP16 16 训练 + 推理标准 极小
FP8 (E4M3 / E5M2) 8 H100 训练 + 推理 可控(DeepSeek-V3 已规模化)
INT8 8 推理(SmoothQuant) <1%
INT4 (AWQ/GPTQ) 4 0.5× 推理(消费卡) 1-3%
INT3 / INT2 3 / 2 <0.4× 端侧 显著,需重训

核心动机:模型参数量 N 决定推理延迟(带宽 bound),量化让同一卡跑更大模型。70B FP16 需 140GB(双 H100),INT4 仅 35GB(单 A100/4090)。

生产建议

  • 首选:FP8(需 H100+)或 INT4 AWQ/GPTQ(A100 及以下)
  • 对精度零容忍:BF16
  • 极端显存受限(端侧):INT3 或更低,必须做任务级评测确认精度可接受

二、权重量化方法

2.1 GPTQ:基于 Hessian 的精确量化

GPTQ 的核心思想:量化第 i 列时,用后续列补偿误差,保持输出层的激活尽可能接近原始。参考 GPTQ (Frantar et al., 2022)

def gptq_quantize(W, calibration_X, n_bits=4, block_size=128):
    """
    W: [out_features, in_features] 权重矩阵
    calibration_X: [n_samples, in_features] 校准数据
    """
    n_rows, n_cols = W.shape

    # 1. 计算 Hessian: H = 2 * X^T X
    # Hessian 编码了输入的二阶统计信息
    H = 2 * calibration_X.T @ calibration_X
    H += 0.01 * torch.eye(n_cols)  # 正则化防奇异
    H_inv = torch.linalg.inv(H)

    Q = torch.zeros_like(W)
    E = torch.zeros_like(W)  # 累积误差

    for col in range(n_cols):
        # 2. 量化当前列 (考虑已累积的误差)
        w = W[:, col] + E[:, col]
        Q[:, col] = quantize_to_n_bits(w, n_bits)

        # 3. 计算量化误差
        err = w - Q[:, col]

        # 4. 用 Hessian 信息将误差分配到后续列
        # 直觉: 如果 col 和 j 相关性高, 把误差转到 j
        for j in range(col + 1, n_cols):
            E[:, j] += err * H_inv[col, j] / H_inv[col, col]

    return Q

2.2 AWQ:保护激活值大的通道

AWQ 的观察:不同通道的激活值差异巨大,激活值大的通道对应的权重更重要。参考 AWQ (Lin et al., 2023)

def awq_quantize(W, activation_stats, n_bits=4, alpha=0.5):
    """
    W: 权重 [out, in]
    activation_stats: 校准数据中各通道的激活值平均幅度 [in]
    """
    # 1. 找出重要通道
    # 激活值越大 → 通道越重要 → 权重对量化越敏感
    importance = activation_stats.abs().mean(dim=0)  # [in]

    # 2. 计算保护性缩放因子
    # 重要通道 scale up → 量化时相对误差小
    scales = (importance / importance.mean())  alpha  # [in]

    # 3. 缩放权重
    W_scaled = W * scales.unsqueeze(0)

    # 4. 标准量化缩放后的权重
    Q = standard_quantize(W_scaled, n_bits)

    # 5. 推理时反缩放
    # 输出 = W_scaled @ (X / scales)
    # 等价于 W @ X (如果精确量化)
    # 实际因为量化有损, 这种设计让重要通道损失最小

    return Q, scales

# AWQ vs GPTQ:
# AWQ: 基于激活值设计, 无需梯度信息, 更快
# GPTQ: 基于 Hessian, 质量略高但慢
# 实际: 两者质量接近, AWQ 部署更普及

2.3 GGUF:llama.cpp 生态

GGUF 是 llama.cpp 的量化格式,支持多种精度混合(Q2_K / Q3_K / Q4_K / Q5_K / Q6_K / Q8_0)。社区最流行的边缘部署格式。 实用建议:端侧部署首选 Q4_K_M(质量/大小最佳平衡)。若显存允许可升到 Q5_K_M 获得更好质量。Q2_K/Q3_K_M 仅在设备极端受限时使用,建议先在目标任务上评测精度再部署。


三、激活量化

3.1 为什么难:Outlier 问题

LLM 的激活值有严重的 outlier。某些通道的值可能是其他通道的 100 倍,标准量化会把小值全压成 0。

3.2 SmoothQuant:难度转移

核心思想:将激活的量化难度转移到权重上。参考 SmoothQuant (Xiao et al., 2023)

def smooth_quant(W, activation_stats, alpha=0.5):
    # 激活的 per-channel 最大值
    s_act = activation_stats.abs().max(dim=0)  # [in]
    # 权重的 per-channel 最大值
    s_w = W.abs().max(dim=0)  # [in]

    # 平滑因子: 平衡激活和权重的难度
    smooth_scales = s_act.pow(alpha) / s_w.pow(1 - alpha)

    # 应用平滑
    W_smooth = W * smooth_scales.unsqueeze(0)  # 权重变大
    # X_smooth = X / smooth_scales (推理时应用)  # 激活变小

    # 效果:
    # 激活: outlier 被缩小 → 容易量化
    # 权重: 略微放大 → 仍然容易量化

    return W_smooth, smooth_scales

# W8A8 (权重+激活都 INT8):
# 标准量化: 质量严重下降
# SmoothQuant 后: 几乎无损


四、浮点数据类型全谱(FP4 → FP64)

4.1 现代 GPU 支持的所有 FP 类型

NVIDIA Blackwell(5th-gen Tensor Core)原生支持:FP4 / FP6 / FP8 / FP16 / BF16 / TF32 / FP32 / FP64。每种格式的位分配:

类型 总位 sign exp mantissa 表示形式 动态范围 最小正非零 用例
FP4 (E2M1) 4 1 2 1 NVFP4 / MXFP4 ±6 0.5 推理(权重 + 激活)
FP6 (E2M3) 6 1 2 3 MXFP6 高精度 ±7.5 0.0625 高精度 4-bit 替代
FP6 (E3M2) 6 1 3 2 MXFP6 大范围 ±28 ~0.0156 训练激活
FP8 (E4M3) 8 1 4 3 NV E4M3 / MXFP8 ±448 ~0.00195 训练 forward / 推理
FP8 (E5M2) 8 1 5 2 NV E5M2 / MXFP8 ±57344 ~1.5e-5 训练 backward(梯度)
FP16 16 1 5 10 IEEE 754 half ±65504 ~6e-5 历史训练,已被 BF16 替代
BF16 16 1 8 7 Brain Float ±3.4e38 ~1.2e-38 训练默认(FP32 同范围)
TF32 19 1 8 10 NVIDIA TF32 ±3.4e38 ~1.2e-38 A100+ 自动 promote 模式
FP32 32 1 8 23 IEEE 754 single ±3.4e38 ~1.2e-38 累加器 / 主权重副本
FP64 64 1 11 52 IEEE 754 double ±1.8e308 ~2.2e-308 科学计算(HPC)

位布局可视化(按总位长展开):

FP4 (E2M1):    [s][ee][m]                                                4 bit
FP6 (E2M3):    [s][ee][mmm]                                              6 bit
FP6 (E3M2):    [s][eee][mm]                                              6 bit
FP8 (E4M3):    [s][eeee][mmm]                                            8 bit
FP8 (E5M2):    [s][eeeee][mm]                                            8 bit
BF16:          [s][eeeeeeee][mmmmmmm]                                   16 bit
FP16:          [s][eeeee][mmmmmmmmmm]                                   16 bit
TF32:          [s][eeeeeeee][mmmmmmmmmm]                                19 bit
FP32:          [s][eeeeeeee][mmmmmmmmmmmmmmmmmmmmmmm]                   32 bit
FP64:          [s][eeeeeeeeeee][mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm]   64 bit

s=sign, e=exponent, m=mantissa
"E4M3" 即 Exponent=4 bits, Mantissa=3 bits

两个 trade-off

        ↑ 范围(exponent 多)
  FP8-E5M2 (5/2)  ←─── 损失精度  
  BF16  (8/7)
  FP16  (5/10)
  FP8-E4M3 (4/3) ←─── 范围/精度均衡,FP8 主流选择
  FP6-E3M2 (3/2)
  FP4-E2M1 (2/1) ←─── 极致压缩,需要 microscaling 救场
                                           精度 →(mantissa 多)

算力差距(H100 → B200,相对 BF16 = 1×):

精度 H100 TFLOPS B200 TFLOPS 倍数 vs BF16
FP64 67 90 0.05×
FP32 (TF32) 989 1100 0.55×
BF16 / FP16 1979 2200 (基线)
FP8 3958 4500
FP6 4500 (B200 起)
FP4 9000 4.5× (B200 起)

→ 每降一精度约 2×,FP4 在 Blackwell 上是 BF16 的 4.5× 算力。这是为什么 frontier 一定要用 FP8/FP4。

4.2 FP8 详解(已成主流)

NVIDIA H100 / B200 原生支持 FP8 计算。FP8 质量接近 BF16,速度 2×,内存 ½。参考 FP8 Formats for Deep Learning (Micikevicius et al., 2022)

E4M3 vs E5M2 选用

  • E4M3(精度高、范围小):训练 forward + 推理(权重 + 激活)
  • E5M2(范围大、精度低):训练 backward(梯度量级跨度大需要大 exponent)
  • FP32:累加器,防止 1000+ 次乘加累积误差
# 训练时混合精度示意
def fp8_matmul(x_fp8, w_fp8):
    # FP8 × FP8 → FP32 累加(H100 Tensor Core 原生)
    out_fp32 = tensorcore.matmul(x_fp8, w_fp8, accumulator='fp32')
    # 量化回 FP8 进入下一层
    return to_fp8(out_fp32, format='e4m3')

# H100 FP8: 4000 TFLOPS (vs BF16 2000,提速 2×)
# B200 FP8: 4500 TFLOPS (vs BF16 2200, 提速 2×)

训练时的"双精度"权重(关键技巧): - FP8 forward / backward - FP32 主权重副本(master copy),优化器在 FP32 上更新 - 每 step 把 FP32 主权重 cast 回 FP8 给下一 step

否则 FP8 累积误差会让训练发散。这是 Micikevicius 2022 推荐的标准配置。

4.3 FP4 — Blackwell 起的新一代量化

FP4 单 nibble 只能表示 16 个值(含正负),动态范围极小(±6)。直接当 weight 量化精度损失严重,必须 microscaling 才能用。

4.3.1 NVFP4 vs MXFP4 对比

NVIDIA Blackwell 上有两种 FP4 标准

维度 MXFP4 (OCP 标准) NVFP4 (NVIDIA 优化)
数据元素 E2M1(FP4) E2M1(FP4)
Block size 32 个值共享一 scale 16 个值 共享一 scale
Block scale 类型 E8M0(power-of-2,整数 exp) E4M3(非整数 exp,更细)
Tensor-level scale FP32 标量(Level 2 scaling)
Block scale 量化误差 高(power-of-2 step) 低(fractional E4M3)
内存(每 16/32 个 4-bit 值) 4×32+8 = 136 bit 4×16+8 = 72 bit + per-tensor FP32
实际开销 4.25 bit / value ~4.5 bit / value
准确度(vs FP8) 1-3pt 损失 <1pt 损失

4.3.2 NVFP4 的两级 scaling 直观图

  Tensor (e.g. weight matrix W shape [4096, 4096]):
  ┌───────────────────────────────────────────────────┐
  │                                                   │
  │   ┌─16─┐ ┌─16─┐ ┌─16─┐ ┌─16─┐ ...               │
  │   │░░░░│ │░░░░│ │░░░░│ │░░░░│      ← 每 16 个 FP4│
  │   └─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘        值一组      │
  │     │      │      │      │                       │
  │  s_E4M3 s_E4M3 s_E4M3 s_E4M3      ← Level 1:    │
  │  (per-block, 8 bit each)               每组 1 个   │
  │                                          E4M3 scale│
  │                                                   │
  │   全 tensor 1 个 FP32 scalar:                     │
  │   S_FP32 ──── 用于校准整个 tensor 分布             │
  │                                                   │
  └───────────────────────────────────────────────────┘

  恢复一个值: x = x_q × s_block × S_tensor
             ↑     ↑       ↑           ↑
            FP4  E4M3    FP32       原始浮点
            (4)  (8)     (32)

为什么 NVFP4 比 MXFP4 准:

  1. Block 更小(16 vs 32)→ 局部分布动态范围更窄 → scale 更准
  2. Block scale 用 E4M3 而非 E8M0 → power-of-2 量化变 fractional 量化,误差降一个数量级
  3. 多一层 FP32 tensor scale → 整个 tensor 的"全局分布"被先归一化,块内只算偏差

实测(NVIDIA 自报,DeepSeek-R1-0528):FP8 → NVFP4 PTQ 后,< 1% 准确度下降,AIME 2024 反而 +2%

4.3.3 MXFP6 / MXFP8 等中间档

OCP MX 标准其实是个家族,FP4 只是最激进的:

精度排序 (低→高):  MXFP4  <  MXFP6 (E2M3)  <  MXFP6 (E3M2)  <  MXFP8 (E4M3)
                                              <  MXFP8 (E5M2)  <  BF16

实操:

  • MXFP4:最大压缩,Blackwell 起的推理新主流(取代 INT4)
  • MXFP6:精度更好但压缩比一般,transitional,少有团队专门用
  • MXFP8:和 NVIDIA 的 FP8 几乎等价,块 scale 比单 scale 略好

4.4 DeepSeek-V3 的 FP8 训练

DeepSeek-V3 是第一个在 671B MoE 规模验证 FP8 训练可行性的工作(详见 DeepSeek 工程深读 §四)。

要点: - per-tile fine-grained scaling:1×128 (act) / 128×128 (weight) - 全 forward + backward 用 E4M3(而非 E5M2 backward),靠 tile-scaling 吸收 dynamic range - FP32 累加 + FP32 主权重副本

V3 FP8 训练成果证明 ≤ BF16 的精度损失,671B 训练成本节省 ~50%。


五、Outlier 解决方案的演进

权重 + 激活 同时 4-bit 量化的核心难题是 outlier(§3.1 已说激活 outlier 的存在)。2022-2025 演进:

flowchart LR
    raw["原始 LLM<br/>有 outlier"]
    raw --> sq["SmoothQuant<br/>(2022)<br/>难度转移到权重"]
    raw --> llm8["LLM.int8()<br/>(2022)<br/>混合 INT8 + FP16<br/>outlier 走 FP16"]
    sq --> qrot["QuaRot<br/>(2024.04)<br/>Hadamard 旋转<br/>消除 outlier"]
    llm8 --> qrot
    qrot --> spin["SpinQuant<br/>(2024.05)<br/>学习旋转矩阵"]
    spin --> mxnv["MXFP4 / NVFP4<br/>(2025)<br/>microscaling 自带<br/>tile-level outlier 控制"]

    classDef era fill:#fff,stroke:#cc785c,color:#1a1a1a;
    class raw,sq,llm8,qrot,spin,mxnv era

5.1 LLM.int8()(2022)

Dettmers 2022 思路:outlier 通道走 FP16,剩余通道走 INT8,混合精度矩阵乘。

  • 优点:保住 outlier 信息
  • 缺点:实现复杂,混合 dtype 拖慢推理 30%

5.2 SmoothQuant(2022)

详见 §3.2。把激活 outlier 通过缩放转移到权重。第一个能让 LLaMA 类全 INT8 的方法

5.3 QuaRot(2024.04,arXiv:2404.00456

关键 insight:outlier 是 hidden state 中特定通道的特性。如果给 hidden state 做一个正交旋转,outlier 就被打散到所有通道,平均化。

旋转矩阵 R 是正交的: R^T R = I
attention 输出经过旋转: y' = y · R
后续层先 unrotate: y = y' · R^T  (因为 R^T = R^{-1})
等价于原网络,但 hidden state 分布变均匀

QuaRot 用 Hadamard 矩阵(特殊的 ±1 正交阵)做随机旋转。结果:LLaMA-2 全 4-bit 量化(W4A4KV4),accuracy 损失 < 1pt。

5.4 SpinQuant(2024.05,arXiv:2405.16406

思路:QuaRot 用随机 Hadamard,能不能学一个最优旋转

SpinQuant 把 R 设为可学习的正交矩阵,用 Cayley 参数化保证正交性,在 calibration data 上 fine-tune 50 step。

实测:LLaMA-3-8B 4-bit W4A4KV4,SpinQuant 比 QuaRot 缩小 accuracy gap 45.1%;比 SmoothQuant 强 25 pt。

5.5 NVFP4 / MXFP4(2025+)

Microscaling 自带 outlier 控制:每 16/32 个值就重新归一,再大 outlier 也只影响一个 block。不需要旋转 / smoothing 等"预处理"

→ 2026 工业界主流是 直接 NVFP4 PTQ(NVIDIA TensorRT-Model-Optimizer / vLLM 默认 path),不再走 QuaRot/SpinQuant 那一套。


六、量化选型

6.1 实际选择建议(2026)

  • Blackwell B200 / B100 / RTX 50NVFP4 推理(W4A4),训练 FP8(E4M3 + per-tile)
  • Hopper H100 / H200:FP8 推理 + 训练(NVFP4 不原生支持但可用 MXFP4 软件 emulate)
  • Ampere A100 及以下:AWQ INT4(速度优先)或 GPTQ INT4(质量优先)
  • CPU / 端侧:GGUF Q4_K_M,显存允许升 Q5_K_M
  • 精度敏感:从 FP8 起步逐步降到精度临界点

不确定时从高精度(FP8)开始,逐步降低直到精度不可接受。


七、MoE 量化的特殊考虑

MoE 量化不能用统一的 calibration 数据,因为不同专家看到的数据分布完全不同。

def per_expert_quantization(moe_model, calibration_data):
    for layer in moe_model.moe_layers:
        for expert_id, expert in enumerate(layer.experts):
            # 只用路由到此专家的 token 做 calibration
            expert_tokens = collect_routed_tokens(
                calibration_data, layer, expert_id)

            # 冷门专家数据少, 可能需要保守量化
            if len(expert_tokens) < 100:
                quantize(expert, expert_tokens, bits=8)  # 保守
            else:
                quantize(expert, expert_tokens, bits=4)  # 激进


八、量化的实用工具

场景 量化工具 推理后端
快速上手 AutoAWQ vLLM
极致性能 TensorRT-Model-Optimizer TensorRT-LLM
端侧部署 llama.cpp(GGUF) llama.cpp
微调(QLoRA) bitsandbytes bitsandbytes / vLLM

参考文献


上级 · G. 推理与部署