模型优化概述
做深度学习这么久,我一直觉得模型优化是个“四两拨千斤”的活儿。你辛辛苦苦训出一个大模型,精度挺高,可一部署就傻眼——手机跑不动,延迟爆表,内存撑爆。这时候你才发现,优化不是锦上添花,是生存刚需。
这一章,我们先把底牌摊开:优化到底在追什么目标?会遇到哪些坑?用什么尺子量好坏?
优化目标
说白了,优化就是在资源受限的条件下,让模型表现得尽可能好。我总结为三个词:小、快、准。
- 小 —— 模型体积小,参数量少,存储占用低。这样才好塞进手机、嵌入式设备。
- 快 —— 推理延迟低,吞吐量高。用户可不想等两秒才看到识别结果。
- 准 —— 精度尽量不掉。你不能为了省内存让分类准确率掉5个点,那还不如不用。
这三个目标经常打架。你想想看,剪枝掉一半参数,速度上去了,但精度可能跳水。量化到int8,体积小了,但推理时数值误差可能累积。我记得有个项目里,业务要求模型从500MB压到50MB以内,同时推理延迟<30ms,精度下降<1%。嗯,这就是典型的“既要又要”。
| 目标维度 | 典型目标值 | 常用手段 |
|---|---|---|
| 模型体积 | 从GB级降到MB级 | 剪枝、量化、知识蒸馏 |
| 推理速度 | 单帧<50ms(移动端) | 算子融合、内存布局优化、量化 |
| 精度保持 | Top-1下降<0.5% | 感知量化训练、蒸馏损失设计 |
我的经验:别一上来就堆优化技术。先摸清业务上哪个目标最要紧,是体积还是速度?我曾经做过一个安防项目,客户只关心延迟,模型大点无所谓。那量化精度掉1个点也可以接受,但剪枝带来的速度提升不明显,最终我们只做了混合精度和算子融合。
常见挑战
优化路上没那么多坦途。我踩过的坑,大概能列一张清单。
- 过拟合 —— 数据量不够,优化后的小模型更容易记住噪声。
- 计算资源限制 —— GPU显存只有8GB,你想做更大batch的量化校准?没门。
- 硬件兼容性 —— 有些NPU不支持某些算子,量化后的自定义算子没法跑。
- 量化误差累积 —— 尤其是小模型,量化后某些层的激活值分布偏移严重,精度直接崩。
- 回退成本 —— 你辛辛苦苦优化了一个月,结果精度不达标,得重训。我遇到过类似情况,教训就是优化之前先做精度基线复现。
避坑指南:千万不要假设优化后的模型和原模型完全等价。我曾有一次把ResNet-50量化到int8,在验证集上精度只掉0.3%,高兴坏了。但一上真实场景,图像噪声大,精度直接掉了2%。后来才明白,量化对数据分布敏感,离线校准的数据集要尽量覆盖真实分布。
评估指标
没有度量就没有优化。我习惯从四个维度去评估:大小、速度、精度、资源占用。
模型大小
- 参数量(Params):单位通常是百万(M)或十亿(B)
- 存储体积(Model Size):量化后.pt或.pth文件的实际大小
推理速度
- 延迟(Latency):单次推理耗时,常用毫秒
- 吞吐量(Throughput):每秒处理的样本数,常用FPS
精度
- 分类:Top-1 / Top-5准确率
- 检测:mAP
- 分割:mIoU
资源占用
- 峰值内存(Peak Memory):推理过程中显存或内存的最大占用
- 功耗(Power):尤其在移动和边缘设备上
这些指标怎么测?我一般会用一段类似这样的脚本快速计算参数量和模型体积:
import torch
from torchvision import models
model = models.resnet18(pretrained=True)
total_params = sum(p.numel() for p in model.parameters())
print(f"参数量: {total_params / 1e6:.2f}M")
torch.save(model.state_dict(), "resnet18.pth")
import os
size_mb = os.path.getsize("resnet18.pth") / (1024 * 1024)
print(f"模型体积: {size_mb:.2f} MB")
输出结果:参数量大约11.7M,模型体积约44.7MB(float32)。如果你量化到int8,体积会直接降到~11MB左右。
至于延迟,我建议用实际推理框架测,而不是简单用PyTorch的`time`——Python的计时受调度影响很大。最好用`torch.cuda.Event`或者专门的profiling工具。
import torch
import time
dummy_input = torch.randn(1, 3, 224, 224)
model = models.resnet18().eval()
# 预热
with torch.no_grad():
for _ in range(10):
_ = model(dummy_input)
# 正式测100次
start = time.time()
with torch.no_grad():
for _ in range(100):
_ = model(dummy_input)
end = time.time()
avg_latency = (end - start) / 100 * 1000 # ms
print(f"平均延迟: {avg_latency:.2f} ms")
核心:评估指标必须和部署环境对齐。你在4090上测1ms,换到Jetson Nano可能变50ms。所以我建议在目标硬件上做最后的指标验收。
下面这张图概括了模型优化的全貌:目标、挑战、评估之间怎么关联。
我个人习惯在做优化之前,先拿这张图把项目当前所处的位置标出来:目标优先级是什么?最大的挑战在哪?评估指标怎么量化?这样才不会走偏。
好,模型优化的大框架就说到这里。后面的章节我们会逐个深入剪枝、量化、蒸馏、算子融合……到时候再一个个拆解。记住,优化不是一锤子买卖,而是持续迭代的过程。