跳转至

十九:模型压缩:量化、ZeroQuant、LLM.int8() 与 SmoothQuant

来源:http://mp.weixin.qq.com/s?__biz=MzYyNTk3Njg1NA==&mid=2247484148&idx=1&sn=ca39ffcd2215dfd05a405e1d42dc5aac&chksm=f01eb18dc769389b47cab9551b217ead7a649e816398b0acfe4444e9022f0231c6cc2f528ed3#rd

1. 学习定位

模型压缩的目标是降低模型部署成本,包括显存、内存、带宽、延迟、吞吐和能耗。量化是最常见、最实用的模型压缩方法之一。它把 FP16/BF16/FP32 等高精度数值映射到 INT8/INT4/NF4/FP8 等低精度表示,从而减少存储和计算开销。 第十九天聚焦 8-bit 和 W8A8 量化路线:

量化基础
-> PTQ / QAT
-> per-tensor / per-channel / per-token / per-group
-> weight-only / weight+activation
-> ZeroQuant
-> LLM.int8()
-> SmoothQuant
-> NF4 与 QLoRA 回顾
-> 部署和排错
面试中最常被问到的是:为什么大模型不能直接 naive INT8,activation outlier 是什么,LLM.int8() 如何处理 outlier,SmoothQuant 如何把量化难点从 activation 迁移到 weight,以及 ZeroQuant 的 layer-by-layer knowledge distillation 做什么。

2. 模型压缩方法版图

模型压缩常见方法: - 量化:降低数值精度,例如 FP16 到 INT8/INT4。

  • 剪枝:删除权重、通道、attention head、MLP 神经元或层。

  • 知识蒸馏:让小模型学习大模型输出或中间表示。

  • 低秩分解:用低秩矩阵近似大矩阵。

  • 权重共享和编码:减少参数存储冗余。

  • 高效结构设计:直接训练小模型、稀疏模型或 MoE 模型。

量化的优势是部署收益直接、通常不需要重新训练完整模型,也是大模型推理落地最常用的压缩手段。

3. 量化的基本公式

线性均匀量化通常把实数 x 映射到整数 q

q = clamp(round(x / scale) + zero_point, q_min, q_max)

img

反量化:

x_hat = scale * (q - zero_point)

img

其中: - scale 控制实数和整数之间的比例。

  • zero_point 表示实数 0 对应的整数位置。

  • q_min/q_max 由 bit 数决定,例如 int8 通常范围是 [-128,127][0,255]

对称量化:

zero_point = 0
q = round(x / scale)
非对称量化:

zero_point != 0
非对称量化更适合分布不以 0 为中心的数据(如某些图像输入、ReLU 输出),但实现和 kernel 可能更复杂。

4. 量化粒度

量化粒度决定 scale 的共享范围。 同一个 scale/zero_point 管多大范围的数据,就是什么粒度。 粒度越细 → 精度越高 → 但存储 / 计算开销越大。 Per-tensor(最粗粒度):

整个 tensor 共用一个 scale
优点是实现简单、计算开销小,缺点是对 outlier(异常值) 敏感【只要有一个很大的值,整个tensor精度都会变得很差】。 Per-channel(最常用、精度最好):

每个输出通道或输入通道一个 scale
常用于权重量化(CNN、Transformer权重都用这个),精度更好。 Per-token(Transformer 专用):

activation 每个 token 一个 scale
适合激活动态范围随 token 变化明显的场景。适用于Transformer的激活值(activation)优点是不同 token 数值范围差异巨大,per-token 能极大提升精度,缺点则是在推理时需要动态计算scale。 Per-group(4-bit 量化标配):

一组连续权重共享一个 scale,例如 group size = 32/64/128
常用于 4-bit weight-only quantization,在精度和元数据开销之间折中。缺点是实现复杂。 粒度 共享范围 直观特点 Per-tensor 整个张量 1 个 scale 最简单,怕异常值 Per-group 连续一组元素 1 个 scale 精度 & 开销折中,4-bit 常用 Per-channel 单个通道 1 个 scale 权重标配,精度高 Per-token 单个 token1 个 scale Transformer 激活专用 划分范围越小(粒度越细),scale 越贴合局部数据,量化精度就越高,但需要存储更多 scale,开销也越大。

