ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

迁移避坑指南:从旧量化工具链切到 Model-Optimizer,这些 API 差异逐个对

迁移避坑指南:从旧量化工具链切到 Model-Optimizer,这些 API 差异逐个对

迁移避坑指南:从旧量化工具链切到 Model-Optimizer,这些 API 差异逐个对

【免费下载链接】Model-OptimizerA unified library of SOTA model optimization techniques like quantization, distillation, pruning, neural architecture search, speculative decoding, etc. It compresses deep learning models for downstream deployment frameworks like TensorRT-LLM, TensorRT, vLLM, etc. to optimize inference speed.项目地址: https://gitcode.com/GitHub_Trending/te/Model-Optimizer

当 2025 年底 "TensorRT Model Optimizer" 正式更名 "NVIDIA Model Optimizer"、并于 2026 年初发布提速 50% 的 v1.2 版本时,大量仍握着pytorch-quantization、TensorRTtrtexec、ONNX Runtime 量化脚本的老玩家开始动手迁移。社区里流传的迁移指南往往只讲"新工具更好用",却很少回答一个实际问题:把旧的量化/校准/导出代码改成 Model Optimizer(ModelOpt)风格,究竟要动哪些 API?哪些"看似一样"的参数其实语义全变?

本文不写营销话术,而是直接对照仓库源码与官方变更记录,把从旧工具链迁到 ModelOpt 的真实差异逐条拆开:量化配置怎么写、校准怎么驱动、导出链路怎么换,以及迁移后如何做精度与性能回退检查。文中的每一段代码都来自仓库真实文件,你可以按路径逐一核对。

一、先认清一件事:这是"另一套范式",不是"换皮"

旧工具链(TensorRT 隐式量化、trtexec --int8、ONNX Runtime 静态量化)的核心是"量化 + 编译一步到位":calibrator 收集激活范围,builder 直接产出 engine。而 ModelOpt 的哲学完全不同——它在 PyTorch 里做模拟量化(fake quantization),只把低精度计算"演"出来用于校准和验证,真正的加速要等导出到 TensorRT / TensorRT-LLM / vLLM / SGLang 等后端才发生。官方文档在 PyTorch Quantization 里写得很直白:

ModelOpt quantization is fake quantization, which means it only simulates the low-precision computation in PyTorch. Real speedup and memory saving should be achieved by exporting the model to deployment frameworks.

这句提示是所有迁移误区的总源头:如果你在 ModelOpt 上做完量化后直接跑 PyTorch 推理测速度,性能一定"没提升",这不是工具坏了,而是流程不对。迁移的第一步,是把"量化即编译"的旧心智,换成"量化→校准→导出→部署"四段式的新心智。

二、API 对照:量化配置从"塞模块"变成"写配置"

2.1 旧世界:手动插 quantizer + calibrator

在pytorch-quantization时代,你需要手写TensorQuantizer替换网络层、维护自己的CalibQuantizer,再对接 TensorRT 的EntropyCalibrator2/MinMaxCalibrator生成 calibration cache。配置与代码深度耦合,换一个量化格式往往要改多处。

2.2 新世界:一个 config 字典 + 一个 forward loop

ModelOpt 把"量化哪些层、用什么格式、怎么校准"收敛成一份配置,一行调用完成替换与校准(model_quant.py 中的quantize()):

import modelopt.torch.quantization as mtq model = mtq.quantize(model, mtq.NVFP4_DEFAULT_CFG, forward_loop)

官方入口在 hf_ptq/README.md 中给出完整示例:mtq.quantize(model, config, forward_loop)会原地把模块替换为量化版本,并按config["algorithm"]驱动forward_loop完成校准。forward_loop是迁移中第一个容易踩坑的点:

  • 旧习惯:calibrator 是一个独立对象,喂数据、收统计都靠它;
  • 新习惯:forward loop 只是一个带model参数的函数,内部把校准数据 loader 跑一遍即可。仓库还明确警告:无参的forward_loop用法已进入弃用流程(model_quant.py中calibrate()会发DeprecationWarning)。

配置结构本身是"字典 + 顺序覆盖"(详见 config.py):

