209 lines
4.7 KiB
Markdown
209 lines
4.7 KiB
Markdown
# 量化基础与INT8
|
||
|
||
## 量化概述
|
||
|
||
量化(Quantization)是将模型参数和计算从高精度(FP32/FP16)转换为低精度(INT8/INT4/INT2)表示的技术,目的是降低显存占用、加速推理、减少计算量。
|
||
|
||
### 量化优势
|
||
|
||
| 精度 | 内存占用 | 计算量(相对FP32) | 精度损失 |
|
||
|------|---------|-------------------|---------|
|
||
| FP32 | 4 bytes/param | 1x | 无 |
|
||
| FP16/BF16 | 2 bytes/param | ~1x | 极小 |
|
||
| INT8 | 1 byte/param | ~0.25x | 较小 |
|
||
| INT4 | 0.5 byte/param | ~0.125x | 中等 |
|
||
| INT2 | 0.25 byte/param | ~0.0625x | 较大 |
|
||
|
||
## 量化基本原理
|
||
|
||
### 量化映射
|
||
|
||
将连续值映射到离散值域:
|
||
|
||
```
|
||
x_float = scale * x_quant + zero_point
|
||
x_quant = round((x_float - zero_point) / scale)
|
||
```
|
||
|
||
其中:
|
||
- `scale`:量化缩放因子
|
||
- `zero_point`:零点(用于非对称量化)
|
||
|
||
### 量化精度类型
|
||
|
||
#### 1. 对称量化(Symmetric Quantization)
|
||
|
||
使用零对称的映射,zero_point = 0:
|
||
|
||
```
|
||
x_quant = round(x_float / scale)
|
||
x_float = x_quant * scale
|
||
|
||
scale = max(|x_float|) / 127 (INT8范围: -127~127)
|
||
```
|
||
|
||
**特点**:
|
||
- 数值范围关于0对称
|
||
- 适合权重分布接近正态分布的情况
|
||
- 实现简单
|
||
|
||
#### 2. 非对称量化(Asymmetric Quantization)
|
||
|
||
允许zero_point非0:
|
||
|
||
```
|
||
scale = (max(x_float) - min(x_float)) / 255
|
||
zero_point = -min(x_float) / scale
|
||
|
||
x_quant = round((x_float - zero_point) / scale)
|
||
x_float = scale * x_quant + zero_point
|
||
```
|
||
|
||
**特点**:
|
||
- 可处理偏置分布
|
||
- 适合激活值(通常不是零对称)
|
||
|
||
## 量化粒度
|
||
|
||
### Per-Tensor量化
|
||
|
||
整个张量使用同一个scale和zero_point:
|
||
|
||
```
|
||
scale = max(|W|) / 127
|
||
W_quant = round(W / scale)
|
||
```
|
||
|
||
**优点**:实现简单,通信量小
|
||
**缺点**:精度损失较大(受最值限制)
|
||
|
||
### Per-Channel量化
|
||
|
||
按通道(layer)分别设置scale:
|
||
|
||
```
|
||
对于Linear层 weight shape: (out_features, in_features)
|
||
每个out_channel有独立的scale
|
||
```
|
||
|
||
**优点**:精度更高,每个通道自适应范围
|
||
**缺点**:通信量增加(需传输多个scale)
|
||
|
||
### Per-Token量化
|
||
|
||
按token维度量化(主要用于激活值):
|
||
|
||
```
|
||
每行(sequence length维度)有独立scale
|
||
适合Attention score等动态范围大的张量
|
||
```
|
||
|
||
## INT8推理流程
|
||
|
||
### 推理框架
|
||
|
||
```
|
||
Input FP32 → Quantize → INT8 GEMM → Dequantize → Output FP32
|
||
↓
|
||
Lookup Table (可选)
|
||
```
|
||
|
||
### 核心计算:INT8 GEMM
|
||
|
||
硬件支持INT8矩阵乘法加速(如NVIDIA Tensor Core):
|
||
- INT8 输入: (M, K) ⊗ (K, N) → INT32 累加
|
||
- INT32 → FP16/FP32 反量化输出
|
||
|
||
### 量化流程示例
|
||
|
||
```python
|
||
# 权重量化
|
||
W_fp32 = weight.data
|
||
W_max = W_fp32.abs().max()
|
||
W_scale = W_max / 127.0
|
||
W_int8 = (W_fp32 / W_scale).round().clamp(-128, 127)
|
||
|
||
# 激活量化(动态)
|
||
X_fp32 = input.data
|
||
X_scale = X_fp32.abs().max(dim=-1, keepdim=True)[0] / 127.0
|
||
X_int8 = (X_fp32 / X_scale).round().clamp(-128, 127)
|
||
|
||
# INT8矩阵乘法
|
||
Y_int32 = F.linear(X_int8, W_int8, bias=None)
|
||
|
||
# 反量化
|
||
Y_fp32 = Y_int32 * X_scale * W_scale
|
||
```
|
||
|
||
## 量化误差分析
|
||
|
||
### 量化误差来源
|
||
|
||
1. **截断误差**:超出量化范围的值被截断
|
||
2. **舍入误差**:round操作的近似误差
|
||
3. **尺度误差**:scale选取不最优
|
||
|
||
### 误差度量
|
||
|
||
```python
|
||
# 量化误差计算
|
||
error = W_fp32 - W_dequantized # 反量化后误差
|
||
mse = (error ** 2).mean()
|
||
snr = 10 * torch.log10(var(W_fp32) / var(error))
|
||
```
|
||
|
||
### 误差传播
|
||
|
||
量化误差在网络中的传播:
|
||
- **卷积层**:误差累积,但可被激活函数平滑
|
||
- **残差连接**:误差直接累积,影响显著
|
||
- **Softmax**:指数放大误差,需特别注意
|
||
|
||
## 训练后量化(Post-Training Quantization, PTQ)
|
||
|
||
### 静态量化(Static Quantization)
|
||
|
||
预计算激活值范围,需要校准数据集:
|
||
|
||
```python
|
||
model.eval()
|
||
model.qconfig = QConfig(activation=None, weight=None) # 指定量化配置
|
||
torch.quantization.prepare(model, inplace=True)
|
||
# 校准
|
||
with torch.no_grad():
|
||
for data in calibration_loader:
|
||
model(data)
|
||
torch.quantization.convert(model, inplace=True)
|
||
```
|
||
|
||
### 动态量化(Dynamic Quantization)
|
||
|
||
运行时动态决定激活范围:
|
||
|
||
```python
|
||
# 权重静态量化,激活动态量化
|
||
model_dynamic = torch.quantization.quantize_dynamic(
|
||
model, {nn.Linear}, dtype=torch.qint8
|
||
)
|
||
```
|
||
|
||
## 量化感知训练(Quantization-Aware Training, QAT)
|
||
|
||
在前向传播中模拟量化效果:
|
||
|
||
```python
|
||
# Fake quantization
|
||
x_quant = (x / scale).round() * scale
|
||
|
||
# 使用STE (Straight-Through Estimator) 反向传播
|
||
# gradient = gradient.round() * scale ≈ gradient
|
||
```
|
||
|
||
### QAT vs PTQ
|
||
|
||
| 特性 | PTQ | QAT |
|
||
|------|-----|-----|
|
||
| 训练需求 | 不需要 | 需要微调 |
|
||
| 精度 | 较低 | 较高 |
|
||
| 成本 | 低 | 中等 |
|
||
| 适用场景 | 快速部署 | 追求精度 | |