SmoothQuant:用于大语言模型的准确高效训练后量化

SmoothQuant:用于大语言模型的准确高效训练后量化

ArXiv ID: 2211.10438
作者: Guangxuan Xiao, Ji Lin, Mickael Seznec, Hao Wu, Julien Demouth, Song Han
机构: MIT Han Lab, NVIDIA
发表: ICML 2023
引用量: 3000+ (截至 2025 年)


摘要

大语言模型(LLM)的推理成本高企,量化是降低部署成本的关键技术。然而,LLM 的激活存在极端离群值,使得 INT8 量化会导致不可接受的精度下降。本文提出的 SmoothQuant 通过数学上的等价变换,将量化难度从激活迁移到权重,实现了无需训练的 W8A8 量化,在保持精度的同时实现 1.56 倍推理加速和 2 倍内存减少。


问题背景

LLM 量化的挑战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FP16 推理的问题:
┌─────────────────────────────────────┐
│ OPT-175B FP16 需要 350GB 内存 │
│ 需要 8×A100 80GB 才能运行 │
│ 内存带宽瓶颈限制吞吐量 │
│ 计算成本高,部署门槛高 │
└─────────────────────────────────────┘

理想目标:
┌─────────────────────────────────────┐
│ INT8 推理只需 175GB 内存 │
│ 单节点即可运行 175B 模型 │
│ 2 倍内存带宽效率提升 │
│ 更低部署成本 │
└─────────────────────────────────────┘

为什么 LLM 量化如此困难?

核心问题:激活离群值(Activation Outliers)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
激活值分布示意图:

普通神经网络:
████
██████
████████
██████████
██████████████
└────────────┘
近似正态分布,适合量化

LLM 激活:

██
███
████ █
█████████████
└────────────┘
^
离群值(极端大/小的激活)

离群值的影响

  • 量化范围被离群值拉大
  • 正常值的量化精度严重下降
  • 导致模型整体精度崩溃

SmoothQuant 方法

核心洞察

权重和激活对量化难度的影响不同

权重 激活
量化难度 低(静态、可预先分析) 高(动态、有离群值)
数据特性 固定、可校准 输入相关、变化大
离群值 较少 频繁出现

数学原理

SmoothQuant 变换

对于线性层 Y = XW,引入平滑因子 s

1
2
3
4
5
6
7
Y = XW
= (X diag(s)^(-1)) · (diag(s) W)
= X' · W'

其中:
- X' = X diag(s)^(-1) ← 平滑后的激活
- W' = diag(s) W ← 吸收缩放因子的权重

关键性质

  • 数学上完全等价,输出不变
  • 可以将激活的量化难度”迁移”到权重
  • 权重可以离线处理,激活在线平滑

平滑因子选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def compute_smooth_factors(W, X, alpha=0.5):
"""
计算逐通道平滑因子

Args:
W: 权重矩阵 [out_features, in_features]
X: 激活统计 [batch, in_features]
alpha: 迁移强度 (0-1)

Returns:
s: 平滑因子 [in_features]
"""
# 计算激活的每通道最大绝对值
x_max = X.abs().max(dim=0).values

# 计算权重的每通道最大绝对值
w_max = W.abs().max(dim=0).values

# 平滑因子 = 激活难度^alpha / 权重难度^(1-alpha)
s = torch.pow(x_max, alpha) / torch.pow(w_max, 1 - alpha)

return s

alpha 参数的作用

alpha 效果 适用场景
0.0 不平滑,原权重 激活无离群值
0.5 平衡迁移 推荐默认值
1.0 最大平滑 离群值极端

量化流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
SmoothQuant 工作流程:

步骤 1: 收集激活统计
┌─────────────────────────────────────┐
│ 在小型校准集上运行模型 │
│ 收集每层激活的最大值统计 │
│ 无需训练,只需前向传播 │
└─────────────────────────────────────┘