5. PTQ 与 QAT

PTQ(Post-Training Quantization):训练后量化

训练完成后做量化
通常只需要少量校准数据
部署成本低
QAT(Quantization-Aware Training):感知量化训练

训练或微调时模拟量化误差
模型学习适应低精度
精度通常更好,但成本更高
大模型场景中,PTQ 更常见,因为从头或全量 QAT 成本太高。ZeroQuant、LLM.int8()、SmoothQuant、GPTQ、AWQ 都属于重要 PTQ 路线。

6. Weight-only 与 W8A8

Weight-only quantization:只量化权重

weights quantized
activations usually remain FP16/BF16
优点是实现相对简单,能显著降低权重显存和内存带宽。缺点是 activation 和 GEMM 计算未必完全 INT8。 W8A8:(权重 + 激活 都量化)

weights int8
activations int8
优点是更容易获得 INT8 GEMM 加速。难点是 activation outlier 很强,激活量化比权重量化更困难。 LLM.int8() 是 mixed-precision INT8 路线;SmoothQuant 目标是让 LLM 也能做高效 W8A8。

7. Activation Outlier

大模型中 activation 常出现 outlier,即某些 hidden dimensions 的数值远大于其他维度。LLM.int8() 论文指出,随着模型规模增大,会出现大幅 outlier features,naive INT8 量化很容易被这些 outlier 破坏。 问题直觉:

如果一个 tensor 里大多数值在 [-1,1]
但少数 outlier 在 [-50,50]
per-tensor scale 必须覆盖 [-50,50]
大多数普通值会被量化得很粗
结果是小值信息损失严重,模型性能下降。

8. ZeroQuant

ZeroQuant 是微软 DeepSpeed 提出的面向大 Transformer 的「低成本、高精度、端到端 PTQ 量化方案」。它包含三类核心设计:

1. Fine-grained hardware-friendly quantization(细粒度、硬件友好量化)    就是选对量化粒度,同时让GPU跑得快
    权重分组量化、激活值token-wise
2. Layer-by-layer Knowledge Distillation, LKD(LKD,逐层知识蒸馏)    这是 ZeroQuant 最关键、最聪明的地方。
    直接把整个模型量化 → 误差逐层累积,最后层烂掉。
    LKD 做法(逐层 “校准 + 微调”):将原始FP16模型作为Teacher,将量化模型作为Student,然后一次只处理一层,先将Teacher这一层的输入喂给Student的对应层,让Student量化层的输出尽量逼近Teacher的输出,只微调这一层的量化参数(很少几步),之后固定这一层,继续对下一层进行微调。

3. Optimized backend to reduce quant/dequant overhead(优化后端,减少量化 / 反量化开销)量化不是只算公式,真正部署时 quant/dequant 很耗时间。ZeroQuant把量化 / 反量化和前后算子融合(fusion),专门为 GPU(Ampere 等)写高效 kernel,让量化开销几乎消失 → 加速比接近理论值。
ZeroQuant 同时关注权重和激活量化,目标是在尽量不访问原始训练数据、低成本校准的情况下完成高效部署。 Layer-by-layer KD 的思想是:

逐层让量化层输出逼近 FP teacher 对应层输出
不需要全模型端到端重训
降低量化误差逐层累积
ZeroQuant 的贡献不只是量化公式,还包括系统后端和可部署性。

9. LLM.int8() 的核心问题

LLM.int8() 关注大规模 Transformer 的 INT8 矩阵乘。它观察到: - 大模型 activation 中存在 emergent outlier features。

  • naive vector quantization 在模型规模变大后会明显失效。

  • outlier 维度数量很少,但影响很大。

LLM.int8() 的目标是:

绝大多数矩阵乘使用 int8
少数 outlier 维度单独用 fp16 处理
在接近无性能损失的情况下减少显存和计算开销

10. LLM.int8() 的 Mixed-precision Decomposition

LLM.int8() 的核心是混合精度分解:

normal dimensions:
  int8 matrix multiplication

