ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

模型优化实战:从量化剪枝到TensorRT部署提速指南

模型优化实战:从量化剪枝到TensorRT部署提速指南 从训得动到用得动这是每个做深度学习落地的工程师都绕不过去的坎。我最早接触Model-Optimizer这个词是在一个半夜上线的AI推理服务连续超时的故障现场。模型在训练机上跑得好好的FPS高得感人一上生产环境就原形毕露延迟翻倍、显存爆掉、QPS上不去。后来我才明白训练时我们关心的是loss能不能降下去而部署时关心的是能不能在有限的算力和预算里把模型跑起来、跑得快、跑得稳。这两个目标之间隔着一整套模型优化的方法论。这篇文章我不打算讲太多理论推导而是把两年多来在实际项目里做模型压缩、剪枝、量化、蒸馏、部署调优的经验完整复盘一遍包括每一步为什么这么做、踩了哪些坑、最后效果如何希望能给正在做模型上线或者被推理性能折磨的同行一些能直接落地的参考。1. 从训得动到用得动模型优化到底在优化什么1.1 一个让我彻夜难眠的线上事故先讲一个真实场景。半年前我负责一个图像识别服务的上线模型是ResNet-50结构PyTorch训练完精度在验证集上91.2%一切正常。当时我把模型直接丢到生产环境的GPU上用torchserve部署心想这有什么难的。结果压测一出来我人傻了单张图片推理延迟485msQPS只有2.5显存占用接近5.7GB。当时服务器配的是T416GB显存看起来够用但实际并发一上来显存直接飙到13GB再往上加并发就OOM了。业务方给的指标是单图延迟小于80msQPS大于20。差了整整一个数量级。后来我做了三件事把模型导出成ONNX再转TensorRT用FP16推理延迟从485ms降到142ms然后做了一轮INT8量化压到58ms再配合批处理优化和显存复用最终稳定在单图52ms、QPS 26显存占用压到3.1GB。整个过程中我没有改一行网络结构代码全部靠模型优化手段完成。这件事给我的最大教训是训练好一个模型只是第一步把模型优化到能高效部署是另一门独立的手艺。1.2 模型优化的四个维度速度、体积、精度、成本很多人一提到模型优化就以为是把模型变小这是个很片面的理解。我通常把优化拆成四个维度来评估缺一不可维度核心指标典型手段优化目标速度单次推理延迟、吞吐量算子融合、量化、推理框架加速延迟越低越好吞吐越高越好体积模型文件大小、显存占用剪枝、量化、蒸馏、权重共享减小存储和内存需求精度准确率、mAP等业务指标量化感知训练、蒸馏、微调补偿尽量不损失或小幅损失成本硬件预算、功耗、运维压力模型压缩后部署到更低端设备降低单次推理成本这四个维度是互相牵制的。你为了追求速度做INT8量化精度可能会掉你为了精度保住FP32模型体积就降不下来推理延迟也压不下去。所以做模型优化本质上是在一个四维空间里找平衡点而不是把某一个指标做到极致。1.3 什么情况下你才真正需要动优化不是所有项目都需要做模型优化。我见过不少团队业务还没跑通就急着上量化结果精调了一周收益基本为零。我自己的判断标准是这样的如果推理延迟和吞吐已经满足业务指标就不要为了优化而优化。模型优化是有风险的量化可能掉点剪枝可能收敛不稳蒸馏可能损失表达能力每一次压缩都是在拿精度换效率。只有当下面这几种情况出现时我才会启动优化流程业务指标算过了但当前模型在目标硬件上跑不满比如要50ms以内结果要100ms模型要部署到边缘设备存储和内存上限卡死不压缩就装不下GPU成本占总成本比例过高老板要求降本增效并发上来之后服务不稳定显存频繁告警另外我要提醒一句模型优化的最佳介入时机是在训练阶段而不是训练完成之后。如果一开始就决定要部署到某款硬件训练时就考虑量化感知训练、蒸馏的师生结构后面会省下大量返工时间。我在多个项目里体会过这个差别——训练完再压缩相当于做完菜再回锅能改善但总有局限。2. 动手前先量体温性能基线勘测是优化的第一步2.1 别急着上工具先把这三张表填了很多新手拿到模型就往上套剪枝、量化工具结果优化了半天连优化了多少都说不清。我的习惯是动手之前先花一个下午把基线数据完全跑清楚。我通常做一张性能基线表至少在优化前后各填一次模型名称、参数量、模型文件大小FP32/FP16/INT8分别记录单次推理延迟分别测batch1和最大可行batch下的延迟记录P50和P95吞吐量每秒能处理的样本数需要和batch大小一起记录单独一个数字没有意义显存占用加载模型后空闲显存、单样本推理显存峰值、满batch推理显存峰值精度指标验证集准确率或目标任务的mAP、F1等硬件环境GPU型号、CPU型号、TensorRT/ONNX Runtime版本、CUDA版本有个非常容易忽略的点是CUDA和推理框架的版本。同一套模型在不同版本的TensorRT上跑性能差异可能达到20%以上。基线记录里必须写清楚环境版本否则回头复现优化结果时根本对不上。2.2 我的Profile三板斧torch.profiler、onnxruntime、Nsight填完表之后下一步是搞清楚时间都花在哪了。我常用的工具核心是这三个各自解决不同层次的问题PyTorch自带的torch.profiler适合在训练/推理脚本里快速定位算子级别的耗时分布。我一般这样用import torch from torch.profiler import profile, ProfilerActivity model.eval() x torch.randn(1, 3, 224, 224).cuda() with profile(activities[ProfilerActivity.CPU, ProfilerActivity.CUDA]) as prof: with torch.no_grad(): for _ in range(100): model(x) torch.cuda.synchronize() print(prof.key_averages().table(sort_bycuda_time_total, row_limit20))这个输出的价值在于让你一眼看出来是卷积层占了大头还是某些奇怪的op比如transpose、padding、自定义op在拖后腿。我见过最离谱的一次一个LayerNorm在GPU上的耗时占比达到37%原因是用了一种非融合的PyTorch实现换成分离的CUDA实现后时间直接少了15%。onnxruntime的profiler适合排查ONNX导出之后的性能问题。这里有个判断技巧如果PyTorch直接推理和ONNX推理的耗时差异过大通常不是框架问题而是某些算子导出成ONNX后变成了低效的算子组合。NVIDIA Nsight Systems解决的是全局视角问题。它能看到整个GPU的利用率、kernel启动开销、CPU和GPU之间的数据传输时间。很多新手只看算子级耗时忽略了数据加载和CPU预处理占据的时间实际生产环境里这部分常常是瓶颈。2.3 定目标优化不是玄学是算账基线测完就该定目标了。我强烈建议把优化目标写成一个数字表达式而不是一句更快更小。举个例子我们的目标是在T4上单图延迟从485ms降到80ms以内且精确率下降不超过0.5%。这个目标拆解下来485ms到80ms需要压缩差不多6倍延迟单纯FP16量化通常只能拿到1.5-2倍加速叠加TensorRT的算子融合能再拿1.2-1.5倍再上INT8量化能再拿2-3倍这样算下来路线就很清晰FP16 TensorRT INT8量化三步走每一步用基线表验证是否达到阶段目标。如果某一步的精度损失超过了预算比如INT8这一步就掉点0.8%就要停下来考虑改走QAT或者混合精度方案而不是硬着头皮继续往下压。这个算账的过程特别重要因为它决定了你优化的整体策略。我见过一个同事花了两周做剪枝最后发现模型速度瓶颈根本不在参数量而在反卷积算子太低效换成等效的上采样加卷积速度快了三倍。这就是没做基线分析就动手的典型反面教材。3. 剪枝实战砍掉冗余参数的正确姿势3.1 结构化剪枝和非结构化剪枝的本质区别剪枝的核心思想是神经网络里有大量冗余参数把它们去掉模型更快更小精度还能基本保持。但去掉参数有两种完全不同的做法很多人没搞清楚就上手了。非结构化剪枝是把权重矩阵中绝对值较小的单个权重直接置零。这种方式压缩比很高但问题是稀疏后的权重矩阵是横七竖八的GPU上的密集矩阵运算根本用不了需要专门的稀疏矩阵库支持很多时候反而更慢。我自己的经验是除非你的推理框架对稀疏张量有专门优化否则非结构化剪枝在GPU部署场景基本是负优化。结构化剪枝则是对整个通道、整个filter或整个层做删除。比如一个卷积层有256个输出通道你判断其中60个通道的贡献很小就把它们整体删掉同时把下一层对应的输入通道也删掉。这种方式能直接缩小矩阵的尺寸对硬件友好真正实现推理加速和显存下降。我的建议是面向GPU部署的剪枝一律走结构化路线这是效率和可行性的最佳平衡点。3.2 剪枝阈值怎么定按绝对值还是按幅度分布确定要剪哪些通道常见做法有两种一是看权重绝对值。计算每个filter或每个输出通道的权重L2范数范数小说明这个通道学到的特征幅度弱砍掉它对输出影响最小。把范数排序从最小的开始砍。道理简单实操也稳。二是看通道对激活值的贡献度。跑一批校准数据统计每个通道输出的激活值分布——激活值平均接近0的通道基本就是惰性通道可以安全剪掉。两种方法我都在用后者效果通常更好但需要跑一遍前向成本略高。实际项目里我一般先用L2范数做快速筛选再用激活值统计做二次确认。这里有个非常重要的细节剪枝阈值不要一刀切。不同层对剪枝的容忍度差异巨大。靠近输入的层提取的是通用底层特征边缘、纹理冗余很少要少剪靠近输出的层是任务特化层冗余较多可以多剪。如果你对每一层都用同一个剪枝比例剪出来的模型精度会掉得很难看。我一般是每层单独看权重分布低层剪10%-15%高层剪30%-50%。3.3 剪枝后的微调策略不是全部层都该回血剪完枝之后模型精度一定会掉这时候需要微调恢复。但微调不是简单地拿原学习率再训练几个epoch我踩过的坑不少总结下来有三个原则第一学习率要比正常训练小一个数量级。剪枝之前的模型已经收敛到比较平滑的loss曲面上了强行用大学习率会把参数推出原来的良好区域。我一般用正常训练的1/10甚至1/20配合余弦退火。第二剪枝多的层和剪枝少的层要区别对待。我会先用一段代码找出被剪掉通道比例最大的层给这些层设置较高的学习率倍数其余层保持低学习率。这个操作在PyTorch里可以通过给参数分组实现optimizer torch.optim.AdamW([ {params: high_pruned_params, lr: 3e-4}, {params: low_pruned_params, lr: 3e-5}, ], weight_decay1e-4)第三微调数据的分布必须跟原始训练数据一致。听起来是废话但很多人在微调时偷懒直接用手头仅有的一部分数据结果模型的泛化能力反而下降了。剪枝后的模型表达空间比原模型小对数据分布的敏感度更高数据这块别省。3.4 一个真实案例ResNet-50剪掉30%参数后发生了什么我在一个图像分类项目里对ResNet-50做结构剪枝目标是在保持Top-1准确率不低于88%的前提下尽量减小模型体积。基线参数量25.5MTop-1准确率91.2%模型文件96MBFP16格式。我的做法是逐层分析每个残差块中3x3卷积的通道重要性低层保守、高层激进最终整体剪掉约30%的通道。剪完后的参数量降到了17.8M模型文件68MB体积下降30%。剪完直接测试准确率掉到了86.4%掉了近5个点吓我一跳。然后我按上面说的方式做了3轮微调准确率恢复到89.1%。最终结果体积减小30%准确率损失2.1个百分点。说实话这个精度损失超出了我的预算。后来我复盘发现问题出在ResNet的残差连接上——剪枝改变了残差分支的维度匹配我虽然对shortcut做了padding处理但操作上有些粗糙影响了梯度回流。如果你要剪残差网络一定要仔细核对残差连接的维度对齐逻辑最好在剪枝代码里加断言逐层打印维度变化。剪枝给我最大的体会是它适合你需要减小存储体积、降低显存占用的场景但每次剪枝都会带来一定精度损失需要微调补回来整体人力成本不低。如果你对latency的要求主要来自计算量而不是带宽/显存那么量化往往比剪枝的投入产出比更高。4. 量化攻坚战FP16、INT8与混合精度的选择逻辑4.1 量化为什么能让推理快三倍量化把模型里的浮点数FP32/FP16压缩成更低位宽的整数通常是INT8。好处有两层第一层是计算加速。GPU上的INT8矩阵乘法算力通常是FP32的4倍以上对T4这类卡尤其明显。T4的FP32算力约8.1 TFLOPS而INT8算力是65 TOPS差了整整8倍。当然实际跑模型不可能达到理论峰值但2-3倍的提升是很现实的。第二层是内存减半再减半。INT8比FP32小4倍比FP16小2倍模型权重占用的显存和带宽成本直接下降。对Batch推理场景节省的带宽时间会直接反映在延迟上。我习惯用一张表来记录量化的收益预期精度权重占用理论算力以T4为例单图延迟预估精度风险FP32100%8.1 TFLOPS基准无FP1650%65 TFLOPSTensor Core比FP32快1.5-2倍极低INT825%65 TOPS比FP32快2-4倍中等4.2 PTQ与QAT两条路线的时间成本权衡量化有两条实现路线训练后量化Post-Training Quantization简称PTQ和量化感知训练Quantization-Aware Training简称QAT。PTQ就是模型训练完后拿一批校准数据跑一遍前向统计各层激活值的范围然后用这个范围算出每个张量的缩放因子和零点把浮点权重映射成INT8整数。整个过程只要几十分钟不需要重新训练。缺点是遇到分布不均匀的激活值量化误差会比较大。QAT是在训练阶段就模拟量化的舍入误差让模型在训练过程中主动适应量化带来的扰动。训练完的模型直接量化部署精度损失通常比PTQ小得多。代价是要完整跑一遍训练耗时几天到几周。我的选择逻辑是这样的先跑PTQ如果精度损失小于1%就用PTQ简单省事如果损失超过1%再考虑QAT或者混合精度。直接上QAT是很多新手的通病花了几周训练成本其实PTQ就够用了。4.3 Calibration数据集的选取决定INT8的生死PTQ里最关键的一步是Calibration。很多人以为随便拿几百张训练图片跑一跑就完事了但Calibration数据选得不好量化精度会崩得莫名其妙。Calibration的目的是为了让模型统计到每层激活值的真实动态范围。如果校准数据太单一比如全是猫的图片模型统计出来的激活值范围就偏向猫图特征上线遇到狗的图片激活值跑到统计范围之外截断误差瞬间放大。我的经验是校准集至少要有500-1000个样本而且要尽量贴近真实上线时的数据分布。目标检测模型就用包含各种目标大小、遮挡、光照变化的图片NLP模型就覆盖各种句式长度和领域的文本。另一个值得注意的点是校准时的batch大小要和推理时接近否则BatchNorm统计量会有偏差。在TensorRT里指定校准集的代码大概是这样import tensorrt as trt # 自定义校准器读取校准图片并返回batch class EntropyCalibrator(trt.IInt8EntropyCalibrator2): def __init__(self, calibration_data, batch_size8): super().__init__() self.calibration_data calibration_data self.batch_size batch_size self.index 0 # 申请GPU显存buffer self.device_buffer cuda.mem_alloc(batch_size * 3 * 224 * 224 * 4) def get_batch(self, names): if self.index self.batch_size len(self.calibration_data): return None batch self.calibration_data[self.index:self.index self.batch_size] self.index self.batch_size # 数据拷入GPU并返回地址 cuda.memcpy_htod(self.device_buffer, batch.flatten()) return [int(self.device_buffer)] def get_batch_size(self): return self.batch_size4.4 量化后精度掉的排查顺序INT8量化之后精度掉了先别慌按顺序排查第一个查校准数据。是不是选得不够代表性换一批更贴近线上分布的样本往往掉点问题直接解决一半。第二个查敏感层。某些层对量化特别敏感比如检测头的回归分支、注意力模块里的softmax分母。用TensorRT的per-channel量化、或者把这些层单独保留FP16计算混合精度通常能救回来。我做过一个项目只是把最后一层分类层保留FP16INT8整体的精度损失就从2.3%降到了0.6%。第三个查边界异常值。激活值里有极小的离群值会把动态范围拉得很大导致大多数正常值被挤压到低精度区间信息丢失严重。把离群值做clip或者剔除对量化效果改善明显。PyTorch里可以用torch.quantization的observer来检查激活值分布看到长尾就处理掉。最后实在不行再上QAT。这条路成本最高但效果最可靠。有过一个分割模型PTQ之后mIoU从0.72掉到0.61QAT训练三天之后恢复到了0.70几乎无损。5. 知识蒸馏与算子融合两条不伤精度的优化捷径5.1 蒸馏的本质让学生模型学到老师的软知识剪枝和量化本质上是压缩——拿精度换效率。但知识蒸馏Knowledge Distillation不一样它是在训练阶段同时提升学生模型的上限用大模型教师指导小模型学生训练让学生模型在更小规模下逼近大模型的精度。蒸馏为什么有用因为传统的训练只告诉模型正确答案是猫hard label而教师模型能提供软化的概率分布——它有80%的可能是猫15%的可能是狐狸5%的可能是狗。这个分布携带了类别之间的相似性信息对小模型来说是极其丰富的训练信号。我在一个文本分类项目里做过对比同一个4层的BERT蒸馏模型只拿hard label训练F1是82.3%用12层BERT的软标签训练F1是86.7%。同样的参数量只因为训练信号更丰富涨了4个多点。5.2 蒸馏温度与损失函数配比的经验值蒸馏有两个超参数需要调温度T和损失权重。温度T的作用是把教师模型的概率分布软化。T越大分布越平滑类别间的细微差异体现得越充分。但T太高会把所有类别拉得差不多失去信息量。我试过T1到10在大部分分类任务上T3到4效果最好。损失函数一般由两部分组成学生模型对硬标签的交叉熵损失加上学生软输出对教师软输出的KL散度损失。配比上我有自己的习惯前期让蒸馏损失占比大一些0.7左右后期逐渐让硬标签损失占比上来0.5甚至0.6这样学生模型既学到了教师的泛化知识又能最终对齐真实标签。这里补充一个实战细节蒸馏时教师模型最好用FP32全精度跑学生的输入数据增强不要和教师完全一致否则学生容易过度模仿教师的错误模式。5.3 算子融合减少计算图调度开销算子融合是推理框架层面的优化不需要动模型权重但收益非常可观。它的核心思想是把多个连续的小算子合并成一个大算子减少kernel启动次数和中间张量的读写。最经典的例子是Conv BN ReLU的融合。在GPU上每个kernel启动都有固定开销大概几微秒几十个op就是几十次启动。更关键的是每次kernel执行完毕都要把中间结果写回显存下一个kernel再读出来这个访存开销在深度网络里往往比计算开销还大。融合之后一个kernel里面完成卷积、归一化和激活中间结果直接留在寄存器或缓存里。再比如LayerNorm里的多个element-wise操作、Attention里QKV变换和softmax的组合都是可融合的对象。这些融合在TensorRT的engine构建阶段会自动完成但你如果在纯PyTorch里推理这些优化一个都不会发生。这就是为什么很多人在PyTorch里跑模型很慢、导成TensorRT后快一大截的核心原因之一。5.4 融合前后的延迟对比我整理过一组真实的对比数据模型是两个视觉Transformer变体分别在PyTorch Eager模式、torch.compile和TensorRT上跑batch8的推理延迟推理方式延迟ms相对PyTorch提升PyTorch Eager FP322451xtorch.compile FP321831.34xTensorRT FP16882.78xTensorRT INT8445.57x有些对延时要求不高、但追求灵活性的场景torch.compile已经很够用因为它不改变训练/推理的代码只是加一行装饰器的关系。但如果你追求极致性能、而且模型结构稳定TensorRT几乎是绕不开的选择——它把算子融合、内存池、kernel自选这些工作都做完了你要做的就是喂给它一个ONNX模型和一份校准数据。6. 部署侧的隐性优化显存、批大小与推理框架调优6.1 显存瓶颈往往不在模型本身做到这个环节很多人以为优化已经到头了。实际上我见过太多模型层的优化做得很好部署配置一塌糊涂性能和显存依然拉胯。部署侧的隐性优化可能占最后20%的收益。显存占用和模型权重大小不是一回事。模型权重可能只占2GB但推理时的激活值、中间buffer、CUDA context、推理框架的内存池这些加起来可能翻好几倍。用nvidia-smi看显存占用很多时候大头在激活值上。减少显存占用的几个实用手段开启CUDA graph / 推理框架的内存池复用。TensorRT有显存池机制PyTorch也有CUDA caching allocator但注意在推理服务里要避免每个请求都重新加载模型或重建上下文控制最大batch size。不要为了省事把batch设得很大够用就行用FP16存储权重推理时也不会带来明显精度损失显存直接减半检查是否有cudaMalloc/cudaFree的频繁调用这类调用开销极高且会产生显存碎片我在一个文本生成服务里做过一个测试纯PyTorch推理三条并发请求显存峰值飙到9.8GB换用带显存池的推理引擎后峰值降到5.1GB延迟反而下降18%。原因是cudaMalloc调用大幅减少省下了大量时间片。6.2 Batch Size与延迟/吞吐的权衡曲线很多人有个误解batch size越大一定越好。实际上batch size和延迟是正相关的——batch越大单个请求排队等GPU计算的时间越长。我们做的图像识别服务实测过一个数据Batch Size单batch延迟ms单图平均延迟ms吞吐img/s1525219496244181501953162681759325101662看出来了吗单图平均延迟随着batch变大而下降但单batch的绝对延迟一直在涨。如果你的业务对P95延迟敏感比如线上画面分析、实时审核就不能贪图吞吐而把batch拉大否则极端流量下P95会爆。我的调整经验是按QPS目标反推batch。假设目标QPS是50单GPU单次推理52ms理论最大吞吐19QPS那一个GPU根本不够要么上两个卡要么batch调到4达到41QPS再上两张卡。优化的本质是让硬件和业务指标匹配而不是无脑冲最大吞吐。6.3 推理框架选择TensorRT、ONNX Runtime与原生PyTorch的取舍推理框架选型这块我的建议很直接TensorRTGPU部署追求极致性能首选。前提是你愿意做ONNX导出、写engine构建脚本、处理动态shape的麻烦。它的优化深度最深这部分我们在第5节已经看过数据。ONNX Runtime跨平台、多硬件支持CPU和GPU都能跑转换成本低。如果你要在CPU上部署或者需要频繁切换硬件ONNX Runtime的性价比很高。CPU场景下ONNX Runtime对比PyTorch的加速通常是1.3到1.8倍不需要GPU就能吃到一部分优化收益。原生PyTorchtorch.compile迭代快、调试方便适合模型结构还在频繁变动的阶段。等模型结构稳定之后再迁移到TensorRT是很多团队的实际路径。框架部署成本加速效果适用场景PyTorch最低1x原型验证、快速迭代ONNX Runtime低1.2-2xCPU/多硬件部署TensorRT中高2-6xGPU性能极致优化7. 我的优化工具箱与踩坑日记7.1 常用工具清单做模型优化这两年我的核心工具箱沉淀下来就这么几样分享给大家torch.profiler / pytorch profiler算子级别性能定位日常使用频率最高Nsight Systems / Nsight ComputeGPU全局分析和kernel内部分析Netron可视化ONNX模型结构检查导出的算子是否异常比如某些PyTorch高阶操作被拆成一大堆细碎算子onnxsimONNX模型简化工具能自动合并部分可消除的算子导出后第一件事就是跑它TensorRT自带的trtexec工具快速验证engine性能和正确性不写代码就能拿到延迟、吞吐和显存数据PolygraphyNVIDIA出品的调试工具我用它来做TensorRT和ONNX Runtime的输出对拍排查量化后哪些层输出偏差过大这些工具配合baseline表使用基本能覆盖从跑不动到上线稳定的整个链路。7.2 三个让人崩溃的坑做优化一定会踩坑我把记忆最深的三次踩坑经历写出来这些都是文档里很少提到的。第一个坑ONNX导出时动态轴设置错误。拿batch1的静态图去导出上线后几路并发直接报shape不匹配。后来我养成了习惯导出ONNX时一律用dynamic_axes把batch和宽高都设成动态宁可部署时指定最小/最大/最优形状也不要偷这个懒。第二个坑TensorRT的engine是和GPU型号强绑定的。在V100上构建的engine拿到T4上是跑不起来的报错信息还不明显。我们线上出过一次测试环境全绿、生产环境全部不可用的事故原因就是构建engine用的卡和生产的卡不是同一个型号。现在我们的流程里engine构建必须和生产环境同型号的卡并加入GPU型号的校验逻辑。第三个坑INT8量化检测模型时小目标全丢了。校准集里大目标占多数激活值分布整体被大目标的高响应主导小目标的低幅度响应在量化后直接被截断。后来我把校准集按目标尺寸做了分层采样保证小目标样本占40%以上这个坑才算解掉。这件事给我的教训是量化不是纯算数问题它和你的数据和业务目标是强耦合的。7.3 优化完成后必须做的回归验证最后一步也是最容易被跳过的回归验证。优化完模型不要急着上生产我建议准备一份固定的回归清单精度回归完整跑一遍验证集和基线表对比各项指标确认掉点是否在预算内鲁棒性回归喂一些边界样本模糊图片、长文本、空输入确认优化后模型不会在这些样本上崩掉性能回归重新测P50/P95延迟、QPS、显存峰值确认达到目标A/B灰度上线后切小流量对比线上原模型和优化模型的业务指标至少观察3-5天我在实际项目里还保留了一个习惯把每次优化的基线表、配置、模型文件版本、量化校准集版本全部归档建立对应关系。这听起来很繁琐但等你三个月后要回滚或者排查线上精度问题时就会发现这套归档的价值——不然你根本说不清楚当前线上这个模型是用哪个校准集、哪个版本的脚本做出来的。模型优化这条路没有什么银弹每一步都是权衡和迭代。从测基线、定目标到剪枝、量化、蒸馏再到部署调优和回归验证每一环都能帮你在有限硬件上多榨出一些性能重要的是形成自己的标准化流程而不是东一榔头西一棒子。现在项目的性能指标稳定之后我开始琢磨下一步把优化流程沉淀成团队内部的自动化工具把校准集生成、engine构建、精度回归这些重复性工作串起来新模型上线直接跑一套流水线出报告。这个内容后续如果跑通了我再写一篇专门的文章分享具体实现。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进