4. 模型轻量化技术(下):量化技术实战与对比
各位同学,咱们接着聊模型轻量化。上一节讲了剪枝和蒸馏,这一节咱们把量化这块硬骨头啃下来。说实话,量化是我在边缘端落地模型时,用得最多、也最头疼的技术。为什么?因为搞不好精度就崩了,搞好了推理速度能翻倍。
4.1 量化到底在做什么?
量化说白了,就是把模型里的高精度数字(比如FP32的浮点数),用低精度的数字(比如INT8的整数)来表示。你想想看,一个FP32的数值占4个字节,一个INT8只占1个字节。模型体积直接缩到四分之一,推理速度也能快上好几倍。
但问题来了——精度会掉。我在一个工业质检项目里遇到过,模型量化后准确率从98.5%掉到了92%。嗯,这可不是小数目。所以量化不是简单的“砍精度”,而是要在速度和精度之间找平衡。
核心思路:用更少的比特数,尽可能保留原始数值的分布信息。
4.2 INT8量化 vs FP16量化
先说说两种最常见的量化精度。
| 对比项 | INT8量化 | FP16量化 |
|---|---|---|
| 存储大小 | FP32的1/4 | FP32的1/2 |
| 推理加速 | 2-4倍(取决于硬件) | 1.5-2倍 |
| 精度损失 | 较大,需要校准 | 较小,几乎无损 |
| 硬件支持 | 需要INT8指令集 | 大部分GPU原生支持 |
| 适用场景 | 边缘端、移动端 | 云端、训练阶段 |
我个人习惯是:能上INT8就上INT8,毕竟压缩比摆在那里。但如果模型对精度特别敏感,比如医疗影像或者金融风控,我会先用FP16试试水。
小技巧:FP16量化其实不需要太多额外工作,很多框架(比如PyTorch)直接加一行代码就能跑。INT8量化则需要做校准,选好校准数据集很关键。
4.3 混合精度训练
混合精度训练,说白了就是训练时用FP16,但关键地方保留FP32。为什么?因为FP16的数值范围太小,梯度容易溢出或者下溢。
我记得第一次用混合精度训练一个BERT模型,结果loss直接飞了。后来才发现,是梯度更新那一步用了FP16,数值太小直接变成0了。解决办法很简单:主权重保持FP32,前向和反向用FP16。
PyTorch里实现起来也很方便:
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()
for data, label in dataloader:
with autocast():
output = model(data)
loss = loss_fn(output, label)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
你看,就这几行代码。但效果很明显——训练速度能提升30%-50%,显存占用也少了一大截。
注意:混合精度训练不是万能的。如果你的模型很小(比如几百万参数),那收益不大。我建议参数量超过1亿的模型再用混合精度。
4.4 模型蒸馏实战
蒸馏和量化是两回事,但经常一起用。蒸馏是让一个小模型(学生)去学大模型(老师)的输出分布。量化是压缩数值精度。两者结合,效果往往1+1>2。
我做过一个实验:先用蒸馏把ResNet-50压缩成MobileNet-V2,再对MobileNet-V2做INT8量化。最终模型大小只有原来的1/10,精度只掉了0.3%。
蒸馏的核心代码其实不复杂:
# 定义蒸馏损失
def distillation_loss(student_logits, teacher_logits, labels, T=4, alpha=0.7):
# 软标签损失(KL散度)
soft_loss = nn.KLDivLoss()(
F.log_softmax(student_logits / T, dim=1),
F.softmax(teacher_logits / T, dim=1)
) * (T * T)
# 硬标签损失(交叉熵)
hard_loss = nn.CrossEntropyLoss()(student_logits, labels)
return alpha * soft_loss + (1 - alpha) * hard_loss
这里有个参数T(温度),我一般设成4。T越大,软标签的分布越平滑,学生模型学到的知识越丰富。但T太大也不行,我试过T=10,结果学生模型学了一堆噪声。
实战建议:蒸馏时,老师模型一定要先训好。我曾经偷懒,老师模型只训了50%的epoch就开始蒸馏,结果学生模型精度还不如自己从头训。
4.5 QAT vs PTQ:两种量化路线
这是量化里最核心的对比。很多同学问我:“到底该用QAT还是PTQ?”我的回答是:看你的时间预算和精度要求。
4.5.1 后训练量化(PTQ)
PTQ就是模型训完之后,直接做量化。流程很简单:
- 加载训练好的FP32模型
- 用一小部分校准数据跑一遍前向
- 统计每层激活值的分布(min/max或者直方图)
- 根据统计结果计算量化参数(scale和zero_point)
- 把权重和激活值都转成INT8
PTQ最大的优点是快。我做过一个OCR模型,PTQ只花了10分钟,精度掉了不到1%。但缺点也很明显——如果模型对量化特别敏感,PTQ可能直接崩掉。
4.5.2 量化感知训练(QAT)
QAT是在训练过程中就模拟量化操作。模型前向时,权重和激活值会被“伪量化”(先量化再反量化),这样模型就能学会适应量化带来的误差。
QAT的流程:
- 在FP32模型里插入伪量化节点(FakeQuantize)
- 用原始训练数据继续训练几个epoch
- 训练完成后,把伪量化节点转成真正的量化操作
- 导出INT8模型
QAT的效果通常比PTQ好,但代价是训练时间翻倍。我有个项目,PTQ精度掉了3%,换成QAT后只掉了0.5%。但多花了2天训练时间。
| 对比项 | PTQ | QAT |
|---|---|---|
| 实现难度 | 低,几行代码搞定 | 中,需要修改训练流程 |
| 精度损失 | 较大(1%-5%) | 较小(0.1%-1%) |
| 所需数据 | 少量校准数据(几百张) | 完整训练数据 |
| 训练时间 | 几分钟到几小时 | 额外几个epoch |
| 适用场景 | 快速验证、精度不敏感 | 精度敏感、生产部署 |
我的选择策略:先做PTQ看看精度。如果掉得不多(<1%),直接用PTQ。如果掉得厉害,再上QAT。别一上来就搞QAT,浪费时间。
4.6 量化实战避坑指南
做量化这几年,我踩过的坑不少。分享几个最典型的:
- 校准数据集要选对。我曾经用ImageNet的1000张图做校准,结果模型在真实场景下精度崩了。后来换成真实场景的图片,问题解决。
- 对称量化 vs 非对称量化。权重一般用对称量化(范围对称),激活值用非对称量化(范围不对称)。搞反了精度会掉。
- 逐层量化 vs 逐通道量化。逐通道量化精度更高,但计算量也大。我一般对卷积层用逐通道,对全连接层用逐层。
- 量化后的模型要测全链路。不只是精度,还要测推理速度、内存占用。我见过量化后速度反而变慢的情况——因为硬件不支持INT8加速。
曾经踩过的坑:有一次我把BatchNorm层也量化了,结果推理结果全错。后来才发现,BatchNorm的均值和方差在量化后需要特殊处理。建议量化时冻结BN层。
4.7 本章知识体系
为了让你更直观地理解量化技术的全貌,我画了一张图:
这张图把量化技术的核心脉络理清楚了。从精度类型到训练方式,再到量化路线,每一步都有对应的技术和策略。你可以在实际项目中对照这张图,快速定位自己该用哪种方案。
4.8 小结
量化技术是边缘端部署模型的必修课。INT8和FP16各有优劣,混合精度训练能帮你省显存,蒸馏和量化可以组合使用。PTQ和QAT的选择,取决于你对精度的容忍度和时间成本。
我个人建议:先跑PTQ,不行再上QAT。别一上来就搞复杂的方案,很多时候简单的PTQ就能满足需求。记住,在工程里,够用就好。
一句话总结:量化不是玄学,是工程。多试几次,找到最适合你模型和硬件的方案。
公众号:蓝海资料掘金营,微信deep3321