步骤 2: 计算平滑因子
┌─────────────────────────────────────┐
│ 对每个线性层计算 s │
│ 根据 alpha 参数调整迁移强度 │
│ 逐通道处理,保持细粒度控制 │
└─────────────────────────────────────┘


步骤 3: 变换权重
┌─────────────────────────────────────┐
│ W' = diag(s) · W │
│ 离线处理一次即可 │
│ 可以预先保存量化后权重 │
└─────────────────────────────────────┘


步骤 4: 量化推理
┌─────────────────────────────────────┐
│ X' = X / s (激活平滑) │
│ W' 已量化为 INT8 │
│ X' 量化为 INT8 │
│ INT8 GEMM 计算 │
└─────────────────────────────────────┘

模型架构支持

适用层类型

层类型 是否量化 说明
Attention QKV 最关键的计算
Attention Output 投影层
MLP Up/Down FFN 主要计算
MLP Gate 门控层
Embedding 保持 FP16
LayerNorm 保持 FP32
Softmax 保持 FP32

支持的模型架构

SmoothQuant 已验证支持:

  • OPT (125M - 175B)
  • BLOOM (1.1B - 176B)
  • GLM (130B)
  • MT-NLG (530B)
  • Llama/Llama2 (7B - 70B)
  • Falcon (7B - 40B)
  • Mistral (7B)
  • Mixtral MoE (8x7B)

实验结果

语言建模困惑度

模型 精度 WikiText2 PTB C4
OPT-175B FP16 10.23 23.41 8.92
OPT-175B SmoothQuant 10.28 23.55 8.97
精度损失 +0.49% +0.60% +0.56%
模型 精度 WikiText2 PTB C4
BLOOM-176B FP16 12.45 28.33 10.21
BLOOM-176B SmoothQuant 12.51 28.52 10.29
精度损失 +0.48% +0.67% +0.78%

结论:困惑度增加小于 1%,精度损失可忽略不计。

零样本任务准确性

任务 FP16 SmoothQuant 相对保持率
LAMBADA 72.4% 72.1% 99.6%
HellaSwag 84.2% 83.8% 99.5%
PIQA 81.5% 81.1% 99.5%
Winogrande 73.8% 73.2% 99.2%
OpenBookQA 67.3% 66.8% 99.3%

推理加速

OPT-175B 推理性能

GPU FP16 SmoothQuant 加速比
NVIDIA A100 12.3 tok/s 18.6 tok/s 1.51×
NVIDIA A6000 8.7 tok/s 13.5 tok/s 1.55×
NVIDIA V100 5.2 tok/s 7.8 tok/s 1.50×

BLOOM-176B 推理性能

GPU FP16 SmoothQuant 加速比
NVIDIA A100 11.8 tok/s 18.4 tok/s 1.56×
NVIDIA A6000 8.3 tok/s 12.8 tok/s 1.54×

内存减少

模型 FP16 内存 SmoothQuant 内存 减少
OPT-175B 350 GB 175 GB
BLOOM-176B 352 GB 176 GB
GLM-130B 260 GB 130 GB

实际影响

  • 530B 参数模型可在单节点运行(FP16 需要多节点)
  • 消费级 GPU 可运行更大模型
  • 云服务成本显著降低

工业界采用

NVIDIA TensorRT-LLM (2023)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TensorRT-LLM 集成:

import tensorrt_llm
from tensorrt_llm.quantization import SmoothQuant

# 配置 SmoothQuant 量化
config = SmoothQuantConfig(
model="llama-70b",
act_scale=0.5, # alpha 参数
per_channel=True
)

# 构建量化引擎
engine = build_engine(config)

# 推理性能提升 1.5-1.8×

Microsoft ONNX Runtime (2024)

1
2
3
4
5
6
7
8
9
10
ONNX Runtime 配置:

{
"quantization": {
"method": "smoothquant",
"activation_type": "int8",
"weight_type": "int8",
"per_channel": true
}
}

Amazon SageMaker (2023)

  • SageMaker JumpStart 支持 SmoothQuant
  • 一键部署量化模型
  • 成本降低 40-50%

