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,73 @@
|
|
|
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
|
+
"""Handles quantization plugins to correctly quantize third-party modules.
|
|
17
|
+
|
|
18
|
+
Please check out the source code of this module for examples of how plugins work and how you can
|
|
19
|
+
write your own one. Currently, we support plugins for
|
|
20
|
+
|
|
21
|
+
- :meth:`apex<modelopt.torch.quantization.plugins.apex>`
|
|
22
|
+
- :meth:`attention<modelopt.torch.quantization.plugins.attention>`
|
|
23
|
+
- :meth:`diffusers<modelopt.torch.quantization.plugins.diffusers>`
|
|
24
|
+
- :meth:`fairscale<modelopt.torch.quantization.plugins.fairscale>`
|
|
25
|
+
- :meth:`huggingface<modelopt.torch.quantization.plugins.huggingface>`
|
|
26
|
+
- :meth:`megatron<modelopt.torch.quantization.plugins.megatron>`
|
|
27
|
+
- :meth:`peft<modelopt.torch.quantization.plugins.peft>`
|
|
28
|
+
- :meth:`transformer_engine<modelopt.torch.quantization.plugins.transformer_engine>`
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from modelopt.torch.utils import import_plugin
|
|
32
|
+
|
|
33
|
+
with import_plugin("accelerate"):
|
|
34
|
+
from .accelerate import *
|
|
35
|
+
|
|
36
|
+
with import_plugin("apex"):
|
|
37
|
+
from .apex import *
|
|
38
|
+
|
|
39
|
+
from .attention import *
|
|
40
|
+
from .custom import *
|
|
41
|
+
|
|
42
|
+
with import_plugin("diffusers"):
|
|
43
|
+
from .diffusers import *
|
|
44
|
+
|
|
45
|
+
with import_plugin("fairscale"):
|
|
46
|
+
from .fairscale import *
|
|
47
|
+
|
|
48
|
+
with import_plugin("huggingface"):
|
|
49
|
+
from .huggingface import *
|
|
50
|
+
|
|
51
|
+
with import_plugin("megatron"):
|
|
52
|
+
from .megatron import *
|
|
53
|
+
|
|
54
|
+
with import_plugin("nemo"):
|
|
55
|
+
from .nemo import *
|
|
56
|
+
|
|
57
|
+
with import_plugin("peft"):
|
|
58
|
+
from .peft import *
|
|
59
|
+
|
|
60
|
+
with import_plugin("transformer_engine"):
|
|
61
|
+
from .transformer_engine import *
|
|
62
|
+
|
|
63
|
+
with import_plugin("transformers trainer"):
|
|
64
|
+
from .transformers_trainer import *
|
|
65
|
+
|
|
66
|
+
with import_plugin("transformers"):
|
|
67
|
+
from .transformers import *
|
|
68
|
+
|
|
69
|
+
with import_plugin("vllm"):
|
|
70
|
+
from .vllm import *
|
|
71
|
+
|
|
72
|
+
with import_plugin("trl"):
|
|
73
|
+
from .trl import *
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2023-2025 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
|
+
"""Quantization support for accelerate modified models."""
|
|
16
|
+
|
|
17
|
+
import warnings
|
|
18
|
+
from contextlib import contextmanager
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
import torch
|
|
22
|
+
import transformers
|
|
23
|
+
from accelerate import init_empty_weights, load_checkpoint_and_dispatch
|
|
24
|
+
from accelerate.hooks import AlignDevicesHook, SequentialHook
|
|
25
|
+
from accelerate.utils import get_max_memory, infer_auto_device_map
|
|
26
|
+
from accelerate.utils.dataclasses import CustomDtype
|
|
27
|
+
from accelerate.utils.offload import PrefixedDataset
|
|
28
|
+
|
|
29
|
+
import modelopt.torch.quantization as mtq
|
|
30
|
+
|
|
31
|
+
__all__ = ["init_quantized_weights"]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _get_cpu_offload_hook(hook):
|
|
35
|
+
if isinstance(hook, AlignDevicesHook) and hook.offload and hook.weights_map is not None:
|
|
36
|
+
assert "weight" in hook.weights_map
|
|
37
|
+
if (
|
|
38
|
+
isinstance(hook.weights_map, PrefixedDataset)
|
|
39
|
+
and hook.weights_map.prefix + "weight" not in hook.weights_map.dataset.state_dict
|
|
40
|
+
):
|
|
41
|
+
raise NotImplementedError(
|
|
42
|
+
"This layer could be offloaded to disk. We don't support this yet."
|
|
43
|
+
)
|
|
44
|
+
return hook
|
|
45
|
+
elif isinstance(hook, SequentialHook):
|
|
46
|
+
for h in hook.hooks:
|
|
47
|
+
align_hook = _get_cpu_offload_hook(h)
|
|
48
|
+
if align_hook is not None:
|
|
49
|
+
return align_hook
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@contextmanager
|
|
54
|
+
def weight_access_and_writeback_context(module):
|
|
55
|
+
"""Context manager for weight access and writeback for modules managed by accelerate."""
|
|
56
|
+
assert hasattr(module, "_hf_hook")
|
|
57
|
+
align_hook = _get_cpu_offload_hook(module._hf_hook)
|
|
58
|
+
|
|
59
|
+
if align_hook:
|
|
60
|
+
# Accelerate uses AlignDevicesHook to offload weights to CPU/Disk and then reload them in the forward pass
|
|
61
|
+
# The CPU/Disk offloaded weights are managed by PrefixDataset and OffloadedWeightsLoader
|
|
62
|
+
# See https://github.com/huggingface/accelerate/blame/f48d95c4939b281505a45b3d6e0bf554b65cc1ea/src/accelerate/utils/offload.py#L104-L141
|
|
63
|
+
# TODO: Add support for disk-offloaded models if needed (they will be really slow, hence low priority)
|
|
64
|
+
|
|
65
|
+
# This will load the weights from CPU state_dict and move it to the GPU from meta device
|
|
66
|
+
align_hook.pre_forward(module)
|
|
67
|
+
try:
|
|
68
|
+
yield
|
|
69
|
+
finally:
|
|
70
|
+
if align_hook:
|
|
71
|
+
# Update the weight in the CPU state_dict
|
|
72
|
+
if isinstance(align_hook.weights_map, PrefixedDataset):
|
|
73
|
+
key = align_hook.weights_map.prefix + "weight"
|
|
74
|
+
w_map = align_hook.weights_map.dataset.state_dict
|
|
75
|
+
else:
|
|
76
|
+
key, w_map = "weight", align_hook.weights_map
|
|
77
|
+
w_map[key] = module.weight.data.to(w_map[key].device, dtype=w_map[key].dtype)
|
|
78
|
+
align_hook.post_forward(module, None)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@contextmanager
|
|
82
|
+
def init_quantized_weights(
|
|
83
|
+
quant_cfg: dict[str, Any], gpu_mem_percentage: float = 0.8, quant_gemm: bool = False
|
|
84
|
+
):
|
|
85
|
+
"""Context manager for initializing and loading HuggingFace models with quantized and compressed weights.
|
|
86
|
+
|
|
87
|
+
This context manager patches `from_pretrained` to automatically:
|
|
88
|
+
1. Initialize the model with an empty config
|
|
89
|
+
2. Apply quantization configuration
|
|
90
|
+
3. Compress the weights
|
|
91
|
+
4. Load and dispatch the model to appropriate devices
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
quant_cfg: The quantization config to apply to the model
|
|
95
|
+
gpu_mem_percentage: Percentage of GPU memory to use (0.0-1.0)
|
|
96
|
+
quant_gemm: Whether to enable quantized GEMM
|
|
97
|
+
|
|
98
|
+
Example:
|
|
99
|
+
```python
|
|
100
|
+
from transformers import AutoModelForCausalLM
|
|
101
|
+
from modelopt.torch.quantization.plugins.huggingface import init_quantized_weights
|
|
102
|
+
import modelopt.torch as mtq
|
|
103
|
+
|
|
104
|
+
# Load quantized and compressed model in one step
|
|
105
|
+
with init_quantized_weights(mtq.NVFP4_DEFAULT_CFG):
|
|
106
|
+
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-70b-hf")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# The model is already quantized, compressed, and loaded to appropriate devices
|
|
110
|
+
|
|
111
|
+
# calibrate model
|
|
112
|
+
mtq.calibrate(model, "max", forward_loop)
|
|
113
|
+
```
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
def get_no_split_module_classes(model):
|
|
117
|
+
"""Get no-split module classes for device mapping."""
|
|
118
|
+
no_split_classes = set()
|
|
119
|
+
|
|
120
|
+
# Get all named modules
|
|
121
|
+
for name, module in model.named_modules():
|
|
122
|
+
# Look for first layer patterns
|
|
123
|
+
if name.endswith((".layers.0", ".layer.0", ".h.0", ".blocks.0")):
|
|
124
|
+
no_split_classes.add(module.__class__.__name__)
|
|
125
|
+
|
|
126
|
+
return list(no_split_classes)
|
|
127
|
+
|
|
128
|
+
def get_model_device_map(model, gpu_mem_percentage):
|
|
129
|
+
"""Create optimized device map for the quantized and compressed model."""
|
|
130
|
+
# Get compression ratio to adjust memory usage
|
|
131
|
+
max_memory = get_max_memory()
|
|
132
|
+
no_split_classes = get_no_split_module_classes(model)
|
|
133
|
+
if len(no_split_classes) == 0:
|
|
134
|
+
warnings.warn(
|
|
135
|
+
"No no-split module classes found for the model. Default device map might be incorrect."
|
|
136
|
+
)
|
|
137
|
+
max_memory = {k: v * gpu_mem_percentage for k, v in max_memory.items()}
|
|
138
|
+
|
|
139
|
+
def _get_special_dtype_map(model):
|
|
140
|
+
special_dtype_map = {}
|
|
141
|
+
|
|
142
|
+
def _get_byte_size(module):
|
|
143
|
+
weight_num_bit = module.weight_quantizer.num_bits
|
|
144
|
+
if isinstance(weight_num_bit, tuple):
|
|
145
|
+
assert len(weight_num_bit) == 2, (
|
|
146
|
+
"Weight num bit must be a tuple of two integers."
|
|
147
|
+
)
|
|
148
|
+
weight_num_bit = weight_num_bit[0] + weight_num_bit[1] + 1
|
|
149
|
+
if weight_num_bit == 8:
|
|
150
|
+
return CustomDtype.FP8
|
|
151
|
+
elif weight_num_bit == 4:
|
|
152
|
+
return CustomDtype.INT4
|
|
153
|
+
else:
|
|
154
|
+
raise ValueError(f"Unsupported weight num bit: {weight_num_bit}")
|
|
155
|
+
|
|
156
|
+
for name, module in model.named_modules():
|
|
157
|
+
if (
|
|
158
|
+
hasattr(module, "weight")
|
|
159
|
+
and hasattr(module, "weight_quantizer")
|
|
160
|
+
and not module.weight_quantizer.fake_quant
|
|
161
|
+
):
|
|
162
|
+
special_dtype_map[name + ".weight"] = _get_byte_size(module)
|
|
163
|
+
return special_dtype_map
|
|
164
|
+
|
|
165
|
+
special_dtype_map = _get_special_dtype_map(model)
|
|
166
|
+
inferred_device_map = infer_auto_device_map(
|
|
167
|
+
model,
|
|
168
|
+
max_memory=max_memory,
|
|
169
|
+
no_split_module_classes=no_split_classes,
|
|
170
|
+
special_dtypes=special_dtype_map,
|
|
171
|
+
)
|
|
172
|
+
return inferred_device_map
|
|
173
|
+
|
|
174
|
+
# Store original from_pretrained methods that we'll override
|
|
175
|
+
original_auto_causal_lm_from_pretrained = transformers.AutoModelForCausalLM.from_pretrained
|
|
176
|
+
|
|
177
|
+
# Create patched from_pretrained that applies quantization and compression
|
|
178
|
+
def patched_from_pretrained(cls, /, pretrained_model_name_or_path, *args, **kwargs):
|
|
179
|
+
"""Patched from_pretrained that handles quantization, compression and device mapping."""
|
|
180
|
+
# Initialize with empty config first to avoid loading weights twice
|
|
181
|
+
config = kwargs.pop("config", None)
|
|
182
|
+
trust_remote_code = kwargs.pop("trust_remote_code", False)
|
|
183
|
+
if config is None:
|
|
184
|
+
# Get config from pretrained model
|
|
185
|
+
from transformers import AutoConfig
|
|
186
|
+
|
|
187
|
+
config = AutoConfig.from_pretrained(
|
|
188
|
+
pretrained_model_name_or_path, trust_remote_code=trust_remote_code
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
with init_empty_weights():
|
|
192
|
+
# Fix torch_dtype to match original model
|
|
193
|
+
torch_dtype = kwargs.get("torch_dtype", getattr(config, "torch_dtype", torch.float16))
|
|
194
|
+
model = cls.from_config(config, torch_dtype=torch_dtype)
|
|
195
|
+
|
|
196
|
+
mtq.quantize(model, quant_cfg)
|
|
197
|
+
mtq.compress(model, config=mtq.CompressConfig(quant_gemm=quant_gemm))
|
|
198
|
+
_device_map = get_model_device_map(model, gpu_mem_percentage)
|
|
199
|
+
|
|
200
|
+
return load_checkpoint_and_dispatch(
|
|
201
|
+
model,
|
|
202
|
+
checkpoint=pretrained_model_name_or_path,
|
|
203
|
+
device_map=_device_map,
|
|
204
|
+
*args,
|
|
205
|
+
**kwargs,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
# Apply our patches
|
|
210
|
+
transformers.AutoModelForCausalLM.from_pretrained = classmethod(patched_from_pretrained)
|
|
211
|
+
yield
|
|
212
|
+
finally:
|
|
213
|
+
# Restore original methods
|
|
214
|
+
transformers.AutoModelForCausalLM.from_pretrained = original_auto_causal_lm_from_pretrained
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
"""Support quantization for apex linear layers."""
|
|
17
|
+
|
|
18
|
+
from functools import partial
|
|
19
|
+
|
|
20
|
+
import apex.transformer.tensor_parallel.layers as apex_parallel
|
|
21
|
+
from apex.transformer.parallel_state import get_data_parallel_group, get_tensor_model_parallel_group
|
|
22
|
+
|
|
23
|
+
from modelopt.torch.utils.distributed import ParallelState
|
|
24
|
+
|
|
25
|
+
from ..nn import QuantModuleRegistry
|
|
26
|
+
from ..nn.modules.quant_linear import _QuantLinear
|
|
27
|
+
from .custom import _ParallelLinear
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class _ApexParallelLinear(_ParallelLinear):
|
|
31
|
+
def _setup(self):
|
|
32
|
+
quantized_linear_fn = partial(
|
|
33
|
+
_QuantLinear.quantized_linear_fn,
|
|
34
|
+
apex_parallel,
|
|
35
|
+
"linear_with_grad_accumulation_and_async_allreduce",
|
|
36
|
+
self,
|
|
37
|
+
)
|
|
38
|
+
self._forward_impl = quantized_linear_fn
|
|
39
|
+
self.parallel_state = ParallelState(
|
|
40
|
+
get_data_parallel_group(), get_tensor_model_parallel_group()
|
|
41
|
+
)
|
|
42
|
+
super()._setup()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@QuantModuleRegistry.register({apex_parallel.ColumnParallelLinear: "apex_ColumnParallelLinear"})
|
|
46
|
+
class _ApexColumnParallelLinear(_ApexParallelLinear):
|
|
47
|
+
_is_column_parallel = True
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@QuantModuleRegistry.register({apex_parallel.RowParallelLinear: "apex_RowParallelLinear"})
|
|
51
|
+
class _ApexRowParallelLinear(_ApexParallelLinear):
|
|
52
|
+
_is_row_parallel = True
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# Inspired by https://github.com/ELS-RD/transformer-deploy/blob/6b88e24ade6ce199e825adc0477b28a07f51f17d/src/transformer_deploy/QDQModels/ast_operator_patch.py
|
|
2
|
+
|
|
3
|
+
# Apache License
|
|
4
|
+
# Copyright 2022, Lefebvre Dalloz Services
|
|
5
|
+
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
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
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
17
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
18
|
+
#
|
|
19
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
20
|
+
# you may not use this file except in compliance with the License.
|
|
21
|
+
# You may obtain a copy of the License at
|
|
22
|
+
#
|
|
23
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
24
|
+
#
|
|
25
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
26
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
27
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
28
|
+
# See the License for the specific language governing permissions and
|
|
29
|
+
# limitations under the License.
|
|
30
|
+
|
|
31
|
+
"""Support quantization for KV Cache in attention layers."""
|
|
32
|
+
|
|
33
|
+
import ast
|
|
34
|
+
import inspect
|
|
35
|
+
import tempfile
|
|
36
|
+
import types
|
|
37
|
+
from warnings import warn
|
|
38
|
+
|
|
39
|
+
from ..conversion import register
|
|
40
|
+
from ..nn import TensorQuantizer
|
|
41
|
+
|
|
42
|
+
__all__ = ["register_attention_for_kv_quant"]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def register_attention_for_kv_quant(attention_cls: type) -> bool:
|
|
46
|
+
"""Register attention layer for quantization of KV Cache.
|
|
47
|
+
|
|
48
|
+
Generate a quantized version of the attention class on the fly,
|
|
49
|
+
and register it with the original class for quantization.
|
|
50
|
+
"""
|
|
51
|
+
source_code = inspect.getsource(attention_cls)
|
|
52
|
+
model_module = inspect.getmodule(attention_cls)
|
|
53
|
+
head = ast.parse(source_code)
|
|
54
|
+
|
|
55
|
+
bmm_ops = ("matmul", "bmm", "baddbmm")
|
|
56
|
+
sdpa_ops = ("scaled_dot_product_attention",)
|
|
57
|
+
|
|
58
|
+
def is_bmm(node):
|
|
59
|
+
return (
|
|
60
|
+
isinstance(node, ast.Call)
|
|
61
|
+
and isinstance(node.func, ast.Attribute)
|
|
62
|
+
and node.func.attr in bmm_ops
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
def is_sdpa(node):
|
|
66
|
+
return (
|
|
67
|
+
isinstance(node, ast.Call)
|
|
68
|
+
and isinstance(node.func, ast.Attribute)
|
|
69
|
+
and node.func.attr in sdpa_ops
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def is_bin_matmul(node):
|
|
73
|
+
return isinstance(node, ast.BinOp) and isinstance(node.op, ast.MatMult)
|
|
74
|
+
|
|
75
|
+
def patch(node, quantizer_names, transpose=False):
|
|
76
|
+
for index, quantizer_name in enumerate(quantizer_names):
|
|
77
|
+
if quantizer_name is None:
|
|
78
|
+
continue
|
|
79
|
+
arg = node.args[index]
|
|
80
|
+
|
|
81
|
+
if not transpose:
|
|
82
|
+
node.args[index] = ast.Call(
|
|
83
|
+
func=ast.Attribute(
|
|
84
|
+
value=ast.Name(id="self", ctx=ast.Load()),
|
|
85
|
+
attr=quantizer_name,
|
|
86
|
+
ctx=ast.Load(),
|
|
87
|
+
),
|
|
88
|
+
args=[arg],
|
|
89
|
+
keywords=[],
|
|
90
|
+
)
|
|
91
|
+
else:
|
|
92
|
+
node.args[index] = ast.Call(
|
|
93
|
+
func=ast.Attribute(
|
|
94
|
+
value=ast.Call(
|
|
95
|
+
func=ast.Attribute(
|
|
96
|
+
value=ast.Name(id="self", ctx=ast.Load()),
|
|
97
|
+
attr=quantizer_name,
|
|
98
|
+
ctx=ast.Load(),
|
|
99
|
+
),
|
|
100
|
+
args=[
|
|
101
|
+
ast.Call(
|
|
102
|
+
func=ast.Attribute(value=arg, attr="transpose", ctx=ast.Load()),
|
|
103
|
+
args=[ast.Constant(value=-1), ast.Constant(value=-2)],
|
|
104
|
+
keywords=[],
|
|
105
|
+
)
|
|
106
|
+
],
|
|
107
|
+
keywords=[],
|
|
108
|
+
),
|
|
109
|
+
attr="transpose",
|
|
110
|
+
ctx=ast.Load(),
|
|
111
|
+
),
|
|
112
|
+
args=[ast.Constant(value=-1), ast.Constant(value=-2)],
|
|
113
|
+
keywords=[],
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def patch_binop(node, quantizer_names, transpose=False):
|
|
117
|
+
assert len(quantizer_names) == 2
|
|
118
|
+
if quantizer_names[0] is not None:
|
|
119
|
+
node.left = ast.Call(
|
|
120
|
+
func=ast.Attribute(
|
|
121
|
+
value=ast.Name(id="self", ctx=ast.Load()),
|
|
122
|
+
attr=quantizer_names[0],
|
|
123
|
+
ctx=ast.Load(),
|
|
124
|
+
),
|
|
125
|
+
args=[node.left],
|
|
126
|
+
keywords=[],
|
|
127
|
+
)
|
|
128
|
+
if quantizer_names[1] is not None:
|
|
129
|
+
arg = node.right
|
|
130
|
+
if transpose:
|
|
131
|
+
arg = ast.Call(
|
|
132
|
+
func=ast.Attribute(
|
|
133
|
+
ast.Name(id="torch", ctx=ast.Load()),
|
|
134
|
+
attr="transpose",
|
|
135
|
+
ctx=ast.Load(),
|
|
136
|
+
),
|
|
137
|
+
args=[arg, ast.Constant(value=-1), ast.Constant(value=-2)],
|
|
138
|
+
keywords=[],
|
|
139
|
+
)
|
|
140
|
+
quant_arg = ast.Call(
|
|
141
|
+
func=ast.Attribute(
|
|
142
|
+
value=ast.Name(id="self", ctx=ast.Load()),
|
|
143
|
+
attr=quantizer_names[1],
|
|
144
|
+
ctx=ast.Load(),
|
|
145
|
+
),
|
|
146
|
+
args=[arg],
|
|
147
|
+
keywords=[],
|
|
148
|
+
)
|
|
149
|
+
if transpose:
|
|
150
|
+
quant_arg = ast.Call(
|
|
151
|
+
func=ast.Attribute(
|
|
152
|
+
ast.Name(id="torch", ctx=ast.Load()),
|
|
153
|
+
attr="transpose",
|
|
154
|
+
ctx=ast.Load(),
|
|
155
|
+
),
|
|
156
|
+
args=[quant_arg, ast.Constant(value=-1), ast.Constant(value=-2)],
|
|
157
|
+
keywords=[],
|
|
158
|
+
)
|
|
159
|
+
node.right = quant_arg
|
|
160
|
+
|
|
161
|
+
nodes = list(ast.walk(head))
|
|
162
|
+
org_class_name = nodes[1].name # type: ignore[attr-defined]
|
|
163
|
+
new_class_name = nodes[1].name = "_Quant" + nodes[1].name # type: ignore[attr-defined]
|
|
164
|
+
|
|
165
|
+
bmm_nodes = []
|
|
166
|
+
sdpa_nodes = []
|
|
167
|
+
bin_matmul_nodes = []
|
|
168
|
+
for node in ast.walk(head):
|
|
169
|
+
if is_bmm(node):
|
|
170
|
+
bmm_nodes.append(node)
|
|
171
|
+
if is_sdpa(node):
|
|
172
|
+
sdpa_nodes.append(node)
|
|
173
|
+
if is_bin_matmul(node):
|
|
174
|
+
bin_matmul_nodes.append(node)
|
|
175
|
+
if len(bmm_nodes) != 2 and len(sdpa_nodes) != 1 and len(bin_matmul_nodes) != 2:
|
|
176
|
+
print(f"Expect 2 bmm/matmul op in the {org_class_name}, found {len(bmm_nodes)}")
|
|
177
|
+
print(f"Or expect 1 sdpa op in the {org_class_name}, found {len(sdpa_nodes)}")
|
|
178
|
+
print(f"Or expect 2 @ op in the {org_class_name}, found {len(bin_matmul_nodes)}")
|
|
179
|
+
print("Auto quantization of KV Cache fails")
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
if len(bmm_nodes) == 2:
|
|
183
|
+
# transpose k cache here to enable per-token quantization
|
|
184
|
+
# without transpose, the quantization will be per-channel, i.e.,
|
|
185
|
+
# self.k_bmm_quantizer(key_states.transpose(-1, -2))
|
|
186
|
+
# after transpose, the quantization will be per-token, i.e.,
|
|
187
|
+
# self.k_bmm_quantizer(key_states.transpose(-1, -2).transpose(-1, -2)).transpose(-1, -2)
|
|
188
|
+
# removing the additional transpose is doable but not trivial
|
|
189
|
+
patch(bmm_nodes[0], quantizer_names=(None, "v_bmm_quantizer"))
|
|
190
|
+
patch(bmm_nodes[1], quantizer_names=("q_bmm_quantizer", "k_bmm_quantizer"), transpose=True)
|
|
191
|
+
print("Patching 2 BMM/Matmul operators with quantizers")
|
|
192
|
+
if len(bin_matmul_nodes) == 2:
|
|
193
|
+
patch_binop(
|
|
194
|
+
bin_matmul_nodes[1],
|
|
195
|
+
quantizer_names=("q_bmm_quantizer", "k_bmm_quantizer"),
|
|
196
|
+
transpose=True,
|
|
197
|
+
)
|
|
198
|
+
patch_binop(bin_matmul_nodes[0], quantizer_names=(None, "v_bmm_quantizer"))
|
|
199
|
+
print("Patching 2 @ operators with quantizers")
|
|
200
|
+
|
|
201
|
+
if len(sdpa_nodes) == 1:
|
|
202
|
+
patch(
|
|
203
|
+
sdpa_nodes[0], quantizer_names=("q_bmm_quantizer", "k_bmm_quantizer", "v_bmm_quantizer")
|
|
204
|
+
)
|
|
205
|
+
print("Patching 1 scaled_dot_product_attention operator with quantizers")
|
|
206
|
+
|
|
207
|
+
head = ast.fix_missing_locations(head)
|
|
208
|
+
org_class = model_module.__dict__[org_class_name]
|
|
209
|
+
|
|
210
|
+
quant_class = _create_quantized_class_from_ast(head, org_class, new_class_name, model_module)
|
|
211
|
+
register(original_cls=org_class, quantized_cls=quant_class)
|
|
212
|
+
print(f"Successfully registered {org_class_name} for quantization")
|
|
213
|
+
return True
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _create_quantized_class_from_ast(
|
|
217
|
+
head, org_class, new_class_name, model_module, temp_file_name=None
|
|
218
|
+
):
|
|
219
|
+
"""Create a quantized class from an AST representation.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
head: The AST head containing the modified class definition
|
|
223
|
+
org_class: The original class to be quantized
|
|
224
|
+
new_class_name: Name for the new quantized class
|
|
225
|
+
model_module: The module containing the original class
|
|
226
|
+
temp_file_name: Optional file name to save the generated code
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
The newly created quantized class
|
|
230
|
+
"""
|
|
231
|
+
head = ast.fix_missing_locations(head)
|
|
232
|
+
|
|
233
|
+
# Save the generated code to a temporary file if requested
|
|
234
|
+
module_code_str = ast.unparse(head)
|
|
235
|
+
if temp_file_name is None:
|
|
236
|
+
with tempfile.NamedTemporaryFile(
|
|
237
|
+
prefix="modelopt_", suffix=".py", delete=False
|
|
238
|
+
) as temp_file:
|
|
239
|
+
temp_file.write(module_code_str.encode())
|
|
240
|
+
temp_file_name = temp_file.name
|
|
241
|
+
print(f"Definition of {new_class_name} saved to {temp_file_name}")
|
|
242
|
+
else:
|
|
243
|
+
with open(temp_file_name, "w") as f:
|
|
244
|
+
f.write(module_code_str)
|
|
245
|
+
|
|
246
|
+
# Exec with python runtime and extract the new class
|
|
247
|
+
# This could lead to side effects if the class code is not properly isolated
|
|
248
|
+
# Therefore, it is recommended to run this function only when necessary
|
|
249
|
+
# exec(
|
|
250
|
+
# new_class_code,
|
|
251
|
+
# globals=model_module.__dict__,
|
|
252
|
+
# locals=model_module.__dict__
|
|
253
|
+
# ) # bandit throws error here
|
|
254
|
+
# quant_class = model_module.__dict__[new_class_name]
|
|
255
|
+
|
|
256
|
+
# Extract the bytecode and create a new class on the fly
|
|
257
|
+
# This is more tricky but doesn't require runtime execution
|
|
258
|
+
module_code = compile(head, filename=f"{temp_file_name}", mode="exec")
|
|
259
|
+
class_code = module_code.co_consts[0]
|
|
260
|
+
assert class_code.co_name == new_class_name
|
|
261
|
+
method_codes = [const for const in class_code.co_consts if isinstance(const, types.CodeType)]
|
|
262
|
+
|
|
263
|
+
new_methods = {}
|
|
264
|
+
for method_code in method_codes:
|
|
265
|
+
method_name = method_code.co_name
|
|
266
|
+
original_method = getattr(org_class, method_name, None)
|
|
267
|
+
if not isinstance(original_method, types.FunctionType):
|
|
268
|
+
continue
|
|
269
|
+
|
|
270
|
+
# Check if the method is a decorated method
|
|
271
|
+
# The exec path can handle decorated methods, but the safety compliance disallows exec
|
|
272
|
+
closure = original_method.__closure__
|
|
273
|
+
globals = original_method.__globals__
|
|
274
|
+
if method_code.co_freevars != original_method.__code__.co_freevars:
|
|
275
|
+
warn(f"{new_class_name}.{method_name} is a decorated method. Ignoring the decorator!")
|
|
276
|
+
|
|
277
|
+
new_closure = ()
|
|
278
|
+
for freevar in method_code.co_freevars:
|
|
279
|
+
assert freevar in original_method.__closure__
|
|
280
|
+
new_closure += (
|
|
281
|
+
original_method.__closure__[ # type: ignore[index]
|
|
282
|
+
original_method.__code__.co_freevars.index(freevar)
|
|
283
|
+
],
|
|
284
|
+
)
|
|
285
|
+
closure = new_closure
|
|
286
|
+
for closure_item in original_method.__closure__: # type: ignore[union-attr]
|
|
287
|
+
item = closure_item.cell_contents
|
|
288
|
+
if isinstance(item, types.FunctionType) and item.__name__ == method_name:
|
|
289
|
+
globals = item.__globals__
|
|
290
|
+
break
|
|
291
|
+
else:
|
|
292
|
+
raise ValueError(f"Cannot find the original method {method_name} in the closure")
|
|
293
|
+
|
|
294
|
+
# Create a new class method from bytecode
|
|
295
|
+
new_method = types.FunctionType(method_code, globals=globals, closure=closure)
|
|
296
|
+
new_method.__annotations__ = original_method.__annotations__
|
|
297
|
+
new_method.__defaults__ = original_method.__defaults__
|
|
298
|
+
new_method.__kwdefaults__ = original_method.__kwdefaults__
|
|
299
|
+
new_methods[method_name] = new_method
|
|
300
|
+
|
|
301
|
+
def setup_method(self):
|
|
302
|
+
self.q_bmm_quantizer = TensorQuantizer()
|
|
303
|
+
self.k_bmm_quantizer = TensorQuantizer()
|
|
304
|
+
self.v_bmm_quantizer = TensorQuantizer()
|
|
305
|
+
|
|
306
|
+
assert "_setup" not in new_methods, "Method _setup already exists"
|
|
307
|
+
new_methods["_setup"] = setup_method
|
|
308
|
+
|
|
309
|
+
# Create a new subclass on the fly
|
|
310
|
+
quant_class = type(new_class_name, (org_class,), new_methods)
|
|
311
|
+
|
|
312
|
+
return quant_class
|