MY_QUANT_CFG = { "quant_cfg": [ # 先全部禁用 {"quantizer_name": "*", "enable": False}, # 再按通配符精确开启 {"quantizer_name": "*weight_quantizer", "cfg": {"num_bits": 8, "axis": 0}}, {"quantizer_name": "*input_quantizer", "cfg": {"num_bits": 8, "axis": None}}, # 对特定父模块排除 {"quantizer_name": "*input_quantizer", "parent_class": "nn.LeakyReLU", "enable": False}, ], "algorithm": "max", }

三个细节务必记牢:

  1. 列表按顺序生效,后写的覆盖先写的。官方推荐"先deny-all、再逐格式开启、最后追加默认排除(BatchNorm、LM head、MoE router)"的三段式写法,这直接决定了你的自定义 config 写在哪、怎么写。
  2. cfg可以是一个列表,用于 W4A8 这类"先 INT4 再 FP8"的顺序量化(SequentialQuantizer),这是旧工具链没有的概念。
  3. 通配符直接打在模块名上(*weight_quantizer、*input_quantizer),想跳过lm_head只需追加一条{"quantizer_name": "*lm_head*", "enable": False}。

2.3 CLI 迁移:--qformat让位给--recipe

如果你靠命令行脚本过日子,这条差异杀伤力最大。CHANGELOG 记录了明确的演进:旧版examples/hf_ptq/hf_ptq.py的--qformat/--kv_cache_qformat已弃用,改由--recipe承载;modelopt_recipes/configs/ptq/presets/model/下躺着 40 个预置配方(presets/model),从fp8.yaml、int8_smoothquant.yaml到nvfp4.yaml、w4a8_awq_beta.yaml一应俱全。YAML 配方用$import机制组合公共单元(nvfp4.yaml):

imports: base_disable_all: configs/ptq/units/base_disable_all w4a4_nvfp4_nvfp4: configs/ptq/units/w4a4_nvfp4_nvfp4 default_disabled_quantizers: configs/ptq/units/default_disabled_quantizers algorithm: max quant_cfg: - $import: base_disable_all - $import: w4a4_nvfp4_nvfp4 - $import: default_disabled_quantizers

配方把"量化配置 + 校准算法 + KV cache 设置"收在一个文件里,避免了三组 flag 各自为政导致的漂移。迁移时注意:旧--qformat的短名(int8_sq、nvfp4_awq等)已在 0.47 移除,直接用配方 basename。

三、校准:从 calibrator 对象到 forward-loop 驱动

3.1 算法选择与参数注入

calibrate()(model_quant.py)支持的算法与旧工具链差异明显:

  • max:最简校准,等价于旧 MinMax,但"仅权重量化 + max"时不需要 forward loop;
  • smoothquant:迁移量最大的旧用户通常会选它做 INT8 平滑量化;
  • awq_lite / awq_full / awq_clip:AWQ 家族,旧链路上要自己找脚本;
  • 新算法SVDQuant、Local-Hessian、NVFP4 MSE等则是旧工具链完全没有的。

算法可以用字符串,也可以传字典注入超参,例如{"method": "awq_clip", "max_co_batch_size": 4096}。仓库示例还展示了按专家比例校准 MoE:build_quant_cfg()(example_utils.py)会把moe_calib_experts_ratio注入 algorithm 字典——这是迁移 MoE 模型时最容易被忽略的调参入口。

3.2 校准数据要求

校准数据规模在官方指南里有明确推荐:128–512 个样本(PyTorch Quantization),默认混合cnn_dailymail与nemotron-post-training-dataset-v2两个数据集。旧工具链的 calibration cache 文件(.cache)在 ModelOpt 里没有对应概念——统计量全部在校准过程中动态收集并写入模型内部的_amax,你不需要也不能"复用"旧的 cache 文件。

3.3 迁移后新增的"防呆"检查

这是 ModelOpt 比旧工具链体贴的地方,也是迁移后第一次跑通时最容易愣住的点:quantize()内部有两个"量化是否真的生效"的守卫(model_quant.py):

  • _check_weight_quantization_took_effect:配置要求量化权重、但通配符没匹配到任何模块时,直接抛RuntimeError,而不是像旧工具那样"静默产出一个根本没量化的模型"。官方注释点出了动机:早期版本就是"默默导出quant_algo: null的空量化 checkpoint",MoE 架构的模块命名与通用配方不匹配时尤其容易触发。误报时可用环境变量MODELOPT_SKIP_WEIGHT_QUANT_CHECK=1跳过(管道并行下某 rank 本地确实没有目标模块时用)。
  • _check_indexer_quantization_took_effect:稀疏注意力 indexer 的同类守卫。

迁移时的正确姿势:报错先读信息——它通常会告诉你"配方没匹配上架构的模块命名",去 model_type 配方目录 找对应模型的专属配方,而不是改错环境变量硬跳过。

四、导出链路:从"出 engine"到"出统一 checkpoint"

4.1 最大的 API 替换:export_hf_checkpoint

旧链路终点是trtexec --onnx=xxx.onnx --saveEngine=xxx.engine或 Python builder 直接出 engine。ModelOpt 的终点是统一 Hugging Face checkpoint(Unified HuggingFace Checkpoint):

from modelopt.torch.export import export_hf_checkpoint with torch.inference_mode(): export_hf_checkpoint(model, export_dir)

产出物是"safetensors 权重 +hf_quant_config.json量化配置 + 结构/分词器/元数据 json",TensorRT-LLM(≥ v1.2.0)、vLLM(≥ v0.10.1)、SGLang(≥ v0.4.10)都能直接加载。同一个 checkpoint 喂三个后端,这是旧"一次编译一个 engine"的链路做不到的。

4.2 旧导出 API 正在退场

CHANGELOG 白纸黑字:export_tensorrt_llm_checkpoint与torch_to_tensorrt_llm_checkpoint已发DeprecationWarning,计划在 0.49.0 移除,实现迁移到modelopt.torch.export.trtllm子包;get_model_type等返回 TRT-LLM 模型名的辅助函数同样弃用,改用model.config.model_type。如果你还在 import 这些旧函数,迁移的第一步就是换成export_hf_checkpoint。

4.3 状态保存:modelopt_state是新的"checkpoint 灵魂"

量化、剪枝、蒸馏都会改动模型架构和权重,ModelOpt 把修改记录在modelopt_state里(Saving & Restoring):

import modelopt.torch.opt as mto # 一起存 mto.save(model, "modelopt_model.pth") # 或只存状态,权重用自己的方式存 torch.save(mto.modelopt_state(model), "modelopt_state.pth") # 恢复 mto.restore(model, "modelopt_model.pth")

迁移要点:架构修改必须随状态一起恢复,光存state_dict会丢掉量化的_amax与替换后的模块结构。另外 CHANGELOG 提醒:携带 0.45 之前旧版layerwise布尔字段的modelopt_state,在新版恢复时直接校验失败而不是自动迁移,需先用 0.45/0.46 重存一次。

4.4 真实压缩与 GGML:新的导出分支

对显存敏感的场景,mtq.compress()(compress.py)把权重真正压成低比特存储。统一导出还支持 GGML 布局(IQ1_S / IQ2_XS / Q8_0),此时权重被替换为字节精确的 GGML block(uint8 张量,形状为[*logical_shape[:-1], logical_shape[-1] // block_size, payload_bytes])。坑在于:ModelOpt 的 per-tensor FP8 格式在 GGML 中没有等价类型,FP8 checkpoint 转 GGUF 必须先转成 GGML 支持的张量类型,做不到无损直转。规划 GGML 落地前务必先确认这条限制。

五、ONNX 侧的对应差异:QDQ 是终点不是起点

如果迁移的是 ONNX 链路(检测、分割等视觉模型),入口换成modelopt.onnx.quantization的quantize()(quantize.py、ONNX Quantization):

import modelopt.onnx.quantization as moq calibration_data = np.load(calibration_data_path) # npz/npy,dict 键必须与 ONNX 输入名一致 moq.quantize( onnx_path=onnx_path, calibration_data=calibration_data, output_path="quant.onnx", quantize_mode="int8", # 'int8' | 'int4' | 'fp8' calibration_method="max", # int8/fp8: 'entropy'|'max'; int4: 'awq_clip'|'awq_lite'|'awq_full'|'rtn_dq' )

对照旧 ONNX Runtime 静态量化,差异集中在四点:

  1. 校准数据格式:只收 npz/npy,且字典键必须匹配模型输入名;也可以用CalibrationDataReader或calibration_shapes指定输入形状。
  2. 执行 Provider 顺序:calibration_eps=["cpu", "cuda:0", "trt"],含自定义算子时trt会被自动追加;TensorRT ≥ 10.0 才支持 TensorRT EP。
  3. 产出是带 QDQ 的 ONNX,不是 engine:文档明确"generated ONNX should be compiled into TensorRT engine"才真正提速——trtexec --onnx=quant.onnx --saveEngine=quant.engine。这一步与旧链路看似相同,但 QDQ 插入遵循 TensorRT 的显式量化规则,op_types_to_quantize、nodes_to_exclude、trt_plugins等参数直接决定 QDQ 放哪、精度损失在哪。
  4. 模块被移除:modelopt.onnx.quantization.ort_patching与graph_utils已无兼容层直接删除,旧的 patch 写法需按迁移映射改用graph_rewrites等新模块(见 CHANGELOG)。

六、迁移后必做的回退检查清单

6.1 精度回退

  1. 打印量化摘要:mtq.print_quant_summary(model)核对 quantizer 是否落到预期层上,这是定位"静默未量化"最快的抓手。
  2. 读导出配置:导出的hf_quant_config.json里quant_algo不能是null,quantized_layers不能为空——这正是 3.3 节守卫要防的失败模式。
  3. 先选对配方再谈精度:NVFP4 默认配方全层量化,官方在 hf_ptq/README.md 明确建议高精度优先用NVFP4_MLP_ONLY_CFG(只量化 MLP/MoE)、NVFP4_EXPERTS_ONLY_CFG(只量化专家层)或NVFP4_OMLP_ONLY_CFG(外加o_proj),保住注意力 QKV 投影的高精度。
  4. 校准数据一致性:旧 cache 不可复用,校准集与验证集要隔离;MoE 记得调moe_calib_experts_ratio。
  5. 低比特精度救济:若精度回退超预期,检查block_sizes(config.py 的QuantizerAttributeConfig)——低比特必须用 per-block(如{-1: 32}、{"type": "dynamic"}),还能用"scale_bits"/"scale_block_sizes"做 scale 的双重量化;4-bit 精度不足时考虑 AWQ 或 SVDQuant,再不行就上 QAT/QAD 训练补救。

6.2 性能回退

  1. 别在 PyTorch 里测速:fake quant 不加速,只有导出后测(4.1/5.1 节)。对比基准用trtexec报告的 Latency / Throughput,同参数、同 GPU、同精度档位才可比。
  2. 确认后端版本:统一 checkpoint 有最低版本要求(TRT-LLM v1.2.0 / vLLM v0.10.1 / SGLang v0.4.10),旧容器加载新格式大概率报KeyError或结构错误——这是迁移后最常见的"环境坑"。
  3. 验证收益预期:仓库最新成果给出了可参照的量化收益量级——Nemotron 3 Ultra 550B 量化到 NVFP4 后解码吞吐最高 5.9×、精度持平 BF16;Qwen3.6-35B-A3B 走 NVFP4 W4A4 PTQ + 量化感知蒸馏后,vLLM 吞吐 1.30×、checkpoint 缩小 3.1×。你的模型跑不出这个数不等于失败,先对照是否漏了导出步骤、KV cache 是否量化、配方是否选对。
  4. 分布式注意:FSDP2 下export_hf_checkpoint需要所有 rank 参与 collectives(只有 rank 0 写文件);offload 流式导出路径不支持save_modelopt_state=True——这些都会在迁移后第一次多卡运行时冒出来。

七、迁移总路线图

把上面的差异收敛成一张行动清单,照着走基本不会踩空:

  1. 改入口:mtq.quantize(model, config, forward_loop)替换"手动插 quantizer + calibrator";forward_loop记得带model参数。
  2. 改配置:--qformat换成--recipe或 YAML 配方;自定义格式按"deny-all → 逐格式开启 → 默认排除"顺序写quant_cfg。
  3. 改校准:丢弃旧 cache 文件;用 128–512 样本的 forward loop;先max打底,再按需上 SmoothQuant / AWQ / SVDQuant。
  4. 改导出:export_hf_checkpoint替代export_tensorrt_llm_checkpoint/trtexec;状态用mto.save/mto.restore或单独存modelopt_state。
  5. 回退验证:print_quant_summary+hf_quant_config.json验结构,导出后测速验证性能,必要时用block_sizes、配方选择与 QAT/QAD 兜底精度。

ModelOpt 这套链路最反直觉、也最值得记住的一句话是:量化在校准,性能在导出。想清楚这句,迁移路上的大多数"性能没提升"和"导出就报错",都能少走一半弯路。

【免费下载链接】Model-OptimizerA unified library of SOTA model optimization techniques like quantization, distillation, pruning, neural architecture search, speculative decoding, etc. It compresses deep learning models for downstream deployment frameworks like TensorRT-LLM, TensorRT, vLLM, etc. to optimize inference speed.项目地址: https://gitcode.com/GitHub_Trending/te/Model-Optimizer

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

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