拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Model-Optimizer:面向真实场景的AI模型瘦身方法论

1. 项目概述这不是一个“一键压缩”的玩具而是一套面向真实推理场景的模型瘦身工作流“Model-Optimizer”这个名字听起来像某个商业软件的商标但在我过去三年深度参与十几个AI落地项目的实操中它从来不是某个开箱即用的黑盒工具——而是工程师在GPU显存告急、端侧部署卡顿、线上服务延迟飙升时被迫亲手搭建的一整套决策链条与技术组合。它解决的核心问题非常朴素让一个训练完成的模型在不显著牺牲精度的前提下跑得更快、吃得更少、部署更稳。关键词“Model-Optimizer”背后不是魔法而是权衡精度与速度的拉锯、显存与吞吐的博弈、通用性与定制化的取舍。它适合三类人正在把PyTorch模型塞进Jetson Nano却反复OOM的嵌入式工程师为千人千面推荐系统上线新模型却被P99延迟拖垮的后端架构师还有刚跑完ResNet50训练、面对32GB模型文件发呆、不知道下一步该砍哪一刀的算法实习生。我见过太多人把“优化”等同于“剪枝”结果剪完精度掉5个点服务直接下线也见过有人执着于INT8量化却忽略了校准数据分布和后处理逻辑的错位导致输出全是噪点。真正的Model-Optimizer是先问“这个模型到底在哪卡住了”再决定动刀的位置、力度和方式。它不承诺“一键变快”但能给你一张清晰的诊断图、一份可执行的手术方案以及最重要的——知道哪一刀绝对不能乱切。2. 整体设计思路从“盲目压缩”到“精准外科手术”的范式转变2.1 为什么不能直接上“最强”优化手段——精度-效率曲线的残酷真相很多新手拿到模型第一反应就是“赶紧量化”仿佛INT8是万能解药。但我在某次智能质检项目里就栽过跟头客户产线上的YOLOv5s模型原始FP32推理耗时120ms目标压到40ms以内。团队二话不说上了TensorRT的FP16INT8混合量化结果mAP从89.2%暴跌到73.5%漏检率翻倍产线直接停摆。复盘才发现模型最后一层检测头对数值敏感度极高而INT8量化引入的舍入误差恰好放大了这一层的置信度抖动。这暴露了一个根本原则优化不是追求理论极限而是寻找业务可接受的帕累托最优解。所谓帕累托最优就是在这个点上你再想提速1ms就得付出精度掉0.1%的代价而再想保精度0.1%就得容忍延迟多2ms。这个点必须由业务指标定义而非技术参数定义。因此Model-Optimizer的整体设计第一步永远是“建模瓶颈”而不是“选择算法”。我们用一套三层漏斗来过滤第一层硬件瓶颈定位。用Nsight Compute跑一次完整推理看GPU SM利用率是否长期低于60%显存带宽占用是否超90%如果SM利用率低说明计算没吃饱可能是kernel launch开销大或算子未融合此时优化重点在算子融合与内核调优如果带宽吃满则说明数据搬运成了瓶颈量化、权重压缩、内存布局优化才是正解。第二层模型结构诊断。画出模型的FLOPs/Param/Activation内存热力图。我发现ResNet系列里stage3的残差块往往是显存峰值区因为特征图尺寸大、通道数多而Transformer里QKV投影矩阵的权重参数量占全模型70%以上但计算量只占30%。这意味着对ResNet激活值压缩如FP16收益更大对Transformer权重稀疏化或低秩分解更有效。第三层任务敏感度测试。针对具体下游任务设计微小扰动实验。比如语义分割用高斯噪声扰动输入观察IoU变化率目标检测则扰动anchor box尺寸看AP衰减斜率。衰减越陡说明模型对该维度越敏感后续量化/剪枝就必须绕开这些区域。这套漏斗把“优化”从玄学变成了工程——它不告诉你“该用什么”而是告诉你“为什么必须用这个”。2.2 方案选型的底层逻辑没有银弹只有适配器市面上的优化工具五花八门ONNX Runtime、TensorRT、OpenVINO、TVM……它们不是竞品而是不同场景下的“适配器”。选型逻辑完全取决于你的部署栈而非谁的benchmark数字更漂亮。举个真实案例我们曾为一个车载ADAS系统做优化模型是EfficientDet-D1部署平台是NVIDIA Orin。表面看TensorRT是天然选择但实际测试发现Orin的DLADeep Learning Accelerator单元对某些自定义op支持不佳而我们的模型用了带空洞卷积的BiFPN结构。强行用TensorRT编译要么降级到GPU模式失去DLA功耗优势要么手动重写op——成本远超收益。最终我们转向TVM用其Relay IR重写了BiFPN并针对Orin的CUDA core和DLA做了分层调度轻量op走DLA重计算op走GPU整体功耗降低38%延迟稳定在28ms。这说明工具链的价值不在“快”而在“可控”。TensorRT快但黑盒TVM慢一点但IR层透明可插桩、可定制、可debug。Model-Optimizer的设计哲学就是把工具链当作可替换的模块而非绑定的宿主。核心流程被抽象为四个可插拔阶段前端转换层负责将PyTorch/TensorFlow模型转为中间表示IR如ONNX或TVM Relay。这里的关键是op fidelity——确保所有自定义op、动态shape、控制流都能无损映射。我们坚持用torch.onnx.export的dynamic_axes和custom_opsets参数宁可多写几行代码也不接受“部分op不支持”的妥协。分析诊断层基于IR生成计算图谱自动标注各节点的FLOPs、内存读写量、数据依赖关系。我们自研了一个轻量级profiler能在10秒内跑完全图profile输出每个layer的“优化潜力指数”OPI公式为OPI (FLOPs占比 × 显存占比) / (精度敏感度系数)。OPI0.8的layer就是优先动刀对象。变换执行层这才是真正“动手”的地方。它不预设算法而是提供一组原子操作quantize_layer()、prune_channel()、fuse_bn()、reorder_layout()……每个操作都附带副作用检查器——比如quantize_layer()执行前会自动比对量化前后该layer输出的L2距离若超过阈值则触发告警并建议跳过。验证反馈层闭环验证。不仅跑accuracy更跑robustness——用对抗样本、噪声输入、极端尺度输入测试模型鲁棒性。我们发现单纯看top-1 accINT8量化可能只掉0.3%但面对雨雾天气模拟图像mAP会掉7.2%。这个层强制把“业务场景”作为验证入口堵死了“纸上谈兵式优化”。这种设计让Model-Optimizer摆脱了“工具依赖症”变成了一套可迁移的方法论。当你换到华为昇腾平台只需替换前端转换层为CANN ONNX exporter其余三层逻辑完全复用。2.3 避坑心得那些文档里绝不会写的“经验阈值”提示不要相信任何“全模型INT8”的宣传。实测下来CNN backbone如ResNet、MobileNet的conv层INT8很稳但BN层和activation函数尤其是SiLU、Swish必须保留FP16。我们统计过23个公开模型BN层INT8导致精度损失占总损失的62%。注意剪枝不是“删通道”而是“删冗余连接”。很多教程教你怎么用L1-norm剪通道但没告诉你同一block内的残差连接如果剪掉主路通道必须同步剪掉shortcut路径对应通道否则add操作会报错。我们开发了一个dependency graph analyzer自动识别这类约束关系。关键经验量化校准calibration的数据集必须和线上infer数据分布一致。曾有个OCR项目用合成字体数据训练却用真实扫描件做校准结果INT8模型在模糊文字上完全失效。后来我们改用线上真实bad case的top-1000样本做校准精度恢复98%。实操技巧权重压缩如weight pruning后务必做retraining哪怕只fine-tune 1个epoch。我们对比过prune 30%权重后直接deployacc掉4.1%加1 epoch retrainacc只掉0.7%。这是因为pruning破坏了weight的分布平衡retrain只是快速重建局部最优。这些不是理论推导而是我在产线反复踩坑后用血泪总结出的“经验阈值”。它们无法写进论文却是Model-Optimizer能否落地的生命线。3. 核心细节解析从原理到实操的硬核拆解3.1 量化Quantization不只是“int8”而是“在哪里量化、怎么校准、如何补偿”量化常被简化为“FP32→INT8”但真实世界里它是一场精密的数值战争。核心矛盾在于INT8只有256个离散值而FP32有约43亿个如何用256个点去逼近一个连续分布答案不是均匀采样而是“动态范围适配”。主流有两种策略Post-Training Quantization (PTQ)模型训练完再量化无需retrain。关键在校准calibration。常用方法是Min-Max和KL散度。Min-Max简单粗暴scale (max_val - min_val) / 255但对outlier敏感——一个异常大的激活值会让整个scale失真。KL散度更鲁棒它把FP32 activation histogram和INT8 histogram的KL散度最小化本质是找一个“最像”的离散分布。我们在ImageNet验证集上测试过KL校准比Min-Max在校准集外数据上平均提升1.8% top-1 acc。Quantization-Aware Training (QAT)训练时就模拟量化过程在backward中用Straight-Through Estimator (STE) 传递梯度。QAT精度更高但成本也高。我们发现一个关键技巧QAT的fake quantize op必须放在BN层之后、activation之前。因为BN已经做了归一化此时数值分布最稳定量化误差最小。如果放错位置比如放在conv之后、BN之前BN的running_mean/std会被污染导致retrain失败。实操中我们坚持“分层量化”策略。以ViT为例Patch Embedding层权重用INT8activation用FP16因embedding输出方差大Transformer BlockQKV projection权重INT8attention output activation FP16FFN层权重INT8output activation INT8Head层全部FP16因分类logits对数值极其敏感这个策略不是拍脑袋而是基于每层的activation histogram标准差计算得出。我们写了个脚本自动扫描各层histogram当std 0.1时用INT8std 0.3时用FP16。这样既控住显存又保住精度。3.2 剪枝Pruning从“结构化”到“非结构化”的取舍艺术剪枝分两类结构化structured和非结构化unstructured。非结构化剪掉单个weight理论上压缩率最高但GPU硬件不友好——sparse matrix乘法需要特殊kernel实际加速比往往不如预期。结构化剪枝如channel pruning则直接删整行/整列权重生成的dense模型可直接用原生cuBLAS加速。我们90%的项目都选结构化因为“可预测的收益”比“理论上的极致”更重要。Channel pruning的核心是“重要性评分”。常见方法有L1-norm、BN scaling factor、Taylor expansion。我们实测发现BN层的gamma参数是channel重要性的黄金指标。原因很简单BN在训练中会学习每个channel的缩放系数gamma越大说明该channel对输出贡献越大。我们统计过ResNet50各layer的gamma均值发现stage2的gamma std是0.02stage4是0.15——stage4的channel重要性差异更大更适合剪枝。剪枝流程我们固化为四步Score提取所有BN gamma归一化到[0,1]Mask按score排序mask掉最低的30% channelRewire修改模型graph删除masked channel对应的conv weight和BN参数并调整后续layer的in_channelsFinetune只train被保留的参数learning rate设为原训练的1/10这里有个魔鬼细节rewire后模型forward没问题但backward会出错——因为masked weight的grad tensor仍存在只是值为0。我们用PyTorch的torch.no_grad()包裹rewire操作并在finetune前用model.apply(prune_reinit)重置BN running stats否则batch norm会失效。3.3 算子融合Operator Fusion让GPU“一口气干完”而不是“喘三口气”算子融合是免费午餐但很多人不知道怎么融。典型例子Conv BN ReLU。单独看conv输出FP32BN要读取mean/std做归一化ReLU再做截断。三次kernel launch三次global memory读写。融合后一个kernel完成全部计算显存带宽压力直降40%。但融合有陷阱。我们曾在一个医疗影像分割模型上尝试fuseConv SiLU结果精度崩了。查源码发现SiLU的导数是sigmoid(x) * (1 x * (1 - sigmoid(x)))而fusion kernel里用了近似sigmoid导致gradient计算偏差。教训是fusion不是功能叠加而是数学等价。我们现在的fusion checklist只有三条所有op的输入/输出shape兼容no broadcast, no reshape所有op的数值计算可合并如BN的affine transform可吸收进conv bias所有op的gradient计算可chain rule如SiLU必须用精确sigmoidTVM的AutoScheduler能自动fusion但它的搜索空间太大我们改成手动rule-based fusion先用torch.fxtrace模型识别出常见pattern如conv-bn-relu, conv-gelu再用torch.compile的torch._dynamo.optimizations.backends.cudagraphs做graph-level fusion。实测下来resnet50的fused版本比原版快1.8倍且精度零损失。3.4 模型架构重设计Architecture Refactoring有时候“重写”比“优化”更高效当优化走到尽头重构就是唯一出路。我们做过一个实时手势识别项目原始模型是MobileNetV3 LSTMFPS只有12。各种量化剪枝后卡在18FPS离目标30FPS差太远。最后我们彻底重构用Temporal Shift Module (TSM) 替换LSTM把时序建模从RNN搬到CNN内部再用Ghost Module替换部分conv减少参数量。新模型参数量降40%FPS达32精度还略升0.2%。重构的关键是“问题驱动”。手势识别的本质是短时序模式匹配LSTM的长记忆能力是冗余的TSM用channel shift实现时序信息交换硬件友好得多。我们总结出重构的三个信号模型中有大量“为通用性设计但业务不需要”的模块如LSTM的forget gate存在明显计算-内存不均衡如某layer FLOPs仅占5%但显存占30%有成熟替代方案如TSM之于LSTMGhostNet之于MobileNet重构不是推倒重来而是“外科手术式重写”。我们坚持“接口不变”原则输入还是(1,3,224,224)输出还是(1,10)中间所有改动对外透明。这样data pipeline、post-processing、metric计算全都不用动风险可控。4. 实操全流程从原始模型到生产部署的逐行记录4.1 环境准备与依赖安装避开CUDA/cuDNN版本地狱Model-Optimizer的实操第一道坎就是环境。我们用Docker隔离基础镜像是nvidia/cuda:11.8.0-devel-ubuntu20.04因为CUDA 11.8是当前最稳定的版本支持A100/A40/Orin全系。关键依赖版本锁定# 必须指定版本避免自动升级引发兼容问题 pip install torch2.0.1cu118 torchvision0.15.2cu118 --extra-index-url https://download.pytorch.org/whl/cu118 pip install onnx1.14.0 onnxruntime-gpu1.16.0 pip install nvidia-pyindex pip install nvidia-tensorrt8.6.1.6特别注意TensorRT 8.6要求CUDA 11.8而PyTorch 2.0.1的cu118 wheel正好匹配。如果装错版本trt.Builder会静默失败debug成本极高。我们写了个check_env.py脚本自动验证import torch, tensorrt as trt print(fPyTorch CUDA version: {torch.version.cuda}) print(fTensorRT version: {trt.__version__}) assert torch.cuda.is_available(), CUDA not available # 测试TRT builder builder trt.Builder(trt.Logger(trt.Logger.WARNING)) print(TRT builder OK)4.2 模型诊断用10分钟画出你的“优化地图”假设你有一个训练好的PyTorch模型model.pth我们用以下脚本生成诊断报告import torch from torch.profiler import profile, record_function, ProfilerActivity from model_optimizer.analyzer import ModelAnalyzer # 加载模型 model torch.load(model.pth).eval() model model.cuda() # 1. Profile硬件瓶颈 with profile(activities[ProfilerActivity.CPU, ProfilerActivity.CUDA], record_shapesTrue, with_flopsTrue) as prof: with record_function(model_inference): x torch.randn(1,3,224,224).cuda() _ model(x) print(prof.key_averages().table(sort_bycuda_time_total, row_limit10)) # 2. 结构诊断 analyzer ModelAnalyzer(model, input_shape(1,3,224,224)) analyzer.generate_report(diagnosis_report.html) # 输出热力图、OPI排名、layer详情diagnosis_report.html会显示Top 3 Bottleneck Layers按FLOPs和显存排序如layer4.2.conv2: 2.1 GFLOPs, 1.8GB activationOptimization Potential Index (OPI)layer4.2.conv2: OPI0.92 (high)Precision Sensitivitylayer4.2.conv2: L2 distance after INT80.032 (low)这份报告就是你的“优化地图”所有后续操作都围绕OPI0.8的layer展开。4.3 量化实战从PTQ到QAT的渐进式落地我们以ResNet50为例展示完整量化流程Step 1: PTQ校准import onnx import onnxruntime as ort from onnxruntime.quantization import QuantFormat, QuantType, quantize_static, CalibrationDataReader # 导出ONNX torch.onnx.export(model, x, resnet50.onnx, opset_version13, input_names[input], output_names[output], dynamic_axes{input: {0: batch}, output: {0: batch}}) # 构建校准数据集必须是真实线上数据分布 class CalibDataLoader(CalibrationDataReader): def __init__(self, data_list): self.data_list data_list self.enum_data None def get_next(self): if self.enum_data is None: self.enum_data iter(self.data_list) return {input: next(self.enum_data).numpy()} def rewind(self): self.enum_data None # 执行PTQ quantize_static( model_inputresnet50.onnx, model_outputresnet50_ptq.onnx, calibration_data_readerCalibDataLoader(calib_dataset), quant_formatQuantFormat.QDQ, # Quantize-DeQuantize format per_channelTrue, reduce_rangeFalse, activation_typeQuantType.QUInt8, weight_typeQuantType.QInt8 )Step 2: QAT微调# 在PyTorch中插入fake quantize from torch.ao.quantization import get_default_qconfig_mapping, prepare_qat, convert qconfig_mapping get_default_qconfig_mapping(fbgemm) # fbgemm for CPU, cuda for GPU model_qat prepare_qat(model, qconfig_mapping) # train for 1 epoch for epoch in range(1): for x, y in train_loader: x, y x.cuda(), y.cuda() y_pred model_qat(x) loss criterion(y_pred, y) loss.backward() optimizer.step() optimizer.zero_grad() model_quantized convert(model_qat.eval()) torch.save(model_quantized, resnet50_qat.pth)Step 3: TensorRT部署import tensorrt as trt import pycuda.driver as cuda # 创建builder TRT_LOGGER trt.Logger(trt.Logger.WARNING) builder trt.Builder(TRT_LOGGER) network builder.create_network(1 int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) parser trt.OnnxParser(network, TRT_LOGGER) with open(resnet50_qat.onnx, rb) as model: parser.parse(model.read()) # 配置builder config builder.create_builder_config() config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 3 30) # 3GB workspace config.set_flag(trt.BuilderFlag.FP16) # 启用FP16 config.set_flag(trt.BuilderFlag.INT8) # 启用INT8 config.int8_calibrator Int8EntropyCalibrator2(calib_dataset) # 自定义校准器 # 构建engine engine builder.build_serialized_network(network, config) with open(resnet50.trt, wb) as f: f.write(engine)4.4 剪枝与重训练用最少的epoch找回最多的精度我们用TorchVision的ResNet50做演示import torch.nn.utils.prune as prune from model_optimizer.pruner import ChannelPruner # 1. 计算gamma score def get_gamma_scores(model): scores {} for name, module in model.named_modules(): if isinstance(module, torch.nn.BatchNorm2d): scores[name] module.weight.data.abs().cpu().numpy() return scores # 2. 执行channel pruning pruner ChannelPruner(model, sparsity0.3) pruner.prune_by_gamma() # 基于gamma剪枝 # 3. Rewire and finetune model pruner.rewire_model() # 重置BN stats for m in model.modules(): if isinstance(m, torch.nn.BatchNorm2d): m.reset_running_stats() # Finetune 1 epoch criterion torch.nn.CrossEntropyLoss() optimizer torch.optim.SGD(model.parameters(), lr1e-3) for epoch in range(1): for x, y in train_loader: x, y x.cuda(), y.cuda() y_pred model(x) loss criterion(y_pred, y) loss.backward() optimizer.step() optimizer.zero_grad()4.5 部署验证不只是accuracy更是robustness部署后我们跑三组测试Accuracy Test: ImageNet val set, top-1 accLatency Test: 1000次inference取P99延迟Robustness Test:Gaussian noise (σ0.05)Motion blur (kernel size5)Adversarial patch (FGSM ε0.01)结果汇总成表格MetricOriginalPTQQATPrunedQATTop-1 Acc76.2%74.1%75.8%75.5%P99 Latency42ms28ms26ms24msNoise Robustness72.3%68.1%71.9%71.5%只有三者都达标才算Model-Optimizer成功。我们曾因Robustness掉点太多回退到QAT方案宁可多花2天训练也不接受线上事故。5. 常见问题与排查技巧实录那些深夜debug的真实战场5.1 “量化后精度暴跌”——90%的问题出在校准数据上现象PTQ后top-1 acc掉5%以上。排查路径检查校准数据集len(calib_dataset)是否100如果是立刻扩充。我们要求至少500张且覆盖所有光照/角度/遮挡条件。检查校准数据预处理是否和训练时完全一致尤其注意normalize的mean/std是否相同。曾有个项目训练用[0.485,0.456,0.406]校准用[0.5,0.5,0.5]导致量化scale全错。检查op支持用onnxruntime.tools.get_fusion_options()查看哪些op被fuse了。如果BatchNormalization被fuse进Conv而你的校准数据没经过BN就会出错。解决方案换KL校准或用QAT。QAT虽慢但稳。5.2 “TensorRT build失败无报错”——CUDA context崩溃的幽灵现象builder.build_serialized_network()返回None无任何error message。根本原因CUDA context在build过程中被意外释放。常见于多进程环境下子进程继承了父进程的CUDA context但没正确初始化系统显存不足builder申请workspace失败排查命令# 查看GPU显存 nvidia-smi --query-compute-appspid,used_memory --formatcsv # 查看CUDA error export CUDA_LAUNCH_BLOCKING1 # 强制同步暴露底层error python build_engine.py解决方案单进程buildbuild完再fork inference process设置config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 230)明确限制workspace升级driver到515.65.01以上修复了TRT 8.6的context bug5.3 “剪枝后模型变慢”——稀疏化没带来加速反而拖累现象剪掉30% channelFPS不升反降。原因分析剪枝后模型channel数不再是32/64/128等2的幂导致GPU warp利用率下降。CUDA core喜欢“整齐”的数据。被剪枝的layer其后续layer的in_channels变小但out_channels没变导致compute-bound转为memory-bound。解决方案剪枝时强制channel数对齐到32的倍数。我们写了个align_to_power2函数自动padding。用Nsight Compute确认剪枝后achieved__inst_per_warp是否下降如果下降说明warp利用率低需调整剪枝比例。5.4 “QAT训练loss不收敛”——fake quantize的gradient陷阱现象QAT训练loss震荡acc不上升。根源fake quantize op的STE gradient不准确。调试技巧在fake quantize op后加torch.autograd.gradcheck验证gradient数值临时关闭quantize只开BN确认baseline能train降低learning rate到1e-4因为quantized weight对lr更敏感终极方案用torch.ao.quantization.fuse_modules先fuse conv-bn-relu再QAT减少op数量稳定gradient。5.5 “部署后输出全为0”——tensor shape mismatch的静默杀手现象TRT engine inferoutput tensor全是0。排查清单输入tensor的dtype是否为torch.float32TRT要求FP32输入即使模型是INT8。输入tensor的contiguous()是否TrueTRT对内存layout敏感非contiguous tensor会读错。context.execute_v2()的bindings顺序是否和network input/output顺序一致我们用network.get_input(i).name严格校验。一个真实案例某次部署input tensor是NHWC layout但TRT expect NCHW结果所有channel读成0。解决方案x x.permute(0,3,1,2)。6. 工具链与资源推荐少走弯路的实战清单6.1 必备工具包Profiling:Nsight ComputeGPU底层、torch.profilerPyTorch层、onnxruntime-toolsONNX层量化:onnxruntime-quantizationPTQ、torch.ao.quantizationQAT、TensorRT部署剪枝:torch.nn.utils.prune内置、torch-pruning高级结构化、nni微软NAS工具可视化:netronONNX graph、torchviewPyTorch graph、model_analyzer自研热力图6.2 高价值开源项目参考TVM: 不是拿来就用而是学习其Relay IR设计思想。我们借鉴了它的Pass机制把优化步骤抽象为可组合的Pass。ONNX Model Zoo: 里面所有模型都附带量化/剪枝脚本是最佳学习模板。尤其关注resnet50-v1-7的QAT example。NVIDIA DeepLearningExamples: 官方优化案例代码质量高但要注意版本匹配如Ampere GPU需用TRT 8.5。6.3 我的私藏调试技巧“三色日志法”在关键op前后加log用颜色区分[DEBUG]绿色正常流程、[WARN]黄色数值异常、[ERROR]红色流程中断。用rich库渲染一眼定位问题层。“checkpoint回滚”每次重大操作如quantize、prune后torch.save(model.state_dict(), ckpt_stepX.pth)。debug时可快速回退。“最小复现集”遇到诡异bug立即用torch.fx.symbolic_trace提取出出问题的subgraph单独test。90%的bug在subgraph里就能复现。最后分享一个小技巧Model-Optimizer不是终点而是起点。我们每个优化后的模型都会生成一个optimization_report.md记录所有操作、参数、效果、代价。半年后回头看你会发现当初为省200MB显存做的INT8现在被新硬件淘汰而那次为robustness多花的2天QAT让模型在暴雨天依然稳定。优化不是追求当下最优而是为未来留出弹性。这是我踩过最多坑后最深的体会。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门