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就是模型训完之后,直接做量化。流程很简单:

  1. 加载训练好的FP32模型
  2. 用一小部分校准数据跑一遍前向
  3. 统计每层激活值的分布(min/max或者直方图)
  4. 根据统计结果计算量化参数(scale和zero_point)
  5. 把权重和激活值都转成INT8

PTQ最大的优点是。我做过一个OCR模型,PTQ只花了10分钟,精度掉了不到1%。但缺点也很明显——如果模型对量化特别敏感,PTQ可能直接崩掉。

4.5.2 量化感知训练(QAT)

QAT是在训练过程中就模拟量化操作。模型前向时,权重和激活值会被“伪量化”(先量化再反量化),这样模型就能学会适应量化带来的误差。

QAT的流程:

  1. 在FP32模型里插入伪量化节点(FakeQuantize)
  2. 用原始训练数据继续训练几个epoch
  3. 训练完成后,把伪量化节点转成真正的量化操作
  4. 导出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 本章知识体系

为了让你更直观地理解量化技术的全貌,我画了一张图:

模型量化技术全景图 模型量化 精度类型 训练方式 量化路线 INT8 FP16 混合精度训练 模型蒸馏 PTQ(后训练量化) QAT(量化感知训练) 关键要点 • 校准数据集要选对 • 对称/非对称量化选择 选择策略 • 先PTQ快速验证 • 精度不够再上QAT 目标:在精度损失可控的前提下,最大化推理速度

这张图把量化技术的核心脉络理清楚了。从精度类型到训练方式,再到量化路线,每一步都有对应的技术和策略。你可以在实际项目中对照这张图,快速定位自己该用哪种方案。

4.8 小结

量化技术是边缘端部署模型的必修课。INT8和FP16各有优劣,混合精度训练能帮你省显存,蒸馏和量化可以组合使用。PTQ和QAT的选择,取决于你对精度的容忍度和时间成本。

我个人建议:先跑PTQ,不行再上QAT。别一上来就搞复杂的方案,很多时候简单的PTQ就能满足需求。记住,在工程里,够用就好

一句话总结:量化不是玄学,是工程。多试几次,找到最适合你模型和硬件的方案。


公众号:蓝海资料掘金营,微信deep3321