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,816 @@
|
|
|
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
|
+
"""Basic tensor quantization functions."""
|
|
17
|
+
|
|
18
|
+
import warnings
|
|
19
|
+
|
|
20
|
+
import torch
|
|
21
|
+
from torch.autograd import Function
|
|
22
|
+
from torch.onnx import symbolic_helper
|
|
23
|
+
|
|
24
|
+
import modelopt.torch.quantization.triton as triton_kernel
|
|
25
|
+
|
|
26
|
+
from .config import QuantizerAttributeConfig
|
|
27
|
+
from .extensions import get_cuda_ext, get_cuda_ext_fp8, get_cuda_ext_mx
|
|
28
|
+
|
|
29
|
+
mx_format_map = {
|
|
30
|
+
(4, 3): "E4M3",
|
|
31
|
+
(5, 2): "E5M2",
|
|
32
|
+
(3, 2): "E3M2",
|
|
33
|
+
(2, 3): "E2M3",
|
|
34
|
+
8: "INT8",
|
|
35
|
+
(8, 0): "E8M0",
|
|
36
|
+
(2, 1): "E2M1",
|
|
37
|
+
(1, 2): "E1M2",
|
|
38
|
+
(0, 3): "E0M3",
|
|
39
|
+
(3, 0): "E3M0",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
DISABLE_TRITON_KERNEL = False
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def scaled_e4m3_impl(
|
|
46
|
+
inputs: torch.Tensor, # TODO: check support for multiple inputs
|
|
47
|
+
amax: torch.Tensor,
|
|
48
|
+
disable_fused_kernel=True,
|
|
49
|
+
) -> torch.Tensor:
|
|
50
|
+
"""Implementation of fake quantizing input to FP8.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
inputs: Torch tensor.
|
|
54
|
+
amax: Absolute max range of the input tensor.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Input tensors faked quantized to FP8.
|
|
58
|
+
"""
|
|
59
|
+
cuda_ext_fp8 = get_cuda_ext_fp8(raise_if_failed=True)
|
|
60
|
+
|
|
61
|
+
def is_fusable():
|
|
62
|
+
# ignore no scaling and shape([]) cases
|
|
63
|
+
if amax is None or len(amax.shape) == 0:
|
|
64
|
+
return False
|
|
65
|
+
else:
|
|
66
|
+
# can't have amax.shape = [1, 1, 4, 1] and the like
|
|
67
|
+
amax_last_dim_only = amax.numel() == amax.shape[-1]
|
|
68
|
+
# must be cuda
|
|
69
|
+
all_cuda = inputs.is_cuda and amax.is_cuda
|
|
70
|
+
|
|
71
|
+
# also check explicit disable.
|
|
72
|
+
return amax_last_dim_only and all_cuda and (not disable_fused_kernel)
|
|
73
|
+
|
|
74
|
+
with torch.cuda.device(
|
|
75
|
+
None if inputs.device.index == torch.cuda.current_device() else inputs.device.index
|
|
76
|
+
):
|
|
77
|
+
# differentiate between fused & unfused cases
|
|
78
|
+
if is_fusable():
|
|
79
|
+
zero_threshold = 1.0 / (1 << 24)
|
|
80
|
+
outputs = cuda_ext_fp8.fused_fake_e4m3fy(inputs, amax.float(), zero_threshold)
|
|
81
|
+
else:
|
|
82
|
+
zero_mask = inputs.abs() < 1.0 / (1 << 24)
|
|
83
|
+
|
|
84
|
+
if amax is None:
|
|
85
|
+
outputs = cuda_ext_fp8.fake_e4m3fy(inputs)
|
|
86
|
+
else:
|
|
87
|
+
scale = 448.0 / amax
|
|
88
|
+
outputs = cuda_ext_fp8.fake_e4m3fy(inputs * scale) / scale
|
|
89
|
+
|
|
90
|
+
# Zero out values that are tiny.
|
|
91
|
+
# Tiny values could lead to tiny amax and then large scale which cause overflow/saturation
|
|
92
|
+
# and won't go back to normal value after dividing by scale. The right behavior is to mark them
|
|
93
|
+
# as zero which also get rid of inf/nan
|
|
94
|
+
outputs[zero_mask] = 0.0
|
|
95
|
+
|
|
96
|
+
return outputs
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def fake_quant_impl(
|
|
100
|
+
inputs: torch.Tensor,
|
|
101
|
+
amax: torch.Tensor,
|
|
102
|
+
num_bits=8,
|
|
103
|
+
unsigned=False,
|
|
104
|
+
narrow_range=True,
|
|
105
|
+
):
|
|
106
|
+
"""Implementation of fake quantizing input according to number of bits."""
|
|
107
|
+
cuda_ext = get_cuda_ext()
|
|
108
|
+
|
|
109
|
+
with torch.cuda.device(
|
|
110
|
+
None if inputs.device.index == torch.cuda.current_device() else inputs.device.index
|
|
111
|
+
):
|
|
112
|
+
if amax.numel() == 1:
|
|
113
|
+
outputs = cuda_ext.fake_tensor_quant(inputs, amax, num_bits, unsigned, narrow_range)
|
|
114
|
+
else:
|
|
115
|
+
axis = amax.shape.index(amax.numel())
|
|
116
|
+
outputs = cuda_ext.fake_tensor_quant_with_axis(
|
|
117
|
+
inputs, amax.squeeze(), axis, num_bits, unsigned, narrow_range
|
|
118
|
+
)
|
|
119
|
+
return outputs
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _quantize_impl(
|
|
123
|
+
inputs: torch.Tensor,
|
|
124
|
+
amax: torch.Tensor,
|
|
125
|
+
num_bits: int = 8,
|
|
126
|
+
exponent_bits: int = 0,
|
|
127
|
+
unsigned: bool = False,
|
|
128
|
+
narrow_range: bool = True,
|
|
129
|
+
):
|
|
130
|
+
if num_bits == 8 and exponent_bits == 4:
|
|
131
|
+
return scaled_e4m3_impl(inputs=inputs, amax=amax)
|
|
132
|
+
elif isinstance(num_bits, int):
|
|
133
|
+
return fake_quant_impl(
|
|
134
|
+
inputs=inputs,
|
|
135
|
+
amax=amax,
|
|
136
|
+
num_bits=num_bits,
|
|
137
|
+
unsigned=unsigned,
|
|
138
|
+
narrow_range=narrow_range,
|
|
139
|
+
)
|
|
140
|
+
else:
|
|
141
|
+
raise ValueError(
|
|
142
|
+
f"Invalid combination of (num_bits, exponent_bits): ({num_bits}, {exponent_bits})."
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _quantize_impl_abstract(
|
|
147
|
+
input: torch.Tensor,
|
|
148
|
+
amax: torch.Tensor,
|
|
149
|
+
num_bits: int = 8,
|
|
150
|
+
exponent_bits: int = 0,
|
|
151
|
+
unsigned: bool = False,
|
|
152
|
+
narrow_range: bool = True,
|
|
153
|
+
) -> torch.Tensor:
|
|
154
|
+
"""Register an abstract implementation for quantizing tensor.
|
|
155
|
+
|
|
156
|
+
This abstract function returns an empty tensor with the same shape and dtype.
|
|
157
|
+
"""
|
|
158
|
+
output = torch.empty_like(input)
|
|
159
|
+
|
|
160
|
+
return output
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# Argument types: Tensor, int, NoneType, int, int, int, int,
|
|
164
|
+
def _dynamic_block_quantize_impl(
|
|
165
|
+
inputs: torch.Tensor,
|
|
166
|
+
block_size: int,
|
|
167
|
+
amax: torch.Tensor,
|
|
168
|
+
num_bits: int,
|
|
169
|
+
exponent_bits: int,
|
|
170
|
+
scale_num_bits: int,
|
|
171
|
+
scale_exponent_bits: int,
|
|
172
|
+
):
|
|
173
|
+
scale_bits = (scale_exponent_bits, scale_num_bits - scale_exponent_bits - 1)
|
|
174
|
+
if exponent_bits != 0:
|
|
175
|
+
num_bits = (exponent_bits, num_bits - exponent_bits - 1)
|
|
176
|
+
if num_bits in mx_format_map:
|
|
177
|
+
assert scale_bits in mx_format_map, f"Scale bits should be in {mx_format_map.keys()}"
|
|
178
|
+
if scale_bits != (8, 0):
|
|
179
|
+
assert amax.is_cuda, "amax must be a CUDA tensor for dynamic block quantization."
|
|
180
|
+
if amax.numel() != 1:
|
|
181
|
+
amax = amax.amax()
|
|
182
|
+
with torch.cuda.device(
|
|
183
|
+
None if inputs.device.index == torch.cuda.current_device() else inputs.device.index
|
|
184
|
+
):
|
|
185
|
+
if (
|
|
186
|
+
num_bits == (2, 1) # type: ignore[comparison-overlap]
|
|
187
|
+
and scale_bits == (4, 3)
|
|
188
|
+
and triton_kernel.IS_AVAILABLE
|
|
189
|
+
and not DISABLE_TRITON_KERNEL
|
|
190
|
+
and amax is not None
|
|
191
|
+
):
|
|
192
|
+
return triton_kernel.fp4_fake_quant_block(inputs, amax)
|
|
193
|
+
cuda_ext_mx = get_cuda_ext_mx(raise_if_failed=True)
|
|
194
|
+
return cuda_ext_mx.fused_amax_convert(
|
|
195
|
+
inputs,
|
|
196
|
+
block_size,
|
|
197
|
+
getattr(cuda_ext_mx.Types, mx_format_map[num_bits]),
|
|
198
|
+
getattr(cuda_ext_mx.Types, mx_format_map[scale_bits]),
|
|
199
|
+
amax,
|
|
200
|
+
)
|
|
201
|
+
else:
|
|
202
|
+
raise NotImplementedError(
|
|
203
|
+
f"Unsupported num_bits: {num_bits}, scale_bits: {scale_bits} for dynamic block quantization."
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _dynamic_block_quantize_impl_abstract(
|
|
208
|
+
inputs: torch.Tensor,
|
|
209
|
+
block_size: int,
|
|
210
|
+
amax: torch.Tensor,
|
|
211
|
+
num_bits: int,
|
|
212
|
+
exponent_bits: int,
|
|
213
|
+
scale_num_bits: int,
|
|
214
|
+
scale_exponent_bits: int,
|
|
215
|
+
):
|
|
216
|
+
"""Register an abstract implementation for dynamic block quantization.
|
|
217
|
+
|
|
218
|
+
This abstract function returns an empty tensor with the same shape and dtype.
|
|
219
|
+
"""
|
|
220
|
+
output = torch.empty_like(inputs)
|
|
221
|
+
|
|
222
|
+
return output
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
quantize_op = _quantize_impl
|
|
226
|
+
dynamic_block_quantize_op = _dynamic_block_quantize_impl
|
|
227
|
+
# Define custom operators via torch.library if supported:
|
|
228
|
+
# 1. quantize_op: Applies static quantization to the input tensor using a specified amax.
|
|
229
|
+
# 2. dynamic_block_quantize_op: Performs blockwise double quantization with dynamically
|
|
230
|
+
# determined scales, governed by the given quantization format (scale_num_bits, scale_exponent_bits).
|
|
231
|
+
try:
|
|
232
|
+
torch.library.define(
|
|
233
|
+
"tensorrt::quantize_op",
|
|
234
|
+
"(Tensor input, Tensor amax, int num_bits, int exponent_bits, "
|
|
235
|
+
"bool unsigned, bool narrow_range) -> Tensor",
|
|
236
|
+
)
|
|
237
|
+
torch.library.define(
|
|
238
|
+
"tensorrt::dynamic_block_quantize_op",
|
|
239
|
+
"(Tensor input, int block_size, Tensor amax, int num_bits, int exponent_bits, "
|
|
240
|
+
"int scale_num_bits, int scale_exponent_bits) -> Tensor",
|
|
241
|
+
)
|
|
242
|
+
torch.library.define(
|
|
243
|
+
"tensorrt::dynamic_block_quantize_op.overload",
|
|
244
|
+
"(Tensor input, int block_size, None amax, int num_bits, int exponent_bits, "
|
|
245
|
+
"int scale_num_bits, int scale_exponent_bits) -> Tensor",
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# Implement the None amax case
|
|
249
|
+
def _dynamic_block_quantize_impl_none_amax(
|
|
250
|
+
inputs: torch.Tensor,
|
|
251
|
+
block_size: int,
|
|
252
|
+
amax: None,
|
|
253
|
+
num_bits: int,
|
|
254
|
+
exponent_bits: int,
|
|
255
|
+
scale_num_bits: int,
|
|
256
|
+
scale_exponent_bits: int,
|
|
257
|
+
):
|
|
258
|
+
return torch.empty_like(inputs)
|
|
259
|
+
|
|
260
|
+
# Register the implementation for both CPU and CUDA
|
|
261
|
+
torch.library.impl("tensorrt::quantize_op", ["cpu", "cuda"])(_quantize_impl)
|
|
262
|
+
torch.library.impl("tensorrt::dynamic_block_quantize_op", ["cpu", "cuda"])(
|
|
263
|
+
_dynamic_block_quantize_impl
|
|
264
|
+
)
|
|
265
|
+
torch.library.impl("tensorrt::dynamic_block_quantize_op.overload", ["cpu", "cuda"])(
|
|
266
|
+
_dynamic_block_quantize_impl_none_amax
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Register the fake implementation
|
|
270
|
+
torch.library.register_fake("tensorrt::quantize_op")(_quantize_impl_abstract)
|
|
271
|
+
torch.library.register_fake("tensorrt::dynamic_block_quantize_op")(
|
|
272
|
+
_dynamic_block_quantize_impl_abstract
|
|
273
|
+
)
|
|
274
|
+
torch.library.register_fake("tensorrt::dynamic_block_quantize_op.overload")(
|
|
275
|
+
_dynamic_block_quantize_impl_abstract
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
quantize_op = torch.ops.tensorrt.quantize_op
|
|
279
|
+
dynamic_block_quantize_op = torch.ops.tensorrt.dynamic_block_quantize_op
|
|
280
|
+
except (AttributeError, RuntimeError) as e:
|
|
281
|
+
# torch.library is an experimental feature, the function signatures may change overtime.
|
|
282
|
+
warnings.warn(
|
|
283
|
+
"Unable to register operators with torch.library. Exporting quantized models with"
|
|
284
|
+
f" torch.export will not be supported.\n{e}"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
# Predefined descriptors
|
|
288
|
+
QUANT_DESC_8BIT_PER_TENSOR = QuantizerAttributeConfig(num_bits=8)
|
|
289
|
+
QUANT_DESC_UNSIGNED_8BIT_PER_TENSOR = QuantizerAttributeConfig(num_bits=8, unsigned=True)
|
|
290
|
+
QUANT_DESC_8BIT_CONV1D_WEIGHT_PER_CHANNEL = QuantizerAttributeConfig(num_bits=8, axis=(0))
|
|
291
|
+
QUANT_DESC_8BIT_CONV2D_WEIGHT_PER_CHANNEL = QuantizerAttributeConfig(num_bits=8, axis=(0))
|
|
292
|
+
QUANT_DESC_8BIT_CONV3D_WEIGHT_PER_CHANNEL = QuantizerAttributeConfig(num_bits=8, axis=(0))
|
|
293
|
+
QUANT_DESC_8BIT_LINEAR_WEIGHT_PER_ROW = QuantizerAttributeConfig(num_bits=8, axis=(0))
|
|
294
|
+
QUANT_DESC_8BIT_CONVTRANSPOSE1D_WEIGHT_PER_CHANNEL = QuantizerAttributeConfig(num_bits=8, axis=(0))
|
|
295
|
+
QUANT_DESC_8BIT_CONVTRANSPOSE2D_WEIGHT_PER_CHANNEL = QuantizerAttributeConfig(num_bits=8, axis=(0))
|
|
296
|
+
QUANT_DESC_8BIT_CONVTRANSPOSE3D_WEIGHT_PER_CHANNEL = QuantizerAttributeConfig(num_bits=8, axis=(0))
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@torch.jit.script
|
|
300
|
+
def _fake_tensor_quant_backward(inputs, amax: torch.Tensor | None, grad_outputs):
|
|
301
|
+
# Skip clip for MX formats
|
|
302
|
+
if amax is None:
|
|
303
|
+
return grad_outputs
|
|
304
|
+
|
|
305
|
+
zero = grad_outputs.new_zeros(1)
|
|
306
|
+
grad_inputs = torch.where(inputs.abs() <= amax, grad_outputs, zero)
|
|
307
|
+
return grad_inputs
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def _fake_quant_backward_function(ctx, grad_outputs, num_args=1):
|
|
311
|
+
saved_tensors = ctx.saved_tensors
|
|
312
|
+
if len(saved_tensors) == 0:
|
|
313
|
+
return (grad_outputs,) + (None,) * (num_args - 1)
|
|
314
|
+
inputs, amax = saved_tensors
|
|
315
|
+
return (_fake_tensor_quant_backward(inputs, amax, grad_outputs),) + (None,) * (num_args - 1)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def _save_for_backward_if_needed(ctx, pass_through_bwd, inputs, amax):
|
|
319
|
+
if not pass_through_bwd and amax is not None:
|
|
320
|
+
amax = (
|
|
321
|
+
amax
|
|
322
|
+
if isinstance(amax, torch.Tensor)
|
|
323
|
+
else torch.tensor(amax, device=inputs.device, dtype=inputs.dtype)
|
|
324
|
+
)
|
|
325
|
+
ctx.save_for_backward(inputs, amax)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class FakeTensorQuantFunction(Function):
|
|
329
|
+
"""Fake version of TensorQuantFunction use CUDA extension."""
|
|
330
|
+
|
|
331
|
+
@staticmethod
|
|
332
|
+
@symbolic_helper.parse_args("v", "t", "t", "i", "b", "b", "s", "b", "i", "i")
|
|
333
|
+
def symbolic(
|
|
334
|
+
g,
|
|
335
|
+
inputs,
|
|
336
|
+
amax,
|
|
337
|
+
bias=None,
|
|
338
|
+
num_bits=8,
|
|
339
|
+
unsigned=False,
|
|
340
|
+
narrow_range=True,
|
|
341
|
+
trt_high_precision_dtype=None,
|
|
342
|
+
pass_through_bwd=False,
|
|
343
|
+
block_size=None,
|
|
344
|
+
axis=None,
|
|
345
|
+
):
|
|
346
|
+
"""ONNX symbolic function."""
|
|
347
|
+
from .export_onnx import export_int4, export_int8
|
|
348
|
+
|
|
349
|
+
if num_bits == 4:
|
|
350
|
+
return export_int4(
|
|
351
|
+
g, inputs, amax, num_bits, trt_high_precision_dtype, block_size, axis
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
return export_int8(
|
|
355
|
+
g, inputs, amax, num_bits, unsigned, narrow_range, trt_high_precision_dtype
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
@staticmethod
|
|
359
|
+
def forward(
|
|
360
|
+
ctx,
|
|
361
|
+
inputs,
|
|
362
|
+
amax,
|
|
363
|
+
bias=None,
|
|
364
|
+
num_bits=8,
|
|
365
|
+
unsigned=False,
|
|
366
|
+
narrow_range=True,
|
|
367
|
+
trt_high_precision_dtype=None,
|
|
368
|
+
pass_through_bwd=False,
|
|
369
|
+
block_size=None,
|
|
370
|
+
axis=None,
|
|
371
|
+
):
|
|
372
|
+
"""Forward method."""
|
|
373
|
+
if bias is not None:
|
|
374
|
+
inputs = inputs - bias
|
|
375
|
+
|
|
376
|
+
_save_for_backward_if_needed(ctx, pass_through_bwd, inputs, amax)
|
|
377
|
+
|
|
378
|
+
def legacy_quant_func():
|
|
379
|
+
# The LegacyFakeTensorQuantFunction support cpu and amax with any shape that can be broadcasted to inputs.
|
|
380
|
+
outputs, scale = _tensor_quant(inputs, amax, num_bits, unsigned, narrow_range)
|
|
381
|
+
return outputs / scale.to(inputs.dtype)
|
|
382
|
+
|
|
383
|
+
if not inputs.is_cuda:
|
|
384
|
+
outputs = legacy_quant_func()
|
|
385
|
+
else:
|
|
386
|
+
try:
|
|
387
|
+
outputs = quantize_op(
|
|
388
|
+
inputs,
|
|
389
|
+
amax,
|
|
390
|
+
num_bits=num_bits,
|
|
391
|
+
exponent_bits=0,
|
|
392
|
+
unsigned=unsigned,
|
|
393
|
+
narrow_range=narrow_range,
|
|
394
|
+
)
|
|
395
|
+
except (AttributeError, ValueError):
|
|
396
|
+
# AttributeError: cuda_ext is not imported, possibly due to CPU only installation
|
|
397
|
+
# ValueError: cuda_ext is installed, but trying to perform multidimensional quantization (amax dim > 1)
|
|
398
|
+
outputs = legacy_quant_func()
|
|
399
|
+
|
|
400
|
+
if bias is not None:
|
|
401
|
+
outputs = outputs + bias
|
|
402
|
+
|
|
403
|
+
return outputs
|
|
404
|
+
|
|
405
|
+
@staticmethod
|
|
406
|
+
def backward(ctx, grad_outputs):
|
|
407
|
+
"""Implements straight through estimation with clipping."""
|
|
408
|
+
return _fake_quant_backward_function(ctx, grad_outputs, num_args=10)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
class ScaledE4M3Function(Function):
|
|
412
|
+
"""E4M3fy input with scale."""
|
|
413
|
+
|
|
414
|
+
@staticmethod
|
|
415
|
+
@symbolic_helper.parse_args("v", "t", "t", "i", "i", "s", "b")
|
|
416
|
+
def symbolic(
|
|
417
|
+
g,
|
|
418
|
+
inputs,
|
|
419
|
+
amax=None,
|
|
420
|
+
bias=None,
|
|
421
|
+
E=4, # noqa: N803
|
|
422
|
+
M=3, # noqa: N803
|
|
423
|
+
trt_high_precision_dtype=None,
|
|
424
|
+
pass_through_bwd=False,
|
|
425
|
+
):
|
|
426
|
+
"""ONNX symbolic function."""
|
|
427
|
+
from .export_onnx import export_fp8
|
|
428
|
+
|
|
429
|
+
return export_fp8(g, inputs, amax, trt_high_precision_dtype)
|
|
430
|
+
|
|
431
|
+
@staticmethod
|
|
432
|
+
# Default values could cause errors from TorchDynamo during torch.export
|
|
433
|
+
def forward(
|
|
434
|
+
ctx,
|
|
435
|
+
inputs,
|
|
436
|
+
amax,
|
|
437
|
+
bias,
|
|
438
|
+
E, # noqa: N803
|
|
439
|
+
M, # noqa: N803
|
|
440
|
+
trt_high_precision_dtype=None,
|
|
441
|
+
pass_through_bwd=False,
|
|
442
|
+
):
|
|
443
|
+
"""Forward method."""
|
|
444
|
+
if E != 4 or M != 3:
|
|
445
|
+
raise NotImplementedError("Only support E=4 & M=3 for now.")
|
|
446
|
+
|
|
447
|
+
if bias is not None:
|
|
448
|
+
inputs = inputs - bias
|
|
449
|
+
|
|
450
|
+
_save_for_backward_if_needed(ctx, pass_through_bwd, inputs, amax)
|
|
451
|
+
|
|
452
|
+
outputs = quantize_op(
|
|
453
|
+
inputs,
|
|
454
|
+
amax,
|
|
455
|
+
num_bits=8,
|
|
456
|
+
exponent_bits=4,
|
|
457
|
+
unsigned=False,
|
|
458
|
+
narrow_range=False,
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
if bias is not None:
|
|
462
|
+
outputs = outputs + bias
|
|
463
|
+
|
|
464
|
+
return outputs
|
|
465
|
+
|
|
466
|
+
@staticmethod
|
|
467
|
+
def backward(ctx, grad_outputs):
|
|
468
|
+
"""Implements straight through estimation with clipping."""
|
|
469
|
+
return _fake_quant_backward_function(ctx, grad_outputs, num_args=7)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def _dynamic_block_quantize_forward(
|
|
473
|
+
ctx,
|
|
474
|
+
inputs,
|
|
475
|
+
block_size,
|
|
476
|
+
amax,
|
|
477
|
+
num_bits,
|
|
478
|
+
scale_bits,
|
|
479
|
+
trt_high_precision_dtype=None,
|
|
480
|
+
onnx_quantizer_type="dynamic",
|
|
481
|
+
pass_through_bwd=True,
|
|
482
|
+
):
|
|
483
|
+
"""Forward method."""
|
|
484
|
+
if isinstance(num_bits, int):
|
|
485
|
+
# special case for INT dynamic block quantization, e.g. MXINT8
|
|
486
|
+
exponent_bits = 0
|
|
487
|
+
else:
|
|
488
|
+
assert isinstance(num_bits, tuple) and len(num_bits) == 2
|
|
489
|
+
exponent_bits = num_bits[0]
|
|
490
|
+
num_bits = num_bits[0] + num_bits[1] + 1
|
|
491
|
+
assert isinstance(scale_bits, tuple) and len(scale_bits) == 2
|
|
492
|
+
scale_exponent_bits = scale_bits[0]
|
|
493
|
+
scale_num_bits = scale_bits[0] + scale_bits[1] + 1
|
|
494
|
+
outputs = dynamic_block_quantize_op(
|
|
495
|
+
inputs,
|
|
496
|
+
block_size,
|
|
497
|
+
amax,
|
|
498
|
+
num_bits,
|
|
499
|
+
exponent_bits,
|
|
500
|
+
scale_num_bits,
|
|
501
|
+
scale_exponent_bits,
|
|
502
|
+
)
|
|
503
|
+
return outputs
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
class DynamicBlockQuantizationFunction(Function):
|
|
507
|
+
"""Dynamic block quantization functional."""
|
|
508
|
+
|
|
509
|
+
@staticmethod
|
|
510
|
+
@symbolic_helper.parse_args("v", "i", "v", "t", "is", "is", "s", "s", "b")
|
|
511
|
+
def symbolic(
|
|
512
|
+
g,
|
|
513
|
+
inputs,
|
|
514
|
+
block_size,
|
|
515
|
+
amax,
|
|
516
|
+
bias,
|
|
517
|
+
num_bits,
|
|
518
|
+
scale_bits,
|
|
519
|
+
trt_high_precision_dtype=None,
|
|
520
|
+
onnx_quantizer_type="dynamic",
|
|
521
|
+
pass_through_bwd=True,
|
|
522
|
+
):
|
|
523
|
+
"""ONNX symbolic function."""
|
|
524
|
+
from .export_onnx import export_fp4, export_mxfp8
|
|
525
|
+
|
|
526
|
+
if num_bits == (2, 1) and scale_bits == (4, 3):
|
|
527
|
+
return export_fp4(
|
|
528
|
+
g,
|
|
529
|
+
inputs,
|
|
530
|
+
block_size,
|
|
531
|
+
amax,
|
|
532
|
+
num_bits,
|
|
533
|
+
trt_high_precision_dtype,
|
|
534
|
+
onnx_quantizer_type,
|
|
535
|
+
)
|
|
536
|
+
if num_bits == (4, 3) and scale_bits == (8, 0):
|
|
537
|
+
return export_mxfp8(
|
|
538
|
+
g,
|
|
539
|
+
inputs,
|
|
540
|
+
onnx_quantizer_type,
|
|
541
|
+
block_size,
|
|
542
|
+
)
|
|
543
|
+
raise NotImplementedError(
|
|
544
|
+
f"Unsupported num_bits: {num_bits} and scale_bits: {scale_bits} for ONNX export."
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
@staticmethod
|
|
548
|
+
def forward(
|
|
549
|
+
ctx,
|
|
550
|
+
inputs,
|
|
551
|
+
block_size,
|
|
552
|
+
amax,
|
|
553
|
+
bias,
|
|
554
|
+
num_bits,
|
|
555
|
+
scale_bits,
|
|
556
|
+
trt_high_precision_dtype=None,
|
|
557
|
+
onnx_quantizer_type="dynamic",
|
|
558
|
+
pass_through_bwd=True,
|
|
559
|
+
):
|
|
560
|
+
"""Forward method."""
|
|
561
|
+
_save_for_backward_if_needed(ctx, pass_through_bwd, inputs, amax)
|
|
562
|
+
return _dynamic_block_quantize_forward(
|
|
563
|
+
ctx,
|
|
564
|
+
inputs,
|
|
565
|
+
block_size,
|
|
566
|
+
amax,
|
|
567
|
+
num_bits,
|
|
568
|
+
scale_bits,
|
|
569
|
+
trt_high_precision_dtype,
|
|
570
|
+
onnx_quantizer_type,
|
|
571
|
+
pass_through_bwd,
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
@staticmethod
|
|
575
|
+
def backward(ctx, grad_outputs):
|
|
576
|
+
"""Implements straight through estimation with clipping."""
|
|
577
|
+
return _fake_quant_backward_function(ctx, grad_outputs, num_args=9)
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
class TensorQuantFunction(Function):
|
|
581
|
+
"""A universal tensor quantization function.
|
|
582
|
+
|
|
583
|
+
Take an input tensor, output an quantized tensor. The granularity of scale can be interpreted from the
|
|
584
|
+
shape of amax.
|
|
585
|
+
output_dtype indicates whether the quantized value will be stored in integer or float. The reason we want to store
|
|
586
|
+
it in float is the pytorch function takes the quantized value may not accept integer input, e.g. Conv2D.
|
|
587
|
+
|
|
588
|
+
It uses 2^num_bits -1 values instead of 2^num_bits. e.g., for num_bits=8, it uses [-127, 127] instead of [-128, 127]
|
|
589
|
+
"""
|
|
590
|
+
|
|
591
|
+
@staticmethod
|
|
592
|
+
@symbolic_helper.parse_args("v", "t", "t", "i", "b", "b", "s")
|
|
593
|
+
def symbolic(
|
|
594
|
+
g,
|
|
595
|
+
inputs,
|
|
596
|
+
amax,
|
|
597
|
+
bias=None,
|
|
598
|
+
num_bits=8,
|
|
599
|
+
unsigned=False,
|
|
600
|
+
narrow_range=True,
|
|
601
|
+
trt_high_precision_dtype=None,
|
|
602
|
+
):
|
|
603
|
+
"""ONNX symbolic function."""
|
|
604
|
+
from .export_onnx import export_int8
|
|
605
|
+
|
|
606
|
+
return export_int8(
|
|
607
|
+
g, inputs, amax, num_bits, unsigned, narrow_range, trt_high_precision_dtype
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
@staticmethod
|
|
611
|
+
def forward(
|
|
612
|
+
ctx,
|
|
613
|
+
inputs,
|
|
614
|
+
amax,
|
|
615
|
+
bias=None,
|
|
616
|
+
num_bits=8,
|
|
617
|
+
unsigned=False,
|
|
618
|
+
narrow_range=True,
|
|
619
|
+
trt_high_precision_dtype=None,
|
|
620
|
+
):
|
|
621
|
+
"""Forward method.
|
|
622
|
+
|
|
623
|
+
Follow tensorflow convention, max value is passed in and used to decide scale, instead of inputting scale
|
|
624
|
+
directly. Though inputting scale directly may be more natural to use.
|
|
625
|
+
|
|
626
|
+
Args:
|
|
627
|
+
ctx: A Context object to store tensors for backward.
|
|
628
|
+
inputs: A Tensor of type float32.
|
|
629
|
+
amax: A Tensor of type float32. Inputs will be quantized within range [-amax, amax]
|
|
630
|
+
amax will be broadcasted to inputs tensor.
|
|
631
|
+
num_bits: A integer used to calculate scaling factor, scale = (2^(num_bits-1) - 1) / max
|
|
632
|
+
Effectively, it indicates how many integer bits is used to represent the value. Default 8.
|
|
633
|
+
output_dtype: A type of Tensor. torch.int32 or torch.float32.
|
|
634
|
+
unsigned: A boolean. Use unsigned integer range. E.g. [0, 255] for num_bits=8. Default False.
|
|
635
|
+
narrow_range: A boolean. Use symmetric integer range for signed quantization
|
|
636
|
+
E.g. [-127,127] instead of [-128,127] for num_bits=8. Default True.
|
|
637
|
+
|
|
638
|
+
Returns:
|
|
639
|
+
outputs: A Tensor of type output_dtype.
|
|
640
|
+
scale: A Tensor of type float32. outputs / scale will dequantize outputs tensor.
|
|
641
|
+
|
|
642
|
+
Raises:
|
|
643
|
+
ValueError:
|
|
644
|
+
"""
|
|
645
|
+
if bias is not None:
|
|
646
|
+
inputs = inputs - bias
|
|
647
|
+
|
|
648
|
+
ctx.save_for_backward(inputs, amax)
|
|
649
|
+
|
|
650
|
+
outputs, scale = _tensor_quant(inputs, amax, num_bits, unsigned, narrow_range)
|
|
651
|
+
# Check if scale overflows FP16
|
|
652
|
+
if outputs.dtype == torch.half and scale.max() > 65504:
|
|
653
|
+
raise ValueError(f"scale is too large for FP16 with amax={amax}")
|
|
654
|
+
|
|
655
|
+
if bias is not None:
|
|
656
|
+
outputs = outputs + bias
|
|
657
|
+
|
|
658
|
+
return outputs, scale.to(inputs.dtype)
|
|
659
|
+
|
|
660
|
+
@staticmethod
|
|
661
|
+
def backward(ctx, grad_outputs, grad_scale):
|
|
662
|
+
"""Implements straight through estimation with clipping.
|
|
663
|
+
|
|
664
|
+
For -amax <= input <= amax the gradient passes straight through, otherwise the gradient is zero.
|
|
665
|
+
|
|
666
|
+
Args:
|
|
667
|
+
ctx: A Context object with saved tensors from forward.
|
|
668
|
+
grad_outputs: A tensor of gradient of outputs.
|
|
669
|
+
grad_scale: A tensor of gradient of scale.
|
|
670
|
+
|
|
671
|
+
Returns:
|
|
672
|
+
grad_inputs: A tensor of gradient.
|
|
673
|
+
"""
|
|
674
|
+
inputs, amax = ctx.saved_tensors
|
|
675
|
+
zero = grad_outputs.new_zeros(1) # create a zero tensor with the same type and device
|
|
676
|
+
grad_inputs = torch.where(inputs.abs() <= amax, grad_outputs, zero)
|
|
677
|
+
return grad_inputs, None, None, None, None, None, None
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
class LegacyFakeTensorQuantFunction(Function):
|
|
681
|
+
"""Fake version of TensorQuantFunction.
|
|
682
|
+
|
|
683
|
+
See comments of TensorQuantFunction, arguments are the same.
|
|
684
|
+
"""
|
|
685
|
+
|
|
686
|
+
@staticmethod
|
|
687
|
+
def forward(ctx, inputs, amax, bias, num_bits=8, unsigned=False, narrow_range=True):
|
|
688
|
+
"""Forward method."""
|
|
689
|
+
if bias is not None:
|
|
690
|
+
inputs = inputs - bias
|
|
691
|
+
|
|
692
|
+
ctx.save_for_backward(inputs, amax)
|
|
693
|
+
|
|
694
|
+
outputs, scale = _tensor_quant(inputs, amax, num_bits, unsigned, narrow_range)
|
|
695
|
+
|
|
696
|
+
if bias is not None:
|
|
697
|
+
outputs = outputs + bias
|
|
698
|
+
|
|
699
|
+
return outputs / scale.to(inputs.dtype)
|
|
700
|
+
|
|
701
|
+
@staticmethod
|
|
702
|
+
def backward(ctx, grad_outputs):
|
|
703
|
+
"""Implements straight through estimation."""
|
|
704
|
+
inputs, amax = ctx.saved_tensors
|
|
705
|
+
zero = grad_outputs.new_zeros(1)
|
|
706
|
+
grad_inputs = torch.where(inputs.abs() <= amax, grad_outputs, zero)
|
|
707
|
+
return grad_inputs, None, None, None, None, None
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
def _tensor_quant(inputs, amax, num_bits=8, unsigned=False, narrow_range=True):
|
|
711
|
+
"""Shared function body between TensorQuantFunction and FakeTensorQuantFunction."""
|
|
712
|
+
# Fine scale, per channel scale will be handled by broadcasting, which could be tricky. Pop a warning.
|
|
713
|
+
if unsigned and inputs.min() < 0.0:
|
|
714
|
+
raise TypeError("Negative values encountered in unsigned quantization.")
|
|
715
|
+
|
|
716
|
+
# Computation can be done in FP32 to prevent potential over flow.
|
|
717
|
+
input_dtype = inputs.dtype
|
|
718
|
+
if inputs.dtype == torch.half:
|
|
719
|
+
inputs = inputs.float()
|
|
720
|
+
if amax.dtype == torch.half:
|
|
721
|
+
amax = amax.float()
|
|
722
|
+
|
|
723
|
+
min_amax = amax.min()
|
|
724
|
+
if min_amax < 0:
|
|
725
|
+
raise ValueError("Negative values in amax")
|
|
726
|
+
|
|
727
|
+
max_bound = torch.tensor((2.0 ** (num_bits - 1 + int(unsigned))) - 1.0, device=amax.device)
|
|
728
|
+
if unsigned:
|
|
729
|
+
min_bound = 0
|
|
730
|
+
elif narrow_range:
|
|
731
|
+
min_bound = -max_bound
|
|
732
|
+
else:
|
|
733
|
+
min_bound = -max_bound - 1
|
|
734
|
+
scale = max_bound / amax
|
|
735
|
+
|
|
736
|
+
epsilon = 1.0 / (1 << 24)
|
|
737
|
+
if min_amax <= epsilon: # Treat amax smaller than minimum representable of fp16 0
|
|
738
|
+
zero_amax_mask = amax <= epsilon
|
|
739
|
+
scale[zero_amax_mask] = 0 # Value quantized with amax=0 should all be 0
|
|
740
|
+
|
|
741
|
+
outputs = torch.clamp((inputs * scale).round_(), min_bound, max_bound)
|
|
742
|
+
|
|
743
|
+
if min_amax <= epsilon:
|
|
744
|
+
scale[zero_amax_mask] = (
|
|
745
|
+
1.0 # Return 1 makes more sense for values quantized to 0 with amax=0
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
if input_dtype == torch.half:
|
|
749
|
+
outputs = outputs.half()
|
|
750
|
+
|
|
751
|
+
return outputs, scale
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
class FakeAffineTensorQuantFunction(Function):
|
|
755
|
+
"""Fake version of affine quantization.
|
|
756
|
+
|
|
757
|
+
gemmlowp style scale+shift quantization. See more details in
|
|
758
|
+
https://github.com/google/gemmlowp/blob/master/doc/quantization.md.
|
|
759
|
+
|
|
760
|
+
We DO NOT recommend affine quantization on weights for performance reason. There might be value to affine quantize
|
|
761
|
+
activation as it can be cancelled by bias and comes with no performance penalty. This functionality is only added
|
|
762
|
+
for experimental purpose.
|
|
763
|
+
"""
|
|
764
|
+
|
|
765
|
+
@staticmethod
|
|
766
|
+
def forward(ctx, inputs, min_range, max_range, num_bits=8):
|
|
767
|
+
"""As it will be only applied on activation with per tensor granularity, broadcast is not needed.
|
|
768
|
+
|
|
769
|
+
Args:
|
|
770
|
+
ctx: Pytorch convention.
|
|
771
|
+
inputs: A Tensor of type float32.
|
|
772
|
+
min_range: A float.
|
|
773
|
+
max_range: A float.
|
|
774
|
+
num_bits: An integer
|
|
775
|
+
|
|
776
|
+
Returns:
|
|
777
|
+
outputs: A Tensor of type output_dtype
|
|
778
|
+
"""
|
|
779
|
+
ctx.save_for_backward(inputs, min_range, max_range)
|
|
780
|
+
|
|
781
|
+
step_size = (max_range - min_range) / (2.0**num_bits - 1)
|
|
782
|
+
|
|
783
|
+
min_bound = -(2.0 ** (num_bits - 1))
|
|
784
|
+
max_bound = 2.0 ** (num_bits - 1) - 1
|
|
785
|
+
|
|
786
|
+
quant_zero = torch.round(min_range / step_size) - min_bound
|
|
787
|
+
quantized = torch.round(inputs / step_size) - quant_zero
|
|
788
|
+
quantized = torch.clamp(quantized, min_bound, max_bound)
|
|
789
|
+
|
|
790
|
+
outputs = (quantized + quant_zero) * step_size
|
|
791
|
+
|
|
792
|
+
return outputs
|
|
793
|
+
|
|
794
|
+
@staticmethod
|
|
795
|
+
def backward(ctx, grad_outputs):
|
|
796
|
+
"""Implements straight through estimation with clipping.
|
|
797
|
+
|
|
798
|
+
Args:
|
|
799
|
+
ctx: Pytorch convention.
|
|
800
|
+
grad_output: A tensor of gradient of outputs.
|
|
801
|
+
|
|
802
|
+
Returns:
|
|
803
|
+
grad_inputs: A tensor of gradient
|
|
804
|
+
"""
|
|
805
|
+
inputs, min_range, max_range = ctx.saved_tensors
|
|
806
|
+
zero = grad_outputs.new_zeros(1)
|
|
807
|
+
grad_inputs = torch.where((inputs <= max_range) * (inputs >= min_range), grad_outputs, zero)
|
|
808
|
+
return grad_inputs, None, None, None
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
tensor_quant = TensorQuantFunction.apply
|
|
812
|
+
legacy_fake_tensor_quant = LegacyFakeTensorQuantFunction.apply
|
|
813
|
+
fake_tensor_quant = FakeTensorQuantFunction.apply
|
|
814
|
+
fake_affine_tensor_quant = FakeAffineTensorQuantFunction.apply
|
|
815
|
+
scaled_e4m3 = ScaledE4M3Function.apply
|
|
816
|
+
dynamic_block_quant = DynamicBlockQuantizationFunction.apply
|