outlier dimensions:
  fp16 matrix multiplication
简化表示:

XW = X_normal W_normal + X_outlier W_outlier

img

其中: - normal 部分占绝大多数,用 INT8 加速。

  • outlier 部分很少,用 FP16 保精度。

这样既保留了 INT8 的效率,又避免 outlier 被粗暴量化。

11. LLM.int8() 的 Vector-wise Quantization向量级量化

LLM.int8() 使用 vector-wise quantization。矩阵乘中,对不同向量使用独立归一化 scale,减少不同通道动态范围差异带来的误差。 Vector-wise:矩阵中每一行 / 每一列向量,单独计算专属 scale,相比起Per-tensor来说粒度更细。 直觉:

不是整个矩阵共用一个 scale
而是对行/列向量分别 scale
这比 per-tensor 量化更精细,也更适合 Transformer 中的矩阵乘。 LLM.int8() 常用于推理加载,例如 bitsandbytes 的 load_in_8bit=True 路线。

12. SmoothQuant 的核心思想

SmoothQuant 目标是让 LLM 做准确高效的 W8A8 PTQ。它的关键观察是:

weights 相对容易量化
activations 更难量化
SmoothQuant 使用数学等价变换,把 activation 的量化难点迁移到 weight:

Y = XW
  = (X diag(s)^-1) (diag(s) W)

img

把激活(X)上的极端大值,按比例分摊转移到权重(W)上,操作后:激活的数值范围被拉平,outlier 被削弱,适合 INT8 量化;权重原本分布均匀,分担部分极值后,依然可以稳定做 INT8 量化。 通过选择 smoothing scale s: - activation 被缩小,outlier 变平滑。

  • weight 被相应放大,承担更多量化难度。

由于 weight 通常更容易量化,这种迁移能提升 W8A8 精度。

13. SmoothQuant 的 Alpha

SmoothQuant 中常用参数 alpha 控制平滑程度:

s_j = max(|X_j|)^alpha / max(|W_j|)^(1-alpha)

img

直觉:α 决定「量化压力」在激活和权重之间如何分配。 - alpha 越大,更多量化难度从 activation 迁移到 weight。

  • alpha 越小,对 activation 平滑较弱。

alpha 需要通过校准数据或经验选择。不同模型、不同层可能有不同最优值。

14. NF4 与 QLoRA 回顾

NF4 是 NormalFloat 4-bit,是 QLoRA 中常用的 4-bit 权重量化格式。它不是普通均匀 int4,而是针对近似正态分布权重设计非均匀 codebook。 QLoRA:

base model: 4-bit NF4 quantized and frozen
adapter: LoRA parameters trainable
compute: often bf16/fp16
- 数值密集区域(正态分布中心):码点更密,精度更高

  • 数值稀疏区域(分布两端、极值):码点稀疏,节省比特

  • 仅做权重量化,不处理激活

它和本日 INT8 推理量化的区别: - QLoRA 主要服务低显存微调。

  • LLM.int8()/SmoothQuant 主要服务推理部署。

  • NF4 是 4-bit 权重量化格式。

  • SmoothQuant 是 W8A8 激活/权重平滑方法。

方案 核心用途 量化对象 精度格式 核心目标 QLoRA + NF4 大模型微调 仅基座权重 权重 NF4 (4bit),计算 BF16/FP16 极致降低微调显存 LLM.int8() 推理部署 权重 + 激活 混合精度:大部分 INT8,异常维度 FP16 INT8 加速、兼容 outlier SmoothQuant 推理部署 权重 + 激活 W8A8 全 INT8 平滑激活异常值,实现全 INT8 全速推理

15. 校准数据

PTQ 通常需要 calibration data,用来估计 activation 范围、scale、smoothing 参数或 Hessian 近似。 校准数据要求: - 数量不一定很大。

  • 分布应接近真实推理输入。

  • prompt 模板应和部署一致。

  • 序列长度要覆盖真实场景。

  • 多语言/代码/数学等场景要有代表性。

校准数据不匹配会导致量化 scale 不合适,部署精度下降。

16. 量化的部署收益与成本

