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.
- modelopt/__init__.py +20 -0
- modelopt/deploy/__init__.py +16 -0
- modelopt/deploy/llm/__init__.py +30 -0
- modelopt/deploy/llm/generate.py +345 -0
- modelopt/onnx/__init__.py +34 -0
- modelopt/onnx/autocast/__init__.py +19 -0
- modelopt/onnx/autocast/__main__.py +187 -0
- modelopt/onnx/autocast/convert.py +202 -0
- modelopt/onnx/autocast/graphsanitizer.py +422 -0
- modelopt/onnx/autocast/logging_config.py +78 -0
- modelopt/onnx/autocast/nodeclassifier.py +416 -0
- modelopt/onnx/autocast/precisionconverter.py +1032 -0
- modelopt/onnx/autocast/referencerunner.py +159 -0
- modelopt/onnx/autocast/utils.py +117 -0
- modelopt/onnx/llm_export_utils/__init__.py +16 -0
- modelopt/onnx/llm_export_utils/export_utils.py +162 -0
- modelopt/onnx/llm_export_utils/quantization_utils.py +122 -0
- modelopt/onnx/llm_export_utils/surgeon_utils.py +120 -0
- modelopt/onnx/logging_config.py +77 -0
- modelopt/onnx/op_types.py +300 -0
- modelopt/onnx/quantization/__init__.py +20 -0
- modelopt/onnx/quantization/__main__.py +291 -0
- modelopt/onnx/quantization/calib_utils.py +178 -0
- modelopt/onnx/quantization/extensions.py +35 -0
- modelopt/onnx/quantization/fp8.py +375 -0
- modelopt/onnx/quantization/graph_utils.py +1479 -0
- modelopt/onnx/quantization/gs_patching.py +112 -0
- modelopt/onnx/quantization/int4.py +1347 -0
- modelopt/onnx/quantization/int8.py +283 -0
- modelopt/onnx/quantization/operators.py +121 -0
- modelopt/onnx/quantization/ort_patching.py +1766 -0
- modelopt/onnx/quantization/ort_utils.py +346 -0
- modelopt/onnx/quantization/partitioning.py +439 -0
- modelopt/onnx/quantization/qdq_utils.py +1349 -0
- modelopt/onnx/quantization/quant_utils.py +283 -0
- modelopt/onnx/quantization/quantize.py +528 -0
- modelopt/onnx/quantization/src/modelopt_round_and_pack_ext.cpp +127 -0
- modelopt/onnx/trt_utils.py +424 -0
- modelopt/onnx/utils.py +753 -0
- modelopt/torch/__init__.py +41 -0
- modelopt/torch/_deploy/__init__.py +23 -0
- modelopt/torch/_deploy/_runtime/__init__.py +29 -0
- modelopt/torch/_deploy/_runtime/common.py +72 -0
- modelopt/torch/_deploy/_runtime/ort_client.py +193 -0
- modelopt/torch/_deploy/_runtime/registry.py +83 -0
- modelopt/torch/_deploy/_runtime/runtime_client.py +154 -0
- modelopt/torch/_deploy/_runtime/tensorrt/constants.py +108 -0
- modelopt/torch/_deploy/_runtime/tensorrt/engine_builder.py +324 -0
- modelopt/torch/_deploy/_runtime/tensorrt/hw_param_config.py +51 -0
- modelopt/torch/_deploy/_runtime/tensorrt/layerwise_profiling.py +178 -0
- modelopt/torch/_deploy/_runtime/tensorrt/parse_trtexec_log.py +151 -0
- modelopt/torch/_deploy/_runtime/tensorrt/tensorrt_utils.py +200 -0
- modelopt/torch/_deploy/_runtime/trt_client.py +219 -0
- modelopt/torch/_deploy/compilation.py +142 -0
- modelopt/torch/_deploy/device_model.py +157 -0
- modelopt/torch/_deploy/profiling.py +193 -0
- modelopt/torch/_deploy/utils/__init__.py +18 -0
- modelopt/torch/_deploy/utils/onnx_optimizer.py +120 -0
- modelopt/torch/_deploy/utils/onnx_utils.py +58 -0
- modelopt/torch/_deploy/utils/torch_onnx.py +593 -0
- modelopt/torch/distill/__init__.py +28 -0
- modelopt/torch/distill/config.py +130 -0
- modelopt/torch/distill/distillation.py +84 -0
- modelopt/torch/distill/distillation_model.py +318 -0
- modelopt/torch/distill/loss_balancers.py +140 -0
- modelopt/torch/distill/losses.py +275 -0
- modelopt/torch/distill/mode.py +223 -0
- modelopt/torch/distill/plugins/__init__.py +24 -0
- modelopt/torch/distill/plugins/huggingface.py +110 -0
- modelopt/torch/distill/plugins/megatron.py +476 -0
- modelopt/torch/distill/registry.py +23 -0
- modelopt/torch/export/__init__.py +24 -0
- modelopt/torch/export/convert_hf_config.py +117 -0
- modelopt/torch/export/distribute.py +300 -0
- modelopt/torch/export/hf_config_map.py +84 -0
- modelopt/torch/export/layer_utils.py +1893 -0
- modelopt/torch/export/mcore_config_map.py +25 -0
- modelopt/torch/export/model_config.py +623 -0
- modelopt/torch/export/model_config_export.py +552 -0
- modelopt/torch/export/model_config_utils.py +392 -0
- modelopt/torch/export/model_utils.py +71 -0
- modelopt/torch/export/plugins/__init__.py +21 -0
- modelopt/torch/export/plugins/mcore_common.py +67 -0
- modelopt/torch/export/plugins/mcore_custom.py +530 -0
- modelopt/torch/export/plugins/mcore_deepseek.py +143 -0
- modelopt/torch/export/plugins/mcore_gptoss.py +71 -0
- modelopt/torch/export/plugins/mcore_llama.py +164 -0
- modelopt/torch/export/plugins/mcore_nemotron.py +90 -0
- modelopt/torch/export/plugins/mcore_qwen.py +99 -0
- modelopt/torch/export/plugins/megatron_importer.py +647 -0
- modelopt/torch/export/postprocess.py +847 -0
- modelopt/torch/export/quant_utils.py +1069 -0
- modelopt/torch/export/tensorrt_llm_type.py +42 -0
- modelopt/torch/export/tensorrt_llm_utils.py +405 -0
- modelopt/torch/export/transformer_engine.py +94 -0
- modelopt/torch/export/unified_export_hf.py +537 -0
- modelopt/torch/export/unified_export_megatron.py +1164 -0
- modelopt/torch/nas/__init__.py +27 -0
- modelopt/torch/nas/algorithms.py +856 -0
- modelopt/torch/nas/autonas.py +808 -0
- modelopt/torch/nas/conversion.py +119 -0
- modelopt/torch/nas/hparams/__init__.py +19 -0
- modelopt/torch/nas/hparams/concat.py +319 -0
- modelopt/torch/nas/hparams/container.py +48 -0
- modelopt/torch/nas/modules/__init__.py +21 -0
- modelopt/torch/nas/modules/container.py +99 -0
- modelopt/torch/nas/modules/conv.py +254 -0
- modelopt/torch/nas/modules/linear.py +76 -0
- modelopt/torch/nas/modules/norm.py +193 -0
- modelopt/torch/nas/modules/utils.py +61 -0
- modelopt/torch/nas/patch.py +261 -0
- modelopt/torch/nas/plugins/__init__.py +29 -0
- modelopt/torch/nas/plugins/megatron.py +1497 -0
- modelopt/torch/nas/plugins/torch.py +71 -0
- modelopt/torch/nas/plugins/transformer_engine.py +42 -0
- modelopt/torch/nas/plugins/transformers.py +210 -0
- modelopt/torch/nas/registry.py +23 -0
- modelopt/torch/nas/search_space.py +260 -0
- modelopt/torch/nas/traced_hp.py +149 -0
- modelopt/torch/nas/utils.py +408 -0
- modelopt/torch/opt/__init__.py +41 -0
- modelopt/torch/opt/_hooks.py +98 -0
- modelopt/torch/opt/config.py +383 -0
- modelopt/torch/opt/conversion.py +620 -0
- modelopt/torch/opt/dynamic.py +1367 -0
- modelopt/torch/opt/hparam.py +275 -0
- modelopt/torch/opt/mode.py +353 -0
- modelopt/torch/opt/plugins/__init__.py +38 -0
- modelopt/torch/opt/plugins/diffusers.py +40 -0
- modelopt/torch/opt/plugins/huggingface.py +162 -0
- modelopt/torch/opt/plugins/mcore_dist_checkpointing.py +211 -0
- modelopt/torch/opt/plugins/megatron.py +142 -0
- modelopt/torch/opt/plugins/peft.py +108 -0
- modelopt/torch/opt/plugins/transformers.py +160 -0
- modelopt/torch/opt/searcher.py +363 -0
- modelopt/torch/opt/utils.py +125 -0
- modelopt/torch/prune/__init__.py +26 -0
- modelopt/torch/prune/fastnas.py +376 -0
- modelopt/torch/prune/gradnas.py +349 -0
- modelopt/torch/prune/plugins/__init__.py +24 -0
- modelopt/torch/prune/plugins/mcore_minitron.py +263 -0
- modelopt/torch/prune/plugins/transformers.py +41 -0
- modelopt/torch/prune/pruning.py +211 -0
- modelopt/torch/quantization/__init__.py +26 -0
- modelopt/torch/quantization/algorithms.py +671 -0
- modelopt/torch/quantization/backends/__init__.py +20 -0
- modelopt/torch/quantization/backends/fp8_per_tensor_gemm.py +204 -0
- modelopt/torch/quantization/backends/gemm_registry.py +156 -0
- modelopt/torch/quantization/backends/nvfp4_gemm.py +201 -0
- modelopt/torch/quantization/backends/utils.py +28 -0
- modelopt/torch/quantization/calib/__init__.py +25 -0
- modelopt/torch/quantization/calib/bias.py +172 -0
- modelopt/torch/quantization/calib/calibrator.py +63 -0
- modelopt/torch/quantization/calib/histogram.py +434 -0
- modelopt/torch/quantization/calib/max.py +110 -0
- modelopt/torch/quantization/compress.py +227 -0
- modelopt/torch/quantization/config.py +1180 -0
- modelopt/torch/quantization/conversion.py +389 -0
- modelopt/torch/quantization/export_onnx.py +639 -0
- modelopt/torch/quantization/extensions.py +89 -0
- modelopt/torch/quantization/mode.py +428 -0
- modelopt/torch/quantization/model_calib.py +949 -0
- modelopt/torch/quantization/model_quant.py +477 -0
- modelopt/torch/quantization/nn/__init__.py +26 -0
- modelopt/torch/quantization/nn/functional.py +109 -0
- modelopt/torch/quantization/nn/modules/__init__.py +16 -0
- modelopt/torch/quantization/nn/modules/quant_activations.py +22 -0
- modelopt/torch/quantization/nn/modules/quant_batchnorm.py +24 -0
- modelopt/torch/quantization/nn/modules/quant_conv.py +123 -0
- modelopt/torch/quantization/nn/modules/quant_instancenorm.py +39 -0
- modelopt/torch/quantization/nn/modules/quant_linear.py +258 -0
- modelopt/torch/quantization/nn/modules/quant_module.py +211 -0
- modelopt/torch/quantization/nn/modules/quant_pooling.py +99 -0
- modelopt/torch/quantization/nn/modules/quant_rnn.py +527 -0
- modelopt/torch/quantization/nn/modules/tensor_quantizer.py +1293 -0
- modelopt/torch/quantization/plugins/__init__.py +73 -0
- modelopt/torch/quantization/plugins/accelerate.py +214 -0
- modelopt/torch/quantization/plugins/apex.py +52 -0
- modelopt/torch/quantization/plugins/attention.py +312 -0
- modelopt/torch/quantization/plugins/custom.py +192 -0
- modelopt/torch/quantization/plugins/diffusers.py +237 -0
- modelopt/torch/quantization/plugins/fairscale.py +45 -0
- modelopt/torch/quantization/plugins/huggingface.py +736 -0
- modelopt/torch/quantization/plugins/megatron.py +462 -0
- modelopt/torch/quantization/plugins/peft.py +148 -0
- modelopt/torch/quantization/plugins/transformer_engine.py +60 -0
- modelopt/torch/quantization/plugins/transformers.py +52 -0
- modelopt/torch/quantization/plugins/transformers_trainer.py +426 -0
- modelopt/torch/quantization/plugins/trl.py +27 -0
- modelopt/torch/quantization/plugins/vllm.py +199 -0
- modelopt/torch/quantization/qtensor/__init__.py +24 -0
- modelopt/torch/quantization/qtensor/base_qtensor.py +370 -0
- modelopt/torch/quantization/qtensor/fp8_tensor.py +151 -0
- modelopt/torch/quantization/qtensor/int4_tensor.py +130 -0
- modelopt/torch/quantization/qtensor/int8_tensor.py +124 -0
- modelopt/torch/quantization/qtensor/mxfp4_tensor.py +144 -0
- modelopt/torch/quantization/qtensor/nf4_tensor.py +199 -0
- modelopt/torch/quantization/qtensor/nvfp4_tensor.py +295 -0
- modelopt/torch/quantization/src/tensor_quant.cpp +77 -0
- modelopt/torch/quantization/src/tensor_quant.h +69 -0
- modelopt/torch/quantization/src/tensor_quant_fp8.cpp +48 -0
- modelopt/torch/quantization/src/tensor_quant_gpu.cu +361 -0
- modelopt/torch/quantization/src/tensor_quant_gpu_fp8.cu +122 -0
- modelopt/torch/quantization/src/tensor_quant_mx.cu +411 -0
- modelopt/torch/quantization/src/tensor_quant_mx.h +247 -0
- modelopt/torch/quantization/tensor_quant.py +816 -0
- modelopt/torch/quantization/triton/__init__.py +36 -0
- modelopt/torch/quantization/triton/fp4_kernel.py +303 -0
- modelopt/torch/quantization/utils.py +443 -0
- modelopt/torch/sparsity/__init__.py +19 -0
- modelopt/torch/sparsity/config.py +51 -0
- modelopt/torch/sparsity/magnitude.py +148 -0
- modelopt/torch/sparsity/mode.py +203 -0
- modelopt/torch/sparsity/module.py +87 -0
- modelopt/torch/sparsity/plugins/__init__.py +27 -0
- modelopt/torch/sparsity/plugins/megatron.py +93 -0
- modelopt/torch/sparsity/searcher.py +84 -0
- modelopt/torch/sparsity/sparsegpt.py +276 -0
- modelopt/torch/sparsity/sparsification.py +123 -0
- modelopt/torch/speculative/__init__.py +20 -0
- modelopt/torch/speculative/config.py +100 -0
- modelopt/torch/speculative/eagle/__init__.py +20 -0
- modelopt/torch/speculative/eagle/conversion.py +67 -0
- modelopt/torch/speculative/eagle/default_config.py +50 -0
- modelopt/torch/speculative/eagle/eagle_model.py +51 -0
- modelopt/torch/speculative/eagle/utils.py +91 -0
- modelopt/torch/speculative/medusa/__init__.py +19 -0
- modelopt/torch/speculative/medusa/conversion.py +59 -0
- modelopt/torch/speculative/medusa/medusa_model.py +32 -0
- modelopt/torch/speculative/mode.py +86 -0
- modelopt/torch/speculative/plugins/__init__.py +33 -0
- modelopt/torch/speculative/plugins/megatron_eagle.py +2119 -0
- modelopt/torch/speculative/plugins/megatron_medusa.py +312 -0
- modelopt/torch/speculative/plugins/transformers.py +1138 -0
- modelopt/torch/speculative/speculative_decoding.py +59 -0
- modelopt/torch/speculative/utils.py +364 -0
- modelopt/torch/trace/__init__.py +25 -0
- modelopt/torch/trace/analyzer.py +1368 -0
- modelopt/torch/trace/modules/__init__.py +19 -0
- modelopt/torch/trace/modules/concat.py +384 -0
- modelopt/torch/trace/modules/nn.py +153 -0
- modelopt/torch/trace/plugins/__init__.py +34 -0
- modelopt/torch/trace/plugins/megatron.py +36 -0
- modelopt/torch/trace/plugins/transformers.py +58 -0
- modelopt/torch/trace/symbols.py +545 -0
- modelopt/torch/trace/tracer.py +331 -0
- modelopt/torch/utils/__init__.py +28 -0
- modelopt/torch/utils/_pytree.py +133 -0
- modelopt/torch/utils/cpp_extension.py +91 -0
- modelopt/torch/utils/dataset_utils.py +484 -0
- modelopt/torch/utils/distributed.py +311 -0
- modelopt/torch/utils/graph.py +123 -0
- modelopt/torch/utils/image_processor.py +112 -0
- modelopt/torch/utils/import_utils.py +35 -0
- modelopt/torch/utils/list.py +54 -0
- modelopt/torch/utils/logging.py +127 -0
- modelopt/torch/utils/memory_monitor.py +146 -0
- modelopt/torch/utils/network.py +658 -0
- modelopt/torch/utils/perf.py +174 -0
- modelopt/torch/utils/plugins/__init__.py +27 -0
- modelopt/torch/utils/plugins/megatron_generate.py +299 -0
- modelopt/torch/utils/plugins/megatron_mmlu.py +152 -0
- modelopt/torch/utils/plugins/megatron_preprocess_data.py +200 -0
- modelopt/torch/utils/random.py +163 -0
- modelopt/torch/utils/speech_dataset_utils.py +134 -0
- modelopt/torch/utils/tensor.py +87 -0
- modelopt/torch/utils/vlm_dataset_utils.py +110 -0
- nvidia_modelopt-0.35.0.dist-info/METADATA +171 -0
- nvidia_modelopt-0.35.0.dist-info/RECORD +272 -0
- nvidia_modelopt-0.35.0.dist-info/WHEEL +5 -0
- nvidia_modelopt-0.35.0.dist-info/licenses/LICENSE +14 -0
- nvidia_modelopt-0.35.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,119 @@
|
|
|
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
|
+
"""Main APIs+entrypoints for model pruning."""
|
|
17
|
+
|
|
18
|
+
from torch import nn
|
|
19
|
+
|
|
20
|
+
from modelopt.torch.opt.conversion import apply_mode
|
|
21
|
+
from modelopt.torch.opt.mode import ModeLike, _ModeRegistryCls
|
|
22
|
+
from modelopt.torch.utils import ModelLike, unwrap_model
|
|
23
|
+
|
|
24
|
+
__all__ = ["convert", "export"]
|
|
25
|
+
|
|
26
|
+
NASModeRegistry = _ModeRegistryCls("nas")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def convert(model: ModelLike, mode: ModeLike) -> nn.Module:
|
|
30
|
+
"""Convert a regular PyTorch model into a model that supports design space optimization.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
model: A model-like object. Can be an nn.Module, a model class type, or a tuple.
|
|
34
|
+
Tuple must be of the form ``(model_cls,)`` or ``(model_cls, args)`` or ``(model_cls, args, kwargs)``.
|
|
35
|
+
Model will be initialized as ``model_cls(*args, **kwargs)``.
|
|
36
|
+
mode: A (list of) string(s) or Mode(s) or a list of tuples containing the mode and its
|
|
37
|
+
config indicating the desired mode(s) (and configurations) for the convert
|
|
38
|
+
process. Modes set up the model for different algorithms for model optimization. The
|
|
39
|
+
following modes are available:
|
|
40
|
+
|
|
41
|
+
* :class:`"autonas"<modelopt.torch.nas.autonas.AutoNASModeDescriptor>`: The ``model`` will
|
|
42
|
+
be converted into a search space and set up to automatically perform operations
|
|
43
|
+
required for AutoNAS-based model training, evaluation, and search. The mode's config
|
|
44
|
+
is described in :class:`AutoNASConfig<modelopt.torch.nas.autonas.AutoNASConfig>`.
|
|
45
|
+
|
|
46
|
+
If the mode argument is specified as a dictionary, the keys should indicate the mode and
|
|
47
|
+
the values specify the per-mode configuration. If not provided, then default
|
|
48
|
+
configuration would be used.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
A converted model with the original weights preserved that can be used for model
|
|
52
|
+
optimization.
|
|
53
|
+
|
|
54
|
+
.. note::
|
|
55
|
+
|
|
56
|
+
Note that model wrappers (such as DataParallel/DistributedDataParallel) are `not` supported
|
|
57
|
+
during the convert process. Please wrap the model after the convert process.
|
|
58
|
+
|
|
59
|
+
.. note::
|
|
60
|
+
``convert()`` relies on `monkey patching <https://en.wikipedia.org/wiki/Monkey_patch>`_ to
|
|
61
|
+
augment the ``forward()``, ``eval()``, and ``train()`` methods of ``model`` as well as
|
|
62
|
+
augment individual modules to make them dynamic. This renders the conversion incompatible
|
|
63
|
+
with other monkey patches to those methods and modules! Note that ``convert()`` is still
|
|
64
|
+
fully compatible with inheritance.
|
|
65
|
+
|
|
66
|
+
.. note::
|
|
67
|
+
|
|
68
|
+
#. Configs can be customized for individual layers using
|
|
69
|
+
`glob expressions <https://docs.python.org/3/library/fnmatch.html#fnmatch.fnmatch>`_ on
|
|
70
|
+
qualified submodule names, e.g., as shown for ``nn.Conv2d`` in the above example.
|
|
71
|
+
|
|
72
|
+
#. Keys in the config that appear earlier in a dict have lower priority, e.g.,
|
|
73
|
+
``backbone.stages.1.0.spatial_conv`` will have ``out_channels_ratio``
|
|
74
|
+
``[0.334, 0.5, 0.667, 1.0]``, not ``[1.0]``.
|
|
75
|
+
|
|
76
|
+
#. Config entries without layer qualifiers are also supported, e.g., as shown for
|
|
77
|
+
``nn.Sequential`` in the above example.
|
|
78
|
+
|
|
79
|
+
#. Mixed usage of configurations with and without layer qualifiers is supported for
|
|
80
|
+
*different* layers, e.g., as shown for ``nn.Conv2d`` and ``nn.Sequential`` in the above
|
|
81
|
+
example. For a specific layer type, only configurations with *or* without layer
|
|
82
|
+
qualifiers are supported.
|
|
83
|
+
|
|
84
|
+
#. Use ``*`` as a wildcard matching any layer.
|
|
85
|
+
"""
|
|
86
|
+
# apply mode and handle model-like object with wrapper
|
|
87
|
+
return apply_mode(model, mode, registry=NASModeRegistry)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def export(model: nn.Module, strict: bool = True, calib: bool = False) -> nn.Module:
|
|
91
|
+
"""Export a pruned subnet to a regular model.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
model: The pruned subnet to be exported.
|
|
95
|
+
strict: Raise an error when the config does not contain all necessary keys.
|
|
96
|
+
calib: Whether to calibrate the subnet to be exported.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
The current active subnet in regular PyTorch model format.
|
|
100
|
+
|
|
101
|
+
.. note::
|
|
102
|
+
|
|
103
|
+
if model is a wrapper such as DistributedDataParallel, it will be unwrapped, e.g.,
|
|
104
|
+
``model.module`` will be returned.
|
|
105
|
+
"""
|
|
106
|
+
# unwrap a DP/DDP model
|
|
107
|
+
model = unwrap_model(
|
|
108
|
+
model,
|
|
109
|
+
warn=True,
|
|
110
|
+
msg=(
|
|
111
|
+
f"Unwrapping a {type(model).__name__} model for export! Note that the export is"
|
|
112
|
+
" in-place and the model wrapper should be re-created after export since the wrapper"
|
|
113
|
+
" might not support changing parameters after initialization."
|
|
114
|
+
),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# apply export mode and return model
|
|
118
|
+
config = {"strict": strict, "calib": calib}
|
|
119
|
+
return apply_mode(model, [("export", config)], registry=NASModeRegistry)
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
"""Module containing implementations for various types of hyperparameters."""
|
|
17
|
+
|
|
18
|
+
from .concat import *
|
|
19
|
+
from .container import *
|
|
@@ -0,0 +1,319 @@
|
|
|
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
|
+
"""Hparam that represent concat."""
|
|
17
|
+
|
|
18
|
+
from collections import defaultdict
|
|
19
|
+
from collections.abc import Callable, Iterator
|
|
20
|
+
from itertools import product
|
|
21
|
+
from math import prod
|
|
22
|
+
from warnings import warn
|
|
23
|
+
|
|
24
|
+
import numpy as np
|
|
25
|
+
import torch
|
|
26
|
+
|
|
27
|
+
from modelopt.torch.trace import ConcatSymbol, Symbol
|
|
28
|
+
|
|
29
|
+
from ..traced_hp import TracedHp, TracedHpRegistry
|
|
30
|
+
|
|
31
|
+
__all__ = ["ConcatTracedHp"]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@TracedHpRegistry.register(ConcatSymbol)
|
|
35
|
+
class ConcatTracedHp(TracedHp):
|
|
36
|
+
"""Concat hparam that can stitch together input hparams."""
|
|
37
|
+
|
|
38
|
+
_inputs: list[TracedHp]
|
|
39
|
+
_sum_to_combo: dict[int, tuple[int]]
|
|
40
|
+
_hp_start_idx: torch.LongTensor
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
def _choice_combos(*all_choices) -> Iterator[tuple[int, ...]]:
|
|
44
|
+
# compute total number of combinations
|
|
45
|
+
n_combos = prod(len(c_list) for c_list in all_choices)
|
|
46
|
+
|
|
47
|
+
# we don't wanna iterate over more than that to keep it fast
|
|
48
|
+
n_max = 5e7 # takes 4s
|
|
49
|
+
|
|
50
|
+
# if we have less than n_max combinations, we can iterate over all of them
|
|
51
|
+
if n_combos <= n_max:
|
|
52
|
+
# use itertools.product and iterate over all combinations
|
|
53
|
+
yield from product(*all_choices)
|
|
54
|
+
return
|
|
55
|
+
else:
|
|
56
|
+
warn(f"ConcatTracedHp: {n_combos=} is larger than {n_max=}. Pruning combinations.")
|
|
57
|
+
|
|
58
|
+
# otherwise, we use a pruned set of combinations based on immediate vicinity of each value
|
|
59
|
+
|
|
60
|
+
# range of choices
|
|
61
|
+
c_min = min(min(c_list) for c_list in all_choices)
|
|
62
|
+
c_max = max(max(c_list) for c_list in all_choices)
|
|
63
|
+
|
|
64
|
+
# figure out for each list what's the closest index for each value in the choice range
|
|
65
|
+
idx_lookup = {
|
|
66
|
+
c: [min(range(len(c_list)), key=lambda i: abs(c_list[i] - c)) for c_list in all_choices]
|
|
67
|
+
for c in range(c_min, c_max + 1)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# solve equation for r such that n_combos = (c_max - c_min) * (2r+1)^n \approx n_max
|
|
71
|
+
# use r = max(1, r) to avoid edge cases
|
|
72
|
+
r = max(int((n_max / (c_max - c_min)) ** (1 / len(all_choices)) - 1) // 2, 1)
|
|
73
|
+
|
|
74
|
+
# yield reduced number of combinations (note that we might have duplicates; hence we filter)
|
|
75
|
+
# each combination is based on the product of choices centered around the value closest to
|
|
76
|
+
# the target value c. We use r to control the size of the neighborhood.
|
|
77
|
+
processed = set()
|
|
78
|
+
for c in range(c_min, c_max + 1):
|
|
79
|
+
all_choices_reduced = []
|
|
80
|
+
for i, c_list in enumerate(all_choices):
|
|
81
|
+
idx_closest = idx_lookup[c][i]
|
|
82
|
+
all_choices_reduced.append(c_list[max(0, idx_closest - r) : idx_closest + r + 1])
|
|
83
|
+
for combo in product(*all_choices_reduced):
|
|
84
|
+
if combo not in processed:
|
|
85
|
+
processed.add(combo)
|
|
86
|
+
yield combo
|
|
87
|
+
|
|
88
|
+
def _get_sum_to_combo(self) -> dict[int, tuple[int, ...]]:
|
|
89
|
+
"""Return dict mapping sum to individual hparam active values."""
|
|
90
|
+
# If two hparams are the same, we need their values to be the same and we need to track that
|
|
91
|
+
hps_unique = list(set(self._inputs))
|
|
92
|
+
idxs_unique = [hps_unique.index(hp) for hp in self._inputs]
|
|
93
|
+
|
|
94
|
+
# Keep the combination with the smallest difference between the individual values
|
|
95
|
+
# e.g. for sum 48, prefer [24, 24] over [16, 32] or [32, 16]
|
|
96
|
+
sum_to_combo = {}
|
|
97
|
+
sum_to_cost = defaultdict(lambda: float("inf"))
|
|
98
|
+
for combo in self._choice_combos(*[hp.choices for hp in hps_unique]):
|
|
99
|
+
# get full combo and sum
|
|
100
|
+
combo_full = tuple(combo[idx] for idx in idxs_unique)
|
|
101
|
+
s = sum(combo_full)
|
|
102
|
+
# check cost (sum of absolute differences between average and individual values)
|
|
103
|
+
cost = sum(abs(c - s / len(combo_full)) for c in combo_full)
|
|
104
|
+
# update if better
|
|
105
|
+
if cost < sum_to_cost[s]:
|
|
106
|
+
sum_to_cost[s] = cost
|
|
107
|
+
sum_to_combo[s] = combo_full
|
|
108
|
+
return sum_to_combo
|
|
109
|
+
|
|
110
|
+
def _set_hp_start_idx(self) -> None:
|
|
111
|
+
"""Compute start indices for input hps."""
|
|
112
|
+
# NOTE: the last index is the length of the concatenated hp
|
|
113
|
+
hp_start_idx = np.concatenate(([0], np.cumsum([hp.max for hp in self._inputs])))
|
|
114
|
+
self._hp_start_idx = torch.asarray(hp_start_idx, dtype=torch.long)
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def active(self) -> int:
|
|
118
|
+
"""Return the sum of active values of all hparams."""
|
|
119
|
+
active = 0
|
|
120
|
+
for hp in self._inputs:
|
|
121
|
+
active += hp.active
|
|
122
|
+
return active
|
|
123
|
+
|
|
124
|
+
@active.setter # type: ignore[override]
|
|
125
|
+
def active(self, val: int | None):
|
|
126
|
+
"""Set the active value with a sanity check for choices and dynamic hparams."""
|
|
127
|
+
val = self.original if val is None else val
|
|
128
|
+
assert val in self._choices, f"val = {val}, choices = {self.choices}"
|
|
129
|
+
assert val in self._sum_to_combo, f"Invalid value: {val} not in {self._sum_to_combo.keys()}"
|
|
130
|
+
for hp_in, v in zip(self._inputs, self._sum_to_combo[val]):
|
|
131
|
+
assert v in hp_in.choices
|
|
132
|
+
hp_in._active = v
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def active_slice(self) -> TracedHp.ActiveSlice:
|
|
136
|
+
"""Return the currently active sorted indices or slice corresponding to the active value.
|
|
137
|
+
|
|
138
|
+
Example:
|
|
139
|
+
hp1(max=4, active=2), hp2(max=4, active=4)
|
|
140
|
+
Then the active indices in the concatenated hp are [0, 1, 4, 5, 6, 7]
|
|
141
|
+
"""
|
|
142
|
+
# get vanilla slice first (with sanity check)
|
|
143
|
+
as_simple = super().active_slice
|
|
144
|
+
assert isinstance(as_simple, slice), "ConcatTracedHp does not support its own slice order!"
|
|
145
|
+
|
|
146
|
+
# get the list of slices as active indices in the concatenated hp
|
|
147
|
+
as_hps = [hp.active_slice for hp in self._inputs]
|
|
148
|
+
as_hps = [torch.arange(_as.stop)[_as] if isinstance(_as, slice) else _as for _as in as_hps]
|
|
149
|
+
|
|
150
|
+
# concatenate the slices with correct offsets
|
|
151
|
+
active_slice = torch.cat([_as + self._hp_start_idx[i] for i, _as in enumerate(as_hps)])
|
|
152
|
+
|
|
153
|
+
# check if active_slice corresponds to the vanilla slice
|
|
154
|
+
if torch.equal(active_slice, torch.arange(as_simple.stop)[as_simple]):
|
|
155
|
+
return as_simple
|
|
156
|
+
|
|
157
|
+
return active_slice
|
|
158
|
+
|
|
159
|
+
def _get_importance(self) -> TracedHp.Importance:
|
|
160
|
+
# compute the regular importance (this hparam + registered dependencies)
|
|
161
|
+
imp_total = super()._get_importance()
|
|
162
|
+
|
|
163
|
+
# now we add in the importance from the individual hparams contributing to the concat node
|
|
164
|
+
# concat inputs are not registered as dependencies, so we need to add them here
|
|
165
|
+
# note that we use ``hp.importance`` instead of the private API ``hp._get_importance()``
|
|
166
|
+
# here. Why? We can have the situation where only one of the concat inputs is searchable.
|
|
167
|
+
# We need to account for that and compute the "partial" importances hence!
|
|
168
|
+
imps_cat = [hp.importance if hp.is_configurable else None for hp in self._inputs]
|
|
169
|
+
|
|
170
|
+
# clean up importances that are None
|
|
171
|
+
imps_cat = [imp or torch.zeros(hp.max) for hp, imp in zip(self._inputs, imps_cat)]
|
|
172
|
+
|
|
173
|
+
# Let's split imp_total
|
|
174
|
+
imps_split = torch.tensor_split(imp_total, self._hp_start_idx[1:-1])
|
|
175
|
+
|
|
176
|
+
# Added split imps
|
|
177
|
+
imps = [imp + imp_cat.to(imp) for imp, imp_cat in zip(imps_split, imps_cat)] # type: ignore[union-attr]
|
|
178
|
+
|
|
179
|
+
# We need to aggregate between split importances when the come from the same hparam!
|
|
180
|
+
imps = [
|
|
181
|
+
sum([imp_ for imp_, hp in zip(imps, self._inputs) if hp is self._inputs[i]])
|
|
182
|
+
for i, imp in enumerate(imps)
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
# concatenate and return
|
|
186
|
+
return torch.cat(imps)
|
|
187
|
+
|
|
188
|
+
def _split_order(self, order: torch.Tensor) -> list[torch.Tensor]:
|
|
189
|
+
"""Split the order tensor into the individual order tensors for each input hparam."""
|
|
190
|
+
return [
|
|
191
|
+
(
|
|
192
|
+
order[torch.logical_and(order >= start, order < start + hp.max)] - start
|
|
193
|
+
if hp.is_configurable and hp.is_sortable
|
|
194
|
+
else torch.arange(hp.max, device=order.device)
|
|
195
|
+
)
|
|
196
|
+
for hp, start in zip(self._inputs, self._hp_start_idx[:-1])
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
@torch.no_grad()
|
|
200
|
+
def _enforce_order(self, order: torch.Tensor | None = None) -> None:
|
|
201
|
+
"""Enforcing the order.
|
|
202
|
+
|
|
203
|
+
Note that we only support intra-group sorting but **not** inter-group sorting. Inter-group
|
|
204
|
+
sorting would not be compatible with the concat operation and is thus not well-defined.
|
|
205
|
+
|
|
206
|
+
E.g., consider a concat operation with multiple inputs from convs. Then a channel that is
|
|
207
|
+
currently assigned to the first group (i.e. first conv) cannot be resorted to appear in any
|
|
208
|
+
other group/conv. Hence, we need to ensure that we maintain inter-group consistency.
|
|
209
|
+
"""
|
|
210
|
+
# vanilla case
|
|
211
|
+
if order is None:
|
|
212
|
+
for hp in self._inputs:
|
|
213
|
+
hp._enforce_order(order) # private API is fine here since it is just None
|
|
214
|
+
return super()._enforce_order(order)
|
|
215
|
+
|
|
216
|
+
# we have to group the order by the hparams to enforce order on each group separately with
|
|
217
|
+
# the correct offset subtracted
|
|
218
|
+
orders_split = self._split_order(order)
|
|
219
|
+
|
|
220
|
+
# ensure that orders are consistent if they come from the same hparam
|
|
221
|
+
for hp, _order in zip(self._inputs, orders_split):
|
|
222
|
+
assert all(
|
|
223
|
+
torch.equal(_order, order_)
|
|
224
|
+
for hp_, order_ in zip(self._inputs, orders_split)
|
|
225
|
+
if hp is hp_
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# enforce order now ...
|
|
229
|
+
# note that we use the public API here for extra sanity checks
|
|
230
|
+
for hp, partial_order in zip(self._inputs, orders_split):
|
|
231
|
+
if hp.is_configurable and hp.is_sortable:
|
|
232
|
+
hp.enforce_order(partial_order)
|
|
233
|
+
|
|
234
|
+
# recombine the order into a single tensor without inter-group sorting for dependencies
|
|
235
|
+
order_intra_only = torch.cat(
|
|
236
|
+
[partial + start for partial, start in zip(orders_split, self._hp_start_idx[:-1])]
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# sort dependencies now
|
|
240
|
+
super()._enforce_order(order_intra_only)
|
|
241
|
+
self._slice_order = None # we don't store slice_order in a ConcatTracedHp
|
|
242
|
+
|
|
243
|
+
def _resolve_dependencies(
|
|
244
|
+
self, sym: Symbol, get_hp: Callable[[Symbol], TracedHp]
|
|
245
|
+
) -> dict[Symbol, TracedHp]:
|
|
246
|
+
# check that we have the correct symbol
|
|
247
|
+
assert type(sym) is ConcatSymbol, f"Sym should be {type(self)}: {sym}."
|
|
248
|
+
|
|
249
|
+
# construct input hparams
|
|
250
|
+
# NOTE: input syms are hidden and corresponding registered symbol should be last dependency!
|
|
251
|
+
# --> if that's not the case we cannot retrieve the hps needed.
|
|
252
|
+
assert all(isinstance(s, ConcatSymbol.Input) for s in sym.input_syms)
|
|
253
|
+
assert all(s._dependencies[-1]._parent == s for s in sym.input_syms)
|
|
254
|
+
self._inputs = [get_hp(s._dependencies[-1]) for s in sym.input_syms]
|
|
255
|
+
|
|
256
|
+
def _get_hp_with_input(s: Symbol) -> TracedHp:
|
|
257
|
+
assert type(sym) is ConcatSymbol
|
|
258
|
+
for s_, hp in zip(sym.input_syms, self._inputs):
|
|
259
|
+
if s is s_:
|
|
260
|
+
return hp
|
|
261
|
+
return get_hp(s)
|
|
262
|
+
|
|
263
|
+
# resolve regular dependencies of all input hparams
|
|
264
|
+
mapping: dict[Symbol, TracedHp] = {}
|
|
265
|
+
for hp_in, sym_in in zip(self._inputs, sym.input_syms):
|
|
266
|
+
mapping.update(hp_in._resolve_dependencies(sym_in, _get_hp_with_input))
|
|
267
|
+
|
|
268
|
+
# remove input syms from mapping...
|
|
269
|
+
for sym_in in set(sym.input_syms):
|
|
270
|
+
mapping.pop(sym_in)
|
|
271
|
+
|
|
272
|
+
# resolve regular dependencies
|
|
273
|
+
mapping.update(super()._resolve_dependencies(sym, get_hp))
|
|
274
|
+
|
|
275
|
+
# compute sum_to_combo values and make sure they are consistent with choices
|
|
276
|
+
sum_to_combo = {s: val for s, val in self._get_sum_to_combo().items() if s in self.choices}
|
|
277
|
+
self._sum_to_combo = sum_to_combo
|
|
278
|
+
self.choices = list(self._sum_to_combo)
|
|
279
|
+
|
|
280
|
+
# update choices for inputs from sum_to_combo and make them non-configurable
|
|
281
|
+
for i, hp_in in enumerate(self._inputs):
|
|
282
|
+
hp_in.choices = [combo[i] for combo in sum_to_combo.values()]
|
|
283
|
+
hp_in._is_configurable = False
|
|
284
|
+
|
|
285
|
+
self._set_hp_start_idx()
|
|
286
|
+
|
|
287
|
+
return mapping
|
|
288
|
+
|
|
289
|
+
def reset_choices(self) -> None:
|
|
290
|
+
"""Reset the choices of the concat hparam.
|
|
291
|
+
|
|
292
|
+
Useful if we want to reset choices after input hparam choices are changed during modify().
|
|
293
|
+
"""
|
|
294
|
+
self._sum_to_combo = self._get_sum_to_combo()
|
|
295
|
+
self._set_hp_start_idx()
|
|
296
|
+
with self._force_configurable():
|
|
297
|
+
self.choices = list(self._sum_to_combo)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def build_concat_hp(inputs: list[TracedHp]):
|
|
301
|
+
"""Initialize a non-configurable concat hparam from a list of input hparams.
|
|
302
|
+
|
|
303
|
+
One key difference from ConcatTracedHp via tracing is that in ConcatTracedHp, the input hparams
|
|
304
|
+
are not configurable, and only the concatenated hparam is configurable. In build_concat_hp, its the opposite.
|
|
305
|
+
|
|
306
|
+
This is useful for building concat hparams from a list of configurable input hparams instead of
|
|
307
|
+
tracing (e.g. for megatron language model DynamicModule).
|
|
308
|
+
"""
|
|
309
|
+
concat_hp = object.__new__(ConcatTracedHp)
|
|
310
|
+
concat_hp._inputs = inputs
|
|
311
|
+
concat_hp._sum_to_combo = concat_hp._get_sum_to_combo()
|
|
312
|
+
concat_hp._set_hp_start_idx()
|
|
313
|
+
|
|
314
|
+
choices = list(concat_hp._sum_to_combo)
|
|
315
|
+
concat_hp.__init__(choices) # type: ignore[misc]
|
|
316
|
+
concat_hp._is_configurable = False
|
|
317
|
+
concat_hp._importance_estimators = None
|
|
318
|
+
|
|
319
|
+
return concat_hp
|
|
@@ -0,0 +1,48 @@
|
|
|
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
|
+
"""Hparam for Depth."""
|
|
17
|
+
|
|
18
|
+
from collections.abc import Callable
|
|
19
|
+
|
|
20
|
+
from modelopt.torch.trace import Symbol, SymDepth
|
|
21
|
+
|
|
22
|
+
from ..traced_hp import TracedHp, TracedHpRegistry
|
|
23
|
+
|
|
24
|
+
__all__ = ["DepthHparam"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@TracedHpRegistry.register(SymDepth)
|
|
28
|
+
class DepthHparam(TracedHp):
|
|
29
|
+
"""Hparam describing depth."""
|
|
30
|
+
|
|
31
|
+
def _resolve_dependencies(
|
|
32
|
+
self, sym: Symbol, get_hp: Callable[[Symbol], TracedHp]
|
|
33
|
+
) -> dict[Symbol, TracedHp]:
|
|
34
|
+
assert isinstance(sym, SymDepth), f"Unexpected type {type(sym)} for {sym}!"
|
|
35
|
+
assert not sym._dependencies, "Depth should not have any dependencies!"
|
|
36
|
+
assert not sym._parent, "Depth should not have any parents!"
|
|
37
|
+
|
|
38
|
+
# we can only skip layers when all layers after are skippable
|
|
39
|
+
choices = {sym.max_depth}
|
|
40
|
+
for i in range(sym.max_depth - 1, -1, -1):
|
|
41
|
+
if not sym.is_skippable(i):
|
|
42
|
+
break
|
|
43
|
+
choices.add(i)
|
|
44
|
+
|
|
45
|
+
# record choices and skippable layers
|
|
46
|
+
self.choices = list(choices & set(self.choices))
|
|
47
|
+
|
|
48
|
+
return super()._resolve_dependencies(sym, get_hp)
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
"""The dynamic equivalents for torch.nn modules."""
|
|
17
|
+
|
|
18
|
+
from .container import *
|
|
19
|
+
from .conv import *
|
|
20
|
+
from .linear import *
|
|
21
|
+
from .norm import *
|
|
@@ -0,0 +1,99 @@
|
|
|
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 sequential implementation (variable depth based on torch.nn.modules.container)."""
|
|
17
|
+
|
|
18
|
+
from collections import OrderedDict
|
|
19
|
+
from collections.abc import Callable
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from torch import nn
|
|
23
|
+
|
|
24
|
+
from modelopt.torch.opt.dynamic import DynamicModule
|
|
25
|
+
|
|
26
|
+
from ..registry import DMRegistry
|
|
27
|
+
from ..traced_hp import TracedHp
|
|
28
|
+
|
|
29
|
+
__all__ = ["_DynamicSequential"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _activate_depth(func: Callable) -> Callable:
|
|
33
|
+
"""A decorator for enabling dynamic depth within methods in _DynamicSequential.
|
|
34
|
+
|
|
35
|
+
The decorated method (and methods within the decorated method) will see any attributes with
|
|
36
|
+
dynamic depth enabled.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def func_with_dynamic_depth(self: "_DynamicSequential", *args, **kwargs) -> Any:
|
|
40
|
+
"""Call func and return value wrapped with temporarily enabling dynamic depth."""
|
|
41
|
+
val = self._dynamic_depth
|
|
42
|
+
self._dynamic_depth = True
|
|
43
|
+
ret = func(self, *args, **kwargs)
|
|
44
|
+
self._dynamic_depth = val
|
|
45
|
+
return ret
|
|
46
|
+
|
|
47
|
+
return func_with_dynamic_depth
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@DMRegistry.register({nn.Sequential: "nn.Sequential"})
|
|
51
|
+
class _DynamicSequential(DynamicModule):
|
|
52
|
+
"""An ``nn.Sequential`` layer with dynamic hyperparams and variable ``depth``."""
|
|
53
|
+
|
|
54
|
+
_dynamic_depth: bool
|
|
55
|
+
|
|
56
|
+
@_activate_depth
|
|
57
|
+
def forward(self, input):
|
|
58
|
+
"""Forward with activated variable depth."""
|
|
59
|
+
return super().forward(input)
|
|
60
|
+
|
|
61
|
+
@_activate_depth
|
|
62
|
+
def export(self) -> nn.Module:
|
|
63
|
+
"""Export with activated variable depth."""
|
|
64
|
+
return super().export()
|
|
65
|
+
|
|
66
|
+
@_activate_depth
|
|
67
|
+
def __repr__(self):
|
|
68
|
+
"""__repr__ with dynamic depth enabled -> str(self) will only show active subnet."""
|
|
69
|
+
return super().__repr__()
|
|
70
|
+
|
|
71
|
+
@_activate_depth
|
|
72
|
+
def extra_repr(self):
|
|
73
|
+
"""extra_repr with dynamic depth enabled -> str(self) will only show active subnet."""
|
|
74
|
+
return super().extra_repr()
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def _get_modules(mod: "_DynamicSequential", modules: OrderedDict) -> OrderedDict:
|
|
78
|
+
if mod.depth < len(modules) and mod._dynamic_depth:
|
|
79
|
+
return OrderedDict(list(modules.items())[: mod.depth])
|
|
80
|
+
return modules
|
|
81
|
+
|
|
82
|
+
def _setup(self):
|
|
83
|
+
# register temp attribute to keep track of whether dynamic depth is on
|
|
84
|
+
self._register_temp_attribute("_dynamic_depth", False)
|
|
85
|
+
|
|
86
|
+
# register hyperparameters
|
|
87
|
+
self._register_hparam("depth", TracedHp(list(range(len(self) + 1))))
|
|
88
|
+
|
|
89
|
+
# register _modules as a dynamic attribute
|
|
90
|
+
self._register_dynamic_attribute("_modules", self._get_modules)
|
|
91
|
+
|
|
92
|
+
def modify(self, *, min_depth: int = 0):
|
|
93
|
+
"""Modify the dynamic choices of the module according to provided keyword arguments.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
min_depth: The minimum depth of the module.
|
|
97
|
+
"""
|
|
98
|
+
hp = self.get_hparam("depth")
|
|
99
|
+
hp.choices = [d for d in hp.choices if d >= min_depth]
|