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,462 @@
|
|
|
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 megatron linear layers."""
|
|
17
|
+
|
|
18
|
+
import warnings
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
import megatron.core.parallel_state as mcore_parallel
|
|
22
|
+
import megatron.core.tensor_parallel.layers as megatron_parallel
|
|
23
|
+
import megatron.core.transformer.mlp as megatron_mlp
|
|
24
|
+
import torch
|
|
25
|
+
from megatron.core.tensor_parallel.mappings import gather_from_sequence_parallel_region
|
|
26
|
+
from megatron.core.transformer import MegatronModule
|
|
27
|
+
from megatron.core.transformer.utils import make_sharded_tensors_for_checkpoint
|
|
28
|
+
from megatron.core.utils import get_tensor_model_parallel_group_if_none
|
|
29
|
+
|
|
30
|
+
from modelopt.torch.opt.plugins.megatron import (
|
|
31
|
+
_MegatronMLP,
|
|
32
|
+
register_modelopt_extra_state_callbacks,
|
|
33
|
+
)
|
|
34
|
+
from modelopt.torch.utils.distributed import ParallelState
|
|
35
|
+
|
|
36
|
+
from ..nn import QuantModuleRegistry, TensorQuantizer
|
|
37
|
+
from ..nn.modules.quant_linear import RealQuantLinear
|
|
38
|
+
from ..qtensor import QTensorWrapper
|
|
39
|
+
from .custom import CUSTOM_MODEL_PLUGINS, _ParallelLinear
|
|
40
|
+
|
|
41
|
+
__all__ = []
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def real_quant_module_get_extra_state(self) -> dict:
|
|
45
|
+
"""Populating real_quantizer_state and q_tensor_state."""
|
|
46
|
+
extra_state = {}
|
|
47
|
+
|
|
48
|
+
if isinstance(self, RealQuantLinear) and isinstance(self.weight, QTensorWrapper):
|
|
49
|
+
real_quantizer_state = self.weight_quantizer.get_modelopt_state()
|
|
50
|
+
q_tensor_state = self.weight.get_state()
|
|
51
|
+
elif isinstance(self, RealQuantLinear):
|
|
52
|
+
real_quantizer_state = self.weight_quantizer.get_modelopt_state()
|
|
53
|
+
q_tensor_state = {}
|
|
54
|
+
else:
|
|
55
|
+
real_quantizer_state = None
|
|
56
|
+
q_tensor_state = None
|
|
57
|
+
|
|
58
|
+
extra_state["modelopt_real_quantizer_state"] = real_quantizer_state
|
|
59
|
+
extra_state["modelopt_q_tensor_state"] = q_tensor_state
|
|
60
|
+
|
|
61
|
+
return extra_state
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def quant_module_get_extra_state(self) -> dict:
|
|
65
|
+
"""Populating the extra_state when state_dict() is called.
|
|
66
|
+
|
|
67
|
+
quantizer_state, real_quantizer_state, and q_tensor_state are usually stored
|
|
68
|
+
with in the modelopt_state metadata where the keys are the full module name. The issue
|
|
69
|
+
is that NeMo-MCore model's full module name can change
|
|
70
|
+
if pipeline-parallelism (PP) and expert-parallelism (EP)
|
|
71
|
+
are changing. Alternatively, we store quantizer_state in
|
|
72
|
+
QuantModule's extra_state with QuantModule.get_extra_state()
|
|
73
|
+
which avoids the need to store the full module name.
|
|
74
|
+
"""
|
|
75
|
+
extra_state = {}
|
|
76
|
+
|
|
77
|
+
is_enabled = self.weight_quantizer.is_enabled if hasattr(self, "weight_quantizer") else False
|
|
78
|
+
|
|
79
|
+
if not is_enabled:
|
|
80
|
+
return extra_state
|
|
81
|
+
|
|
82
|
+
quantizer_state = {}
|
|
83
|
+
for name, module in self.named_modules():
|
|
84
|
+
if isinstance(module, TensorQuantizer):
|
|
85
|
+
quantizer_state[name] = module.get_modelopt_state()
|
|
86
|
+
|
|
87
|
+
extra_state["modelopt_quantizer_state"] = quantizer_state
|
|
88
|
+
|
|
89
|
+
# Handle real_quantizer_state and q_tensor_state
|
|
90
|
+
extra_state.update(real_quant_module_get_extra_state(self))
|
|
91
|
+
|
|
92
|
+
return extra_state
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def real_quant_module_set_extra_state(self, state: Any):
|
|
96
|
+
"""Restore q_tensor_state when load_state_dict() is called.
|
|
97
|
+
|
|
98
|
+
We skip restoring real_quantizer_state (if exists), since it is the same as
|
|
99
|
+
the weight_quantizer fake quantizer_state.
|
|
100
|
+
|
|
101
|
+
Finally, q_tensor_state is restored if meta device initialization is used. During
|
|
102
|
+
meta-device initialization, real_quantize is not called.
|
|
103
|
+
QTensorWrapper should replace the original weight parameter. Due to TP, we also need
|
|
104
|
+
to adjust q_tensor_data_shape and its metadata shape attribute to use the local weight shape.
|
|
105
|
+
|
|
106
|
+
When not using meta device initialization, real_quantize is called during compress mode
|
|
107
|
+
restore where the QTensor will be recomputed based on the local weights. Hence we don't
|
|
108
|
+
need to restore q_tensor_state.
|
|
109
|
+
|
|
110
|
+
Note:
|
|
111
|
+
The entire restore process can happen on meta device and be materialized later
|
|
112
|
+
with to_empty(). However, to_empty() will reassign the parameter and the
|
|
113
|
+
QTensorWrapper will be removed. We patch RealQuantLinear._apply to preserve
|
|
114
|
+
QTensorWrapper when to_empty() is applied.
|
|
115
|
+
"""
|
|
116
|
+
q_tensor_state = state.get("modelopt_q_tensor_state", None)
|
|
117
|
+
|
|
118
|
+
if q_tensor_state is not None:
|
|
119
|
+
q_tensor_metadata = q_tensor_state["metadata"]
|
|
120
|
+
q_tensor_metadata["shape"] = self.weight.shape
|
|
121
|
+
q_tensor_data_dtype = q_tensor_state["quantized_data.dtype"]
|
|
122
|
+
q_tensor_shape = self.weight.shape
|
|
123
|
+
|
|
124
|
+
# If q_tensor_data_type is uint8, then it is compressed format of 2 elements.
|
|
125
|
+
if q_tensor_data_dtype == torch.uint8:
|
|
126
|
+
q_tensor_shape = list(q_tensor_shape)
|
|
127
|
+
q_tensor_shape[-1] = q_tensor_shape[-1] // 2
|
|
128
|
+
q_tensor_shape = torch.Size(q_tensor_shape)
|
|
129
|
+
|
|
130
|
+
self._parameters["weight"] = QTensorWrapper(
|
|
131
|
+
qtensor=torch.empty(
|
|
132
|
+
q_tensor_shape, # Use the local shape directly (TP-aware)
|
|
133
|
+
dtype=q_tensor_data_dtype,
|
|
134
|
+
device=self.weight.device,
|
|
135
|
+
),
|
|
136
|
+
metadata=q_tensor_metadata,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def quant_module_set_extra_state(self, state: Any):
|
|
141
|
+
"""Restore quantizer_state when load_state_dict() is called.
|
|
142
|
+
|
|
143
|
+
With quantizer_state stored in extra_state (NeMo-MCore `torch-dist`),
|
|
144
|
+
set_extra_state() is used to perform the functionality
|
|
145
|
+
conversion.restore_quantizer_state().
|
|
146
|
+
load_state_dict() are called twice during NeMo-MCore resume.
|
|
147
|
+
The state_dict only contains the extra_state in the first time.
|
|
148
|
+
set_extra_state() is trigger by the end of the load_state_dict()
|
|
149
|
+
where QuantModule.modelopt_post_restore() will reinitialize
|
|
150
|
+
amax and scalars to the correct shape.
|
|
151
|
+
The 2nd load_state_dict() is loading all states including amax and
|
|
152
|
+
scalars. We disable QuantModule.modelopt_post_restore() to avoid
|
|
153
|
+
reinitialization since set_extra_state() is called at the end.
|
|
154
|
+
|
|
155
|
+
We first restore all fake quantizer_state. Per QuantModule can have
|
|
156
|
+
weight_quantizer, input_quantizer, and output_quantizer.
|
|
157
|
+
|
|
158
|
+
Once all quantizer_state are resumed, modelopt_post_restore() is called
|
|
159
|
+
to adjust the shape of all buffers (amax, pre_qunat_scale, _scale, ...) since
|
|
160
|
+
the local shape can be different from the shape in the state due to change
|
|
161
|
+
in tensor parallelism (TP).
|
|
162
|
+
"""
|
|
163
|
+
if state is None or not self.allow_post_restore:
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
quantizer_state = state.get("modelopt_quantizer_state", None)
|
|
167
|
+
|
|
168
|
+
if quantizer_state is not None:
|
|
169
|
+
for name, module in self.named_modules():
|
|
170
|
+
if isinstance(module, TensorQuantizer):
|
|
171
|
+
module.set_from_modelopt_state(quantizer_state[name], properties_only=False)
|
|
172
|
+
self.modelopt_post_restore()
|
|
173
|
+
|
|
174
|
+
# Handle real_quantizer_state and q_tensor_state
|
|
175
|
+
real_quant_module_set_extra_state(self, state)
|
|
176
|
+
|
|
177
|
+
self.allow_post_restore = False
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def megatron_replace_quant_module_hook(model: torch.nn.Module):
|
|
181
|
+
"""Configure Megatron-Core model quantization support.
|
|
182
|
+
|
|
183
|
+
This callback is called before the QuantModule replacement to reuse the current
|
|
184
|
+
custom callback infra. However, it is meant to target each QuantModule.
|
|
185
|
+
Since the callback is called when megatron is installed, we do a type check on
|
|
186
|
+
MegatronModule first. For each MegatronModule,
|
|
187
|
+
1. We change TransformerConfig to enable heterogenous distributed checkpointing.
|
|
188
|
+
2. We enable all sub- QuantModule to store quantizer_state as extra_state by
|
|
189
|
+
typing-matching the QuantModuleRegistry.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
def _register_extra_state_callbacks(model: torch.nn.Module):
|
|
193
|
+
for name, module in model.named_modules():
|
|
194
|
+
if type(module) in QuantModuleRegistry:
|
|
195
|
+
# This module will be replaced as a QuantModule
|
|
196
|
+
register_modelopt_extra_state_callbacks(
|
|
197
|
+
module,
|
|
198
|
+
quant_module_get_extra_state,
|
|
199
|
+
quant_module_set_extra_state,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
for name, module in model.named_modules():
|
|
203
|
+
if isinstance(module, MegatronModule):
|
|
204
|
+
if "vision_model" not in name:
|
|
205
|
+
# We only enable hetereogenous_dist_checkpoint for language model, vision model is not quantized
|
|
206
|
+
module.config.hetereogenous_dist_checkpoint = True
|
|
207
|
+
_register_extra_state_callbacks(module)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
CUSTOM_MODEL_PLUGINS.add(megatron_replace_quant_module_hook)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class _MegatronParallelLinear(_ParallelLinear):
|
|
214
|
+
_functionals_to_replace = [
|
|
215
|
+
(megatron_parallel, "linear_with_grad_accumulation_and_async_allreduce"),
|
|
216
|
+
(megatron_parallel, "linear_with_frozen_weight"),
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
def _setup(self):
|
|
220
|
+
self.parallel_state = ParallelState(
|
|
221
|
+
getattr(mcore_parallel, "get_expert_data_parallel_group", "get_data_parallel_group")(),
|
|
222
|
+
mcore_parallel.get_tensor_model_parallel_group(),
|
|
223
|
+
)
|
|
224
|
+
super()._setup()
|
|
225
|
+
|
|
226
|
+
def _process_quantizer_amax(self, k, v, quantizer_state_dict):
|
|
227
|
+
if v.ndim == 4:
|
|
228
|
+
quantizer_state_dict[k] = v.squeeze(1).squeeze(-1)
|
|
229
|
+
else:
|
|
230
|
+
quantizer_state_dict[k] = (
|
|
231
|
+
v.view(self.weight.shape[0], -1) if v.numel() > 1 else v.view(-1)
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def _process_activation_quantizer_pre_quant_scale(self, k, v, quantizer_state_dict):
|
|
235
|
+
quantizer_state_dict[k] = v
|
|
236
|
+
|
|
237
|
+
def _get_shard_axis_dict(self, state_dict):
|
|
238
|
+
raise NotImplementedError
|
|
239
|
+
|
|
240
|
+
def _parameter_to_keep_in_quantizer_state_dict(self, key):
|
|
241
|
+
"""Determine whether a parameter should be kept in the quantizer_state_dict.
|
|
242
|
+
|
|
243
|
+
Used to include additional quantization parameters (e.g., _scale for real quant)
|
|
244
|
+
beyond the default amax and pre_quant_scale tensors.
|
|
245
|
+
|
|
246
|
+
Note: When adding parameters here, update _get_shard_axis_dict accordingly for sharding.
|
|
247
|
+
"""
|
|
248
|
+
return False
|
|
249
|
+
|
|
250
|
+
def sharded_state_dict(self, prefix="", sharded_offsets=(), metadata=None):
|
|
251
|
+
# [WAR]: although we disable output_layer quantization by default but it will
|
|
252
|
+
# still be picked up by mtq.quantize since it is a ColumnParallelLinear. We need
|
|
253
|
+
# to further ensure that its sharded state_dict has no scalars or amax since
|
|
254
|
+
# 1) NeMo-MCore's vocabulary padding may change but we didn't support this feature
|
|
255
|
+
# 2) When embedding and output_layer are sharing weights, PP>1 will have
|
|
256
|
+
# output_layer.input_quantizer._amax but TP-only does not. This lead to
|
|
257
|
+
# state_dict mismatch.
|
|
258
|
+
if prefix.endswith("output_layer."):
|
|
259
|
+
# assert not any("_quantizer" in k for k in self.state_dict()), "quantized output_layer"
|
|
260
|
+
return super().sharded_state_dict(prefix, sharded_offsets)
|
|
261
|
+
|
|
262
|
+
quantizer_state_dict = {}
|
|
263
|
+
for k, v in self.state_dict(prefix="", keep_vars=True).items():
|
|
264
|
+
if "_quantizer" in k and "_amax" in k:
|
|
265
|
+
self._process_quantizer_amax(k, v, quantizer_state_dict)
|
|
266
|
+
elif k == "input_quantizer._pre_quant_scale":
|
|
267
|
+
self._process_activation_quantizer_pre_quant_scale(k, v, quantizer_state_dict)
|
|
268
|
+
elif self._parameter_to_keep_in_quantizer_state_dict(k):
|
|
269
|
+
quantizer_state_dict[k] = v
|
|
270
|
+
elif "quantizer" in k:
|
|
271
|
+
warnings.warn(
|
|
272
|
+
f"Quantizer state {k} is not supported for sharded_state_dict. "
|
|
273
|
+
"Please use regular state_dict."
|
|
274
|
+
)
|
|
275
|
+
sharded_axis_dict = self._get_shard_axis_dict(quantizer_state_dict)
|
|
276
|
+
sharded_state_dict = super().sharded_state_dict(prefix, sharded_offsets)
|
|
277
|
+
sharded_state_dict.update(
|
|
278
|
+
**make_sharded_tensors_for_checkpoint(
|
|
279
|
+
quantizer_state_dict, prefix, sharded_axis_dict, sharded_offsets
|
|
280
|
+
)
|
|
281
|
+
)
|
|
282
|
+
return sharded_state_dict
|
|
283
|
+
|
|
284
|
+
def _load_from_state_dict(self, state_dict, prefix, *args, **kwargs):
|
|
285
|
+
for k in list(state_dict.keys()):
|
|
286
|
+
if not any(qt + "_quantizer" in k for qt in ["weight", "input", "output"]):
|
|
287
|
+
continue
|
|
288
|
+
name = k.split(prefix)[-1] if prefix else k
|
|
289
|
+
state_dict[k] = state_dict[k].view_as(self.state_dict()[name])
|
|
290
|
+
super()._load_from_state_dict(state_dict, prefix, *args, **kwargs)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
@QuantModuleRegistry.register(
|
|
294
|
+
{megatron_parallel.ColumnParallelLinear: "megatron_ColumnParallelLinear"}
|
|
295
|
+
)
|
|
296
|
+
class _MegatronColumnParallelLinear(_MegatronParallelLinear):
|
|
297
|
+
_is_column_parallel = True
|
|
298
|
+
|
|
299
|
+
def _get_shard_axis_dict(self, state_dict):
|
|
300
|
+
"""Getting the sharded axis for amax and pre_quant_scale.
|
|
301
|
+
|
|
302
|
+
By default, ColumnParallelLinear shards the output dimension (dim=0). However,
|
|
303
|
+
depending the quantization algorithm, not all amax or pre_quant_scale need
|
|
304
|
+
to be sharded.
|
|
305
|
+
|
|
306
|
+
We check the quantizer.axis to decide whether an amax needs to be sharded.
|
|
307
|
+
Except for dynamic block quantization (NVFP4, axis: None) or per-tensor (FP8,
|
|
308
|
+
axis: None), the rest of algorithms all need to be sharded
|
|
309
|
+
|
|
310
|
+
Prequant scaling is applied per-input-channel; hence no sharding is required.
|
|
311
|
+
"""
|
|
312
|
+
shard_axis_dict = {}
|
|
313
|
+
for k in state_dict:
|
|
314
|
+
if "weight_quantizer." in k:
|
|
315
|
+
weight_quantizer_axis = self.get_submodule(k.rsplit(".", 1)[0]).axis
|
|
316
|
+
if weight_quantizer_axis is not None:
|
|
317
|
+
shard_axis_dict[k] = 0
|
|
318
|
+
return shard_axis_dict
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
@QuantModuleRegistry.register({megatron_parallel.RowParallelLinear: "megatron_RowParallelLinear"})
|
|
322
|
+
class _MegatronRowParallelLinear(_MegatronParallelLinear):
|
|
323
|
+
_is_row_parallel = True
|
|
324
|
+
|
|
325
|
+
def _get_shard_axis_dict(self, state_dict):
|
|
326
|
+
"""Getting the sharded axis for amax and pre_quant_scale.
|
|
327
|
+
|
|
328
|
+
By default, RowParallelLinear shards the input dimension (dim=1). However,
|
|
329
|
+
depending the quantization algorithm, not all amax or pre_quant_scale need
|
|
330
|
+
to be shard.
|
|
331
|
+
|
|
332
|
+
We check the quantizer.axis to decide whether an amax needs to be sharded.
|
|
333
|
+
Only static block quantization needs to be sharded and its axis is either (0,) or (0, 2).
|
|
334
|
+
The first case is used in AWQ the later case is used in blocked 2D quantization.
|
|
335
|
+
Dynamic block quantization (NVFP4 axis:None), per-tensor (FP8, axis: None)
|
|
336
|
+
and per-channel (INT8_SQ or FP8_PER_CHANNEL, axis: 1) do not require input sharding.
|
|
337
|
+
|
|
338
|
+
Prequant scaling is applied per-input-channel; hence it is always sharded.
|
|
339
|
+
"""
|
|
340
|
+
shard_axis_dict = {}
|
|
341
|
+
for k in state_dict:
|
|
342
|
+
if "weight_quantizer." in k:
|
|
343
|
+
weight_quantizer_axis = None
|
|
344
|
+
if isinstance(self.weight_quantizer, TensorQuantizer):
|
|
345
|
+
weight_quantizer_axis = self.weight_quantizer.axis
|
|
346
|
+
elif "weight_quantizer.0." in k:
|
|
347
|
+
weight_quantizer_axis = self.weight_quantizer[0].axis
|
|
348
|
+
elif "weight_quantizer.1." in k:
|
|
349
|
+
weight_quantizer_axis = self.weight_quantizer[1].axis
|
|
350
|
+
if isinstance(weight_quantizer_axis, tuple):
|
|
351
|
+
shard_axis_dict[k] = 1
|
|
352
|
+
if k == "input_quantizer._pre_quant_scale":
|
|
353
|
+
shard_axis_dict[k] = 0
|
|
354
|
+
return shard_axis_dict
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@QuantModuleRegistry.register({megatron_mlp.MLP: "megatron_MegatronMLP"})
|
|
358
|
+
class _QuantMegatronMLP(_MegatronMLP):
|
|
359
|
+
"""Module to support special handling of `linear_fc1` in `sharded_state_dict()` of MCore `MLP`."""
|
|
360
|
+
|
|
361
|
+
_modelopt_state_keys = [
|
|
362
|
+
r"weight_quantizer\.(\d+\.)*_amax$",
|
|
363
|
+
r"weight_quantizer\.(\d+\.)*_scale$",
|
|
364
|
+
]
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
class _RealQuantMegatronParallelLinear(RealQuantLinear):
|
|
368
|
+
allow_real_quant_gemm = True
|
|
369
|
+
_scale_tensor_shard_axis = None
|
|
370
|
+
|
|
371
|
+
def _parameter_to_keep_in_quantizer_state_dict(self, key):
|
|
372
|
+
return any(k in key for k in self.list_of_scale_tensors)
|
|
373
|
+
|
|
374
|
+
def _get_shard_axis_dict(self, state_dict):
|
|
375
|
+
shard_axis_dict = super()._get_shard_axis_dict(state_dict)
|
|
376
|
+
for k in state_dict:
|
|
377
|
+
if (
|
|
378
|
+
any(k.endswith(suffix) for suffix in self.list_of_scale_tensors)
|
|
379
|
+
and state_dict[k].dim() > 1
|
|
380
|
+
):
|
|
381
|
+
assert self._scale_tensor_shard_axis is not None, (
|
|
382
|
+
"scale_tensor_shard_axis is not set, please set it in the subclass"
|
|
383
|
+
)
|
|
384
|
+
shard_axis_dict[k] = self._scale_tensor_shard_axis
|
|
385
|
+
return shard_axis_dict
|
|
386
|
+
|
|
387
|
+
def modelopt_post_restore(self, prefix: str = ""):
|
|
388
|
+
"""Post restore to correctly configure the realquant scales.
|
|
389
|
+
|
|
390
|
+
ModelOpt restores the TensorQuantizer states such as `_amax` and `_pre_quant_scale` to their
|
|
391
|
+
shape before saving. However this is not enough for MCore/distributed frameworks since the tensor parallelism
|
|
392
|
+
could change between saving and restoring. If the tensor parallelism changes, the shape of the quantizer
|
|
393
|
+
states also changes. So we need to re-calculate the quantizer states.
|
|
394
|
+
|
|
395
|
+
Note:
|
|
396
|
+
During real quantization, weight_quantizer._fake_quant is set to False which trigger the real quant
|
|
397
|
+
forward path and lead to error. We enable the weight_quantizer fake_quant forward path while recompute
|
|
398
|
+
the correct shape.
|
|
399
|
+
"""
|
|
400
|
+
self.weight_quantizer._fake_quant = True
|
|
401
|
+
super().modelopt_post_restore(prefix=prefix)
|
|
402
|
+
self.weight_quantizer._fake_quant = False
|
|
403
|
+
|
|
404
|
+
if hasattr(self.weight_quantizer, "_scale"):
|
|
405
|
+
# Recompute all real quantization buffer shapes
|
|
406
|
+
self.weight_quantizer._real_quantize(self.weight)
|
|
407
|
+
|
|
408
|
+
def _forward_impl(self, input, *args, **kwargs):
|
|
409
|
+
"""Use real quant gemm if available.
|
|
410
|
+
|
|
411
|
+
Here the forward is patched such that real quant gemm can be called if available. Both conditions
|
|
412
|
+
below must be satisfied (static and dynamic check based on input args) to use the kernel.
|
|
413
|
+
Otherwise, we fallback.
|
|
414
|
+
|
|
415
|
+
Note:
|
|
416
|
+
RealQuantLinear.forward() is doing the same check inside and will fall back to use the super
|
|
417
|
+
class forward(). This is not desired since _forward_impl introduces much more args and kwargs
|
|
418
|
+
while the original forward only takes 1 positional argument. We must above the fallback path
|
|
419
|
+
in RealQuantLinear.forward().
|
|
420
|
+
"""
|
|
421
|
+
if self._should_run_real_quant_gemm and self.get_real_quant_gemm_impl(
|
|
422
|
+
input, *args, **kwargs
|
|
423
|
+
):
|
|
424
|
+
allreduce_dgrad = kwargs.get("allreduce_dgrad", False)
|
|
425
|
+
tp_group = kwargs.get("tp_group")
|
|
426
|
+
sequence_parallel = kwargs.get("sequence_parallel", False)
|
|
427
|
+
|
|
428
|
+
tp_group = get_tensor_model_parallel_group_if_none(tp_group)
|
|
429
|
+
|
|
430
|
+
if sequence_parallel:
|
|
431
|
+
input = gather_from_sequence_parallel_region(
|
|
432
|
+
input, tensor_parallel_output_grad=True, group=tp_group
|
|
433
|
+
)
|
|
434
|
+
else:
|
|
435
|
+
input = input
|
|
436
|
+
|
|
437
|
+
return RealQuantLinear.forward(
|
|
438
|
+
self,
|
|
439
|
+
input,
|
|
440
|
+
allreduce_dgrad=allreduce_dgrad,
|
|
441
|
+
tp_group=tp_group,
|
|
442
|
+
)
|
|
443
|
+
else:
|
|
444
|
+
return super()._forward_impl(input, *args, **kwargs)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
class _RealQuantMegatronColumnParallelLinear(
|
|
448
|
+
_RealQuantMegatronParallelLinear, _MegatronColumnParallelLinear
|
|
449
|
+
):
|
|
450
|
+
_scale_tensor_shard_axis = 0
|
|
451
|
+
|
|
452
|
+
def forward(self, input, *args, **kwargs):
|
|
453
|
+
return _MegatronColumnParallelLinear.forward(self, input, *args, **kwargs)
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
class _RealQuantMegatronRowParallelLinear(
|
|
457
|
+
_RealQuantMegatronParallelLinear, _MegatronRowParallelLinear
|
|
458
|
+
):
|
|
459
|
+
_scale_tensor_shard_axis = 1
|
|
460
|
+
|
|
461
|
+
def forward(self, input, *args, **kwargs):
|
|
462
|
+
return _MegatronRowParallelLinear.forward(self, input, *args, **kwargs)
|
|
@@ -0,0 +1,148 @@
|
|
|
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 peft LoRA linear layers."""
|
|
17
|
+
|
|
18
|
+
from contextlib import contextmanager
|
|
19
|
+
|
|
20
|
+
import torch.nn.functional as F
|
|
21
|
+
from peft.tuners.lora.layer import Linear as LoraLinear
|
|
22
|
+
from peft.tuners.lora.layer import ParamWrapper
|
|
23
|
+
|
|
24
|
+
from modelopt.torch.opt.dynamic import DynamicModule
|
|
25
|
+
from modelopt.torch.quantization.qtensor.base_qtensor import QTensorWrapper
|
|
26
|
+
|
|
27
|
+
from ..nn import QuantModule, QuantModuleRegistry, TensorQuantizer
|
|
28
|
+
from .huggingface import _transposed_quantize
|
|
29
|
+
|
|
30
|
+
__all__ = []
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@QuantModuleRegistry.register({LoraLinear: "LoraLinear"})
|
|
34
|
+
class _QuantLoraLinear(QuantModule):
|
|
35
|
+
def _setup(self):
|
|
36
|
+
self.input_quantizer = TensorQuantizer()
|
|
37
|
+
self.weight_quantizer = TensorQuantizer()
|
|
38
|
+
self.output_quantizer = TensorQuantizer()
|
|
39
|
+
|
|
40
|
+
def _is_compressed_weight(self, weight):
|
|
41
|
+
return isinstance(weight, QTensorWrapper)
|
|
42
|
+
|
|
43
|
+
def forward(self, x, *args, **kwargs):
|
|
44
|
+
adapter_names = kwargs.pop("adapter_names", None)
|
|
45
|
+
if self.disable_adapters or adapter_names is not None or self.merged:
|
|
46
|
+
return super().forward(x, *args, **kwargs)
|
|
47
|
+
|
|
48
|
+
x = self.input_quantizer(x)
|
|
49
|
+
weight = self.base_layer.weight
|
|
50
|
+
|
|
51
|
+
if not self._is_compressed_weight(weight):
|
|
52
|
+
for active_adapter in self.active_adapters:
|
|
53
|
+
if active_adapter not in self.lora_A.keys(): # noqa: SIM118
|
|
54
|
+
continue
|
|
55
|
+
lora_a = self.lora_A[active_adapter]
|
|
56
|
+
lora_b = self.lora_B[active_adapter]
|
|
57
|
+
scaling = self.scaling[active_adapter]
|
|
58
|
+
|
|
59
|
+
if not self.use_dora[active_adapter]:
|
|
60
|
+
weight = weight + ((scaling * lora_b.weight) @ lora_a.weight).to(
|
|
61
|
+
weight.device, weight.dtype
|
|
62
|
+
)
|
|
63
|
+
else:
|
|
64
|
+
raise NotImplementedError("dora not implemented")
|
|
65
|
+
weight = self.weight_quantizer(weight)
|
|
66
|
+
output = F.linear(x, weight, self.base_layer.bias)
|
|
67
|
+
else:
|
|
68
|
+
# TODO: Move this to RealQuantLoraLinear
|
|
69
|
+
# For compressed weights, compute base output and LoRA outputs separately
|
|
70
|
+
base_output = self.base_layer(x)
|
|
71
|
+
|
|
72
|
+
# Only compute LoRA outputs if there are active adapters
|
|
73
|
+
if self.active_adapters:
|
|
74
|
+
# Start with zero LoRA output
|
|
75
|
+
lora_output = None
|
|
76
|
+
|
|
77
|
+
for active_adapter in self.active_adapters:
|
|
78
|
+
if active_adapter not in self.lora_A.keys(): # noqa: SIM118
|
|
79
|
+
continue
|
|
80
|
+
lora_a = self.lora_A[active_adapter]
|
|
81
|
+
lora_b = self.lora_B[active_adapter]
|
|
82
|
+
scaling = self.scaling[active_adapter]
|
|
83
|
+
|
|
84
|
+
if not self.use_dora[active_adapter]:
|
|
85
|
+
# Compute LoRA output step by step to maintain gradient flow
|
|
86
|
+
lora_a_output = F.linear(x, lora_a.weight)
|
|
87
|
+
lora_b_output = F.linear(lora_a_output, lora_b.weight)
|
|
88
|
+
adapter_output = scaling * lora_b_output
|
|
89
|
+
|
|
90
|
+
lora_output = (
|
|
91
|
+
adapter_output if lora_output is None else lora_output + adapter_output
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
output = base_output + lora_output
|
|
95
|
+
else:
|
|
96
|
+
output = base_output
|
|
97
|
+
|
|
98
|
+
output = self.output_quantizer(output)
|
|
99
|
+
return output
|
|
100
|
+
|
|
101
|
+
def merge(self, *args, **kwargs):
|
|
102
|
+
assert not self._is_compressed_weight(self.base_layer.weight), (
|
|
103
|
+
"We dont support merging for QLoRA yet!"
|
|
104
|
+
)
|
|
105
|
+
super().merge(*args, **kwargs)
|
|
106
|
+
base_layer = self.get_base_layer()
|
|
107
|
+
base_layer.input_quantizer = self.input_quantizer
|
|
108
|
+
base_layer.weight_quantizer = self.weight_quantizer
|
|
109
|
+
base_layer.output_quantizer = self.output_quantizer
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@QuantModuleRegistry.register({ParamWrapper: "ParamWrapper"})
|
|
113
|
+
class _QuantParamWrapper(QuantModule):
|
|
114
|
+
def _setup(self):
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
@contextmanager
|
|
118
|
+
def _activate_lora(self, active_adapters: list[str]):
|
|
119
|
+
base_layer = self.get_base_layer()
|
|
120
|
+
if not isinstance(base_layer, DynamicModule) or not hasattr(
|
|
121
|
+
base_layer, self.parameter_name + "_weight_quantizer"
|
|
122
|
+
):
|
|
123
|
+
with super()._activate_lora(active_adapters):
|
|
124
|
+
yield
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
delta_weight = None
|
|
128
|
+
for active_adapter in active_adapters:
|
|
129
|
+
if active_adapter not in self.lora_A:
|
|
130
|
+
continue
|
|
131
|
+
delta_weight = (
|
|
132
|
+
self.get_delta_weight(active_adapter)
|
|
133
|
+
if delta_weight is None
|
|
134
|
+
else delta_weight + self.get_delta_weight(active_adapter)
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
quantizer = getattr(base_layer, self.parameter_name + "_weight_quantizer")
|
|
138
|
+
with base_layer.reset_dynamic_attributes():
|
|
139
|
+
base_param = getattr(base_layer, self.parameter_name)
|
|
140
|
+
quantized_val = _transposed_quantize(
|
|
141
|
+
base_param if delta_weight is None else base_param + delta_weight, quantizer
|
|
142
|
+
)
|
|
143
|
+
delattr(base_layer, self.parameter_name)
|
|
144
|
+
setattr(base_layer, self.parameter_name, quantized_val)
|
|
145
|
+
try:
|
|
146
|
+
yield
|
|
147
|
+
finally:
|
|
148
|
+
setattr(base_layer, self.parameter_name, base_param)
|
|
@@ -0,0 +1,60 @@
|
|
|
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 Transformer Engine layers."""
|
|
17
|
+
|
|
18
|
+
import torch
|
|
19
|
+
import transformer_engine as te
|
|
20
|
+
import transformer_engine.pytorch.module.linear as te_linear
|
|
21
|
+
|
|
22
|
+
from ..nn import QuantModuleRegistry
|
|
23
|
+
from .custom import _ParallelLinear
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@QuantModuleRegistry.register({te.pytorch.Linear: "te_Linear"})
|
|
27
|
+
class _QuantTELinear(_ParallelLinear):
|
|
28
|
+
_functionals_to_replace = [
|
|
29
|
+
(
|
|
30
|
+
te_linear._Linear,
|
|
31
|
+
"apply" if torch.is_grad_enabled() else "forward",
|
|
32
|
+
),
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def te_quantized_linear_fn(package, func_name, self, *args, **kwargs):
|
|
37
|
+
"""Quantized version specifically for TE with weight first, then input."""
|
|
38
|
+
if te.__version__ >= "2.0":
|
|
39
|
+
weight, inputs = args[0], args[1]
|
|
40
|
+
remaining_args = args[2:]
|
|
41
|
+
output = getattr(package, func_name)(
|
|
42
|
+
self.weight_quantizer(weight),
|
|
43
|
+
self.input_quantizer(inputs),
|
|
44
|
+
*remaining_args,
|
|
45
|
+
**kwargs,
|
|
46
|
+
)
|
|
47
|
+
else:
|
|
48
|
+
weight, weight_fp8, inputs = args[0], args[1], args[2]
|
|
49
|
+
remaining_args = args[3:]
|
|
50
|
+
output = getattr(package, func_name)(
|
|
51
|
+
self.weight_quantizer(weight),
|
|
52
|
+
weight_fp8,
|
|
53
|
+
self.input_quantizer(inputs),
|
|
54
|
+
*remaining_args,
|
|
55
|
+
**kwargs,
|
|
56
|
+
)
|
|
57
|
+
return self.output_quantizer(output)
|
|
58
|
+
|
|
59
|
+
# Override the quantized linear function
|
|
60
|
+
_quantized_linear_fn = te_quantized_linear_fn
|