第4章:AI模型量化基础
各位同学,今天我们来聊聊量化。说实话,很多做嵌入式AI的朋友一听到「量化」就头大。我刚开始接触时也一样,觉得这东西玄乎得很。但后来在STM32上跑模型跑多了,发现量化其实就是个「精度换速度」的买卖。你想想看,一个模型在电脑上跑得飞起,一放到单片机就卡成PPT,这时候量化就是你的救命稻草。
4.1 浮点模型与定点模型
先搞清楚基本概念。浮点模型,就是参数用float32存的。每个参数占4个字节,精度高,但计算量大。定点模型呢,参数用int8或者int16存,占1-2个字节,计算快,但精度会损失。
我在项目中遇到过这样一个场景:一个语音识别模型,用float32在PC上准确率95%,量化到int8后掉到了91%。嗯,4个点的损失,但模型体积从20MB缩到了5MB,推理速度提升了4倍。对于STM32来说,这买卖划算。
核心区别:
- 浮点模型:高精度、大体积、慢推理
- 定点模型:低精度、小体积、快推理
- STM32上:定点模型是唯一现实选择
说白了,量化就是给每个浮点数找一个对应的整数。比如把[-1, 1]这个范围的浮点数映射到[-128, 127]的整数范围。这个过程需要两个参数:scale(缩放因子)和zero_point(零点偏移)。
// 量化公式:q = round(r / scale) + zero_point
// 反量化公式:r = (q - zero_point) * scale
// 举个例子
float r = 0.523f; // 原始浮点数
float scale = 0.01f; // 缩放因子
int zero_point = 0; // 零点偏移
int8_t q = (int8_t)(r / scale + zero_point); // q = 52
4.2 后训练量化(PTQ)
PTQ,全称Post-Training Quantization。顾名思义,就是模型训练完之后再做量化。这是最省事的方法,也是我推荐初学者先尝试的。
具体做法很简单:拿一小部分验证集数据,跑一遍推理,统计每一层激活值的分布范围,然后根据这个范围计算scale和zero_point。整个过程不需要重新训练,几行代码就能搞定。
我的经验:PTQ适合大模型(参数多、冗余度高)或者对精度要求不高的场景。我做过一个手势识别模型,PTQ后精度只掉了0.5%,完全能接受。但如果你做的是医疗影像或者工业检测,PTQ可能就不够用了。
# PyTorch PTQ示例
import torch
from torch.quantization import quantize_dynamic
model = load_my_model()
model.eval()
# 动态量化(只量化权重,不量化激活)
quantized_model = quantize_dynamic(
model,
{torch.nn.Linear, torch.nn.Conv2d},
dtype=torch.qint8
)
# 保存量化模型
torch.save(quantized_model.state_dict(), 'model_quantized.pth')
4.3 量化感知训练(QAT)
QAT,Quantization-Aware Training。这玩意儿比PTQ复杂,但效果更好。它的核心思想是:在训练过程中模拟量化操作,让模型学会适应量化带来的误差。
为什么会这样?因为PTQ是事后诸葛亮,模型训练时根本不知道后面要被量化。而QAT在训练时就给模型「打预防针」——你在前向传播时插入伪量化节点(fake quantization nodes),让梯度回传时考虑到量化误差。
注意:QAT训练时间会变长,大概增加1.5-2倍。我曾经有个项目,用QAT训练了3天,但换来的是量化后精度只掉了0.1%。值不值?看你项目对精度的要求。
# PyTorch QAT示例
import torch
from torch.quantization import prepare_qat, convert
model = load_my_model()
model.train()
# 配置QAT
model.qconfig = torch.quantization.get_default_qat_qconfig('fbgemm')
model_prepared = prepare_qat(model)
# 正常训练,但前向传播会模拟量化
for epoch in range(num_epochs):
for data, target in dataloader:
output = model_prepared(data)
loss = criterion(output, target)
loss.backward()
optimizer.step()
# 训练完成后,转换为量化模型
model_quantized = convert(model_prepared.eval())
4.4 STM32CubeAI的量化策略
终于说到重点了。STM32CubeAI的量化策略,说白了就是帮你把模型从PC端搬到MCU上。它支持两种量化方式:
| 量化方式 | 适用场景 | 精度损失 | 推荐指数 |
|---|---|---|---|
| float32 | STM32H7等高性能芯片 | 无 | ⭐⭐ |
| int8对称量化 | STM32F4/F7/G4等主流芯片 | 1-3% | ⭐⭐⭐⭐ |
| int8非对称量化 | 对精度要求较高的场景 | 0.5-1% | ⭐⭐⭐⭐⭐ |
| 混合量化 | 部分层用float,部分层用int8 | 可控 | ⭐⭐⭐ |
我个人习惯用int8非对称量化。为什么?因为STM32CubeAI对非对称量化的支持最成熟,而且精度损失最小。对称量化虽然计算更快,但遇到ReLU这种只有正值的激活函数时,会浪费一半的量化范围。
STM32CubeAI量化流程:
- 在PC上用Keras或ONNX训练模型
- 导出为.h5或.onnx格式
- 用STM32CubeAI的量化工具进行校准
- 生成C代码并部署到STM32
我记得有一次,一个学员用PTQ量化了一个人脸识别模型,在STM32F767上跑,精度从98%掉到了85%。后来改用QAT,精度回到了96%。你看,选对量化策略有多重要。
4.5 避坑指南
做量化这么多年,我踩过的坑不少。分享几个最典型的:
- 校准数据集太小:我曾经只用10张图片做校准,结果量化后模型直接崩了。建议至少用100-500张,覆盖各种场景。
- 忽略激活值范围:有些层的激活值范围特别大(比如softmax的输出),直接量化会损失大量信息。建议先做clip处理。
- 量化后不验证:量化完一定要在目标芯片上跑一遍验证集。PC上的模拟结果和实际芯片上的结果可能有差异。
我的建议:如果你刚开始做STM32上的AI模型量化,先从PTQ入手,跑通整个流程。等熟悉了,再尝试QAT。别一上来就搞复杂的,容易劝退。
好了,这一章的内容就到这里。量化是个实践性很强的东西,光看理论没用。下一章我会带大家动手,用STM32CubeAI实际量化一个模型,到时候你们就能感受到量化的魔力了。