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,151 @@
|
|
|
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
|
+
"""Implements FP8 quantization for efficient tensor storage and computation."""
|
|
17
|
+
|
|
18
|
+
import math
|
|
19
|
+
|
|
20
|
+
import torch
|
|
21
|
+
|
|
22
|
+
from ..qtensor.base_qtensor import BaseQuantizedTensor
|
|
23
|
+
from ..utils import (
|
|
24
|
+
convert_quantization_axis_to_reduce_axis,
|
|
25
|
+
reduce_amax,
|
|
26
|
+
reduce_block_amax,
|
|
27
|
+
reduce_block_padding,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
__all__ = ["FP8QTensor"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class FP8QTensor(BaseQuantizedTensor):
|
|
34
|
+
"""Implements the FP8 quantization on tensors for more efficient storage or computation.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
quantized_data (torch.Tensor): The quantized data stored as a packed fp8 tensor.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def quantize(
|
|
42
|
+
cls,
|
|
43
|
+
input: torch.Tensor,
|
|
44
|
+
scales: torch.Tensor = None,
|
|
45
|
+
axis: tuple | int | None = None,
|
|
46
|
+
block_sizes: dict | None = None,
|
|
47
|
+
) -> tuple:
|
|
48
|
+
"""Converting a tensor to a quantized format based on FP8 quantization. Only E4M3 is supported.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
input (torch.Tensor): The input tensor to be quantized.
|
|
52
|
+
scales (torch.Tensor): The scales for quantization.
|
|
53
|
+
axis: The dimensions to reduce for quantization. None or int or tuple of ints.
|
|
54
|
+
block_sizes (dict): A dictionary specifying the block size for each dimension.
|
|
55
|
+
|
|
56
|
+
Note: One can only provide axis or block_sizes for FP8 quantization.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
tuple: FP8QTensor, scales
|
|
60
|
+
"""
|
|
61
|
+
original_input = input
|
|
62
|
+
|
|
63
|
+
# If block_sizes is provided, pad the input so that each dimension is divisible by the block size.
|
|
64
|
+
if block_sizes:
|
|
65
|
+
input = reduce_block_padding(input, block_sizes)
|
|
66
|
+
|
|
67
|
+
# Compute scales if not provided
|
|
68
|
+
if scales is None:
|
|
69
|
+
if block_sizes:
|
|
70
|
+
amax = reduce_block_amax(input, block_sizes)
|
|
71
|
+
else:
|
|
72
|
+
reduce_axis = convert_quantization_axis_to_reduce_axis(input, axis)
|
|
73
|
+
amax = reduce_amax(input, axis=reduce_axis)
|
|
74
|
+
scales = amax / 448.0 # Consider parameterizing the divisor if needed
|
|
75
|
+
|
|
76
|
+
# Determine the expected scales shape from the (possibly padded) input
|
|
77
|
+
expected_shape = list(input.shape)
|
|
78
|
+
expanded_scales = scales.clone()
|
|
79
|
+
if block_sizes:
|
|
80
|
+
for dim, block_size in block_sizes.items():
|
|
81
|
+
# Convert negative indices to positive ones.
|
|
82
|
+
dim = dim if dim >= 0 else len(input.shape) + dim
|
|
83
|
+
# After padding, this should always hold.
|
|
84
|
+
assert input.shape[dim] % block_size == 0, (
|
|
85
|
+
f"Tensor dimension {dim}, {input.shape[dim]} is not divisible by {block_size} even after padding."
|
|
86
|
+
)
|
|
87
|
+
# The scales tensor is expected to have size equal to input.shape[dim] // block_size.
|
|
88
|
+
expected_shape[dim] = input.shape[dim] // block_size
|
|
89
|
+
|
|
90
|
+
# If we get amax from tensor_quantizer, it might not be the expected shape but has the
|
|
91
|
+
# same number of elements, reshape it.
|
|
92
|
+
if scales.shape != tuple(expected_shape) and scales.numel() == math.prod(
|
|
93
|
+
expected_shape
|
|
94
|
+
):
|
|
95
|
+
scales = scales.reshape(expected_shape)
|
|
96
|
+
expanded_scales = expanded_scales.reshape(expected_shape)
|
|
97
|
+
|
|
98
|
+
assert scales.shape == tuple(expected_shape), (
|
|
99
|
+
f"Mismatch in expected scale shape: {scales.shape} vs {tuple(expected_shape)}"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Expand scales along each block dimension for broadcasting.
|
|
103
|
+
for dim, block_size in block_sizes.items():
|
|
104
|
+
expanded_scales = expanded_scales.repeat_interleave(block_size, dim=dim)
|
|
105
|
+
|
|
106
|
+
# Perform quantization using FP8 (E4M3) format.
|
|
107
|
+
quantized_data = (input / expanded_scales).to(torch.float8_e4m3fn)
|
|
108
|
+
|
|
109
|
+
# Crop quantized_data back to the original shape (if padding was added).
|
|
110
|
+
slices = tuple(slice(0, dim) for dim in original_input.shape)
|
|
111
|
+
quantized_data_cropped = quantized_data[slices]
|
|
112
|
+
|
|
113
|
+
return cls(original_input.shape, original_input.dtype, quantized_data_cropped), scales
|
|
114
|
+
|
|
115
|
+
def dequantize(self, dtype: torch.dtype = None, **kwarg):
|
|
116
|
+
"""Dequantze FP8 packed tensor to a target dtype."""
|
|
117
|
+
if dtype is None:
|
|
118
|
+
dtype = self.metadata["dtype"]
|
|
119
|
+
assert "scale" in kwarg, "Require scale for FP8 dequantization."
|
|
120
|
+
|
|
121
|
+
# get args
|
|
122
|
+
scales = kwarg["scale"]
|
|
123
|
+
block_sizes = kwarg.get("block_sizes")
|
|
124
|
+
|
|
125
|
+
quantized_data = self._quantized_data
|
|
126
|
+
if block_sizes:
|
|
127
|
+
# pad the weight if needed
|
|
128
|
+
quantized_data = reduce_block_padding(quantized_data, block_sizes)
|
|
129
|
+
shape = quantized_data.shape
|
|
130
|
+
|
|
131
|
+
# Compute expanded shape for broadcasting scales
|
|
132
|
+
expanded_shape = list(shape)
|
|
133
|
+
for dim, block_size in block_sizes.items():
|
|
134
|
+
assert shape[dim] % block_size == 0, (
|
|
135
|
+
f"Dimension {shape[dim]} is not divisible by {block_size}."
|
|
136
|
+
)
|
|
137
|
+
expanded_shape[dim] //= block_size # Reduce the dimension size for blocks
|
|
138
|
+
|
|
139
|
+
assert tuple(expanded_shape) == scales.shape, (
|
|
140
|
+
f"Scales shape {scales.shape} must match expected {tuple(expanded_shape)}."
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Expand scales for broadcasting
|
|
144
|
+
for dim, block_size in block_sizes.items():
|
|
145
|
+
scales = scales.repeat_interleave(block_size, dim=dim)
|
|
146
|
+
|
|
147
|
+
# handle padded tensors
|
|
148
|
+
slices = tuple(slice(0, dim) for dim in self.metadata["shape"])
|
|
149
|
+
scales = scales.to(self._quantized_data.device)
|
|
150
|
+
|
|
151
|
+
return (quantized_data.to(dtype) * scales.to(dtype))[slices]
|
|
@@ -0,0 +1,130 @@
|
|
|
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
|
+
"""Implements INT4 quantization for efficient tensor storage and computation."""
|
|
17
|
+
|
|
18
|
+
import numpy as np
|
|
19
|
+
import torch
|
|
20
|
+
|
|
21
|
+
from ..extensions import get_cuda_ext
|
|
22
|
+
from ..qtensor.base_qtensor import BaseQuantizedTensor
|
|
23
|
+
from ..utils import reduce_amax, reduce_block_padding
|
|
24
|
+
|
|
25
|
+
__all__ = ["INT4QTensor"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class INT4QTensor(BaseQuantizedTensor):
|
|
29
|
+
"""Implements the INT4 quantization on tensors for more efficient storage or computation.
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
quantized_data (torch.Tensor): The quantized data stored as a packed uint8 tensor.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def _get_quant_maxbound(num_bits):
|
|
37
|
+
return 2 ** (num_bits - 1) - 1
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def quantize(cls, input: torch.Tensor, block_size: int) -> tuple:
|
|
41
|
+
"""Converting a tensor to a quantized format based on INT4 (AWQ) quantization.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
input (torch.Tensor): The input tensor to be quantized.
|
|
45
|
+
block_size (int): The size of each block for quantization.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
tuple: Contains quantized data, input quantization config, and scale quantization config.
|
|
49
|
+
"""
|
|
50
|
+
cuda_ext = get_cuda_ext()
|
|
51
|
+
|
|
52
|
+
scale_quant_maxbound = cls._get_quant_maxbound(num_bits=4)
|
|
53
|
+
|
|
54
|
+
assert input.shape[-1] % 2 == 0, "Input tensor must have even number on last dimension."
|
|
55
|
+
|
|
56
|
+
# pad the input if needed
|
|
57
|
+
original_input = input
|
|
58
|
+
input = reduce_block_padding(input.view(-1), block_sizes={-1: block_size})
|
|
59
|
+
|
|
60
|
+
# get scales for each block
|
|
61
|
+
block_input = input.view(-1, block_size)
|
|
62
|
+
scales = scale_quant_maxbound / reduce_amax(block_input, -1)
|
|
63
|
+
# expand scalers to match shape of input
|
|
64
|
+
scales = scales.view(block_input.shape[0], -1) # shape: (block_input.shape[0], 1)
|
|
65
|
+
|
|
66
|
+
if cuda_ext and input.is_cuda:
|
|
67
|
+
# use a custom cuda kernel if available
|
|
68
|
+
packed_output_uint8 = cuda_ext.INT4_quantize(input, scales, block_size)
|
|
69
|
+
else:
|
|
70
|
+
scaled_blocks = block_input * scales
|
|
71
|
+
flattened = scaled_blocks.flatten()
|
|
72
|
+
# uint4: 0 - 15
|
|
73
|
+
flattened = flattened.round().clamp(
|
|
74
|
+
-(scale_quant_maxbound + 1), scale_quant_maxbound
|
|
75
|
+
) + (scale_quant_maxbound + 1)
|
|
76
|
+
flattened = flattened.to(torch.uint8)
|
|
77
|
+
|
|
78
|
+
packed_output_uint8 = torch.empty(
|
|
79
|
+
input.numel() // 2, dtype=torch.uint8, device=input.device
|
|
80
|
+
)
|
|
81
|
+
# pack the int4 weights into a uint8 tensor
|
|
82
|
+
# packing format: w0, w1, w2, w3, w4, w5, ...
|
|
83
|
+
# | byte | byte | byte |
|
|
84
|
+
packed_output_uint8 = flattened[::2] << 4 | flattened[1::2]
|
|
85
|
+
|
|
86
|
+
# reshape the packed_output_uint8 to the original dimensions for sharding
|
|
87
|
+
packed_output_uint8 = packed_output_uint8.reshape(*original_input.shape[:-1], -1)
|
|
88
|
+
return cls(original_input.shape, input.dtype, packed_output_uint8), scales
|
|
89
|
+
|
|
90
|
+
def dequantize(self, dtype: torch.dtype = None, **kwarg):
|
|
91
|
+
"""Dequantze INT4 packed tensor to a target dtype."""
|
|
92
|
+
if dtype is None:
|
|
93
|
+
dtype = self.metadata["dtype"]
|
|
94
|
+
cuda_ext = get_cuda_ext()
|
|
95
|
+
|
|
96
|
+
# get kwargs
|
|
97
|
+
scales = kwarg["scale"]
|
|
98
|
+
block_sizes = kwarg["block_sizes"]
|
|
99
|
+
scale_quant_maxbound = self._get_quant_maxbound(num_bits=4)
|
|
100
|
+
|
|
101
|
+
if cuda_ext and self._quantized_data.is_cuda:
|
|
102
|
+
# use a custom cuda kernel if available
|
|
103
|
+
scales = scales.to(self._quantized_data.device)
|
|
104
|
+
output = cuda_ext.INT4_dequantize(
|
|
105
|
+
self._quantized_data.view(-1), scales, block_sizes[-1]
|
|
106
|
+
)
|
|
107
|
+
return (
|
|
108
|
+
output.view(-1)[: np.prod(self.metadata["shape"])] # handle padding
|
|
109
|
+
.view(self.metadata["shape"])
|
|
110
|
+
.to(dtype)
|
|
111
|
+
)
|
|
112
|
+
else:
|
|
113
|
+
# indexing in torch required long dtype, we may need to optimize this with customized kernels
|
|
114
|
+
# convert (0, 15) -> (-8, 7)
|
|
115
|
+
first_half = (self._quantized_data >> 4).to(torch.long) - (scale_quant_maxbound + 1)
|
|
116
|
+
second_half = (self._quantized_data & 0x0F).to(torch.long) - (scale_quant_maxbound + 1)
|
|
117
|
+
|
|
118
|
+
# de-quantize tensor
|
|
119
|
+
first_half = first_half.view(-1, block_sizes[-1] // 2) / scales.view(-1, 1)
|
|
120
|
+
second_half = second_half.view(-1, block_sizes[-1] // 2) / scales.view(-1, 1)
|
|
121
|
+
|
|
122
|
+
# merge the interleaving elements
|
|
123
|
+
first_half = first_half.flatten().unsqueeze(-1).transpose(0, 1)
|
|
124
|
+
second_half = second_half.flatten().unsqueeze(-1).transpose(0, 1)
|
|
125
|
+
return (
|
|
126
|
+
torch.stack([first_half, second_half], dim=-1)
|
|
127
|
+
.view(-1)[: np.prod(self.metadata["shape"])] # handle padding
|
|
128
|
+
.reshape(self.metadata["shape"])
|
|
129
|
+
.to(dtype)
|
|
130
|
+
)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
"""Implements INT8 quantization for efficient tensor storage and computation."""
|
|
16
|
+
|
|
17
|
+
import torch
|
|
18
|
+
|
|
19
|
+
from ..qtensor.base_qtensor import BaseQuantizedTensor
|
|
20
|
+
from ..utils import (
|
|
21
|
+
convert_quantization_axis_to_reduce_axis,
|
|
22
|
+
reduce_amax,
|
|
23
|
+
reduce_block_amax,
|
|
24
|
+
reduce_block_padding,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class INT8QTensor(BaseQuantizedTensor):
|
|
29
|
+
"""Implements the INT8 quantization on tensors for more efficient storage or computation.
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
quantized_data (torch.Tensor): The quantized data stored as an INT8 tensor.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def quantize(
|
|
37
|
+
cls,
|
|
38
|
+
input: torch.Tensor,
|
|
39
|
+
scales: torch.Tensor | None = None,
|
|
40
|
+
axis: tuple | int | None = None,
|
|
41
|
+
block_sizes: dict | None = None,
|
|
42
|
+
) -> tuple:
|
|
43
|
+
"""Converting a tensor to a quantized format based on INT8 quantization.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
input (torch.Tensor): The input tensor to be quantized.
|
|
47
|
+
scales (torch.Tensor): The scales for quantization.
|
|
48
|
+
axis: The dimensions to reduce for quantization. None or int or tuple of ints.
|
|
49
|
+
block_sizes (dict): A dictionary specifying the block size for each dimension.
|
|
50
|
+
Note: One can only provide axis or block_sizes for INT8 quantization.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
tuple: INT8QTensor, scales
|
|
54
|
+
"""
|
|
55
|
+
original_input = input
|
|
56
|
+
if scales is None:
|
|
57
|
+
if block_sizes:
|
|
58
|
+
input = reduce_block_padding(input, block_sizes)
|
|
59
|
+
amax = reduce_block_amax(input, block_sizes)
|
|
60
|
+
else:
|
|
61
|
+
reduce_axis = convert_quantization_axis_to_reduce_axis(input, axis)
|
|
62
|
+
amax = reduce_amax(input, axis=reduce_axis)
|
|
63
|
+
scales = amax / 127.0
|
|
64
|
+
|
|
65
|
+
# Calculate the scale shape and make sure it aligns with input and block_sizes
|
|
66
|
+
expected_shape = list(input.shape)
|
|
67
|
+
expanded_scales = scales.clone()
|
|
68
|
+
if block_sizes:
|
|
69
|
+
for dim, block_size in block_sizes.items():
|
|
70
|
+
dim = dim if dim >= 0 else len(input.shape) + dim # Convert negative index
|
|
71
|
+
assert input.shape[dim] % block_size == 0, (
|
|
72
|
+
f"Tensor dimension {dim}, {input.shape[dim]} is not divisible by {block_size}."
|
|
73
|
+
)
|
|
74
|
+
expected_shape[dim] = (
|
|
75
|
+
input.shape[dim] // block_size
|
|
76
|
+
) # Adjust expected shape for blocks
|
|
77
|
+
|
|
78
|
+
# Assert the shape of `scales` matches expected reduced dimensions
|
|
79
|
+
assert scales.shape == tuple(expected_shape), (
|
|
80
|
+
f"Mismatch in expected scale shape: {scales.shape} vs {tuple(expected_shape)}"
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Expand scales for broadcasting
|
|
84
|
+
for dim, block_size in block_sizes.items():
|
|
85
|
+
expanded_scales = expanded_scales.repeat_interleave(block_size, dim=dim)
|
|
86
|
+
|
|
87
|
+
# Quantization
|
|
88
|
+
quantized_data = (input / expanded_scales).round().clamp(-128, 127).to(torch.int8)
|
|
89
|
+
|
|
90
|
+
return cls(original_input.shape, original_input.dtype, quantized_data), scales
|
|
91
|
+
|
|
92
|
+
def dequantize(self, dtype: torch.dtype = None, **kwarg):
|
|
93
|
+
"""Dequantize INT8 packed tensor to a target dtype."""
|
|
94
|
+
if dtype is None:
|
|
95
|
+
dtype = self.metadata["dtype"]
|
|
96
|
+
assert "scale" in kwarg, "Require scale for INT8 dequantization."
|
|
97
|
+
|
|
98
|
+
# Get args
|
|
99
|
+
scales = kwarg["scale"]
|
|
100
|
+
block_sizes = kwarg.get("block_sizes")
|
|
101
|
+
|
|
102
|
+
shape = self._quantized_data.shape
|
|
103
|
+
if block_sizes:
|
|
104
|
+
# Compute expanded shape for broadcasting scales
|
|
105
|
+
expanded_shape = list(shape)
|
|
106
|
+
for dim, block_size in block_sizes.items():
|
|
107
|
+
assert shape[dim] % block_size == 0, (
|
|
108
|
+
f"Dimension {shape[dim]} is not divisible by {block_size}."
|
|
109
|
+
)
|
|
110
|
+
expanded_shape[dim] //= block_size # Reduce the dimension size for blocks
|
|
111
|
+
|
|
112
|
+
assert tuple(expanded_shape) == scales.shape, (
|
|
113
|
+
f"Scales shape {scales.shape} must match expected {tuple(expanded_shape)}."
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Expand scales for broadcasting
|
|
117
|
+
for dim, block_size in block_sizes.items():
|
|
118
|
+
scales = scales.repeat_interleave(block_size, dim=dim)
|
|
119
|
+
|
|
120
|
+
# Handle padded tensors
|
|
121
|
+
slices = tuple(slice(0, dim) for dim in self.metadata["shape"])
|
|
122
|
+
scales = scales.to(self._quantized_data.device)
|
|
123
|
+
|
|
124
|
+
return (self._quantized_data.view(torch.int8).to(dtype) * scales.to(dtype))[slices]
|
|
@@ -0,0 +1,144 @@
|
|
|
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
|
+
"""Implements MXFP4 quantization for efficient tensor storage and computation."""
|
|
17
|
+
|
|
18
|
+
import torch
|
|
19
|
+
|
|
20
|
+
from ..qtensor.base_qtensor import BaseQuantizedTensor
|
|
21
|
+
|
|
22
|
+
__all__ = ["MXFP4QTensor"]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MXFP4QTensor(BaseQuantizedTensor):
|
|
26
|
+
"""Implements the MXFP4 quantization on tensors for more efficient storage or computation.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
quantized_data (torch.Tensor): The quantized data stored as a packed fp8 tensor.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
E2M1_max = 6.0
|
|
33
|
+
|
|
34
|
+
E2M1_values = [0, 0.5, 1, 1.5, 2, 3, 4, 6]
|
|
35
|
+
E2M1_bounds = torch.tensor([0.25, 0.75, 1.25, 1.75, 2.5, 3.5, 5])
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def quantize(cls, input: torch.Tensor, block_size: int | None) -> tuple:
|
|
39
|
+
"""Converting a tensor to a quantized format based on MXFP4 quantization. Only E4M3 is supported.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
input (torch.Tensor): The input tensor to be quantized.
|
|
43
|
+
block_sizes (dict | None): The block sizes for quantization.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def cast_fp4(x):
|
|
47
|
+
sign = torch.sign(x)
|
|
48
|
+
sign_bit = (2 - sign) // 2
|
|
49
|
+
ord_ = torch.sum(
|
|
50
|
+
(x.abs().unsqueeze(-1) - MXFP4QTensor.E2M1_bounds.to(x.device)) > 0, dim=-1
|
|
51
|
+
)
|
|
52
|
+
fp4_val = (sign_bit * 0b1000 + ord_).to(torch.uint8)
|
|
53
|
+
return fp4_val
|
|
54
|
+
|
|
55
|
+
def fuse_uint4_to_uint8(x):
|
|
56
|
+
# If the last dimension is odd, pad with zeros
|
|
57
|
+
# If this behavior is not desired, please modify the code accordingly
|
|
58
|
+
left_side = x[..., 0::2] # Even indices (0, 2, 4...)
|
|
59
|
+
right_side = x[..., 1::2] # Odd indices (1, 3, 5...)
|
|
60
|
+
new_data = right_side.clone() << 4 # Put odd indices (higher addresses) in high bits
|
|
61
|
+
new_data[..., : left_side.shape[-1]] += left_side # Put even indices in low bits
|
|
62
|
+
return new_data
|
|
63
|
+
|
|
64
|
+
if block_size is None:
|
|
65
|
+
block_size = 32
|
|
66
|
+
|
|
67
|
+
original_shape = input.shape
|
|
68
|
+
original_dtype = input.dtype
|
|
69
|
+
input = input.view(-1, block_size)
|
|
70
|
+
# get scales
|
|
71
|
+
# Casting scales to float yields better match with fakequant kernel (needs further investigation)
|
|
72
|
+
input_amax = input.float().abs().max(dim=-1, keepdim=True).values
|
|
73
|
+
descale = input_amax / cls.E2M1_max
|
|
74
|
+
min_value = torch.tensor(-127.0, device=descale.device)
|
|
75
|
+
e8m0_scale = torch.ceil(torch.maximum(torch.log2(descale), min_value))
|
|
76
|
+
|
|
77
|
+
input = (input / torch.exp2(e8m0_scale)).view(original_shape)
|
|
78
|
+
input_q = cast_fp4(input)
|
|
79
|
+
input_q = fuse_uint4_to_uint8(input_q)
|
|
80
|
+
e8m0_scale = (e8m0_scale + 127).to(torch.uint8)
|
|
81
|
+
return cls(original_shape, original_dtype, input_q), e8m0_scale
|
|
82
|
+
|
|
83
|
+
def dequantize(self, dtype: torch.dtype = None, **kwarg):
|
|
84
|
+
"""Dequantze MXFP4 packed tensor to a target dtype."""
|
|
85
|
+
|
|
86
|
+
def unfuse_uint8_to_uint4(x):
|
|
87
|
+
"""Unfuse uint8 values back to uint4 values.
|
|
88
|
+
|
|
89
|
+
This is the inverse operation of fuse_uint4_to_uint8.
|
|
90
|
+
"""
|
|
91
|
+
# Extract the lower 4 bits (even indices)
|
|
92
|
+
left_side = x & 0x0F
|
|
93
|
+
|
|
94
|
+
# Extract the upper 4 bits (odd indices)
|
|
95
|
+
right_side = (x >> 4) & 0x0F
|
|
96
|
+
|
|
97
|
+
# Create a new tensor with alternating values
|
|
98
|
+
shape = list(x.shape)
|
|
99
|
+
shape[-1] = shape[-1] * 2
|
|
100
|
+
result = torch.zeros(shape, dtype=torch.uint8, device=x.device)
|
|
101
|
+
|
|
102
|
+
# Fill in the values - even indices get low bits, odd indices get high bits
|
|
103
|
+
result[..., 0::2] = left_side # Even indices from low bits
|
|
104
|
+
result[..., 1::2] = right_side # Odd indices from high bits
|
|
105
|
+
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
e8m0_scale = kwarg["scale"]
|
|
109
|
+
block_size = kwarg["block_sizes"][-1]
|
|
110
|
+
if dtype is None:
|
|
111
|
+
dtype = self.metadata["dtype"]
|
|
112
|
+
|
|
113
|
+
# Unfuse the uint8 values back to uint4
|
|
114
|
+
x_unfused = unfuse_uint8_to_uint4(self._quantized_data)
|
|
115
|
+
# Extract sign and magnitude
|
|
116
|
+
sign = 1 - 2 * ((x_unfused & 0b1000) >> 3).to(
|
|
117
|
+
torch.float32
|
|
118
|
+
) # Extract sign bit and convert to +1/-1
|
|
119
|
+
magnitude = x_unfused & 0b0111 # Extract magnitude bits
|
|
120
|
+
magnitude = magnitude.to(torch.long)
|
|
121
|
+
|
|
122
|
+
# Create a tensor with the E2M1 values
|
|
123
|
+
values = torch.tensor(MXFP4QTensor.E2M1_values, device=self._quantized_data.device)
|
|
124
|
+
|
|
125
|
+
# Use gather to index the values tensor properly
|
|
126
|
+
# We need to reshape magnitude to match the dimensions we want to gather along
|
|
127
|
+
original_shape = magnitude.shape
|
|
128
|
+
x_float = values[magnitude.reshape(-1)].reshape(original_shape)
|
|
129
|
+
|
|
130
|
+
# Apply sign and scale
|
|
131
|
+
x_float = sign.float() * x_float
|
|
132
|
+
|
|
133
|
+
# Reshape to apply block-wise scaling
|
|
134
|
+
x_float = x_float.reshape(-1, block_size)
|
|
135
|
+
|
|
136
|
+
# Apply the E8M0 scale
|
|
137
|
+
scale_factor = torch.exp2(e8m0_scale.float() - 127)
|
|
138
|
+
scale_factor = scale_factor.reshape(-1, 1) # Reshape for proper broadcasting
|
|
139
|
+
|
|
140
|
+
# Apply scaling and reshape back to original shape
|
|
141
|
+
x_float = x_float * scale_factor
|
|
142
|
+
|
|
143
|
+
# Reshape back to the original shape
|
|
144
|
+
return x_float.reshape(original_shape).to(dtype)
|