模型量化(Quantization)解释
·
量化(Quantization)详解
1. 量化的本质:降低数据精度
量化就是把高精度的浮点数(float32)转换成低精度的整数(int8/int16)。
最直观的例子:
# 原始数据(float32,32位,4字节)
float32: 3.14159265 # 可以表示很精细的数值
↓
# 量化后(int8,8位,1字节)
int8: 3 # 只能表示整数
存储空间减少:4 bytes → 1 byte = 75% 压缩!
2. 为什么需要量化?
实际需求:
# 训练阶段:float32
模型大小: 280 MB
推理速度: 30 ms/frame
功耗: 高
硬件需求: GPU (高算力)
# 部署阶段(量化后int8)
模型大小: 70 MB (↓ 75%)
推理速度: 8 ms/frame (↑ 3.75x)
功耗: 低
硬件需求: Horizon BPU (嵌入式芯片)
核心驱动力:
- ✅ 车载硬件算力有限(Horizon BPU不是GPU)
- ✅ 实时性要求(30FPS,每帧<33ms)
- ✅ 功耗限制(车载不能像数据中心那样散热)
- ✅ 成本控制(边缘芯片比GPU便宜很多)
3. 量化的数学原理
基本公式:
# 量化过程(float → int)
x_float = 原始浮点数值 # 例如: 3.7854
scale = 缩放因子 # 例如: 0.05
zero_point = 零点 # 例如: 0
x_int8 = round(x_float / scale) + zero_point
= round(3.7854 / 0.05) + 0
= round(75.708) + 0
= 76
# 反量化过程(int → float)
x_float_recovered = (x_int8 - zero_point) * scale
= (76 - 0) * 0.05
= 3.8
误差 = |3.7854 - 3.8| = 0.0146 (可接受)
可视化量化过程:
原始float32范围: [-10.0, 10.0]
量化到int8范围: [-128, 127]
计算scale:
scale = (max - min) / (127 - (-128))
= (10.0 - (-10.0)) / 255
= 20.0 / 255
= 0.0784
映射关系:
Float32 Int8
───────────────────────
10.0 → 127
5.0 → 64
0.0 → 0
-5.0 → -64
-10.0 → -128
可视化:
Float32: |----●----●----●----●----| (连续的)
-10 -5 0 5 10
Int8: |●|●|●|●|●|●|●|●|●|●|●|●| (离散的256个值)
-128 0 127
4. 量化的两种方式
PTQ (Post-Training Quantization) - 训练后量化
# PTQ流程
--quant_method ptq # 指定使用PTQ
--calib_num 150 # 使用150张图片校准
步骤1: 加载训练好的float32模型
model = load_checkpoint("mapformer.pth")
步骤2: 插入量化节点(FakeQuantize)
calib_model = prepare_calibration_fx(model)
# FakeQuantize: 模拟量化但仍用float32计算
步骤3: 校准(Calibration)- 统计数据分布
for i, data in enumerate(calib_dataloader):
if i >= 150: # calib_num
break
with torch.no_grad():
calib_model(data) # 前向传播,收集统计信息
# 收集什么?
# - 每层激活值的min/max
# - 权重的min/max
# - 计算最佳scale和zero_point
步骤4: 确定量化参数
scale = (max_val - min_val) / 255
步骤5: 转换成真正的int8模型
quantized_model = convert_fx(calib_model)
PTQ的特点:
- ✅ 简单快速:不需要重新训练
- ✅ 只需少量数据:150张图片校准即可
- ❌ 精度损失较大:直接量化可能掉点2-5%
- 适合:快速验证、原型开发
QAT (Quantization-Aware Training) - 量化感知训练
# QAT训练
bash run_dist.sh --config <cfg> --work-dir <dir> --mode train --qat_v2
步骤1: 从float32模型开始
model = BEVRoadGeometry()
步骤2: 插入FakeQuantize节点
qat_model = prepare_qat_fx(model)
步骤3: 完整训练过程(模拟量化)
for epoch in range(100):
for data in train_dataloader:
# 前向传播
output = qat_model(data)
# FakeQuantize的工作:
# 1. 把激活值量化到int8再反量化回float32
# 2. 梯度可以正常回传
fake_quant(x):
x_int8 = quantize(x, scale)
x_float = dequantize(x_int8, scale)
return x_float # 仍然是float,但模拟了int8的精度损失
# 反向传播 - 网络学会适应量化误差
loss.backward()
optimizer.step()
步骤4: 转换成真正的int8模型
quantized_model = convert_fx(qat_model)
QAT的特点:
- ✅ 精度损失小:网络学会适应量化,通常<1%掉点
- ✅ 鲁棒性好:对量化参数不敏感
- ❌ 训练成本高:需要完整训练流程(几天到几周)
- 适合:生产部署、对精度要求高
5. 量化对不同层的影响
# 卷积层(权重 + 激活都量化)
Conv2d: float32 → int8
权重量化:
原始: weight (128, 64, 3, 3) float32 = 294,912 bytes
量化: weight (128, 64, 3, 3) int8 = 73,728 bytes (↓75%)
激活量化:
原始: activation (B, 128, 64, 128) float32
量化: activation (B, 128, 64, 128) int8
计算:
原始: float32 × float32 = float32 (慢)
量化: int8 × int8 = int32 → rescale to int8 (快4-8倍)
# BatchNorm层(通常融合到卷积)
BN参数:
γ, β → 融合到Conv的权重和bias中
不需要单独量化
# ReLU层(量化友好)
ReLU: max(0, x)
int8下: max(0, x_int8) # 仍然很简单
# Sigmoid/Softmax(量化不友好)
需要查找表(LUT):
Sigmoid(x) → 用分段线性近似
在BPU上用SegmentLUT实现
6. 量化精度损失分析
# 精度对比(从实际项目经验)
Float32 PTQ(int8) QAT(int8)
─────────────────────────────────────────────
Lane mAP 0.79 0.74 0.78
Inference(ms) 30 10 8
Model Size(MB) 280 70 70
BPU Support ❌ ✅ ✅
精度损失来源:
1. 权重量化: ±0.5% - 1%
2. 激活量化: ±1% - 2%
3. 非线性操作(Sigmoid/Softmax): ±0.5% - 1.5%
4. 累积误差: ±0.5%
总损失: PTQ ≈ 3-5%, QAT ≈ 0.5-1.5%
7. 总结
量化 = 用更少的比特表示数值
- 原理: Float32 → Int8
- 32位浮点 → 8位整数
- 通过scale和zero_point映射
- 保留重要的数值范围
- 通过scale和zero_point映射
- 保留重要的数值范围
- 收益:
- 模型大小: ↓ 75%
- 推理速度: ↑ 3-8x
- 功耗: ↓ 60-80%
- 支持嵌入式部署
- 代价:
- 精度损失: PTQ 3-5%, QAT 0.5-1.5%
- 需要校准数据
- QAT需要重新训练
- 方法选择:
- PTQ: 快速验证、原型
- QAT: 生产部署、高精度要求
核心理解:
- 量化是用精度换速度和效率的技术
- 对于自动驾驶等边缘部署场景必不可少
- PTQ简单但掉点多,QAT慢但效果好
- Horizon这样的专用芯片需要量化才能发挥最大性能
更多推荐
所有评论(0)