量化收益: - 权重显存下降。

  • 内存带宽压力下降。

  • KV 以外的模型加载更轻。

  • INT8/INT4 kernel 可提升吞吐。

  • 更容易单卡部署大模型。

量化成本: - 精度损失。

  • scale/zero point 元数据开销。

  • quant/dequant overhead。

  • kernel 兼容性限制。

  • 某些层需要保留高精度。

  • calibration 和评估成本。

量化不等于一定加速。如果硬件和 kernel 不支持低精度高效 GEMM,可能只省显存但不明显提速。

17. 工程排错

量化后常见问题: - 困惑度明显升高。

  • 长文本生成重复或崩坏。

  • 数学/代码能力下降。

  • 某些语言或格式任务退化。

  • 推理速度没有提升。

  • 显存没有按理论比例下降。

排查顺序:

1. 检查量化粒度和 bit 数。
2. 检查 calibration 数据分布。
3. 检查哪些层被量化,哪些层保留高精度。
4. 检查 activation outlier。
5. 检查 kernel 是否真正走 int8/int4。
6. 检查 batch size、sequence length、KV cache 是否成为瓶颈。
7. 用任务指标而不只用 perplexity 评估。

18. LLM.int8()、ZeroQuant、SmoothQuant 对比

方法 核心定位 核心机制 适用场景 补充特点 ZeroQuant 端到端 PTQ + 工程优化 细粒度量化(权重分组 / 激活逐 Token)+ 逐层知识蒸馏 LKD + 算子后端优化 大规模 Transformer 全链路量化部署 权重 + 激活同时量化,误差逐层抑制,硬件友好,兼顾精度与部署 LLM.int8() 异常值感知 INT8 推理 混合精度分解:普通维度 INT8 矩阵乘,少数异常维度 FP16 计算;向量级量化 通用大模型 8bit 推理(bitsandbytes 主流方案) 不改动数值分布,靠拆分计算避 outlier,无法实现纯 W8A8 SmoothQuant W8A8 全 INT8 PTQ 平滑变换 + 系数α,把激活的数值压力迁移到权重,拉平激活分布 激活异常值突出,追求完整 INT8 GEMM 硬件加速 纯数值变换,可落地标准 W8A8,全链路 INT8 计算,推理速度最优 NF4/QLoRA 4bit 低显存微调 权重使用非均匀 NF4 量化并冻结,仅训练 LoRA 适配器,计算保持 BF16/FP16 大模型低成本微调、参数高效训练 主打训练 / 微调,非纯推理方案,几乎不用于线上推理加速

19. 面试表达框架

回答量化问题可以按四层:

定义:
  用低 bit 表示权重/激活,降低存储和计算。

技术点:
  scale、zero point、粒度、PTQ/QAT、weight-only/W8A8。

LLM 难点:
  activation outlier、长尾分布、kernel、校准、误差累积。

方法:
  LLM.int8 处理 outlier,SmoothQuant 平滑 activation,
  ZeroQuant 用细粒度量化和逐层 KD。

20. 核心总结

第十九天需要掌握的最小闭环:

Quantization:
  x -> q -> x_hat
  scale and zero_point are core

Granularity:
  per-tensor, per-channel, per-token, per-group

PTQ:
  post-training, calibration-based

LLM challenge:
  activation outliers

LLM.int8():
  vector-wise int8 quantization
  outlier dimensions in fp16

SmoothQuant:
  migrate difficulty from activation to weight
  Y = XW = (X S^-1)(S W)

ZeroQuant:
  fine-grained quantization
  layer-by-layer distillation
  optimized backend

21. 参考资料

  • ZeroQuant 原论文:https://arxiv.org/abs/2206.01861

  • LLM.int8() 原论文:https://arxiv.org/abs/2208.07339

  • SmoothQuant 原论文:https://arxiv.org/abs/2211.10438

  • NF4 量化讲解:https://www.bilibili.com/video/BV15y411a7so/

  • Hugging Face bitsandbytes 量化文档:https://huggingface.co/docs/transformers/quantization/bitsandbytes

  • SmoothQuant GitHub:https://github.com/mit-han-lab/smoothquant

            预览时标签不可点
    

    <div class="