跳转至

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

更新日期:2026-04-15


一、量化的动机

精度 比特数 内存比 典型应用 精度损失
FP32 32 4× 训练 baseline 0
BF16 / FP16 16 2× 训练 + 推理标准 极小
FP8 (E4M3 / E5M2) 8 1× H100 训练 + 推理 可控(DeepSeek-V3 已规模化)
INT8 8 1× 推理(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 1× (基线)
FP8 3958 4500 2×
FP6 — 4500 2× (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 50:NVFP4 推理(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. 推理与部署