第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量化流程:

  1. 在PC上用Keras或ONNX训练模型
  2. 导出为.h5或.onnx格式
  3. 用STM32CubeAI的量化工具进行校准
  4. 生成C代码并部署到STM32

我记得有一次,一个学员用PTQ量化了一个人脸识别模型,在STM32F767上跑,精度从98%掉到了85%。后来改用QAT,精度回到了96%。你看,选对量化策略有多重要。

4.5 避坑指南

做量化这么多年,我踩过的坑不少。分享几个最典型的:

  • 校准数据集太小:我曾经只用10张图片做校准,结果量化后模型直接崩了。建议至少用100-500张,覆盖各种场景。
  • 忽略激活值范围:有些层的激活值范围特别大(比如softmax的输出),直接量化会损失大量信息。建议先做clip处理。
  • 量化后不验证:量化完一定要在目标芯片上跑一遍验证集。PC上的模拟结果和实际芯片上的结果可能有差异。

我的建议:如果你刚开始做STM32上的AI模型量化,先从PTQ入手,跑通整个流程。等熟悉了,再尝试QAT。别一上来就搞复杂的,容易劝退。

好了,这一章的内容就到这里。量化是个实践性很强的东西,光看理论没用。下一章我会带大家动手,用STM32CubeAI实际量化一个模型,到时候你们就能感受到量化的魔力了。