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,254 @@
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
+ """Dynamic Conv implementations based on torch.nn.modules.conv."""
17
+
18
+ import itertools
19
+
20
+ import torch
21
+ from torch import nn
22
+
23
+ from modelopt.torch.opt.dynamic import DynamicModule
24
+ from modelopt.torch.utils import make_divisible, val2tuple
25
+
26
+ from ..registry import DMRegistry
27
+ from ..traced_hp import TracedHp
28
+ from .utils import get_sliced_tensor, get_sliced_tensor_by_slices
29
+
30
+ __all__ = ["_DynamicConvNd", "_DynamicConvTransposeNd"]
31
+
32
+
33
+ @DMRegistry.register({nn.Conv1d: "nn.Conv1d", nn.Conv2d: "nn.Conv2d", nn.Conv3d: "nn.Conv3d"})
34
+ class _DynamicConvNd(DynamicModule):
35
+ @property
36
+ def ndim(self):
37
+ return len(self.kernel_size)
38
+
39
+ @staticmethod
40
+ def _assert_input_format(
41
+ mod: "_DynamicConvNd", input: tuple[torch.Tensor] | torch.Tensor
42
+ ) -> None:
43
+ if isinstance(input, tuple):
44
+ assert len(input) == 1, f"Expected single input, but got {input}."
45
+ input = input[0]
46
+ if input.dim() != mod.ndim + 2:
47
+ raise ValueError(f"Expected batched input (e.g. NCHW vs CHW), but got {input.shape}.")
48
+
49
+ @staticmethod
50
+ def _get_padding(
51
+ mod: "_DynamicConvNd", padding: str | tuple[int, ...]
52
+ ) -> str | tuple[int, ...]:
53
+ """New padding such that output size stays the same as with max kernel size and padding."""
54
+ if isinstance(padding, str):
55
+ return padding
56
+
57
+ active_padding = []
58
+ max_kernel_size = mod.get_hparam("kernel_size").max
59
+ for dim in range(mod.ndim):
60
+ max_ks = max_kernel_size[dim] # type: ignore[index]
61
+ active_ks = mod.kernel_size[dim]
62
+ assert max_ks % 2 == active_ks % 2, (max_ks, active_ks)
63
+
64
+ max_p = padding[dim]
65
+ active_p = max_p - (max_ks - active_ks) * mod.dilation[dim] // 2
66
+ assert active_p >= 0, (max_p, active_p)
67
+
68
+ active_padding.append(active_p)
69
+ return tuple(active_padding)
70
+
71
+ @staticmethod
72
+ def _get_bias(mod: "_DynamicConvNd", bias: torch.Tensor | None) -> torch.Tensor | None:
73
+ return get_sliced_tensor(mod, bias, "out_channels")
74
+
75
+ @staticmethod
76
+ def _get_channel_order() -> list[str]:
77
+ return ["out_channels", "in_channels"]
78
+
79
+ @staticmethod
80
+ def _get_weight(mod: "_DynamicConvNd", weight: torch.Tensor) -> torch.Tensor:
81
+ slices = []
82
+
83
+ # get channel hparams in correct order
84
+ hps_c = [mod.get_hparam(c_name) for c_name in mod._get_channel_order()]
85
+ c_active = [hp.active for hp in hps_c]
86
+
87
+ # add slice for 1st channel dimension
88
+ slices.append(hps_c[0].active_slice)
89
+
90
+ # slice for 2nd channel dimension depends on groups
91
+ slice_1 = hps_c[1].active_slice
92
+ groups = mod.groups
93
+ if groups == 1:
94
+ slices.append(slice_1)
95
+ elif all(c % groups == 0 for c in c_active):
96
+ assert slice_1 == slice(c_active[1]) or c_active[0] == c_active[1] == groups
97
+ slices.append(slice(c_active[1] // groups))
98
+ else:
99
+ raise NotImplementedError(f"Group size {groups} not supported!")
100
+
101
+ # kernel size slices
102
+ max_kernel_size = mod.get_hparam("kernel_size").max
103
+ active_kernel_size = mod.kernel_size
104
+ assert isinstance(max_kernel_size, tuple)
105
+ for max_ks, active_ks in zip(max_kernel_size, active_kernel_size):
106
+ a = max_ks // 2 - active_ks // 2
107
+ b = max_ks // 2 + active_ks // 2 + max_ks % 2
108
+ slices.append(slice(a, b))
109
+
110
+ return get_sliced_tensor_by_slices(weight, slices)
111
+
112
+ @staticmethod
113
+ def _get_groups(mod: "_DynamicConvNd", groups: int) -> int:
114
+ # retrieve channel hparams
115
+ hp_oc = mod.get_hparam("out_channels")
116
+ hp_ic = mod.get_hparam("in_channels")
117
+
118
+ # vanilla cases
119
+ if groups == 1 or (hp_oc.active == hp_oc.original and hp_ic.active == hp_ic.original):
120
+ return groups
121
+
122
+ # only support out_channels == in_channels if grouped
123
+ assert hp_oc.active == hp_ic.active, (hp_oc.active, hp_ic.active)
124
+
125
+ # compute current number of groups
126
+ assert hp_oc.original % groups == 0, (hp_oc.original, groups)
127
+ active_groups = (hp_oc.active * groups) // hp_oc.original
128
+
129
+ # sanity checks
130
+ assert isinstance(active_groups, int)
131
+ assert hp_oc.active % active_groups == 0, (hp_oc.active, active_groups)
132
+ assert hp_ic.active % active_groups == 0, (hp_ic.active, active_groups)
133
+
134
+ return active_groups
135
+
136
+ def _estimate_importance(self) -> TracedHp.Importance:
137
+ # for group > 1, we do not know how to handle it yet
138
+ if self.groups > 1:
139
+ return None
140
+ weight = self._parameters["weight"] # retrieve full weight tensor
141
+ c_in = weight.shape[1]
142
+ return torch.linalg.vector_norm(
143
+ torch.reshape(weight.detach().transpose(0, 1), (c_in, -1)), dim=1
144
+ )
145
+
146
+ def _setup(self):
147
+ # only support ungrouped conv or grouped conv with in_channels == out_channels
148
+ if self.groups == 1:
149
+ oc_step = ic_step = 1
150
+ elif self.out_channels == self.in_channels:
151
+ oc_step = ic_step = self.out_channels // self.groups
152
+ else:
153
+ oc_step = self.out_channels
154
+ ic_step = self.in_channels
155
+ oc_choices = [c for c in range(1, self.out_channels + 1) if c % oc_step == 0]
156
+ ic_choices = [c for c in range(1, self.in_channels + 1) if c % ic_step == 0]
157
+
158
+ # construct choices for kernel size hyperparameter
159
+ ks_set = {self.kernel_size}
160
+ # TODO: padding mode other than "zeros" is not supported since in other padding mode, conv
161
+ # will use "_reversed_padding_repeated_twice" instead of "padding" in the forward function.
162
+ if self.padding_mode == "zeros":
163
+ for ks in itertools.product(*[range(1, k + 1) for k in self.kernel_size]):
164
+ for d in range(self.ndim):
165
+ # check kernel size: smaller than original, same parity
166
+ if ks[d] > self.kernel_size[d] or ks[d] % 2 != self.kernel_size[d] % 2:
167
+ break
168
+
169
+ # check padding: non-negative
170
+ if (
171
+ isinstance(self.padding, tuple)
172
+ and self.padding[d] < (self.kernel_size[d] - ks[d]) * self.dilation[d] // 2
173
+ ):
174
+ break
175
+ else:
176
+ ks_set.add(ks)
177
+ ks_choices = list(ks_set)
178
+
179
+ # register hyperparameters
180
+ self._register_hparam("out_channels", TracedHp(oc_choices, self.out_channels))
181
+ self._register_hparam("in_channels", TracedHp(ic_choices, self.in_channels))
182
+ self._register_hparam("kernel_size", TracedHp(ks_choices, self.kernel_size))
183
+
184
+ # We restrict the input to be batched (e.g. NCHW) format so searchable_tensor_dims can be 1.
185
+ # Ideally searchable_tensor_dims for Conv2d would be -3, but its more common to use
186
+ # torch.cat([...], dim=1), and enforcing batched input would allow us to support these.
187
+ hook_handle = self.register_forward_pre_hook(self._assert_input_format)
188
+ self._register_temp_attribute(
189
+ "_hook_handle", hook_handle, del_hook=lambda m, n: m._hook_handle.remove()
190
+ )
191
+
192
+ # register dynamic attributes
193
+ self._register_dynamic_attribute("padding", self._get_padding)
194
+ self._register_dynamic_attribute("weight", self._get_weight)
195
+ self._register_dynamic_attribute("bias", self._get_bias)
196
+ self._register_dynamic_attribute("groups", self._get_groups)
197
+
198
+ # register importance for in_channels
199
+ self.get_hparam("in_channels").register_importance(self._estimate_importance)
200
+
201
+ def modify(
202
+ self,
203
+ *,
204
+ channels_ratio: tuple[float, ...] | None = None,
205
+ channel_divisor: int = 1,
206
+ kernel_size: tuple[int | tuple[int, ...], ...] = (),
207
+ ):
208
+ """Modify the dynamic choices of the module according to provided keyword arguments.
209
+
210
+ Args:
211
+ channels_ratio: The ratios of the desired number of out/in channels over original
212
+ number of out/in channels.
213
+ channel_divisor: The divisor of the out/in channels.
214
+ kernel_sizes: The desired kernel sizes.
215
+ """
216
+ # modify both in_channels and out_channels
217
+ channels = ["in_channels", "out_channels"]
218
+ choices: set[float]
219
+ for channel in channels:
220
+ hp = self.get_hparam(channel)
221
+ if channels_ratio is not None:
222
+ assert isinstance(hp.original, int)
223
+ choices = {r * hp.original for r in channels_ratio}
224
+ else:
225
+ choices = set(hp.choices) # type: ignore[arg-type]
226
+ choices_rounded: set[int] = {int(make_divisible(c, channel_divisor)) for c in choices}
227
+ hp.choices = list(set(hp.choices) & choices_rounded | {hp.original})
228
+
229
+ # modify kernel size choices
230
+ hp_ks = self.get_hparam("kernel_size")
231
+ kernel_size = hp_ks.choices if kernel_size is None else kernel_size
232
+ ks_choices = {val2tuple(ks, self.ndim) for ks in kernel_size}
233
+ hp_ks.choices = list(set(hp_ks.choices) & ks_choices | {hp_ks.original})
234
+
235
+
236
+ @DMRegistry.register(
237
+ {
238
+ nn.ConvTranspose1d: "nn.ConvTranspose1d",
239
+ nn.ConvTranspose2d: "nn.ConvTranspose2d",
240
+ nn.ConvTranspose3d: "nn.ConvTranspose3d",
241
+ }
242
+ )
243
+ class _DynamicConvTransposeNd(_DynamicConvNd):
244
+ @staticmethod
245
+ def _get_channel_order() -> list[str]:
246
+ return ["in_channels", "out_channels"]
247
+
248
+ def _estimate_importance(self) -> TracedHp.Importance:
249
+ # for group > 1, we do not know how to handle it yet
250
+ if self.groups > 1:
251
+ return None
252
+ weight = self._parameters["weight"] # retrieve full weight tensor
253
+ c_in = weight.shape[0]
254
+ return torch.linalg.vector_norm(torch.reshape(weight.detach(), (c_in, -1)), dim=1)
@@ -0,0 +1,76 @@
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
+ """Dynamic linear implementations based on torch.nn.modules.linear."""
17
+
18
+ import torch
19
+ from torch import nn
20
+
21
+ from modelopt.torch.opt.dynamic import DynamicModule
22
+ from modelopt.torch.utils import make_divisible
23
+
24
+ from ..registry import DMRegistry
25
+ from ..traced_hp import TracedHp
26
+ from .utils import get_sliced_tensor
27
+
28
+ __all__ = ["_DynamicLinear"]
29
+
30
+
31
+ @DMRegistry.register({nn.Linear: "nn.Linear"})
32
+ class _DynamicLinear(DynamicModule):
33
+ """An ``nn.Linear`` layer with dynamic hyperparams."""
34
+
35
+ @staticmethod
36
+ def _get_weight(mod: "_DynamicLinear", weight: torch.Tensor) -> torch.Tensor:
37
+ return get_sliced_tensor(mod, weight, "out_features", "in_features")
38
+
39
+ @staticmethod
40
+ def _get_bias(mod: "_DynamicLinear", bias: torch.Tensor | None) -> torch.Tensor | None:
41
+ return get_sliced_tensor(mod, bias, "out_features")
42
+
43
+ def _estimate_importance(self) -> TracedHp.Importance:
44
+ return torch.linalg.vector_norm(self._parameters["weight"].detach(), dim=0)
45
+
46
+ def _setup(self):
47
+ # register hyperparameters
48
+ self._register_hparam("in_features", TracedHp(list(range(1, self.in_features + 1))))
49
+ self._register_hparam("out_features", TracedHp(list(range(1, self.out_features + 1))))
50
+
51
+ # register dynamic attributes of the class
52
+ self._register_dynamic_attribute("weight", self._get_weight)
53
+ self._register_dynamic_attribute("bias", self._get_bias)
54
+
55
+ # register importance for in_features
56
+ self.get_hparam("in_features").register_importance(self._estimate_importance)
57
+
58
+ def modify(self, *, features_ratio: tuple[float, ...] | None = None, feature_divisor: int = 1):
59
+ """Modify the dynamic choices of the module according to provided keyword arguments.
60
+
61
+ Args:
62
+ features_ratio: The ratios of the desired number of output/input features over original
63
+ number of output/input features.
64
+ feature_divisor: The divisor of the output/input features.
65
+ """
66
+ # modify both in_features and out_features
67
+ features = ["in_features", "out_features"]
68
+ for feature in features:
69
+ hp = self.get_hparam(feature)
70
+ choices = (
71
+ {r * hp.original for r in features_ratio}
72
+ if features_ratio is not None
73
+ else set(hp.choices)
74
+ )
75
+ choices = {int(make_divisible(c, feature_divisor)) for c in choices}
76
+ hp.choices = list(set(hp.choices) & choices | {hp.original})
@@ -0,0 +1,193 @@
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
+ """Dynamic norm implementations based on norm modules in torch.nn.modules."""
17
+
18
+ from collections.abc import Sequence
19
+
20
+ import torch
21
+ from torch import nn
22
+
23
+ from modelopt.torch.opt.dynamic import DynamicModule
24
+ from modelopt.torch.utils import make_divisible
25
+
26
+ from ..registry import DMRegistry
27
+ from ..traced_hp import TracedHp
28
+ from .utils import get_sliced_tensor
29
+
30
+ __all__ = ["_DynamicBatchNorm", "_DynamicGroupNorm", "_DynamicInstanceNorm", "_DynamicLayerNorm"]
31
+
32
+
33
+ class _DynamicBatchInstance(DynamicModule):
34
+ """Dynamic base class for batch norm and instance norm layers.
35
+
36
+ NOTE: Don't use this class for instance checks. Use _DynamicBatchNorm or _DynamicInstanceNorm
37
+ instead!
38
+ """
39
+
40
+ @staticmethod
41
+ def _cut_to_active_features(mod: "_DynamicBatchInstance", value: torch.Tensor | None):
42
+ return get_sliced_tensor(mod, value, "num_features")
43
+
44
+ def _setup(self):
45
+ # register hyperparameters
46
+ self._register_hparam("num_features", TracedHp(list(range(1, self.num_features + 1))))
47
+
48
+ # register dynamic attributes
49
+ dyn_attrs = ["running_mean", "running_var", "weight", "bias"]
50
+ for attr in dyn_attrs:
51
+ self._register_dynamic_attribute(attr, self._cut_to_active_features)
52
+
53
+ def modify(self, *, features_ratio: tuple[float, ...] | None = None, feature_divisor: int = 1):
54
+ """Modify the dynamic choices of the module according to provided keyword arguments.
55
+
56
+ Args:
57
+ features_ratio: The ratios of the desired number of features over original number of
58
+ features.
59
+ feature_divisor: The divisor of the number of features.
60
+ """
61
+ hp = self.get_hparam("num_features")
62
+ choices = (
63
+ {r * hp.original for r in features_ratio}
64
+ if features_ratio is not None
65
+ else set(hp.choices)
66
+ )
67
+ choices = {int(make_divisible(c, feature_divisor)) for c in choices}
68
+ hp.choices = list(set(hp.choices) & choices | {hp.original})
69
+
70
+
71
+ @DMRegistry.register(
72
+ {
73
+ nn.BatchNorm1d: "nn.BatchNorm1d",
74
+ nn.BatchNorm2d: "nn.BatchNorm2d",
75
+ nn.BatchNorm3d: "nn.BatchNorm3d",
76
+ nn.SyncBatchNorm: "nn.SyncBatchNorm",
77
+ }
78
+ )
79
+ class _DynamicBatchNorm(_DynamicBatchInstance):
80
+ """Just syntactic sugar so we have a common base class for batch norm only."""
81
+
82
+
83
+ @DMRegistry.register(
84
+ {
85
+ nn.InstanceNorm1d: "nn.InstanceNorm1d",
86
+ nn.InstanceNorm2d: "nn.InstanceNorm2d",
87
+ nn.InstanceNorm3d: "nn.InstanceNorm3d",
88
+ }
89
+ )
90
+ class _DynamicInstanceNorm(_DynamicBatchInstance):
91
+ """Just syntactic sugar so we have a common base class for instance norm only."""
92
+
93
+
94
+ @DMRegistry.register({nn.LayerNorm: "nn.LayerNorm"})
95
+ class _DynamicLayerNorm(DynamicModule):
96
+ """An ``nn.LayerNorm`` layer with dynamic hyperparams."""
97
+
98
+ @staticmethod
99
+ def _get_normalized_shape(mod: "_DynamicLayerNorm", value: Sequence[int | TracedHp]) -> tuple:
100
+ return (*tuple(value[:-1]), mod.num_features)
101
+
102
+ @staticmethod
103
+ def _cut_to_active_features(
104
+ mod: "_DynamicLayerNorm", value: torch.Tensor | None
105
+ ) -> torch.Tensor | None:
106
+ if value is None:
107
+ return value
108
+ nf_slice = mod.get_hparam("num_features").active_slice
109
+ return value[..., nf_slice].contiguous()
110
+
111
+ def _setup(self):
112
+ # construct normalized shape with Hparam as last dimension
113
+ normalized_shape = list(self.normalized_shape)
114
+ normalized_shape[-1] = TracedHp(list(range(1, normalized_shape[-1] + 1)))
115
+
116
+ # register the hyperparameter with a new name
117
+ self._register_hparam("num_features", normalized_shape[-1])
118
+
119
+ # register dynamic attributes
120
+ dyn_attrs = ["weight", "bias"]
121
+ for attr in dyn_attrs:
122
+ self._register_dynamic_attribute(attr, self._cut_to_active_features)
123
+
124
+ self._register_dynamic_attribute("normalized_shape", self._get_normalized_shape)
125
+
126
+ def modify(self, *, features_ratio: tuple[float, ...] | None = None, feature_divisor: int = 1):
127
+ """Modify the dynamic choices of the module according to provided keyword arguments.
128
+
129
+ Args:
130
+ features_ratio: The ratios of the desired number of features over original number of
131
+ features.
132
+ feature_divisor: The divisor of the number of features.
133
+ """
134
+ hp = self.get_hparam("num_features")
135
+ choices = (
136
+ {r * hp.original for r in features_ratio}
137
+ if features_ratio is not None
138
+ else set(hp.choices)
139
+ )
140
+ choices = {int(make_divisible(c, feature_divisor)) for c in choices}
141
+ hp.choices = list(set(hp.choices) & choices | {hp.original})
142
+
143
+
144
+ @DMRegistry.register({nn.GroupNorm: "nn.GroupNorm"})
145
+ class _DynamicGroupNorm(DynamicModule):
146
+ """An ``nn.GroupNorm`` layer with dynamic hyperparams."""
147
+
148
+ _group_size: int
149
+
150
+ @staticmethod
151
+ def _get_num_groups(mod: "_DynamicGroupNorm", value: int) -> int:
152
+ return mod.num_channels // mod._group_size
153
+
154
+ @staticmethod
155
+ def _cut_to_active_channels(mod: "_DynamicGroupNorm", value: torch.Tensor | None):
156
+ return get_sliced_tensor(mod, value, "num_channels")
157
+
158
+ def _setup(self):
159
+ # register num_channels as hyperparameter
160
+ group_size = self.num_channels // self.num_groups
161
+ choices = [
162
+ c
163
+ for c in range(self.num_groups, self.num_channels + 1)
164
+ if c % self.num_groups == 0 and c % group_size == 0
165
+ ]
166
+ self._register_hparam("num_channels", TracedHp(choices, original=self.num_channels))
167
+
168
+ # register num_groups as a dynamic attribute so group size is same
169
+ self._register_temp_attribute("_group_size", group_size)
170
+ self._register_dynamic_attribute("num_groups", self._get_num_groups)
171
+
172
+ # register dynamic attributes
173
+ dyn_attrs = ["weight", "bias"]
174
+ for attr in dyn_attrs:
175
+ self._register_dynamic_attribute(attr, self._cut_to_active_channels)
176
+
177
+ def modify(self, *, channels_ratio: tuple[float, ...] | None = None, channel_divisor: int = 1):
178
+ """Modify the dynamic choices of the module according to provided keyword arguments.
179
+
180
+ Args:
181
+ channels_ratio: The ratios of the desired number of out/in channels over original
182
+ number of out/in channels.
183
+ channel_divisor: The divisor of the out/in channels.
184
+ """
185
+ hp = self.get_hparam("num_channels")
186
+ choices: list[int]
187
+ choices = (
188
+ {r * hp.original for r in channels_ratio}
189
+ if channels_ratio is not None
190
+ else set(hp.choices)
191
+ )
192
+ choices = {int(make_divisible(c, channel_divisor)) for c in choices}
193
+ hp.choices = list(set(hp.choices) & choices | {hp.original})
@@ -0,0 +1,61 @@
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
+ """Internal module for utility functions."""
17
+
18
+ import torch
19
+
20
+ from modelopt.torch.opt.dynamic import DynamicModule
21
+ from modelopt.torch.opt.hparam import Hparam
22
+
23
+
24
+ def get_sliced_tensor_by_slices(
25
+ tensor: torch.Tensor | None,
26
+ slices: list[Hparam.ActiveSlice],
27
+ ) -> torch.Tensor | None:
28
+ """Get the tensor based on the active slice."""
29
+ if tensor is None:
30
+ return tensor
31
+
32
+ # check if we can return the original tensor
33
+ if all(
34
+ isinstance(s, slice) and (s.stop is None or s.indices(s.stop) == (0, tensor.shape[i], 1))
35
+ for i, s in enumerate(slices)
36
+ ):
37
+ return tensor
38
+
39
+ # slice tensor with minimal number of index operations
40
+ tensor_sliced = tensor
41
+ for i, _ in enumerate(slices):
42
+ if sum(not isinstance(s, slice) for s in slices) < 2:
43
+ tensor_sliced = tensor_sliced[tuple(slices)]
44
+ break
45
+ tensor_sliced = tensor_sliced[tuple(slices[: i + 1])]
46
+ slices[i] = slice(None) # replace with a vanilla slice ("[:]") for next slicing iteration
47
+
48
+ # return sliced, contiguous tensor
49
+ return tensor_sliced.contiguous()
50
+
51
+
52
+ def get_sliced_tensor(
53
+ mod: DynamicModule,
54
+ tensor: torch.Tensor | None,
55
+ *hp_names: str | None,
56
+ ) -> torch.Tensor | None:
57
+ """Get the tensor based on the slices."""
58
+ slices = [
59
+ mod.get_hparam(hp_name).active_slice if hp_name else slice(None) for hp_name in hp_names
60
+ ]
61
+ return get_sliced_tensor_by_slices(tensor, slices)