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,443 @@
|
|
|
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
|
+
"""Quantization utilities."""
|
|
17
|
+
|
|
18
|
+
from collections import namedtuple
|
|
19
|
+
from collections.abc import Generator
|
|
20
|
+
from contextlib import ExitStack, contextmanager, nullcontext
|
|
21
|
+
|
|
22
|
+
import torch
|
|
23
|
+
import torch.nn as nn
|
|
24
|
+
import torch.nn.functional as F
|
|
25
|
+
from torch.distributed.fsdp import FSDPModule
|
|
26
|
+
from torch.distributed.tensor import Replicate
|
|
27
|
+
|
|
28
|
+
from modelopt.torch.utils import print_rank_0
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"EXPORT_MODE",
|
|
32
|
+
"convert_quantization_axis_to_reduce_axis",
|
|
33
|
+
"export_torch_mode",
|
|
34
|
+
"is_quantized",
|
|
35
|
+
"is_quantized_column_parallel_linear",
|
|
36
|
+
"is_quantized_linear",
|
|
37
|
+
"is_quantized_row_parallel_linear",
|
|
38
|
+
"reduce_amax",
|
|
39
|
+
"replace_function",
|
|
40
|
+
"weight_attr_names",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def reduce_block_amax(input_tensor: torch.Tensor, block_sizes: dict):
|
|
45
|
+
"""Computes the amax of the input tensor using block-based reduction for each dimension.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
input_tensor (torch.Tensor): The input tensor.
|
|
49
|
+
block_sizes (dict): A dictionary specifying the block size for each dimension.
|
|
50
|
+
Example: `{-1: 128, -2: 128}` reduces over 2D blocks.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
torch.Tensor: The reduced tensor with amax computed per block.
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
Input Shape: [256, 512]
|
|
57
|
+
Block Sizes: {-1: 128, -2: 128}
|
|
58
|
+
Process:
|
|
59
|
+
- Block along last dim → Shape [256, 4, 128]
|
|
60
|
+
- Compute block-wise amax → Shape [256, 4]
|
|
61
|
+
- Block along second-to-last dim → Shape [2, 128, 4]
|
|
62
|
+
- Compute block-wise amax → Shape [2, 4]
|
|
63
|
+
"""
|
|
64
|
+
with torch.no_grad():
|
|
65
|
+
amax = input_tensor.clone()
|
|
66
|
+
|
|
67
|
+
for dim, block_size in block_sizes.items():
|
|
68
|
+
# Convert negative dimensions to positive
|
|
69
|
+
dim = dim if dim >= 0 else len(amax.shape) + dim
|
|
70
|
+
assert amax.shape[dim] % block_size == 0, (
|
|
71
|
+
f"Tensor dimension {amax.shape[dim]}, {amax.shape[dim]} is not divisible by {block_size}"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Compute new shape for blocking
|
|
75
|
+
outer_dim = amax.shape[dim] // block_size
|
|
76
|
+
new_shape = [
|
|
77
|
+
*list(amax.shape[:dim]),
|
|
78
|
+
outer_dim,
|
|
79
|
+
block_size,
|
|
80
|
+
*list(amax.shape[dim + 1 :]),
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
# Reshape into blocks
|
|
84
|
+
amax = amax.reshape(new_shape)
|
|
85
|
+
|
|
86
|
+
# Reduce along the newly created block dimension
|
|
87
|
+
# Shift by 1 because we added an extra dimension
|
|
88
|
+
amax = reduce_amax(amax, dim + 1, keepdims=False, squeeze_scalar=False)
|
|
89
|
+
|
|
90
|
+
return amax
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def reduce_block_padding(input: torch.Tensor, block_sizes: dict, pad_value: float = 0):
|
|
94
|
+
"""Padding the input using block-based reduction for each dimension.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
input_tensor (torch.Tensor): The input tensor.
|
|
98
|
+
block_sizes (dict): A dictionary specifying the block size for padding each dimension.
|
|
99
|
+
Example: `{-1: 128, -2: 128}` pads the input over 2D blocks.
|
|
100
|
+
"""
|
|
101
|
+
with torch.no_grad():
|
|
102
|
+
padded_tensor = input
|
|
103
|
+
num_dims = padded_tensor.dim()
|
|
104
|
+
|
|
105
|
+
# Process each specified dimension independently
|
|
106
|
+
for dim, block in block_sizes.items():
|
|
107
|
+
# Convert negative dimension to positive index
|
|
108
|
+
pos_dim = dim if dim >= 0 else num_dims + dim
|
|
109
|
+
|
|
110
|
+
# Calculate how many elements are missing along that dimension
|
|
111
|
+
current_size = padded_tensor.size(pos_dim)
|
|
112
|
+
remainder = current_size % block
|
|
113
|
+
pad_amt = 0 if remainder == 0 else block - remainder
|
|
114
|
+
|
|
115
|
+
if pad_amt > 0:
|
|
116
|
+
# F.pad expects a pad tuple of length 2*num_dims.
|
|
117
|
+
pad = [0] * (2 * num_dims)
|
|
118
|
+
# For dimension pos_dim, the right padding is at index: (num_dims - 1 - pos_dim)*2 + 1.
|
|
119
|
+
pad_index = (num_dims - 1 - pos_dim) * 2
|
|
120
|
+
pad[pad_index + 1] = (
|
|
121
|
+
pad_amt # Set padding on the right side of the target dimension
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
padded_tensor = F.pad(padded_tensor, pad, value=pad_value)
|
|
125
|
+
|
|
126
|
+
return padded_tensor
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def convert_quantization_axis_to_reduce_axis(input, axis):
|
|
130
|
+
"""Convert the quantization axis to the reduce axis.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
input (torch.Tensor): The input tensor.
|
|
134
|
+
axis (int, tuple, list of None): The quantization axis. None means per-tensor quantization.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
list: The axis to reduce. None suggests all dimensions should be reduced.
|
|
138
|
+
"""
|
|
139
|
+
if axis is None:
|
|
140
|
+
return None
|
|
141
|
+
axis = axis if isinstance(axis, (list, tuple)) else [axis]
|
|
142
|
+
# Handle positive and negative axis.
|
|
143
|
+
reduce_axis = [i for i in range(input.dim()) if i not in axis and (i - input.dim()) not in axis]
|
|
144
|
+
return reduce_axis
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@torch.no_grad()
|
|
148
|
+
def reduce_amax(input, axis=None, keepdims=True, squeeze_scalar=True):
|
|
149
|
+
"""Compute the absolute maximum value of a tensor.
|
|
150
|
+
|
|
151
|
+
Reduces input_tensor along the dimensions given in axis. Unless keepdims is true,
|
|
152
|
+
the rank of the tensor is reduced by 1 for each entry in axis. If keepdims is true,
|
|
153
|
+
the reduced dimensions are retained with length 1.
|
|
154
|
+
|
|
155
|
+
.. note::
|
|
156
|
+
Gradient computation is disabled as this function is never meant learning reduces amax
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
input: Input tensor
|
|
160
|
+
axis: The dimensions to reduce. None or int or tuple of ints. If None (the default),
|
|
161
|
+
reduces all dimensions. Must be in the range [-rank(input_tensor), rank(input_tensor)).
|
|
162
|
+
keepdims: A boolean. If true, retains reduced dimensions with length 1. Default True
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
The reduced tensor.
|
|
166
|
+
"""
|
|
167
|
+
# A memory-efficient implementation that avoids copying input tensor
|
|
168
|
+
if axis is None:
|
|
169
|
+
max_val = torch.max(input)
|
|
170
|
+
min_val = torch.min(input)
|
|
171
|
+
output = torch.maximum(torch.abs(max_val), torch.abs(min_val))
|
|
172
|
+
else:
|
|
173
|
+
if isinstance(axis, int):
|
|
174
|
+
axis = (axis,)
|
|
175
|
+
max_val = torch.amax(input, dim=axis, keepdim=keepdims)
|
|
176
|
+
min_val = torch.amin(input, dim=axis, keepdim=keepdims)
|
|
177
|
+
output = torch.maximum(torch.abs(max_val), torch.abs(min_val))
|
|
178
|
+
if squeeze_scalar and output.numel() == 1:
|
|
179
|
+
output.squeeze_()
|
|
180
|
+
return output
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def weight_attr_names(module: nn.Module) -> Generator[str, None, None]:
|
|
184
|
+
"""Get the weight param attribute names in a converted module, non-recursive.
|
|
185
|
+
|
|
186
|
+
We consider the following two cases for each weight param attribute:
|
|
187
|
+
- The standard weight attribute (e.g. nn.Linear).
|
|
188
|
+
- The custom `weight_attr_name`. (e.g. Llama4TextExperts has weight attributes `gate_up_proj` and `down_proj`)
|
|
189
|
+
"""
|
|
190
|
+
from .nn import SequentialQuantizer, TensorQuantizer
|
|
191
|
+
|
|
192
|
+
# the standard weight and quantizer case
|
|
193
|
+
weight = getattr(module, "weight", None)
|
|
194
|
+
weight_quantizer = getattr(module, "weight_quantizer", None)
|
|
195
|
+
if isinstance(weight, nn.Parameter) and isinstance(
|
|
196
|
+
weight_quantizer, (TensorQuantizer, SequentialQuantizer)
|
|
197
|
+
):
|
|
198
|
+
yield "weight"
|
|
199
|
+
|
|
200
|
+
# other weight and quantizer case
|
|
201
|
+
for name, _ in module.named_parameters(recurse=False):
|
|
202
|
+
weight = getattr(module, name, None)
|
|
203
|
+
weight_quantizer = getattr(module, f"{name}_weight_quantizer", None)
|
|
204
|
+
if isinstance(weight, nn.Parameter) and isinstance(
|
|
205
|
+
weight_quantizer, (TensorQuantizer, SequentialQuantizer)
|
|
206
|
+
):
|
|
207
|
+
yield name
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
"""The whole set of quantizer related attribute names for a given weight name."""
|
|
211
|
+
QuantizerAttrNames = namedtuple(
|
|
212
|
+
"QuantizerAttrNames",
|
|
213
|
+
(
|
|
214
|
+
"weight_quantizer",
|
|
215
|
+
"input_quantizer",
|
|
216
|
+
"output_quantizer",
|
|
217
|
+
"weight_scale",
|
|
218
|
+
"weight_scale_2",
|
|
219
|
+
"input_scale",
|
|
220
|
+
"output_scale",
|
|
221
|
+
),
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def quantizer_attr_names(weight_name: str = "weight") -> QuantizerAttrNames:
|
|
226
|
+
"""Get all the quantizer related attribute names for a given weight name."""
|
|
227
|
+
prefix = f"{weight_name}_" if weight_name != "weight" else ""
|
|
228
|
+
return QuantizerAttrNames(
|
|
229
|
+
weight_quantizer=f"{prefix}weight_quantizer",
|
|
230
|
+
input_quantizer=f"{prefix}input_quantizer",
|
|
231
|
+
output_quantizer=f"{prefix}output_quantizer",
|
|
232
|
+
weight_scale=f"{prefix}weight_scale",
|
|
233
|
+
weight_scale_2=f"{prefix}weight_scale_2",
|
|
234
|
+
input_scale=f"{prefix}input_scale",
|
|
235
|
+
output_scale=f"{prefix}output_scale",
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def is_quantized(module):
|
|
240
|
+
"""Check if a module is quantized."""
|
|
241
|
+
from .nn import TensorQuantizer
|
|
242
|
+
|
|
243
|
+
return any(isinstance(_module, TensorQuantizer) for _module in module.modules())
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def is_quantized_linear(module):
|
|
247
|
+
"""Check if a module is a quantized linear module."""
|
|
248
|
+
from .nn import QuantModule, TensorQuantizer
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
isinstance(module, QuantModule)
|
|
252
|
+
and isinstance(getattr(module, "input_quantizer", None), TensorQuantizer)
|
|
253
|
+
and hasattr(module, "weight_quantizer")
|
|
254
|
+
and getattr(module, "weight", None) is not None
|
|
255
|
+
and module.weight.dim() == 2
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def is_quantized_column_parallel_linear(module):
|
|
260
|
+
"""Check if a module is a quantized column parallel linear module."""
|
|
261
|
+
return is_quantized_linear(module) and getattr(module, "_is_column_parallel", False)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def is_quantized_row_parallel_linear(module):
|
|
265
|
+
"""Check if a module is a quantized row parallel linear module."""
|
|
266
|
+
return is_quantized_linear(module) and getattr(module, "_is_row_parallel", False)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def is_quantized_parallel_linear(module):
|
|
270
|
+
"""Check if a module is a quantized parallel linear module."""
|
|
271
|
+
return is_quantized_column_parallel_linear(module) or is_quantized_row_parallel_linear(module)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@contextmanager
|
|
275
|
+
def calibrate_with_adapters(model, args):
|
|
276
|
+
"""Disables LoRA adapters during calibration, then re-enables them afterward."""
|
|
277
|
+
is_lora = getattr(args, "lora", None)
|
|
278
|
+
if is_lora:
|
|
279
|
+
print_rank_0("Disabling LoRA adapters during calibration...")
|
|
280
|
+
model.disable_adapters()
|
|
281
|
+
|
|
282
|
+
yield
|
|
283
|
+
|
|
284
|
+
if is_lora:
|
|
285
|
+
print_rank_0("Enabling LoRA adapters after calibration...")
|
|
286
|
+
model.enable_adapters()
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def disable_lora_quantizers_in_config(config, layers):
|
|
290
|
+
"""Turns off input, weight, and output quantizers for LoRA weights and LoRALinear layers in config."""
|
|
291
|
+
config["quant_cfg"]["*lora*"] = {"enable": False}
|
|
292
|
+
for layer in layers:
|
|
293
|
+
config["quant_cfg"][f"*{layer}.input_quantizer"] = {"enable": False}
|
|
294
|
+
config["quant_cfg"][f"*{layer}.weight_quantizer"] = {"enable": False}
|
|
295
|
+
config["quant_cfg"][f"*{layer}.output_quantizer"] = {"enable": False}
|
|
296
|
+
return config
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@contextmanager
|
|
300
|
+
def replace_function(package, name, new_func):
|
|
301
|
+
"""Replace a function with a new one within a context."""
|
|
302
|
+
old_func = getattr(package, name)
|
|
303
|
+
setattr(package, name, new_func)
|
|
304
|
+
setattr(package, "_" + name, old_func)
|
|
305
|
+
yield
|
|
306
|
+
setattr(package, name, old_func)
|
|
307
|
+
delattr(package, "_" + name)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@contextmanager
|
|
311
|
+
def multi_context(*cms):
|
|
312
|
+
"""Context manager enabling variable number of context managers."""
|
|
313
|
+
with ExitStack() as stack:
|
|
314
|
+
yield [stack.enter_context(cls) for cls in cms]
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
EXPORT_MODE: bool = False
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
@contextmanager
|
|
321
|
+
def export_torch_mode():
|
|
322
|
+
"""Context manager enabling the export mode."""
|
|
323
|
+
global EXPORT_MODE
|
|
324
|
+
original_value = EXPORT_MODE
|
|
325
|
+
EXPORT_MODE = True
|
|
326
|
+
try:
|
|
327
|
+
yield
|
|
328
|
+
finally:
|
|
329
|
+
EXPORT_MODE = original_value
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def is_torch_export_mode():
|
|
333
|
+
"""Check whether in the context of exporting model to torch."""
|
|
334
|
+
return EXPORT_MODE
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def is_pow2(n):
|
|
338
|
+
"""Check if a number is the power of 2."""
|
|
339
|
+
return (n != 0) and (n & (n - 1) == 0)
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def _get_fsdp2_mesh(module: nn.Module):
|
|
343
|
+
"""Get the mesh info of the model."""
|
|
344
|
+
try:
|
|
345
|
+
from torch.distributed._composable_state import _get_module_state
|
|
346
|
+
except ImportError:
|
|
347
|
+
return None
|
|
348
|
+
|
|
349
|
+
fsdp_state = _get_module_state(module)
|
|
350
|
+
if (
|
|
351
|
+
fsdp_state._fsdp_param_group
|
|
352
|
+
and fsdp_state._fsdp_param_group.post_forward_mesh_info is not None
|
|
353
|
+
):
|
|
354
|
+
return fsdp_state._fsdp_param_group.post_forward_mesh_info.mesh
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _get_enclosing_fsdp_module(module: nn.Module, root_model: nn.Module):
|
|
358
|
+
"""Get the enclosing FSDP module for a given module."""
|
|
359
|
+
if isinstance(module, FSDPModule):
|
|
360
|
+
return module
|
|
361
|
+
|
|
362
|
+
name_to_module = dict(root_model.named_modules())
|
|
363
|
+
target_module_name = next((name for name, m in name_to_module.items() if m is module), None)
|
|
364
|
+
|
|
365
|
+
if target_module_name is None:
|
|
366
|
+
raise ValueError(f"Module {module} not found in the root model {root_model}.")
|
|
367
|
+
|
|
368
|
+
current_name = target_module_name
|
|
369
|
+
while "." in current_name:
|
|
370
|
+
parent_name = ".".join(current_name.split(".")[:-1])
|
|
371
|
+
parent_module = name_to_module.get(parent_name)
|
|
372
|
+
if parent_module and isinstance(parent_module, FSDPModule):
|
|
373
|
+
return parent_module
|
|
374
|
+
current_name = parent_name
|
|
375
|
+
|
|
376
|
+
if isinstance(root_model, FSDPModule):
|
|
377
|
+
return root_model
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
@contextmanager
|
|
381
|
+
def fsdp2_weight_access_and_writeback_context(module: nn.Module, root_model: nn.Module):
|
|
382
|
+
"""Context manager for FSDP2 weight access and writeback.
|
|
383
|
+
|
|
384
|
+
Note this context will gather the weight across FSDP/HSDP shards. If TP is implemented with DTensor,
|
|
385
|
+
the weight will be a local tensor of the TP DTensor under this context.
|
|
386
|
+
"""
|
|
387
|
+
assert isinstance(root_model, torch.distributed.fsdp.FSDPModule), "We only support FSDP2"
|
|
388
|
+
|
|
389
|
+
assert not hasattr(module, "_hf_hook"), "We dont support FSDP2 with HF accelerate hooks"
|
|
390
|
+
assert isinstance(module.weight, torch.distributed.tensor.DTensor)
|
|
391
|
+
fsdp_module = _get_enclosing_fsdp_module(module, root_model)
|
|
392
|
+
assert fsdp_module is not None, "Module is not wrapped by FSDP"
|
|
393
|
+
fsdp_device_mesh = _get_fsdp2_mesh(fsdp_module)
|
|
394
|
+
fsdp_dim = fsdp_device_mesh.ndim
|
|
395
|
+
|
|
396
|
+
original_placements = module.weight.placements
|
|
397
|
+
original_device_mesh = module.weight.device_mesh
|
|
398
|
+
original_weight = module.weight
|
|
399
|
+
# Assuming the first fsdp_dim dimensions are for FSDP/HSDP, we only collect the tensor over FSDP/HSDP dimension,
|
|
400
|
+
# the TP will be handled by the TP reduction.
|
|
401
|
+
if fsdp_dim != original_device_mesh.ndim:
|
|
402
|
+
assert fsdp_device_mesh.mesh_dim_names == original_device_mesh.mesh_dim_names[:fsdp_dim], (
|
|
403
|
+
"FSDP2 mesh should be a slice of DTesnor's device mesh."
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
weight_collected = original_weight.redistribute(
|
|
407
|
+
placements=[Replicate()] * fsdp_dim + list(original_placements[fsdp_dim:]),
|
|
408
|
+
device_mesh=original_device_mesh,
|
|
409
|
+
)
|
|
410
|
+
new_weight = nn.Parameter(weight_collected.to_local())
|
|
411
|
+
module._parameters["weight"] = new_weight
|
|
412
|
+
|
|
413
|
+
yield
|
|
414
|
+
|
|
415
|
+
original_weight.to_local().data.copy_(
|
|
416
|
+
weight_collected.redistribute(
|
|
417
|
+
placements=original_placements, device_mesh=original_device_mesh
|
|
418
|
+
).to_local()
|
|
419
|
+
)
|
|
420
|
+
module._parameters["weight"] = original_weight
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
@contextmanager
|
|
424
|
+
def enable_weight_access_and_writeback(module, root_model):
|
|
425
|
+
"""Enable weight access and writeback for a module.
|
|
426
|
+
|
|
427
|
+
Useful for modules with weight not intact such as Linear layer in FSDP wrapped model or
|
|
428
|
+
HF accelerate CPU off-loaded models.
|
|
429
|
+
"""
|
|
430
|
+
if _get_enclosing_fsdp_module(module, root_model) is not None:
|
|
431
|
+
context = fsdp2_weight_access_and_writeback_context(module, root_model)
|
|
432
|
+
elif is_quantized_parallel_linear(module) and hasattr(module, "_hf_tp_plan"):
|
|
433
|
+
# HF transformers TP sharded linear layer
|
|
434
|
+
context = module.enable_weight_access_and_writeback()
|
|
435
|
+
elif hasattr(module, "_hf_hook"):
|
|
436
|
+
from .plugins.accelerate import weight_access_and_writeback_context
|
|
437
|
+
|
|
438
|
+
context = weight_access_and_writeback_context(module)
|
|
439
|
+
else:
|
|
440
|
+
context = nullcontext()
|
|
441
|
+
|
|
442
|
+
with context:
|
|
443
|
+
yield
|
|
@@ -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
|
+
"""API for sparsification algorithms."""
|
|
17
|
+
|
|
18
|
+
from . import mode, module, plugins
|
|
19
|
+
from .sparsification import *
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
"""Default configurations for sparsity modes."""
|
|
17
|
+
|
|
18
|
+
from pydantic import create_model
|
|
19
|
+
|
|
20
|
+
from modelopt.torch.opt.config import ModeloptBaseConfig, get_kwargs_for_create_model_with_rules
|
|
21
|
+
|
|
22
|
+
from .module import SpDMRegistry
|
|
23
|
+
|
|
24
|
+
SparseMagnitudeConfig = create_model(
|
|
25
|
+
"SparseMagnitudeConfig",
|
|
26
|
+
**get_kwargs_for_create_model_with_rules(
|
|
27
|
+
registry=SpDMRegistry,
|
|
28
|
+
default_rules={
|
|
29
|
+
"nn.Linear": {"*": {}, "*lm_head*": None},
|
|
30
|
+
"nn.Conv2d": {"*": {}, "*lm_head*": None},
|
|
31
|
+
},
|
|
32
|
+
doc='Configuration for the ``"sparse_magnitude"`` mode.',
|
|
33
|
+
),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
SparseGPTConfig = create_model(
|
|
38
|
+
"SparseGPTConfig",
|
|
39
|
+
**get_kwargs_for_create_model_with_rules(
|
|
40
|
+
registry=SpDMRegistry,
|
|
41
|
+
default_rules={
|
|
42
|
+
"nn.Linear": {"*": {}, "*lm_head*": None},
|
|
43
|
+
"nn.Conv2d": {"*": {}, "*lm_head*": None},
|
|
44
|
+
},
|
|
45
|
+
doc='Configuration for the ``"sparse_gpt"`` mode.',
|
|
46
|
+
),
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ExportSparseConfig(ModeloptBaseConfig):
|
|
51
|
+
"""Configuration (empty!) for the ``"export_sparse"`` mode."""
|
|
@@ -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
|
+
"""Magnitude-base sparsity inspired by NVIDIA ASP (Automatic SParsity)."""
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
import warnings
|
|
20
|
+
from itertools import permutations
|
|
21
|
+
|
|
22
|
+
import torch
|
|
23
|
+
import torch.nn as nn
|
|
24
|
+
|
|
25
|
+
from .module import SparseModule
|
|
26
|
+
from .searcher import BaseSparseSearcher
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_nmprune_info(pattern: str) -> tuple[bool, int, int]:
|
|
30
|
+
"""Gets the n:m sparsity pattern information from a given string."""
|
|
31
|
+
nm_prune = re.search(r"(\d+):(\d+) sparsity", pattern)
|
|
32
|
+
if nm_prune is not None:
|
|
33
|
+
n, m = map(int, nm_prune.groups())
|
|
34
|
+
return nm_prune is not None, n, m
|
|
35
|
+
return False, 0, 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def fill(x):
|
|
39
|
+
"""Calculates the ratio of non-zero elements in a tensor."""
|
|
40
|
+
return float(x.nonzero().size(0)) / torch.numel(x)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def reshape_1d(matrix, m):
|
|
44
|
+
"""Reshapes a given matrix into m-dimensional vectors: (h,w) -> (hw/m, m)."""
|
|
45
|
+
if matrix.shape[1] % m > 0:
|
|
46
|
+
new_cols = matrix.shape[1] + (m - matrix.shape[1] % m)
|
|
47
|
+
mat = matrix.new_empty(matrix.shape[0], new_cols).fill_(0)
|
|
48
|
+
mat[:, : matrix.shape[1]] = matrix
|
|
49
|
+
|
|
50
|
+
return mat.view(-1, m), mat.shape
|
|
51
|
+
else:
|
|
52
|
+
return matrix.view(-1, m), matrix.shape
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def compute_valid_1d_patterns(m, n):
|
|
56
|
+
"""Computes all possible m:n patterns in a 1D vector.
|
|
57
|
+
|
|
58
|
+
The function generates a tensor of size m with n ones and (m-n) zeros.
|
|
59
|
+
It then generates all permutations of this tensor, removes duplicates,
|
|
60
|
+
and returns the unique patterns as a tensor.
|
|
61
|
+
"""
|
|
62
|
+
patterns = torch.zeros(m)
|
|
63
|
+
patterns[:n] = 1
|
|
64
|
+
valid_patterns = torch.tensor(list(set(permutations(patterns.tolist()))))
|
|
65
|
+
return valid_patterns
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def mn_1d_best(matrix, m, n):
|
|
69
|
+
"""Finds the best m:n pattern in a given matrix.
|
|
70
|
+
|
|
71
|
+
The function computes all possible m:n patterns and selects the one
|
|
72
|
+
that maximizes the sum of non-masked weights in the matrix. The selected
|
|
73
|
+
pattern is then used to create a mask for the matrix.
|
|
74
|
+
"""
|
|
75
|
+
patterns = compute_valid_1d_patterns(m, n).to(matrix.device)
|
|
76
|
+
|
|
77
|
+
# Find the best m:n pattern (sum of non-masked weights).
|
|
78
|
+
mask = torch.IntTensor(matrix.shape).fill_(1).view(-1, m)
|
|
79
|
+
mat, _ = reshape_1d(matrix, m)
|
|
80
|
+
pmax = torch.argmax(torch.matmul(mat.abs(), patterns.t()), dim=1)
|
|
81
|
+
mask[:] = patterns[pmax[:]]
|
|
82
|
+
mask = mask.view(matrix.shape)
|
|
83
|
+
return mask
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def m4n2_1d(mat):
|
|
87
|
+
"""Finds the best 2:4 pattern in a given matrix."""
|
|
88
|
+
return mn_1d_best(mat, 4, 2)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def create_asp_mask(tensor: nn.Parameter, pattern: str) -> torch.BoolTensor:
|
|
92
|
+
"""Creates a mask for a given tensor based on a specified sparse pattern.
|
|
93
|
+
|
|
94
|
+
The function reshapes the tensor and applies the specified pattern to create a sparse mask.
|
|
95
|
+
The default pattern is m4n2_1d, which finds the best 2:4 sparsity pattern in the tensor.
|
|
96
|
+
"""
|
|
97
|
+
pattern_method_lut = {BaseSparseSearcher._pattern_2_4: m4n2_1d}
|
|
98
|
+
if pattern not in pattern_method_lut:
|
|
99
|
+
raise NotImplementedError(f"Unsupported pattern {pattern} for ASP sparsity")
|
|
100
|
+
func = pattern_method_lut[pattern]
|
|
101
|
+
|
|
102
|
+
shape = tensor.shape
|
|
103
|
+
tensor.type()
|
|
104
|
+
t = tensor.float().contiguous()
|
|
105
|
+
|
|
106
|
+
# 1d-tensor
|
|
107
|
+
if len(shape) == 1:
|
|
108
|
+
t = t.view(1, shape[0])
|
|
109
|
+
mask = func(t)
|
|
110
|
+
# 2d-tensor (K, C)
|
|
111
|
+
elif len(shape) == 2:
|
|
112
|
+
# linear
|
|
113
|
+
t = t.view(shape[0], shape[1])
|
|
114
|
+
mask = func(t)
|
|
115
|
+
# 3d-tensor (K, C, R)
|
|
116
|
+
elif len(shape) == 3:
|
|
117
|
+
# 1d convs
|
|
118
|
+
t = t.permute(0, 2, 1).contiguous().view(shape[0] * shape[2], shape[1])
|
|
119
|
+
mask = func(t)
|
|
120
|
+
mask = mask.view(shape[0], shape[2], shape[1]).permute(0, 2, 1).contiguous()
|
|
121
|
+
# 4d-tensor (K, C, R, S)
|
|
122
|
+
elif len(shape) == 4:
|
|
123
|
+
# 2d convs
|
|
124
|
+
t = t.permute(2, 3, 0, 1).contiguous().view(shape[2] * shape[3] * shape[0], shape[1])
|
|
125
|
+
mask = func(t)
|
|
126
|
+
mask = mask.view(shape[2], shape[3], shape[0], shape[1]).permute(2, 3, 0, 1).contiguous()
|
|
127
|
+
|
|
128
|
+
return mask.view(shape).to(dtype=torch.bool)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class MagnitudeSearcher(BaseSparseSearcher):
|
|
132
|
+
"""Searcher for magnitude-based sparsity."""
|
|
133
|
+
|
|
134
|
+
def _check_weight_size(self, weight: torch.nn.Parameter, mod_name: str) -> bool:
|
|
135
|
+
"""Check if the weight size is supported."""
|
|
136
|
+
# rules from ASP
|
|
137
|
+
if weight.size(0) % 8 != 0 or weight.size(1) % 16 != 0:
|
|
138
|
+
warnings.warn(
|
|
139
|
+
f"Skipping sparsifying {mod_name} of size={weight.size()!s} and"
|
|
140
|
+
f" type={weight.dtype!s} for sparsity"
|
|
141
|
+
)
|
|
142
|
+
return False
|
|
143
|
+
|
|
144
|
+
return True
|
|
145
|
+
|
|
146
|
+
def _compute_mask(self, module: SparseModule) -> torch.BoolTensor:
|
|
147
|
+
"""Compute the mask (and weight update) for the given module."""
|
|
148
|
+
return create_asp_mask(module.weight, self.config["pattern"])
|