nvidia-modelopt 0.35.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. modelopt/__init__.py +20 -0
  2. modelopt/deploy/__init__.py +16 -0
  3. modelopt/deploy/llm/__init__.py +30 -0
  4. modelopt/deploy/llm/generate.py +345 -0
  5. modelopt/onnx/__init__.py +34 -0
  6. modelopt/onnx/autocast/__init__.py +19 -0
  7. modelopt/onnx/autocast/__main__.py +187 -0
  8. modelopt/onnx/autocast/convert.py +202 -0
  9. modelopt/onnx/autocast/graphsanitizer.py +422 -0
  10. modelopt/onnx/autocast/logging_config.py +78 -0
  11. modelopt/onnx/autocast/nodeclassifier.py +416 -0
  12. modelopt/onnx/autocast/precisionconverter.py +1032 -0
  13. modelopt/onnx/autocast/referencerunner.py +159 -0
  14. modelopt/onnx/autocast/utils.py +117 -0
  15. modelopt/onnx/llm_export_utils/__init__.py +16 -0
  16. modelopt/onnx/llm_export_utils/export_utils.py +162 -0
  17. modelopt/onnx/llm_export_utils/quantization_utils.py +122 -0
  18. modelopt/onnx/llm_export_utils/surgeon_utils.py +120 -0
  19. modelopt/onnx/logging_config.py +77 -0
  20. modelopt/onnx/op_types.py +300 -0
  21. modelopt/onnx/quantization/__init__.py +20 -0
  22. modelopt/onnx/quantization/__main__.py +291 -0
  23. modelopt/onnx/quantization/calib_utils.py +178 -0
  24. modelopt/onnx/quantization/extensions.py +35 -0
  25. modelopt/onnx/quantization/fp8.py +375 -0
  26. modelopt/onnx/quantization/graph_utils.py +1479 -0
  27. modelopt/onnx/quantization/gs_patching.py +112 -0
  28. modelopt/onnx/quantization/int4.py +1347 -0
  29. modelopt/onnx/quantization/int8.py +283 -0
  30. modelopt/onnx/quantization/operators.py +121 -0
  31. modelopt/onnx/quantization/ort_patching.py +1766 -0
  32. modelopt/onnx/quantization/ort_utils.py +346 -0
  33. modelopt/onnx/quantization/partitioning.py +439 -0
  34. modelopt/onnx/quantization/qdq_utils.py +1349 -0
  35. modelopt/onnx/quantization/quant_utils.py +283 -0
  36. modelopt/onnx/quantization/quantize.py +528 -0
  37. modelopt/onnx/quantization/src/modelopt_round_and_pack_ext.cpp +127 -0
  38. modelopt/onnx/trt_utils.py +424 -0
  39. modelopt/onnx/utils.py +753 -0
  40. modelopt/torch/__init__.py +41 -0
  41. modelopt/torch/_deploy/__init__.py +23 -0
  42. modelopt/torch/_deploy/_runtime/__init__.py +29 -0
  43. modelopt/torch/_deploy/_runtime/common.py +72 -0
  44. modelopt/torch/_deploy/_runtime/ort_client.py +193 -0
  45. modelopt/torch/_deploy/_runtime/registry.py +83 -0
  46. modelopt/torch/_deploy/_runtime/runtime_client.py +154 -0
  47. modelopt/torch/_deploy/_runtime/tensorrt/constants.py +108 -0
  48. modelopt/torch/_deploy/_runtime/tensorrt/engine_builder.py +324 -0
  49. modelopt/torch/_deploy/_runtime/tensorrt/hw_param_config.py +51 -0
  50. modelopt/torch/_deploy/_runtime/tensorrt/layerwise_profiling.py +178 -0
  51. modelopt/torch/_deploy/_runtime/tensorrt/parse_trtexec_log.py +151 -0
  52. modelopt/torch/_deploy/_runtime/tensorrt/tensorrt_utils.py +200 -0
  53. modelopt/torch/_deploy/_runtime/trt_client.py +219 -0
  54. modelopt/torch/_deploy/compilation.py +142 -0
  55. modelopt/torch/_deploy/device_model.py +157 -0
  56. modelopt/torch/_deploy/profiling.py +193 -0
  57. modelopt/torch/_deploy/utils/__init__.py +18 -0
  58. modelopt/torch/_deploy/utils/onnx_optimizer.py +120 -0
  59. modelopt/torch/_deploy/utils/onnx_utils.py +58 -0
  60. modelopt/torch/_deploy/utils/torch_onnx.py +593 -0
  61. modelopt/torch/distill/__init__.py +28 -0
  62. modelopt/torch/distill/config.py +130 -0
  63. modelopt/torch/distill/distillation.py +84 -0
  64. modelopt/torch/distill/distillation_model.py +318 -0
  65. modelopt/torch/distill/loss_balancers.py +140 -0
  66. modelopt/torch/distill/losses.py +275 -0
  67. modelopt/torch/distill/mode.py +223 -0
  68. modelopt/torch/distill/plugins/__init__.py +24 -0
  69. modelopt/torch/distill/plugins/huggingface.py +110 -0
  70. modelopt/torch/distill/plugins/megatron.py +476 -0
  71. modelopt/torch/distill/registry.py +23 -0
  72. modelopt/torch/export/__init__.py +24 -0
  73. modelopt/torch/export/convert_hf_config.py +117 -0
  74. modelopt/torch/export/distribute.py +300 -0
  75. modelopt/torch/export/hf_config_map.py +84 -0
  76. modelopt/torch/export/layer_utils.py +1893 -0
  77. modelopt/torch/export/mcore_config_map.py +25 -0
  78. modelopt/torch/export/model_config.py +623 -0
  79. modelopt/torch/export/model_config_export.py +552 -0
  80. modelopt/torch/export/model_config_utils.py +392 -0
  81. modelopt/torch/export/model_utils.py +71 -0
  82. modelopt/torch/export/plugins/__init__.py +21 -0
  83. modelopt/torch/export/plugins/mcore_common.py +67 -0
  84. modelopt/torch/export/plugins/mcore_custom.py +530 -0
  85. modelopt/torch/export/plugins/mcore_deepseek.py +143 -0
  86. modelopt/torch/export/plugins/mcore_gptoss.py +71 -0
  87. modelopt/torch/export/plugins/mcore_llama.py +164 -0
  88. modelopt/torch/export/plugins/mcore_nemotron.py +90 -0
  89. modelopt/torch/export/plugins/mcore_qwen.py +99 -0
  90. modelopt/torch/export/plugins/megatron_importer.py +647 -0
  91. modelopt/torch/export/postprocess.py +847 -0
  92. modelopt/torch/export/quant_utils.py +1069 -0
  93. modelopt/torch/export/tensorrt_llm_type.py +42 -0
  94. modelopt/torch/export/tensorrt_llm_utils.py +405 -0
  95. modelopt/torch/export/transformer_engine.py +94 -0
  96. modelopt/torch/export/unified_export_hf.py +537 -0
  97. modelopt/torch/export/unified_export_megatron.py +1164 -0
  98. modelopt/torch/nas/__init__.py +27 -0
  99. modelopt/torch/nas/algorithms.py +856 -0
  100. modelopt/torch/nas/autonas.py +808 -0
  101. modelopt/torch/nas/conversion.py +119 -0
  102. modelopt/torch/nas/hparams/__init__.py +19 -0
  103. modelopt/torch/nas/hparams/concat.py +319 -0
  104. modelopt/torch/nas/hparams/container.py +48 -0
  105. modelopt/torch/nas/modules/__init__.py +21 -0
  106. modelopt/torch/nas/modules/container.py +99 -0
  107. modelopt/torch/nas/modules/conv.py +254 -0
  108. modelopt/torch/nas/modules/linear.py +76 -0
  109. modelopt/torch/nas/modules/norm.py +193 -0
  110. modelopt/torch/nas/modules/utils.py +61 -0
  111. modelopt/torch/nas/patch.py +261 -0
  112. modelopt/torch/nas/plugins/__init__.py +29 -0
  113. modelopt/torch/nas/plugins/megatron.py +1497 -0
  114. modelopt/torch/nas/plugins/torch.py +71 -0
  115. modelopt/torch/nas/plugins/transformer_engine.py +42 -0
  116. modelopt/torch/nas/plugins/transformers.py +210 -0
  117. modelopt/torch/nas/registry.py +23 -0
  118. modelopt/torch/nas/search_space.py +260 -0
  119. modelopt/torch/nas/traced_hp.py +149 -0
  120. modelopt/torch/nas/utils.py +408 -0
  121. modelopt/torch/opt/__init__.py +41 -0
  122. modelopt/torch/opt/_hooks.py +98 -0
  123. modelopt/torch/opt/config.py +383 -0
  124. modelopt/torch/opt/conversion.py +620 -0
  125. modelopt/torch/opt/dynamic.py +1367 -0
  126. modelopt/torch/opt/hparam.py +275 -0
  127. modelopt/torch/opt/mode.py +353 -0
  128. modelopt/torch/opt/plugins/__init__.py +38 -0
  129. modelopt/torch/opt/plugins/diffusers.py +40 -0
  130. modelopt/torch/opt/plugins/huggingface.py +162 -0
  131. modelopt/torch/opt/plugins/mcore_dist_checkpointing.py +211 -0
  132. modelopt/torch/opt/plugins/megatron.py +142 -0
  133. modelopt/torch/opt/plugins/peft.py +108 -0
  134. modelopt/torch/opt/plugins/transformers.py +160 -0
  135. modelopt/torch/opt/searcher.py +363 -0
  136. modelopt/torch/opt/utils.py +125 -0
  137. modelopt/torch/prune/__init__.py +26 -0
  138. modelopt/torch/prune/fastnas.py +376 -0
  139. modelopt/torch/prune/gradnas.py +349 -0
  140. modelopt/torch/prune/plugins/__init__.py +24 -0
  141. modelopt/torch/prune/plugins/mcore_minitron.py +263 -0
  142. modelopt/torch/prune/plugins/transformers.py +41 -0
  143. modelopt/torch/prune/pruning.py +211 -0
  144. modelopt/torch/quantization/__init__.py +26 -0
  145. modelopt/torch/quantization/algorithms.py +671 -0
  146. modelopt/torch/quantization/backends/__init__.py +20 -0
  147. modelopt/torch/quantization/backends/fp8_per_tensor_gemm.py +204 -0
  148. modelopt/torch/quantization/backends/gemm_registry.py +156 -0
  149. modelopt/torch/quantization/backends/nvfp4_gemm.py +201 -0
  150. modelopt/torch/quantization/backends/utils.py +28 -0
  151. modelopt/torch/quantization/calib/__init__.py +25 -0
  152. modelopt/torch/quantization/calib/bias.py +172 -0
  153. modelopt/torch/quantization/calib/calibrator.py +63 -0
  154. modelopt/torch/quantization/calib/histogram.py +434 -0
  155. modelopt/torch/quantization/calib/max.py +110 -0
  156. modelopt/torch/quantization/compress.py +227 -0
  157. modelopt/torch/quantization/config.py +1180 -0
  158. modelopt/torch/quantization/conversion.py +389 -0
  159. modelopt/torch/quantization/export_onnx.py +639 -0
  160. modelopt/torch/quantization/extensions.py +89 -0
  161. modelopt/torch/quantization/mode.py +428 -0
  162. modelopt/torch/quantization/model_calib.py +949 -0
  163. modelopt/torch/quantization/model_quant.py +477 -0
  164. modelopt/torch/quantization/nn/__init__.py +26 -0
  165. modelopt/torch/quantization/nn/functional.py +109 -0
  166. modelopt/torch/quantization/nn/modules/__init__.py +16 -0
  167. modelopt/torch/quantization/nn/modules/quant_activations.py +22 -0
  168. modelopt/torch/quantization/nn/modules/quant_batchnorm.py +24 -0
  169. modelopt/torch/quantization/nn/modules/quant_conv.py +123 -0
  170. modelopt/torch/quantization/nn/modules/quant_instancenorm.py +39 -0
  171. modelopt/torch/quantization/nn/modules/quant_linear.py +258 -0
  172. modelopt/torch/quantization/nn/modules/quant_module.py +211 -0
  173. modelopt/torch/quantization/nn/modules/quant_pooling.py +99 -0
  174. modelopt/torch/quantization/nn/modules/quant_rnn.py +527 -0
  175. modelopt/torch/quantization/nn/modules/tensor_quantizer.py +1293 -0
  176. modelopt/torch/quantization/plugins/__init__.py +73 -0
  177. modelopt/torch/quantization/plugins/accelerate.py +214 -0
  178. modelopt/torch/quantization/plugins/apex.py +52 -0
  179. modelopt/torch/quantization/plugins/attention.py +312 -0
  180. modelopt/torch/quantization/plugins/custom.py +192 -0
  181. modelopt/torch/quantization/plugins/diffusers.py +237 -0
  182. modelopt/torch/quantization/plugins/fairscale.py +45 -0
  183. modelopt/torch/quantization/plugins/huggingface.py +736 -0
  184. modelopt/torch/quantization/plugins/megatron.py +462 -0
  185. modelopt/torch/quantization/plugins/peft.py +148 -0
  186. modelopt/torch/quantization/plugins/transformer_engine.py +60 -0
  187. modelopt/torch/quantization/plugins/transformers.py +52 -0
  188. modelopt/torch/quantization/plugins/transformers_trainer.py +426 -0
  189. modelopt/torch/quantization/plugins/trl.py +27 -0
  190. modelopt/torch/quantization/plugins/vllm.py +199 -0
  191. modelopt/torch/quantization/qtensor/__init__.py +24 -0
  192. modelopt/torch/quantization/qtensor/base_qtensor.py +370 -0
  193. modelopt/torch/quantization/qtensor/fp8_tensor.py +151 -0
  194. modelopt/torch/quantization/qtensor/int4_tensor.py +130 -0
  195. modelopt/torch/quantization/qtensor/int8_tensor.py +124 -0
  196. modelopt/torch/quantization/qtensor/mxfp4_tensor.py +144 -0
  197. modelopt/torch/quantization/qtensor/nf4_tensor.py +199 -0
  198. modelopt/torch/quantization/qtensor/nvfp4_tensor.py +295 -0
  199. modelopt/torch/quantization/src/tensor_quant.cpp +77 -0
  200. modelopt/torch/quantization/src/tensor_quant.h +69 -0
  201. modelopt/torch/quantization/src/tensor_quant_fp8.cpp +48 -0
  202. modelopt/torch/quantization/src/tensor_quant_gpu.cu +361 -0
  203. modelopt/torch/quantization/src/tensor_quant_gpu_fp8.cu +122 -0
  204. modelopt/torch/quantization/src/tensor_quant_mx.cu +411 -0
  205. modelopt/torch/quantization/src/tensor_quant_mx.h +247 -0
  206. modelopt/torch/quantization/tensor_quant.py +816 -0
  207. modelopt/torch/quantization/triton/__init__.py +36 -0
  208. modelopt/torch/quantization/triton/fp4_kernel.py +303 -0
  209. modelopt/torch/quantization/utils.py +443 -0
  210. modelopt/torch/sparsity/__init__.py +19 -0
  211. modelopt/torch/sparsity/config.py +51 -0
  212. modelopt/torch/sparsity/magnitude.py +148 -0
  213. modelopt/torch/sparsity/mode.py +203 -0
  214. modelopt/torch/sparsity/module.py +87 -0
  215. modelopt/torch/sparsity/plugins/__init__.py +27 -0
  216. modelopt/torch/sparsity/plugins/megatron.py +93 -0
  217. modelopt/torch/sparsity/searcher.py +84 -0
  218. modelopt/torch/sparsity/sparsegpt.py +276 -0
  219. modelopt/torch/sparsity/sparsification.py +123 -0
  220. modelopt/torch/speculative/__init__.py +20 -0
  221. modelopt/torch/speculative/config.py +100 -0
  222. modelopt/torch/speculative/eagle/__init__.py +20 -0
  223. modelopt/torch/speculative/eagle/conversion.py +67 -0
  224. modelopt/torch/speculative/eagle/default_config.py +50 -0
  225. modelopt/torch/speculative/eagle/eagle_model.py +51 -0
  226. modelopt/torch/speculative/eagle/utils.py +91 -0
  227. modelopt/torch/speculative/medusa/__init__.py +19 -0
  228. modelopt/torch/speculative/medusa/conversion.py +59 -0
  229. modelopt/torch/speculative/medusa/medusa_model.py +32 -0
  230. modelopt/torch/speculative/mode.py +86 -0
  231. modelopt/torch/speculative/plugins/__init__.py +33 -0
  232. modelopt/torch/speculative/plugins/megatron_eagle.py +2119 -0
  233. modelopt/torch/speculative/plugins/megatron_medusa.py +312 -0
  234. modelopt/torch/speculative/plugins/transformers.py +1138 -0
  235. modelopt/torch/speculative/speculative_decoding.py +59 -0
  236. modelopt/torch/speculative/utils.py +364 -0
  237. modelopt/torch/trace/__init__.py +25 -0
  238. modelopt/torch/trace/analyzer.py +1368 -0
  239. modelopt/torch/trace/modules/__init__.py +19 -0
  240. modelopt/torch/trace/modules/concat.py +384 -0
  241. modelopt/torch/trace/modules/nn.py +153 -0
  242. modelopt/torch/trace/plugins/__init__.py +34 -0
  243. modelopt/torch/trace/plugins/megatron.py +36 -0
  244. modelopt/torch/trace/plugins/transformers.py +58 -0
  245. modelopt/torch/trace/symbols.py +545 -0
  246. modelopt/torch/trace/tracer.py +331 -0
  247. modelopt/torch/utils/__init__.py +28 -0
  248. modelopt/torch/utils/_pytree.py +133 -0
  249. modelopt/torch/utils/cpp_extension.py +91 -0
  250. modelopt/torch/utils/dataset_utils.py +484 -0
  251. modelopt/torch/utils/distributed.py +311 -0
  252. modelopt/torch/utils/graph.py +123 -0
  253. modelopt/torch/utils/image_processor.py +112 -0
  254. modelopt/torch/utils/import_utils.py +35 -0
  255. modelopt/torch/utils/list.py +54 -0
  256. modelopt/torch/utils/logging.py +127 -0
  257. modelopt/torch/utils/memory_monitor.py +146 -0
  258. modelopt/torch/utils/network.py +658 -0
  259. modelopt/torch/utils/perf.py +174 -0
  260. modelopt/torch/utils/plugins/__init__.py +27 -0
  261. modelopt/torch/utils/plugins/megatron_generate.py +299 -0
  262. modelopt/torch/utils/plugins/megatron_mmlu.py +152 -0
  263. modelopt/torch/utils/plugins/megatron_preprocess_data.py +200 -0
  264. modelopt/torch/utils/random.py +163 -0
  265. modelopt/torch/utils/speech_dataset_utils.py +134 -0
  266. modelopt/torch/utils/tensor.py +87 -0
  267. modelopt/torch/utils/vlm_dataset_utils.py +110 -0
  268. nvidia_modelopt-0.35.0.dist-info/METADATA +171 -0
  269. nvidia_modelopt-0.35.0.dist-info/RECORD +272 -0
  270. nvidia_modelopt-0.35.0.dist-info/WHEEL +5 -0
  271. nvidia_modelopt-0.35.0.dist-info/licenses/LICENSE +14 -0
  272. nvidia_modelopt-0.35.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,537 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """Code that export quantized Hugging Face models for deployment."""
