1. 模型优化器到底在优化什么
第一次看到 Model-Optimizer 这个词,很多人会下意识觉得它又是一个“调参工具”或者“训练加速库”。但真正在模型部署和推理这条链路上摸爬滚打过的人会明白,模型优化器解决的从来不是单一问题,它更像是一套贯穿训练后到上线前的“体检加手术”方案。你训练完一个模型,参数量可能从几十兆到几十个G,精度看着不错,但一放到实际业务场景里,推理延迟高、显存占用大、吞吐量上不去,这些问题不会因为你模型结构设计得漂亮就自动消失。Model-Optimizer 要做的,就是在尽量不损失精度的前提下,把模型从“能跑”变成“跑得快、跑得省、跑得稳”。
我最早接触这类工具是在一个视觉检测项目上,当时模型在服务器上单张推理要 80 多毫秒,业务要求压到 30 毫秒以内。一开始团队想的是换更小的骨干网络重新训练,但重新训练的时间成本和精度风险都太高。后来用模型优化器做了一轮量化加算子融合,推理时间直接降到 22 毫秒,精度只掉了 0.3 个百分点。这个经历让我意识到,模型优化器不是锦上添花的东西,它在很多场景下是决定项目能不能落地的关键一环。
这篇文章适合谁看?如果你正在做模型部署、推理加速、边缘端适配,或者你训练完模型之后不知道下一步该干什么,那这篇内容就是写给你的。我会从整体设计思路讲到具体实操步骤,再到常见问题的排查,尽量把我在实际项目里踩过的坑和总结出来的经验都摊开来说。不管你是刚接触模型优化的新手,还是已经用过一些优化工具的老手,都能从中找到可以直接参考的东西。
2. 整体设计思路与方案选型拆解
2.1 为什么需要一套完整的优化流水线
很多人对模型优化的理解还停留在“量化一下就行了”的阶段。但实际上,一个完整的模型优化流程至少包含四个环节:模型分析、优化策略选择、优化执行、效果验证。这四个环节缺一不可,而且顺序不能乱。模型分析是基础,你得先知道你的模型瓶颈在哪里,是计算量大、内存访问频繁,还是算子实现效率低。不知道瓶颈就盲目优化,就像医生不给病人做检查直接开药,运气好可能有效,运气不好就是白费功夫。
优化策略选择是核心决策点。量化、剪枝、蒸馏、算子融合、内存布局优化,这些策略各有各的适用场景。量化适合计算密集型模型,剪枝适合参数冗余度高的模型,蒸馏适合你有大模型但需要小模型的场景。选错了策略,不仅效果差,还可能引入难以排查的精度问题。优化执行阶段考验的是工具链的成熟度,能不能支持你的模型格式、能不能覆盖你用到的算子、能不能在目标硬件上生效,这些都是实际问题。效果验证则是最容易被忽视的一环,很多人优化完跑了个精度测试就结束了,但实际上你还需要验证推理速度、内存占用、功耗表现,甚至在不同 batch size 下的稳定性。
2.2 量化、剪枝与蒸馏的取舍逻辑
量化是我用得最多的优化手段,因为它通用性强、收益直观。量化的本质是用更低的数值精度来表示模型参数和激活值,比如从 FP32 降到 FP16 或者 INT8。FP16 量化通常能带来 1.5 到 2 倍的推理加速,精度损失几乎可以忽略。INT8 量化加速比更高,能到 2 到 4 倍,但精度损失就需要仔细评估了。我一般会先做 FP16 量化作为基线,如果速度还不够再尝试 INT8。INT8 量化又分对称量化和非对称量化,对称量化实现简单但对数据分布要求高,非对称量化更灵活但计算稍微复杂一点。
剪枝的逻辑和量化完全不同。剪枝是直接把模型中“不重要”的参数去掉,让模型变得更稀疏。结构化剪枝去掉整个通道或者整个层,非结构化剪枝只去掉单个参数。结构化剪枝对硬件友好,因为稀疏结构规整,但剪枝率通常做不高。非结构化剪枝剪枝率可以很高,但需要专门的稀疏计算库支持,否则加速效果有限。我在实际项目中一般先用结构化剪枝去掉明显冗余的通道,再配合量化做进一步压缩。
蒸馏则是另一种思路,它不直接修改原模型,而是用原模型(教师模型)去指导一个小模型(学生模型)训练。蒸馏的好处是学生模型的结构可以完全重新设计,不受原模型限制。但蒸馏需要重新训练,时间成本高,而且需要精心设计损失函数和训练策略。我一般只在量化加剪枝都达不到要求时才考虑蒸馏,因为它引入的不确定性最大。
2.3 工具链选型的几个关键考量
选模型优化工具,我主要看四个维度:模型格式支持、硬件后端覆盖、算子兼容性、社区活跃度。模型格式方面,ONNX 是目前最通用的中间表示,几乎所有的优化工具都支持 ONNX 输入输出。如果你的模型是 PyTorch 或 TensorFlow 原生格式,最好先转成 ONNX 再做优化,这样工具选择面会宽很多。硬件后端方面,你要明确你的目标部署环境是 GPU、CPU 还是专用加速器。不同工具对不同硬件的支持程度差异很大,有的工具在 GPU 上效果很好,到了 CPU 上就几乎没加速。
算子兼容性是实际落地时最容易出问题的地方。你的模型里可能用了一些自定义算子或者比较少见的算子,优化工具如果不支持这些算子,要么直接报错,要么回退到未优化版本,导致优化效果大打折扣。我在选工具之前一般会先导出模型的算子列表,然后对照工具的算子支持文档逐个确认。社区活跃度也很重要,活跃的社区意味着你遇到问题更容易找到解决方案,工具本身也在持续迭代。一个半年不更新的工具,即使功能看起来再强大,我也不会优先考虑。
3. 核心细节解析与实操要点
3.1 模型分析阶段到底要看哪些指标
模型分析不是简单地跑个summary()看看参数量就完事了。你需要关注的核心指标包括:每层计算量(FLOPs)、每层内存访问量、算子类型分布、激活值分布、权重分布。FLOPs 告诉你计算瓶颈在哪里,内存访问量告诉你带宽瓶颈在哪里。很多时候模型推理慢不是因为计算量大,而是因为内存访问太频繁。算子类型分布帮你判断优化工具能不能覆盖你的模型,如果模型里大量使用了自定义算子,那优化难度会直线上升。
激活值分布和权重分布是量化前必须看的。激活值分布决定了量化时的截断阈值怎么选,权重分布决定了量化粒度是 per-tensor 还是 per-channel。我一般会用校准数据集跑一遍推理,收集每一层的激活值范围,然后画出直方图。如果某一层的激活值分布特别集中,那量化起来就比较容易;如果分布很分散,甚至有一些极端离群值,那就需要特别处理,否则量化后精度会掉得很厉害。
# 以 PyTorch 为例,收集激活值分布的基本思路 import torch from collections import defaultdict activation_stats = defaultdict(list) def hook_fn(name): def hook(module, input, output): activation_stats[name].append(output.detach().cpu()) return hook # 注册 hook for name, module in model.named_modules(): if isinstance(module, torch.nn.Conv2d): module.register_forward_hook(hook_fn(name)) # 跑校准数据 with torch.no_grad(): for data in calibration_loader: model(data) # 分析分布 for name, acts in activation_stats.items(): all_acts = torch.cat(acts, dim=0) print(f"{name}: min={all_acts.min():.4f}, max={all_acts.max():.4f}, " f"mean={all_acts.mean():.4f}, std={all_acts.std():.4f}")这段代码只是最基础的版本,实际项目中你还需要考虑 batch 维度怎么处理、要不要做滑动平均、离群值怎么识别等等。但核心思路就是通过 hook 拿到每一层的输出,然后统计分布特征。
3.2 量化校准的实操细节
量化校准是决定量化效果的关键步骤。校准的本质是用一批有代表性的数据跑一遍模型,收集每一层的激活值范围,然后根据这个范围确定量化参数。校准数据集的选择很重要,它必须能代表实际推理时遇到的数据分布。我一般会从训练集或者验证集里随机采样 100 到 500 个样本作为校准集,样本数量不用太多,但分布要均匀。
校准算法也有讲究。最简单的 min-max 校准直接用激活值的最大值和最小值作为量化范围,实现简单但对离群值敏感。KL 散度校准会寻找一个最优截断阈值,使得量化前后的分布差异最小,效果通常更好但计算量稍大。还有基于百分位的校准,比如取 99.9% 分位数作为截断阈值,能有效过滤离群值。我在实际项目中一般先用 min-max 跑一版看精度,如果精度掉得多再换 KL 散度或者百分位校准。
注意:校准集一定不能和测试集重合,否则你看到的精度指标是虚高的。另外校准集的数量也不是越多越好,超过一定数量后收益递减,反而浪费时间。
还有一个容易被忽视的点是量化粒度。Per-tensor 量化对整个张量用同一组量化参数,实现简单但精度损失大。Per-channel 量化对每个通道单独计算量化参数,精度更好但需要硬件支持。对于卷积层,我强烈建议用 per-channel 量化,尤其是深度可分离卷积,per-tensor 量化几乎一定会出问题。
3.3 算子融合的常见模式与收益
算子融合是另一种非常有效的优化手段,它的核心思想是把多个连续的小算子合并成一个大的算子,减少 kernel launch 开销和中间结果的读写。最常见的融合模式包括:Conv + BN + ReLU 融合、Conv + Add 融合、MatMul + Add 融合。Conv + BN + ReLU 融合是最经典的,因为这三个算子几乎总是连续出现,融合后只需要一次计算就能完成原本三次计算的工作。
融合的收益在 GPU 上尤其明显,因为 GPU 的 kernel launch 开销相对较大。我实测过一个 ResNet-50 模型,做完 Conv + BN + ReLU 融合后,推理速度提升了大约 15%。在 CPU 上收益会小一些,因为 CPU 的 kernel launch 开销本来就低,但融合后减少的中间结果读写仍然能带来一定加速。
融合的难点在于融合规则的编写和验证。不同的框架和工具对融合的支持程度不一样,有的工具能自动识别融合模式,有的需要你手动指定。手动指定融合规则时,一定要仔细验证融合后的计算结果和融合前是否一致。我一般会先用小规模数据跑一遍对比,确认数值误差在可接受范围内再全量应用。
3.4 内存布局优化的隐藏收益
内存布局优化是很多人会忽略的一个点,但它带来的收益有时候比量化还大。内存布局优化的核心是让数据在内存中的排列方式更符合硬件的访问模式。比如在 GPU 上,NHWC 布局通常比 NCHW 布局对卷积计算更友好,因为 GPU 的显存访问更倾向于连续的大块读取。在 CPU 上,情况可能反过来,具体要看 CPU 的缓存结构和 SIMD 指令集支持。
我遇到过一个案例,模型量化后推理速度只提升了 20%,远低于预期。后来发现是内存布局的问题,把 NCHW 改成 NHWC 后,速度直接又提升了 40%。这个经历告诉我,优化不能只盯着量化,内存布局、算子融合、线程调度这些都要综合考虑。
4. 完整实操流程与核心环节实现
4.1 环境准备与工具安装
在开始优化之前,你需要把环境准备好。我一般会创建一个独立的虚拟环境,避免和系统环境或者其他项目的依赖冲突。Python 版本建议用 3.8 到 3.10,太新的版本可能有些工具还没适配,太旧的版本又缺少一些新特性。
# 创建虚拟环境 python -m venv model_optimizer_env source model_optimizer_env/bin/activate # Linux/Mac # model_optimizer_env\Scripts\activate # Windows # 安装基础依赖 pip install torch torchvision onnx onnxruntime pip install numpy matplotlib tqdm # 安装优化工具(以 ONNX Runtime 为例) pip install onnxruntime-gpu # GPU 版本 # pip install onnxruntime # CPU 版本安装完成后,建议跑一个简单的验证脚本,确认工具能正常加载模型并执行推理。这一步看起来简单,但能帮你提前发现环境配置问题,避免后面调试优化效果时被环境问题干扰。
4.2 模型导出与格式转换
模型导出是优化流程的第一步。以 PyTorch 为例,你需要把模型导出成 ONNX 格式。导出时要注意几个关键参数:opset_version 决定了 ONNX 的算子集版本,一般选 11 到 13 之间的版本兼容性最好;input_names 和 output_names 要明确指定,方便后续推理时调用;dynamic_axes 用于指定动态维度,如果你的模型需要支持变长输入,这个参数必须设置。
import torch import torch.onnx # 假设 model 是你的 PyTorch 模型,dummy_input 是示例输入 model.eval() dummy_input = torch.randn(1, 3, 224, 224) torch.onnx.export( model, dummy_input, "model.onnx", opset_version=12, input_names=["input"], output_names=["output"], dynamic_axes={ "input": {0: "batch_size", 2: "height", 3: "width"}, "output": {0: "batch_size"} } )导出完成后,一定要用 ONNX 的检查工具验证模型是否合法。我遇到过好几次导出成功但模型实际有问题的情况,比如某些算子被导出成了自定义算子,或者权重数值出现了异常。用onnx.checker.check_model()可以帮你发现大部分格式问题。
4.3 量化配置与执行
量化配置是实操中最需要耐心的环节。以 ONNX Runtime 的量化工具为例,你需要配置量化类型、校准方法、量化粒度等参数。下面是一个典型的静态量化配置示例:
from onnxruntime.quantization import quantize_static, QuantType, QuantFormat from onnxruntime.quantization.calibrate import CalibrationMethod quantize_static( model_input="model.onnx", model_output="model_quantized.onnx", calibration_data_reader=calibration_reader, quant_format=QuantFormat.QDQ, # 量化-反量化格式 activation_type=QuantType.QUInt8, # 激活值用无符号 INT8 weight_type=QuantType.QInt8, # 权重用有符号 INT8 calibrate_method=CalibrationMethod.MinMax, per_channel=True, # 启用 per-channel 量化 reduce_range=False # 是否缩减量化范围 )calibration_data_reader需要你自己实现,它的作用是逐个 batch 提供校准数据。实现时要注意数据预处理要和训练时保持一致,否则校准结果会不准确。reduce_range这个参数在较老的硬件上建议设为 True,因为部分硬件对 INT8 的完整范围支持不好,缩减范围可以避免溢出问题。
量化执行完成后,你需要对比量化前后的模型大小和推理速度。模型大小一般能压缩到原来的四分之一左右,推理速度的提升取决于硬件和模型结构,通常在 2 到 4 倍之间。如果速度提升不明显,可能是量化没有真正生效,需要检查量化后的模型里是否还有 FP32 算子。
4.4 精度验证与调优
精度验证是量化流程中最关键的一步。我一般会从三个层面验证:单层输出对比、整体精度对比、业务指标对比。单层输出对比是逐层比较量化前后输出的差异,能帮你定位到具体是哪一层出了问题。整体精度对比是在验证集上跑一遍,看 top-1 或者 mAP 掉了多少。业务指标对比则是看最终的业务指标,比如检测任务里的漏检率、误检率。
如果精度掉得太多,调优的方向有几个:换校准方法、增加校准数据、调整量化粒度、对敏感层跳过量化。敏感层识别可以用敏感度分析来做,逐层量化然后看精度变化,找出对精度影响最大的层。这些层可以选择不量化,或者用更高的精度(比如 FP16)来量化。
提示:精度验证时一定要用和训练时完全一致的数据预处理流程,包括归一化参数、resize 方式、颜色空间转换等。我见过太多因为预处理不一致导致精度对不上的案例。
4.5 推理性能测试与对比
性能测试不能只看单次推理时间,还要看吞吐量、内存占用、功耗。单次推理时间受很多因素影响,比如 CPU 频率、GPU 温度、系统负载,所以一定要多次测量取平均值。我一般会跑 100 次推理,去掉前 10 次预热,然后取后 90 次的平均值和 P99 值。P99 值很重要,它能反映最差情况下的延迟表现。
吞吐量测试则是看单位时间内能处理多少样本,这个指标在批量推理场景下比单次延迟更有意义。内存占用包括模型权重占用和推理时的峰值内存占用,后者在边缘设备上尤其重要。功耗测试需要专门的硬件支持,如果你没有功耗仪,可以用 GPU 的功耗读数作为参考。
| 测试项 | 优化前 | 优化后 | 提升幅度 |
|---|---|---|---|
| 模型大小 | 98 MB | 25 MB | 3.9x |
| 单次推理延迟 | 45 ms | 18 ms | 2.5x |
| 吞吐量 (batch=8) | 120 samples/s | 380 samples/s | 3.2x |
| 峰值内存 | 520 MB | 280 MB | 1.9x |
| Top-1 精度 | 76.5% | 76.2% | -0.3% |
这张表是我在一个实际项目中记录的数据,可以看到优化后模型大小压缩了近 4 倍,推理延迟降低了 2.5 倍,精度只掉了 0.3 个百分点。这个收益在大多数业务场景下都是完全可以接受的。
5. 常见问题与排查技巧实录
5.1 量化后精度暴跌的排查思路
精度暴跌是量化中最常见的问题,排查起来需要系统性地逐层分析。第一步先确认量化是否真的生效了,有时候你以为量化了,实际上模型里还是 FP32 算子。用 Netron 打开量化后的模型,看看 Conv、MatMul 这些算子是不是变成了 QLinearConv、QLinearMatMul 或者 QDQ 格式。如果还是普通的 Conv,那说明量化没生效,需要检查量化配置。
第二步做逐层敏感度分析。把每一层单独量化,其他层保持 FP32,看精度变化。精度掉得最多的那几层就是敏感层。敏感层通常出现在网络的浅层或者深层,浅层对输入数据分布敏感,深层对语义信息敏感。找到敏感层后,可以选择跳过这些层的量化,或者对这些层用更精细的量化策略。
第三步检查校准数据。校准数据分布和实际推理数据分布不一致是精度暴跌的常见原因。我遇到过一个案例,校准集用的是白天的图片,但实际推理时有很多夜间图片,导致量化参数完全不对。解决办法是在校准集里加入各种场景的样本,确保覆盖实际推理时的数据分布。
5.2 推理速度没有提升的几种可能
优化后速度没提升,甚至变慢了,这种情况我也遇到过好几次。原因通常有这几个:第一,量化后的算子没有被硬件加速支持,回退到了 CPU 实现。比如你在 GPU 上跑 INT8 量化模型,但 GPU 不支持 INT8 计算,那就会回退到 FP32 计算,速度自然上不去。第二,内存布局不匹配,导致频繁的内存重排操作。第三,线程数配置不合理,CPU 推理时线程数太少或者太多都会影响性能。
排查方法也很直接:用 profiling 工具看每个算子的耗时,找出耗时最长的算子,然后针对性优化。ONNX Runtime 提供了 profiling 功能,可以输出每个算子的执行时间。如果发现某个算子耗时异常,就去看这个算子在目标硬件上有没有优化实现。
import onnxruntime as ort # 启用 profiling options = ort.SessionOptions() options.enable_profiling = True session = ort.InferenceSession("model_quantized.onnx", options) # 跑几次推理后 prof_file = session.end_profiling() print(f"Profiling 结果保存在: {prof_file}")5.3 算子不支持时的替代方案
算子不支持是模型优化中很头疼的问题。你的模型里可能用了某个比较新的算子,但优化工具还没支持。遇到这种情况,有几个解决思路:第一,用功能等价的算子组合来替换。比如某些激活函数可以用基础算子组合实现。第二,修改模型结构,用支持的算子重新训练。第三,自己实现自定义算子并注册到优化工具里。第一种方案成本最低,但需要你对算子语义有深入理解。第二种方案成本最高,但最彻底。第三种方案适合有底层开发能力的团队。
我一般优先尝试第一种方案,因为改动最小、验证最快。比如 Swish 激活函数在某些工具里不支持,但可以用 Sigmoid 乘以输入来实现,效果几乎一样。如果实在找不到等价替换,再考虑修改模型结构。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 量化后精度掉超过 2% | 敏感层未处理 | 逐层敏感度分析 | 跳过敏感层或使用混合精度 |
| 推理速度无提升 | 量化算子未生效 | 用 Netron 查看模型算子 | 检查硬件支持和量化配置 |
| 模型加载失败 | 算子不支持 | 查看错误日志中的算子名 | 替换算子或更新工具版本 |
| 内存占用反而增加 | 中间结果未释放 | 检查推理时的内存分配 | 优化内存复用策略 |
| 批量推理时延迟波动大 | 线程调度问题 | 调整线程数和批处理策略 | 固定线程数或使用动态批处理 |
| 校准后精度正常但测试精度差 | 校准数据分布不匹配 | 对比校准集和测试集分布 | 重新选择校准数据 |
这张表里的问题都是我实际遇到过的,每一个都花了不少时间排查。希望这些经验能帮你少走弯路。
5.5 几个容易被忽视的实操心得
第一个心得是关于校准数据的数量。很多人觉得校准数据越多越好,但实际上 100 到 500 个样本就足够了。超过这个数量后,量化参数的改善非常有限,但校准时间会线性增长。我一般会先用 100 个样本快速跑一版,如果精度达标就不再加了。
第二个心得是关于量化顺序。如果你同时要做剪枝和量化,一定要先剪枝再量化。因为剪枝会改变模型结构,量化参数需要基于剪枝后的模型重新校准。反过来先量化再剪枝,剪枝后的模型量化参数就不准了。
第三个心得是关于版本兼容性。优化工具、推理框架、硬件驱动这三者的版本兼容性非常重要。我遇到过好几次因为版本不匹配导致优化效果异常的情况。建议在项目开始时就锁定版本,并且记录在文档里,避免后面换环境时出问题。
第四个心得是关于测试的全面性。不要只在一个 batch size 下测试,要覆盖实际业务中可能出现的各种 batch size。有些优化策略在小 batch 下效果好,大 batch 下反而变差。我一般会测试 batch size 为 1、4、8、16、32 这几个典型值,确保优化效果在不同场景下都稳定。
6. 优化策略的进阶组合与场景适配
6.1 量化加剪枝的组合策略
单独用量化或者剪枝,收益有时候不够。把两者组合起来,往往能取得更好的效果。组合的基本流程是:先做结构化剪枝去掉冗余通道,然后对剪枝后的模型做量化校准和量化。剪枝率一般控制在 20% 到 40% 之间,剪枝率太高会导致精度不可逆地下降。剪枝后需要做一轮微调,让模型恢复一部分精度,然后再量化。
组合策略的难点在于剪枝和量化的相互影响。剪枝后的模型权重分布会发生变化,量化校准需要重新做。而且剪枝后的模型可能对量化更敏感,因为剪枝已经去掉了一部分冗余,剩下的参数承担了更多的表达责任。我在实际项目中一般会预留 1 到 2 个百分点的精度余量给组合优化,如果业务对精度要求特别严格,组合策略就要谨慎使用。
6.2 不同硬件平台的适配要点
GPU 平台和 CPU 平台的优化策略差异很大。GPU 上量化收益明显,尤其是 INT8 量化,因为现代 GPU 都有专门的 INT8 计算单元。算子融合在 GPU 上收益也很大,因为 kernel launch 开销高。内存布局方面,GPU 上 NHWC 通常比 NCHW 好。CPU 上情况不同,INT8 量化的收益取决于 CPU 是否支持 VNNI 指令集,支持的话收益很大,不支持的话收益有限。算子融合在 CPU 上收益较小,但内存布局优化和线程调度优化收益明显。
边缘设备又是另一套逻辑。边缘设备的计算资源和内存资源都非常有限,优化目标不只是速度,还有功耗和内存占用。量化在边缘设备上几乎是必选项,因为 FP32 模型根本放不下。剪枝也很重要,能直接减少模型大小。算子融合在边缘设备上收益不确定,因为边缘设备的算子库通常比较精简,融合空间有限。
6.3 动态形状模型的优化注意事项
动态形状模型(比如支持变长输入的 NLP 模型)的优化比固定形状模型复杂得多。量化时,激活值的范围会随输入长度变化,用固定校准集得到的量化参数可能不适用于所有输入长度。解决办法是校准集要覆盖各种输入长度,或者使用动态量化,在推理时动态计算量化参数。
算子融合在动态形状模型上也会遇到问题,因为融合后的算子需要处理动态维度,有些融合模式在动态形状下不合法。我一般会先尝试自动融合,如果失败再手动调整融合规则。内存布局优化在动态形状模型上收益也不稳定,因为动态维度会导致内存访问模式变化。
7. 我在实际项目中的几点体会
模型优化这件事,工具和技术只是一部分,更重要的是对业务场景的理解。同样的模型,在不同的业务场景下,优化目标可能完全不同。有的场景追求极致速度,精度掉一点没关系;有的场景精度是红线,速度慢一点也能接受。所以在开始优化之前,一定要和业务方确认清楚优化目标和约束条件,避免做无用功。
另外,优化不是一次性的工作。模型更新了、数据分布变了、硬件换了,优化策略都需要重新评估。我一般会在项目里建立一个优化基线,每次模型更新后都跑一遍基线测试,确保优化效果没有退化。这个习惯帮我避免了好几次线上事故。
最后分享一个小技巧:优化过程中一定要保留中间产物。量化前的模型、量化后的模型、校准数据、测试脚本,这些都要归档保存。因为一旦线上出问题,你需要快速回滚或者对比分析。我见过太多因为中间产物丢失导致问题排查困难的案例,这个坑完全可以避免。