与其他量化方法对比

方法对比

方法 类型 位宽 需要训练 精度保持
SmoothQuant PTQ W8A8 99.5%
LLM.int8() PTQ W8A8 98%
AWQ PTQ W4A16 99%
GPTQ PTQ W4A16 99%
QLoRA QAT W4A16 99.8%

SmoothQuant 的优势

  1. 无需训练:除了小校准集外,不需要微调
  2. 全 INT8:权重和激活都是 INT8,最大化加速
  3. 广泛支持:被主要推理框架采用
  4. 理论保证:数学等价变换,无信息损失

局限性

  1. 位宽限制:INT4 精度下降明显
  2. 平滑因子敏感:需要仔细选择 alpha
  3. 更新方法出现:AWQ/GPTQ 在 W4 上表现更好

实践指南

量化步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from smoothquant.quantization import SmoothQuantizer

# 步骤 1: 加载模型
model = load_model("opt-175b")

# 步骤 2: 准备校准数据
calibration_data = load_calibration_dataset("pile", num_samples=512)

# 步骤 3: 创建量化器
quantizer = SmoothQuantizer(
model=model,
alpha=0.5, # 平滑强度
per_channel=True
)

# 步骤 4: 校准
quantizer.calibrate(calibration_data)

# 步骤 5: 量化权重
quantizer.quantize_weights()

# 步骤 6: 保存量化模型
quantizer.save("opt-175b-smoothquant-int8")

# 步骤 7: 推理
model_int8 = load_quantized_model("opt-175b-smoothquant-int8")
output = model_int8.generate(prompt, max_length=100)

校准数据集选择

数据集 样本数 适用场景
Pile 512 通用任务
C4 512 语言建模
CodeParrot 512 代码任务
领域特定 256-512 垂直领域

超参数建议

参数 推荐值 说明
alpha 0.5 平衡权重和激活动态范围
校准样本 512 更多样本提升精度但增加时间
per_channel True 逐通道平滑效果更好

代码实现

平滑层实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import torch
import torch.nn as nn

class SmoothQuantLinear(nn.Module):
"""SmoothQuant 线性层"""

def __init__(self, linear_layer, smooth_factor, quantize=True):
super().__init__()
self.linear = linear_layer
self.register_buffer('smooth_factor', smooth_factor)
self.quantize = quantize

# 平滑权重
with torch.no_grad():
self.linear.weight.mul_(smooth_factor.unsqueeze(0))

def forward(self, x):
# 平滑激活
x = x / self.smooth_factor

# 量化(如果启用)
if self.quantize:
x = self.quantize_activation(x)
weight = self.quantize_weight(self.linear.weight)
else:
weight = self.linear.weight

return nn.functional.linear(x, weight)

def quantize_activation(self, x):
# INT8 量化激活
scale = x.abs().max() / 127
x_int8 = (x / scale).round().clamp(-128, 127).to(torch.int8)
return x_int8 * scale

def quantize_weight(self, w):
# INT8 量化权重
scale = w.abs().max() / 127
w_int8 = (w / scale).round().clamp(-128, 127).to(torch.int8)
return w_int8 * scale

总结

SmoothQuant 是 LLM 量化领域的里程碑工作:

核心贡献

  1. 发现并解决了 LLM 激活离群值问题
  2. 提出数学等价的平滑变换
  3. 实现无需训练的 W8A8 量化
  4. 被工业界广泛采用

实际影响

  • 使 175B+ 模型能在单节点运行
  • 推理加速 1.5-1.6 倍
  • 内存减少 2 倍
  • 降低 LLM 部署成本

历史地位

  • ICML 2023 发表
  • 引用量 3000+
  • 成为行业标准方法
  • 启发了后续 AWQ、GPTQ 等工作

资源


评分: 4.8/5.0 ⭐⭐⭐⭐⭐

推荐度: 强烈推荐。LLM 部署必备技术,工业界标准方案。

© 2026 Generative AI Discovery All Rights Reserved.
Theme by hiero