17
+
18
+ import collections.abc
19
+ import json
20
+ import re
21
+ import tempfile
22
+ import warnings
23
+ from collections import defaultdict
24
+ from pathlib import Path
25
+ from typing import Any
26
+
27
+ import torch
28
+ import torch.nn as nn
29
+
30
+ from modelopt.torch.quantization import set_quantizer_by_cfg_context
31
+ from modelopt.torch.quantization.nn import SequentialQuantizer, TensorQuantizer
32
+ from modelopt.torch.quantization.qtensor import NVFP4QTensor
33
+ from modelopt.torch.quantization.utils import quantizer_attr_names
34
+
35
+ from .convert_hf_config import convert_hf_quant_config_format
36
+ from .layer_utils import (
37
+ get_expert_linear_names,
38
+ get_experts_list,
39
+ is_layernorm,
40
+ is_moe,
41
+ is_quantlinear,
42
+ set_expert_quantizer_amax,
43
+ )
44
+ from .model_config import (
45
+ KV_CACHE_FP8,
46
+ KV_CACHE_NVFP4,
47
+ KV_CACHE_NVFP4_AFFINE,
48
+ QUANTIZATION_FP8,
49
+ QUANTIZATION_FP8_PB_REAL,
50
+ QUANTIZATION_NONE,
51
+ QUANTIZATION_NVFP4,
52
+ QUANTIZATION_NVFP4_AWQ,
53
+ QUANTIZATION_W4A8_AWQ,
54
+ )
55
+ from .quant_utils import (
56
+ fuse_prequant_layernorm,
57
+ get_activation_scaling_factor,
58
+ get_quant_config,
59
+ get_quantization_format,
60
+ get_weight_block_size,
61
+ get_weight_scaling_factor,
62
+ get_weight_scaling_factor_2,
63
+ maybe_transpose_expert_weight_dimensions,
64
+ postprocess_state_dict,
65
+ preprocess_linear_fusion,
66
+ to_quantized_weight,
67
+ )
68
+
69
+ __all__ = ["export_hf_checkpoint"]
70
+
71
+ SPECULATIVE_DECODING_MODULE_NAMES = ["medusa_heads", "eagle_module", "drafter"]
72
+
73
+
74
+ def _is_enabled_quantizer(quantizer):
75
+ if hasattr(quantizer, "is_enabled") and quantizer.is_enabled:
76
+ return True
77
+
78
+ if isinstance(quantizer, SequentialQuantizer):
79
+ return any(q.is_enabled for q in quantizer)
80
+
81
+ return False
82
+
83
+
84
+ def requantize_resmooth_fused_llm_layers(model: torch.nn.Module):
85
+ """Group modules that take the same input and register shared parameters in module."""
86
+ # TODO: Handle DBRX MoE
87
+ input_to_linear = defaultdict(list)
88
+ output_to_layernorm = defaultdict(None)
89
+ quantization_format = get_quantization_format(model)
90
+
91
+ def _input_hook(module, input, output):
92
+ """Update dictionary with list of all modules that share the same input."""
93
+ # TODO: Handle DBRX MoE case
94
+ input_to_linear[input[0]].append(module)
95
+
96
+ def _output_hook(module, input, output):
97
+ """Update dictionary with mapping of layernorms and their outputs."""
98
+ output_to_layernorm[output] = module
99
+
100
+ handles = []
101
+ model_type = type(model).__name__.lower()
102
+
103
+ fused_linears = {}
104
+ module_names = set()
105
+
106
+ for name, module in model.named_modules():
107
+ module_names.add(name)
108
+
109
+ # For MoE models update pre_quant_scale to average pre_quant_scale amongst experts
110
+ if is_moe(module) and ("awq" in quantization_format):
111
+ # update_experts_avg_prequant_scale(module)
112
+ grouped_experts = get_experts_list(module, model_type)
113
+ for modules in grouped_experts:
114
+ preprocess_linear_fusion(modules, resmooth_only=True)
115
+
116
+ # Attach hook to layernorm modules that need to be fused
117
+ if is_layernorm(module):
118
+ module.name = name
119
+ handle = module.register_forward_hook(_output_hook)
120
+ handles.append(handle)
121
+ elif is_quantlinear(module) and (
122
+ _is_enabled_quantizer(module.input_quantizer)
123
+ or _is_enabled_quantizer(module.weight_quantizer)
124
+ ):
125
+ module.name = name
126
+ handle = module.register_forward_hook(_input_hook)
127
+ handles.append(handle)
128
+
129
+ with torch.no_grad():
130
+ fake_input = torch.ones([1, 2], dtype=torch.long).to(model.device)
131
+ decoder_fake_input = fake_input
132
+ if model_type.startswith("whisper"):
133
+ # For Whisper models, we need to pass a fake input with the specific sequence length
134
+ from transformers import AutoFeatureExtractor
135
+
136
+ feature_extractor = AutoFeatureExtractor.from_pretrained(model.name_or_path)
137
+ fake_input = torch.ones(
138
+ [1, model.config.num_mel_bins, feature_extractor.nb_max_frames], dtype=model.dtype
139
+ ).to(model.device)
140
+
141
+ # Run forward pass so that all modules sharing the same input are collected using forward hook.
142
+
143
+ with set_quantizer_by_cfg_context(model, {"*": {"enable": False}}):
144
+ if getattr(model.config, "is_encoder_decoder", False):
145
+ # For encoder-decoder models, we need to pass both the encoder and decoder input ids
146
+ model(fake_input, decoder_input_ids=decoder_fake_input)
147
+ else:
148
+ model(fake_input)
149
+
150
+ for handle in handles:
151
+ handle.remove()
152
+
153
+ for tensor, modules in input_to_linear.items():
154
+ quantization_format = get_quantization_format(modules[0])
155
+ if len(modules) > 1 and quantization_format not in [
156
+ QUANTIZATION_FP8,
157
+ QUANTIZATION_NONE,
158
+ QUANTIZATION_FP8_PB_REAL,
159
+ ]:
160
+ # Fuse modules that have the same input
161
+ preprocess_linear_fusion(modules)
162
+ fused_linears[modules[0].name] = [module.name for module in modules]
163
+
164
+ # Fuse layernorms
165
+ if (
166
+ quantization_format is not QUANTIZATION_NONE
167
+ and "awq" in quantization_format
168
+ and tensor in output_to_layernorm
169
+ ):
170
+ # Pre quant scale of modules is already updated to avg_pre_quant_scale
171
+ fuse_prequant_layernorm(output_to_layernorm[tensor], modules)
172
+
173
+ # The dummy forward may not be able to activate all the experts.
174
+ # Process experts by naming rules like experts.0, experts.1, etc.
175
+ for name, modules_fused in fused_linears.items():
176
+ if re.search(r"experts?\.\d+", name):
177
+ expert_id = 0
178
+ while True:
179
+ new_expert_name = re.sub(r"(experts?\.)\d+", rf"\g<1>{expert_id}", name, count=1)
180
+ if new_expert_name in fused_linears:
181
+ expert_id += 1
182
+ continue
183
+ if new_expert_name not in module_names:
184
+ break
185
+
186
+ new_expert_modules = []
187
+ for name_fused in modules_fused:
188
+ new_expert_name = re.sub(r"(experts?\.)\d+", rf"\g<1>{expert_id}", name_fused)
189
+ assert new_expert_name in module_names
190
+ new_expert_modules.append(model.get_submodule(new_expert_name))
191
+
192
+ preprocess_linear_fusion(new_expert_modules)
193
+
194
+ expert_id += 1
195
+
196
+
197
+ def _export_quantized_weight(
198
+ sub_module: nn.Module, dtype: torch.dtype, weight_name: str = "weight"
199
+ ):
200
+ """For the given weight attr of the sub_module, export the quantization info of it.
201
+
202
+ The export includes converting weight tensor to correct quantized values and quantized dtype,
203
+ and registering scaling factors.
204
+ """
205
+ quantization_format = get_quantization_format(sub_module)
206
+ if quantization_format == QUANTIZATION_NONE:
207
+ return
208
+
209
+ block_size = get_weight_block_size(sub_module, weight_name)
210
+ quantizer_attrs = quantizer_attr_names(weight_name)
211
+ weight: nn.Parameter = getattr(sub_module, weight_name)
212
+ weight_quantizer: TensorQuantizer | SequentialQuantizer = getattr(
213
+ sub_module, quantizer_attrs.weight_quantizer
214
+ )
215
+ input_quantizer: TensorQuantizer | SequentialQuantizer | None = getattr(
216
+ sub_module, quantizer_attrs.input_quantizer, None
217
+ )
218
+ output_quantizer: TensorQuantizer | SequentialQuantizer | None = getattr(
219
+ sub_module, quantizer_attrs.output_quantizer, None
220
+ )
221
+
222
+ if quantization_format == QUANTIZATION_FP8:
223
+ # Convert amax to float32
224
+ weight_quantizer._amax = weight_quantizer._amax.to(torch.float32)
225
+
226
+ if weight_quantizer._amax.dim() == 1:
227
+ # Per-tensor amax
228
+ weight_scaling_factor = torch.tensor(
229
+ weight_quantizer.amax.item() / weight_quantizer.maxbound
230
+ )
231
+ else:
232
+ # Per-channel amax
233
+ weight_scaling_factor = torch.tensor(weight_quantizer.amax / weight_quantizer.maxbound)
234
+
235
+ sub_module.register_buffer(
236
+ quantizer_attrs.weight_scale,
237
+ weight_scaling_factor,
238
+ )
239
+
240
+ if hasattr(input_quantizer, "_amax"):
241
+ assert input_quantizer is not None
242
+ input_quantizer._amax = input_quantizer._amax.to(torch.float32)
243
+
244
+ sub_module.register_buffer(
245
+ quantizer_attrs.input_scale,
246
+ get_activation_scaling_factor(
247
+ sub_module, input_quantizer_name=quantizer_attrs.input_quantizer
248
+ ).squeeze(),
249
+ )
250
+
251
+ if hasattr(output_quantizer, "_amax"):
252
+ assert output_quantizer is not None
253
+ output_quantizer._amax = output_quantizer._amax.to(torch.float32)
254
+ else:
255
+ # Register weight_scale and input_scale
256
+ if quantization_format == QUANTIZATION_FP8_PB_REAL:
257
+ sub_module.register_buffer(
258
+ quantizer_attrs.weight_scale,
259
+ weight_quantizer._scale.to(torch.float32),
260
+ )
261
+ del weight_quantizer._scale
262
+ else:
263
+ sub_module.register_buffer(
264
+ quantizer_attrs.weight_scale, get_weight_scaling_factor(sub_module, weight_name)
265
+ )
266
+
267
+ if (
268
+ input_quantizer is not None
269
+ and "disabled" not in repr(input_quantizer)
270
+ and input_quantizer.amax is not None
271
+ ):
272
+ sub_module.register_buffer(
273
+ quantizer_attrs.input_scale,
274
+ get_activation_scaling_factor(
275
+ sub_module, input_quantizer_name=quantizer_attrs.input_quantizer
276
+ ).squeeze(),
277
+ )
278
+
279
+ if quantization_format in [
280
+ QUANTIZATION_NVFP4_AWQ,
281
+ QUANTIZATION_NVFP4,
282
+ QUANTIZATION_W4A8_AWQ,
283
+ ]:
284
+ # Register weight_scale_2
285
+ sub_module.register_buffer(
286
+ quantizer_attrs.weight_scale_2,
287
+ get_weight_scaling_factor_2(sub_module, weight_name).squeeze(),
288
+ )
289
+
290
+ weight_scale: torch.Tensor | None = getattr(sub_module, quantizer_attrs.weight_scale, None)
291
+ weight_scale_2: torch.Tensor | None = getattr(sub_module, quantizer_attrs.weight_scale_2, None)
292
+
293
+ # Transpose weight for bmm-style expert quantization (llama4, gpt-oss)
294
+ if quantization_format in [QUANTIZATION_NVFP4, QUANTIZATION_NVFP4_AWQ]:
295
+ # Transpose weight from (num_experts, input_dim, output_dim) to (num_experts, output_dim, input_dim)
296
+ # for NVFP4 quantization functions that expect input_dim as the last dimension for block quantization
297
+ is_bmm_expert_weight = weight.dim() == 3 and any(
298
+ expert_type in type(sub_module).__name__
299
+ for expert_type in ["Llama4TextExperts", "GptOssExperts"]
300
+ )
301
+ weight, _ = maybe_transpose_expert_weight_dimensions(
302
+ weight, is_bmm_expert_weight=is_bmm_expert_weight
303
+ )
304
+ weight_scale = NVFP4QTensor.get_weights_scaling_factor(
305
+ weight,
306
+ block_size=block_size,
307
+ weights_scaling_factor_2=weight_scale_2,
308
+ )[0]
309
+
310
+ quantized_weight = to_quantized_weight(
311
+ weight.to(dtype),
312
+ weight_scale,
313
+ quantization_format,
314
+ weight_scale_2,
315
+ block_size,
316
+ )
317
+
318
+ quantized_weight, weight_scale = maybe_transpose_expert_weight_dimensions(
319
+ quantized_weight, weight_scale, is_bmm_expert_weight=is_bmm_expert_weight
320
+ )
321
+ else:
322
+ quantized_weight = to_quantized_weight(
323
+ weight.to(dtype),
324
+ weight_scale,
325
+ quantization_format,
326
+ weight_scale_2,
327
+ block_size,
328
+ )
329
+
330
+ setattr(sub_module, weight_name, nn.Parameter(quantized_weight, requires_grad=False))
331
+
332
+
333
+ def _export_hf_checkpoint(
334
+ model: nn.Module, dtype: torch.dtype | None = None
335
+ ) -> tuple[dict[str, Any], dict[str, Any]]:
336
+ """Exports the torch model to the packed checkpoint with original HF naming.
337
+
338
+ The packed checkpoint will be consumed by the TensorRT-LLM unified converter.
339
+
340
+ Args:
341
+ model: the torch model.
342
+ dtype: the weights data type to export the unquantized layers or the default model data type if None.
343
+
344
+ Returns:
345
+ post_state_dict: Dict containing quantized weights
346
+ quant_config: config information to export hf_quant_cfg.json
347
+ """
348
+ if dtype is None:
349
+ dtype = model.config.torch_dtype
350
+ elif dtype != model.config.torch_dtype:
351
+ warnings.warn(
352
+ f"Model's original dtype ({model.config.torch_dtype}) differs from target dtype "
353
+ f"({dtype}), which may lead to numerical errors."
354
+ )
355
+
356
+ # Create a model layer pool
357
+ # If `model.model` exists use that, otherwise use `model` itself, e.g., Nemotron-H
358
+ root = getattr(model, "model", model)
359
+ # If that has a `.layers`, use it, otherwise fall back to the object itself
360
+ root = getattr(root, "layers", root)
361
+ layer_pool = {f"model.layers.{name}": sub_module for name, sub_module in root.named_modules()}
362
+
363
+ # Handle input quantizers of experts that are not calibrated
364
+ for name, sub_module in model.named_modules():
365
+ if is_moe(sub_module) and hasattr(sub_module, "experts"):
366
+ expert_linear_names = get_expert_linear_names(sub_module)
367
+ for linear_name in expert_linear_names:
368
+ # Handle DBRX experts specifically
369
+ if "QuantDbrxExperts" in type(sub_module.experts).__name__:
370
+ # For DBRX, experts are in sub_module.experts.mlp and linear layers are ModuleLists
371
+ experts_mlp = sub_module.experts.mlp
372
+ if hasattr(experts_mlp, linear_name):
373
+ linear_modulelist = getattr(experts_mlp, linear_name)
374
+ if hasattr(linear_modulelist, "__iter__"):
375
+ set_expert_quantizer_amax(
376
+ modules=list(linear_modulelist),
377
+ quantizer_attrs=["input_quantizer"],
378
+ )
379
+ elif "QuantGptOssExperts" in type(sub_module.experts).__name__:
380
+ # Handle GPT-OSS experts specifically
381
+ # GPT-OSS experts use gate_up_proj and down_proj
382
+ gpt_oss_linear_names = ["gate_up_proj", "down_proj"]
383
+ for linear_name in gpt_oss_linear_names:
384
+ if hasattr(sub_module.experts, linear_name):
385
+ linear_module = getattr(sub_module.experts, linear_name)
386
+ if hasattr(linear_module, "input_quantizer"):
387
+ set_expert_quantizer_amax(
388
+ modules=[linear_module],
389
+ quantizer_attrs=["input_quantizer"],
390
+ )
391
+ elif isinstance(sub_module.experts, collections.abc.Iterable):
392
+ # For other MoE models (like Mixtral) with iterable experts
393
+ try:
394
+ set_expert_quantizer_amax(
395
+ modules=[getattr(expert, linear_name) for expert in sub_module.experts],
396
+ quantizer_attrs=["input_quantizer"],
397
+ )
398
+ except AttributeError as e:
399
+ # Provide more helpful debugging information
400
+ expert_types = [type(expert).__name__ for expert in sub_module.experts]
401
+ raise AttributeError(
402
+ f"Failed to access attribute '{linear_name}' on experts. "
403
+ f"MoE module type: {type(sub_module).__name__}, "
404
+ f"Expert types: {expert_types}, "
405
+ f"Expected linear names: {expert_linear_names}. "
406
+ f"This suggests the get_expert_linear_names function may need "
407
+ f"to be updated for this model architecture. "
408
+ f"Original error: {e}"
409
+ ) from e
410
+ else:
411
+ # Unsupported MoE model structure
412
+ raise NotImplementedError(
413
+ f"MoE model with experts type '{type(sub_module.experts).__name__}' is not supported in export."
414
+ f"Please file an issue or add support for this model architecture."
415
+ )
416
+
417
+ # NOTE: Speculative decoding models have extra modules that may be quantized
418
+ # Need to add these modules to the layer_pool
419
+ for key in SPECULATIVE_DECODING_MODULE_NAMES:
420
+ if hasattr(model, key):
421
+ for name, sub_module in getattr(model, key).named_modules():
422
+ layer_pool.update({f"{key}.{name}": sub_module})
423
+
424
+ # Resmooth and requantize fused layers
425
+ # TODO: Handle mixed precision
426
+ requantize_resmooth_fused_llm_layers(model)
427
+
428
+ # Remove all hooks from the model
429
+ try:
430
+ from accelerate.hooks import remove_hook_from_module
431
+
432
+ remove_hook_from_module(model, recurse=True)
433
+ except ImportError:
434
+ warnings.warn("accelerate is not installed, hooks will not be removed")
435
+
436
+ quant_config = get_quant_config(layer_pool)
437
+
438
+ kv_cache_max_bound = 0
439
+ kv_cache_format = quant_config["quantization"]["kv_cache_quant_algo"]
440
+
441
+ cache_bound_mapping = {
442
+ KV_CACHE_NVFP4: 6 * 448,
443
+ KV_CACHE_NVFP4_AFFINE: 6 * 448,
444
+ KV_CACHE_FP8: 448,
445
+ }
446
+
447
+ # Only update kv_cache_max_bound if a quantization is applied.
448
+ if kv_cache_format != QUANTIZATION_NONE:
449
+ kv_cache_max_bound = cache_bound_mapping.get(kv_cache_format)
450
+
451
+ # Track if any layers are quantized to properly set exclude_modules
452
+ has_quantized_layers = False
453
+
454
+ for name, sub_module in layer_pool.items():
455
+ if get_quantization_format(sub_module) != QUANTIZATION_NONE:
456
+ has_quantized_layers = True
457
+ if is_quantlinear(sub_module):
458
+ _export_quantized_weight(sub_module, dtype)
459
+ elif (
460
+ "Llama4TextExperts" in type(sub_module).__name__
461
+ or "GptOssExperts" in type(sub_module).__name__
462
+ ):
463
+ # TODO: consolidate uncalibrated experts handling logic
464
+ # Handle weight quantizers amax values using smart fallback logic
465
+ set_expert_quantizer_amax(
466
+ modules=sub_module,
467
+ quantizer_attrs=["gate_up_proj_weight_quantizer", "down_proj_weight_quantizer"],
468
+ )
469
+ # Handle input quantizers amax values using smart fallback logic
470
+ set_expert_quantizer_amax(
471
+ modules=sub_module,
472
+ quantizer_attrs=["gate_up_proj_input_quantizer", "down_proj_input_quantizer"],
473
+ )
474
+ # Export the quantized weights
475
+ for weight_name in ["gate_up_proj", "down_proj"]:
476
+ _export_quantized_weight(sub_module, dtype, weight_name)
477
+
478
+ quantized_state_dict = model.state_dict()
479
+
480
+ quantized_state_dict = postprocess_state_dict(
481
+ quantized_state_dict, kv_cache_max_bound, kv_cache_format
482
+ )
483
+
484
+ # Check if any layers are quantized
485
+ if has_quantized_layers:
486
+ quant_config["quantization"].setdefault("exclude_modules", []).append("lm_head")
487
+
488
+ return quantized_state_dict, quant_config
489
+
490
+
491
+ def export_hf_checkpoint(
492
+ model: nn.Module,
493
+ dtype: torch.dtype | None = None,
494
+ export_dir: Path | str = tempfile.gettempdir(),
495
+ save_modelopt_state: bool = False,
496
+ ):
497
+ """Exports the torch model to unified checkpoint and saves to export_dir.
498
+
499
+ Args:
500
+ model: the torch model.
501
+ dtype: the weights data type to export the unquantized layers or the default model data type if None.
502
+ export_dir: the target export path.
503
+ save_modelopt_state: whether to save the modelopt state_dict.
504
+ """
505
+ export_dir = Path(export_dir)
506
+ export_dir.mkdir(parents=True, exist_ok=True)
507
+ try:
508
+ post_state_dict, hf_quant_config = _export_hf_checkpoint(model, dtype)
509
+
510
+ # Save hf_quant_config.json for backward compatibility
511
+ with open(f"{export_dir}/hf_quant_config.json", "w") as file:
512
+ json.dump(hf_quant_config, file, indent=4)
513
+
514
+ hf_quant_config = convert_hf_quant_config_format(hf_quant_config)
515
+
516
+ # Save model
517
+ model.save_pretrained(
518
+ export_dir, state_dict=post_state_dict, save_modelopt_state=save_modelopt_state
519
+ )
520
+
521
+ original_config = f"{export_dir}/config.json"
522
+ config_data = {}
523
+
524
+ with open(original_config) as file:
525
+ config_data = json.load(file)
526
+
527
+ config_data["quantization_config"] = hf_quant_config
528
+
529
+ with open(original_config, "w") as file:
530
+ json.dump(config_data, file, indent=4)
531
+
532
+ except Exception as e:
533
+ warnings.warn(
534
+ "Cannot export model to the model_config. The modelopt-optimized model state_dict"
535
+ " can be saved with torch.save for further inspection."
536
+ )
537
+ raise e