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,78 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 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
|
+
|
|
16
|
+
"""Logging configuration module for AutoCast.
|
|
17
|
+
|
|
18
|
+
This module provides centralized logging configuration for all AutoCast components,
|
|
19
|
+
including console and file-based logging with customizable log levels. It ensures
|
|
20
|
+
consistent logging behavior across all components of the AutoCast tool.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
import os
|
|
25
|
+
|
|
26
|
+
# Create a parent logger for all AutoCast components
|
|
27
|
+
logger = logging.getLogger("autocast")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def configure_logging(level=logging.INFO, log_file=None):
|
|
31
|
+
"""Configure logging for all AutoCast components.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
level: The logging level to use (default: logging.INFO).
|
|
35
|
+
log_file: Optional path to a log file. If provided, logs will be written to this file
|
|
36
|
+
in addition to stdout (default: None).
|
|
37
|
+
"""
|
|
38
|
+
# Set level for the parent logger and all child loggers
|
|
39
|
+
logger.setLevel(level)
|
|
40
|
+
|
|
41
|
+
# Remove any existing handlers to ensure clean configuration
|
|
42
|
+
for handler in logger.handlers[:]:
|
|
43
|
+
logger.removeHandler(handler)
|
|
44
|
+
|
|
45
|
+
# Create console handler with formatting
|
|
46
|
+
console_handler = logging.StreamHandler()
|
|
47
|
+
formatter = logging.Formatter(
|
|
48
|
+
"%(asctime)s - %(name)s - %(levelname)s - %(filename)s - %(message)s"
|
|
49
|
+
)
|
|
50
|
+
console_handler.setFormatter(formatter)
|
|
51
|
+
logger.addHandler(console_handler)
|
|
52
|
+
|
|
53
|
+
# Add file handler if log_file is specified
|
|
54
|
+
if log_file:
|
|
55
|
+
try:
|
|
56
|
+
# Create directory if it doesn't exist
|
|
57
|
+
log_dir = os.path.dirname(log_file)
|
|
58
|
+
if log_dir:
|
|
59
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
60
|
+
|
|
61
|
+
file_handler = logging.FileHandler(log_file)
|
|
62
|
+
file_handler.setFormatter(formatter)
|
|
63
|
+
logger.addHandler(file_handler)
|
|
64
|
+
logger.info(f"Logging configured to write to file: {log_file}")
|
|
65
|
+
except Exception as e:
|
|
66
|
+
logger.error(f"Failed to setup file logging to {log_file}: {e!s}")
|
|
67
|
+
|
|
68
|
+
# Prevent log messages from propagating to the root logger
|
|
69
|
+
logger.propagate = False
|
|
70
|
+
|
|
71
|
+
# Ensure all child loggers inherit the level setting
|
|
72
|
+
for name in logging.root.manager.loggerDict:
|
|
73
|
+
if name.startswith("autocast"):
|
|
74
|
+
logging.getLogger(name).setLevel(level)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# Configure with default settings if not already configured
|
|
78
|
+
configure_logging()
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 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
|
+
|
|
16
|
+
"""Node classification module for AutoCast.
|
|
17
|
+
|
|
18
|
+
This module provides classes for classifying ONNX nodes based on various rules
|
|
19
|
+
to determine which nodes should be converted to lower precision and which should
|
|
20
|
+
remain in high precision. It includes rules for handling node names, operation types,
|
|
21
|
+
initializer ranges, and I/O value ranges.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import abc
|
|
25
|
+
import re
|
|
26
|
+
|
|
27
|
+
import numpy as np
|
|
28
|
+
import onnx
|
|
29
|
+
|
|
30
|
+
from modelopt.onnx.autocast.logging_config import configure_logging, logger
|
|
31
|
+
|
|
32
|
+
configure_logging()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class NodeRuleBase:
|
|
36
|
+
"""Base class for node classification rules.
|
|
37
|
+
|
|
38
|
+
This class defines the interface for rules that determine whether a node
|
|
39
|
+
should be kept in high precision or converted to low precision.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
@abc.abstractmethod
|
|
43
|
+
def _check_inner(self, node):
|
|
44
|
+
"""Implement this method to check if node conversion should be skipped based on rule criteria."""
|
|
45
|
+
|
|
46
|
+
def _log_skipped(self, node, **kwargs):
|
|
47
|
+
"""Log information about skipped nodes."""
|
|
48
|
+
logger.info(f"Skipping node {node.name}: {self.__class__.__name__}")
|
|
49
|
+
|
|
50
|
+
def check(self, node):
|
|
51
|
+
"""Check if a node should be skipped based on the rule.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
node: The ONNX node to check.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
bool: True if the node should be kept in high precision, False otherwise.
|
|
58
|
+
"""
|
|
59
|
+
result = self._check_inner(node)
|
|
60
|
+
if result:
|
|
61
|
+
self._log_skipped(node)
|
|
62
|
+
return True
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class DisabledNodeNameRegexRule(NodeRuleBase):
|
|
67
|
+
"""Rule for keeping nodes with matching names in high precision."""
|
|
68
|
+
|
|
69
|
+
def __init__(self, disabled_node_name_regex):
|
|
70
|
+
"""Initialize the rule.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
disabled_node_name_regex: List of regex patterns for node names to keep in high precision.
|
|
74
|
+
"""
|
|
75
|
+
self.disabled_node_name_regex = disabled_node_name_regex
|
|
76
|
+
|
|
77
|
+
def _check_inner(self, node):
|
|
78
|
+
return any(re.match(regex, node.name) for regex in self.disabled_node_name_regex)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class DisabledOpTypes(NodeRuleBase):
|
|
82
|
+
"""Rule for keeping nodes with specific operation types in high precision."""
|
|
83
|
+
|
|
84
|
+
def __init__(self, op_types_to_exclude):
|
|
85
|
+
"""Initialize the rule.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
op_types_to_exclude: List of operation types to keep in high precision.
|
|
89
|
+
"""
|
|
90
|
+
self.op_types_to_exclude = op_types_to_exclude
|
|
91
|
+
|
|
92
|
+
def _check_inner(self, node):
|
|
93
|
+
return node.op_type in self.op_types_to_exclude
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class InitializerRangeRule(NodeRuleBase):
|
|
97
|
+
"""Rule for keeping nodes with out-of-range initializers in high precision."""
|
|
98
|
+
|
|
99
|
+
def __init__(self, init_max, node_to_init_map):
|
|
100
|
+
"""Initialize the rule.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
init_max: Maximum absolute value allowed for initializers.
|
|
104
|
+
node_to_init_map: Mapping from node names to their initializers.
|
|
105
|
+
"""
|
|
106
|
+
self.init_max = init_max
|
|
107
|
+
self.node_to_init_map = node_to_init_map
|
|
108
|
+
self.init_data = (None, None)
|
|
109
|
+
|
|
110
|
+
def _check_inner(self, node):
|
|
111
|
+
for init in self.node_to_init_map[node.name]:
|
|
112
|
+
np_array = onnx.numpy_helper.to_array(init)
|
|
113
|
+
if np_array.dtype == np.float32 and np.any(np.abs(np_array) > self.init_max):
|
|
114
|
+
self.init_data = init.name, np_array
|
|
115
|
+
return True
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
def _log_skipped(self, node, **kwargs):
|
|
119
|
+
"""Log information about skipped nodes with initializer range violations."""
|
|
120
|
+
if self.init_data[1] is not None:
|
|
121
|
+
logger.info(
|
|
122
|
+
f"Skipping node {node.name}: initializer {self.init_data[0]} out of range: "
|
|
123
|
+
f"min={self.init_data[1].min()}, max={self.init_data[1].max()}, range=[{-self.init_max},"
|
|
124
|
+
f"{self.init_max}]"
|
|
125
|
+
)
|
|
126
|
+
else:
|
|
127
|
+
super()._log_skipped(node, **kwargs)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class IORangeRule(NodeRuleBase):
|
|
131
|
+
"""Rule for keeping nodes with out-of-range inputs/outputs in high precision."""
|
|
132
|
+
|
|
133
|
+
def __init__(self, data_max, reference_data, node_to_init_map):
|
|
134
|
+
"""Initialize the rule.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
data_max: Maximum absolute value allowed for node I/O.
|
|
138
|
+
reference_data: Reference data for checking I/O ranges.
|
|
139
|
+
node_to_init_map: Mapping from node names to their initializers.
|
|
140
|
+
"""
|
|
141
|
+
self.data_max = data_max
|
|
142
|
+
self.reference_data = reference_data
|
|
143
|
+
self.node_to_init_map = node_to_init_map
|
|
144
|
+
self.output_data = None
|
|
145
|
+
|
|
146
|
+
def _check_inner(self, node):
|
|
147
|
+
def is_io_out_of_range(node, tensor_name):
|
|
148
|
+
if tensor_name not in self.reference_data:
|
|
149
|
+
# Issue a warning only if the tensor is not an initializer/network input
|
|
150
|
+
init_names = [init.name for init in self.node_to_init_map.get(node.name, [])]
|
|
151
|
+
init_names.extend(node.input)
|
|
152
|
+
if tensor_name not in init_names:
|
|
153
|
+
logger.warning(
|
|
154
|
+
f"Node {node.name}: Tensor {tensor_name} not found in reference data."
|
|
155
|
+
)
|
|
156
|
+
return False
|
|
157
|
+
ref_data = self.reference_data[tensor_name]
|
|
158
|
+
if ref_data.size == 0:
|
|
159
|
+
logger.debug(
|
|
160
|
+
f"Node {node.name}: Tensor {tensor_name} has size 0. Skipping I/O range check."
|
|
161
|
+
)
|
|
162
|
+
return False
|
|
163
|
+
logger.debug(
|
|
164
|
+
f"Node {node.name}: reference data: min={np.min(ref_data)}, max={np.max(ref_data)}"
|
|
165
|
+
)
|
|
166
|
+
if np.any(np.abs(ref_data) > self.data_max):
|
|
167
|
+
self.output_data = ref_data
|
|
168
|
+
return True
|
|
169
|
+
|
|
170
|
+
if node.op_type == "Constant":
|
|
171
|
+
return False
|
|
172
|
+
if self.reference_data:
|
|
173
|
+
for in_name in node.input:
|
|
174
|
+
if is_io_out_of_range(node, in_name):
|
|
175
|
+
return True
|
|
176
|
+
for out_name in node.output:
|
|
177
|
+
if is_io_out_of_range(node, out_name):
|
|
178
|
+
return True
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
def _log_skipped(self, node, **kwargs):
|
|
182
|
+
"""Log information about skipped nodes with I/O range violations."""
|
|
183
|
+
if self.output_data is not None:
|
|
184
|
+
logger.info(
|
|
185
|
+
f"Skipping node {node.name}: reference IO out of range: min={np.min(self.output_data)}, "
|
|
186
|
+
f"max={np.max(self.output_data)}, range=[{-self.data_max}, {self.data_max}]"
|
|
187
|
+
)
|
|
188
|
+
else:
|
|
189
|
+
super()._log_skipped(node, **kwargs)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class DepthOfReductionRule(NodeRuleBase):
|
|
193
|
+
"""Rule for keeping nodes with high depth of reduction in high precision."""
|
|
194
|
+
|
|
195
|
+
def __init__(self, max_depth_of_reduction, reference_data, node_to_init_map, initializer_map):
|
|
196
|
+
"""Initialize the rule.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
max_depth_of_reduction: Maximum depth of reduction allowed in low precision.
|
|
200
|
+
reference_data: Reference data for checking I/O ranges.
|
|
201
|
+
node_to_init_map: Mapping from node names to their initializers.
|
|
202
|
+
initializer_map: Mapping from initializer names to initializers.
|
|
203
|
+
"""
|
|
204
|
+
self.max_depth_of_reduction = max_depth_of_reduction
|
|
205
|
+
self.reference_data = reference_data
|
|
206
|
+
self.node_to_init_map = node_to_init_map
|
|
207
|
+
self.initializer_map = initializer_map
|
|
208
|
+
self.reduction_depth = 0
|
|
209
|
+
|
|
210
|
+
def _get_tensor_shape(self, tensor_name):
|
|
211
|
+
"""Get tensor shape from reference data."""
|
|
212
|
+
if tensor_name in self.reference_data:
|
|
213
|
+
return self.reference_data[tensor_name].shape
|
|
214
|
+
if tensor_name in self.initializer_map:
|
|
215
|
+
return self.initializer_map[tensor_name].dims
|
|
216
|
+
return None
|
|
217
|
+
|
|
218
|
+
def _log_skipped(self, node, **kwargs):
|
|
219
|
+
"""Log information about skipped nodes with depth of reduction violations."""
|
|
220
|
+
if self.reduction_depth > 0:
|
|
221
|
+
logger.info(
|
|
222
|
+
f"Skipping node {node.name}: depth of reduction {self.reduction_depth} exceeds "
|
|
223
|
+
f"{self.max_depth_of_reduction}."
|
|
224
|
+
)
|
|
225
|
+
else:
|
|
226
|
+
super()._log_skipped(node, **kwargs)
|
|
227
|
+
|
|
228
|
+
def _check_inner(self, node):
|
|
229
|
+
# All reduction ops rely on shape of input[0]
|
|
230
|
+
input_0_dims = self._get_tensor_shape(node.input[0]) if len(node.input) > 0 else None
|
|
231
|
+
if input_0_dims is None:
|
|
232
|
+
return False
|
|
233
|
+
self.reduction_depth = 0
|
|
234
|
+
if node.op_type == "Attention":
|
|
235
|
+
# Attention: input (batch_size, sequence_length, hidden_size)
|
|
236
|
+
# or (batch_size, kv_num_heads, total_sequence_length, head_size)
|
|
237
|
+
assert len(input_0_dims) == 3 or len(input_0_dims) == 4
|
|
238
|
+
hidden_size = (
|
|
239
|
+
input_0_dims[2] if len(input_0_dims) == 3 else input_0_dims[1] * input_0_dims[3]
|
|
240
|
+
)
|
|
241
|
+
self.reduction_depth = hidden_size
|
|
242
|
+
elif node.op_type == "Conv":
|
|
243
|
+
# Conv: input (N x C x D1 x D2 ... x Dn)
|
|
244
|
+
# weight (out_channels, in_channels, kD1, kD2, ... kDn)
|
|
245
|
+
# Reduction depth = in_channels * kernel_volume
|
|
246
|
+
weight_shape = self._get_tensor_shape(node.input[1]) if len(node.input) > 1 else None
|
|
247
|
+
if weight_shape is None:
|
|
248
|
+
return False
|
|
249
|
+
in_channels = weight_shape[1]
|
|
250
|
+
kernel_volume = np.prod(weight_shape[2:])
|
|
251
|
+
self.reduction_depth = in_channels * kernel_volume
|
|
252
|
+
elif node.op_type == "CumSum":
|
|
253
|
+
axis_name = node.input[1] if len(node.input) > 1 else None
|
|
254
|
+
if axis_name is None:
|
|
255
|
+
return False
|
|
256
|
+
# Find the axis initializer
|
|
257
|
+
axis_init = None
|
|
258
|
+
for init in self.node_to_init_map.get(node.name, []):
|
|
259
|
+
if init.name == axis_name:
|
|
260
|
+
axis_init = init
|
|
261
|
+
break
|
|
262
|
+
if axis_init is None:
|
|
263
|
+
return False
|
|
264
|
+
axis_array = onnx.numpy_helper.to_array(axis_init)
|
|
265
|
+
assert axis_array.ndim == 0 or (axis_array.ndim == 1 and axis_array.size == 1)
|
|
266
|
+
axis = int(axis_array.item())
|
|
267
|
+
if input_0_dims[axis] > self.max_depth_of_reduction:
|
|
268
|
+
self.reduction_depth = input_0_dims[axis]
|
|
269
|
+
|
|
270
|
+
elif node.op_type == "Gemm":
|
|
271
|
+
# GEMM: A (M, K) @ B (K, N) = C (M, N)
|
|
272
|
+
# Check for transpose attributes
|
|
273
|
+
trans_a = False
|
|
274
|
+
for attr in node.attribute:
|
|
275
|
+
if attr.name == "transA":
|
|
276
|
+
trans_a = bool(attr.i)
|
|
277
|
+
|
|
278
|
+
# Get K dimension based on transpose flag
|
|
279
|
+
self.reduction_depth = (
|
|
280
|
+
input_0_dims[0] if trans_a else input_0_dims[1]
|
|
281
|
+
) # A is (K, M) when transposed
|
|
282
|
+
|
|
283
|
+
elif node.op_type == "MatMul":
|
|
284
|
+
# MatMul: (..., M, K) @ (..., K, N) = (..., M, N)
|
|
285
|
+
# K is the last dimension of first input
|
|
286
|
+
if len(input_0_dims) >= 2:
|
|
287
|
+
self.reduction_depth = input_0_dims[-1]
|
|
288
|
+
elif node.op_type == "Mean":
|
|
289
|
+
self.reduction_depth = len(input_0_dims)
|
|
290
|
+
elif node.op_type in [
|
|
291
|
+
"ReduceL1",
|
|
292
|
+
"ReduceL2",
|
|
293
|
+
"ReduceLogSum",
|
|
294
|
+
"ReduceLogSumExp",
|
|
295
|
+
"ReduceMean",
|
|
296
|
+
"ReduceProd",
|
|
297
|
+
"ReduceSum",
|
|
298
|
+
"ReduceSumSquare",
|
|
299
|
+
]:
|
|
300
|
+
if len(node.input) > 1:
|
|
301
|
+
axes_name = node.input[1]
|
|
302
|
+
# Find the axes initializer
|
|
303
|
+
axes_init = None
|
|
304
|
+
for init in self.node_to_init_map.get(node.name, []):
|
|
305
|
+
if init.name == axes_name:
|
|
306
|
+
axes_init = init
|
|
307
|
+
break
|
|
308
|
+
if axes_init is None:
|
|
309
|
+
return False
|
|
310
|
+
axes_array = onnx.numpy_helper.to_array(axes_init)
|
|
311
|
+
if axes_array.ndim == 0:
|
|
312
|
+
axes_array = [int(axes_array.item())]
|
|
313
|
+
else:
|
|
314
|
+
assert axes_array.ndim == 1
|
|
315
|
+
axes_array = axes_array.astype(np.int64)
|
|
316
|
+
else:
|
|
317
|
+
axes_array = range(len(input_0_dims))
|
|
318
|
+
for axis in axes_array:
|
|
319
|
+
if input_0_dims[axis] > self.max_depth_of_reduction:
|
|
320
|
+
self.reduction_depth = input_0_dims[axis]
|
|
321
|
+
return True
|
|
322
|
+
return self.reduction_depth > self.max_depth_of_reduction
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class NodeClassifier:
|
|
326
|
+
"""Main class for classifying nodes into high and low precision groups."""
|
|
327
|
+
|
|
328
|
+
def __init__(
|
|
329
|
+
self,
|
|
330
|
+
model: onnx.ModelProto,
|
|
331
|
+
node_to_init_map: dict[str, list[onnx.TensorProto]] | None = None,
|
|
332
|
+
initializer_map: dict[str, onnx.TensorProto] | None = None,
|
|
333
|
+
nodes_to_exclude: list[str] | None = None,
|
|
334
|
+
op_types_to_exclude: list[str] | None = None,
|
|
335
|
+
custom_rule: NodeRuleBase | None = None,
|
|
336
|
+
data_max: float | None = 1000.0,
|
|
337
|
+
init_max: float | None = np.finfo(np.float16).max,
|
|
338
|
+
max_depth_of_reduction: int | None = None,
|
|
339
|
+
):
|
|
340
|
+
"""Initialize the node classifier.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
model: The ONNX model to classify nodes for.
|
|
344
|
+
node_to_init_map: Mapping from node names to their initializers.
|
|
345
|
+
initializer_map: Mapping from initializer names to their tensors.
|
|
346
|
+
nodes_to_exclude: List of regex patterns for node names to keep in high precision.
|
|
347
|
+
op_types_to_exclude: List of operation types to keep in high precision.
|
|
348
|
+
custom_rule: Optional custom classification rule.
|
|
349
|
+
data_max: Maximum absolute value allowed for node I/O.
|
|
350
|
+
init_max: Maximum absolute value allowed for initializers.
|
|
351
|
+
max_depth_of_reduction: Maximum depth of reduction allowed in low precision.
|
|
352
|
+
"""
|
|
353
|
+
self.model = model
|
|
354
|
+
self.node_to_init_map = node_to_init_map
|
|
355
|
+
self.initializer_map = initializer_map
|
|
356
|
+
self.nodes_to_exclude = nodes_to_exclude
|
|
357
|
+
self.op_types_to_exclude = op_types_to_exclude
|
|
358
|
+
self.custom_rule = custom_rule
|
|
359
|
+
self.data_max = data_max
|
|
360
|
+
self.init_max = init_max
|
|
361
|
+
self.max_depth_of_reduction = max_depth_of_reduction
|
|
362
|
+
|
|
363
|
+
def _gen_block_node_rules(self, reference_data):
|
|
364
|
+
"""Generate list of rules for blocking nodes from precision conversion.
|
|
365
|
+
|
|
366
|
+
Args:
|
|
367
|
+
reference_data: Reference data for checking I/O ranges.
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
list[NodeRuleBase]: List of rules to apply.
|
|
371
|
+
"""
|
|
372
|
+
block_node_rules: list[NodeRuleBase] = []
|
|
373
|
+
if self.nodes_to_exclude:
|
|
374
|
+
block_node_rules.append(DisabledNodeNameRegexRule(self.nodes_to_exclude))
|
|
375
|
+
if self.op_types_to_exclude:
|
|
376
|
+
block_node_rules.append(DisabledOpTypes(self.op_types_to_exclude))
|
|
377
|
+
if self.init_max is not None:
|
|
378
|
+
block_node_rules.append(InitializerRangeRule(self.init_max, self.node_to_init_map))
|
|
379
|
+
if reference_data:
|
|
380
|
+
block_node_rules.append(
|
|
381
|
+
IORangeRule(self.data_max, reference_data, self.node_to_init_map)
|
|
382
|
+
)
|
|
383
|
+
if self.max_depth_of_reduction is not None:
|
|
384
|
+
block_node_rules.append(
|
|
385
|
+
DepthOfReductionRule(
|
|
386
|
+
self.max_depth_of_reduction,
|
|
387
|
+
reference_data,
|
|
388
|
+
self.node_to_init_map,
|
|
389
|
+
self.initializer_map,
|
|
390
|
+
)
|
|
391
|
+
)
|
|
392
|
+
if self.custom_rule:
|
|
393
|
+
block_node_rules.append(self.custom_rule)
|
|
394
|
+
return block_node_rules
|
|
395
|
+
|
|
396
|
+
def run(self, ref_outputs_dict=None):
|
|
397
|
+
"""Run node classification.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
ref_outputs_dict: Optional tensors' reference data.
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
tuple: Lists of node names (low_precision_nodes, high_precision_nodes).
|
|
404
|
+
"""
|
|
405
|
+
block_node_rules = self._gen_block_node_rules(ref_outputs_dict)
|
|
406
|
+
low_precision_nodes = []
|
|
407
|
+
high_precision_nodes = []
|
|
408
|
+
for node in self.model.graph.node:
|
|
409
|
+
# If any condition is met - node will be executed in high precision
|
|
410
|
+
if any(rule.check(node) for rule in block_node_rules):
|
|
411
|
+
high_precision_nodes.append(node.name)
|
|
412
|
+
else:
|
|
413
|
+
low_precision_nodes.append(node.name)
|
|
414
|
+
logger.debug(f"Low Precision Nodes: {low_precision_nodes}")
|
|
415
|
+
logger.debug(f"High Precision Nodes: {high_precision_nodes}")
|
|
416
|
+
return low_precision_nodes, high_precision_nodes
|