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,1164 @@
|
|
|
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
|
+
# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
"""Code that export quantized Megatron Core models for deployment."""
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
import tempfile
|
|
24
|
+
from collections import OrderedDict
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import Any
|
|
27
|
+
from warnings import warn
|
|
28
|
+
|
|
29
|
+
import torch
|
|
30
|
+
import torch.distributed
|
|
31
|
+
import torch.nn as nn
|
|
32
|
+
from huggingface_hub import snapshot_download
|
|
33
|
+
from safetensors.torch import safe_open, save_file
|
|
34
|
+
from tqdm import tqdm
|
|
35
|
+
|
|
36
|
+
from modelopt import __version__
|
|
37
|
+
from modelopt.torch.utils import import_plugin
|
|
38
|
+
|
|
39
|
+
from .model_config import (
|
|
40
|
+
KV_CACHE_FP8,
|
|
41
|
+
QUANTIZATION_FP8,
|
|
42
|
+
QUANTIZATION_FP8_PB_REAL,
|
|
43
|
+
QUANTIZATION_FP8_PB_WO,
|
|
44
|
+
QUANTIZATION_NVFP4,
|
|
45
|
+
)
|
|
46
|
+
from .plugins.mcore_common import all_mcore_hf_export_mapping
|
|
47
|
+
from .plugins.mcore_custom import CustomModuleMapping, save_safetensors
|
|
48
|
+
from .plugins.megatron_importer import GPTModelImporter
|
|
49
|
+
from .quant_utils import (
|
|
50
|
+
get_activation_scaling_factor,
|
|
51
|
+
get_kv_cache_dtype,
|
|
52
|
+
get_quantization_format,
|
|
53
|
+
get_scaling_factor,
|
|
54
|
+
get_weight_block_size,
|
|
55
|
+
get_weight_scaling_factor,
|
|
56
|
+
get_weight_scaling_factor_2,
|
|
57
|
+
to_quantized_weight,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
with import_plugin("transformers", verbose=False):
|
|
61
|
+
import transformers
|
|
62
|
+
from transformers import AutoProcessor
|
|
63
|
+
|
|
64
|
+
has_mcore = False
|
|
65
|
+
with import_plugin("megatron"):
|
|
66
|
+
from megatron.core.models.gpt import GPTModel
|
|
67
|
+
from megatron.core.models.mamba import MambaModel
|
|
68
|
+
from megatron.core.models.multimodal.llava_model import LLaVAModel
|
|
69
|
+
from megatron.core.parallel_state import (
|
|
70
|
+
get_pipeline_model_parallel_rank,
|
|
71
|
+
get_pipeline_model_parallel_world_size,
|
|
72
|
+
)
|
|
73
|
+
from megatron.core.ssm.mamba_layer import MambaLayer
|
|
74
|
+
from megatron.core.transformer.identity_op import IdentityOp
|
|
75
|
+
from megatron.core.transformer.torch_norm import L2Norm
|
|
76
|
+
from megatron.core.transformer.transformer_layer import TransformerLayer
|
|
77
|
+
|
|
78
|
+
has_mcore = True
|
|
79
|
+
|
|
80
|
+
__all__ = ["export_mcore_gpt_to_hf", "import_mcore_gpt_from_hf"]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# This path uses output_quantizer for KV cache quantization.
|
|
84
|
+
# The function below is the old version of get_kv_cache_scaling_factor which is now refactored to handle bmm_quantizer.
|
|
85
|
+
def get_kv_cache_scaling_factor(kv_module: nn.Module) -> torch.Tensor:
|
|
86
|
+
"""Returns the kv_cache scaling factor if output quantizer is set. Else returns None by default."""
|
|
87
|
+
scaling_factor = (
|
|
88
|
+
get_scaling_factor(kv_module.output_quantizer)
|
|
89
|
+
if hasattr(kv_module, "output_quantizer")
|
|
90
|
+
else None
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
if not scaling_factor:
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
# For FP8, we recommend default kv cache scaling factor to be 1.
|
|
97
|
+
if get_kv_cache_dtype(kv_module) == KV_CACHE_FP8:
|
|
98
|
+
if scaling_factor.item() > 0.5:
|
|
99
|
+
warn(
|
|
100
|
+
f"!!!!Large KV activations detected: {scaling_factor.item()}, "
|
|
101
|
+
"Quantized KV cache may lead to higher accuracy drop.\n!!!!"
|
|
102
|
+
)
|
|
103
|
+
scaling_factor = torch.max(
|
|
104
|
+
scaling_factor,
|
|
105
|
+
torch.tensor([1.0], dtype=torch.float, device=scaling_factor.device),
|
|
106
|
+
)
|
|
107
|
+
return scaling_factor
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def get_quantized_state(
|
|
111
|
+
module: torch.nn.Module,
|
|
112
|
+
dtype: torch.dtype = torch.float16,
|
|
113
|
+
) -> tuple[dict[str, torch.Tensor], str, int]:
|
|
114
|
+
"""Return a state_dict, quantization format, and block_size of the module.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
module: The target module to perform real quantization.
|
|
118
|
+
dtype: The default data type.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Tuple: state_dict, quantization format, and block_size of the module.
|
|
122
|
+
"""
|
|
123
|
+
name_to_value = {}
|
|
124
|
+
qformat: str = get_quantization_format(module)
|
|
125
|
+
block_size = get_weight_block_size(module)
|
|
126
|
+
|
|
127
|
+
if hasattr(module, "weight") and module.weight is not None:
|
|
128
|
+
weight = module.weight.to(dtype).cpu()
|
|
129
|
+
name_to_value["weight"] = weight
|
|
130
|
+
else:
|
|
131
|
+
return name_to_value, qformat, block_size
|
|
132
|
+
|
|
133
|
+
if hasattr(module, "bias") and module.bias is not None:
|
|
134
|
+
name_to_value["bias"] = module.bias.to(dtype).cpu()
|
|
135
|
+
|
|
136
|
+
if hasattr(module, "expert_bias") and module.expert_bias is not None:
|
|
137
|
+
name_to_value["expert_bias"] = module.expert_bias.to(dtype).cpu()
|
|
138
|
+
|
|
139
|
+
# Getting the weight scales
|
|
140
|
+
weight_scale = get_weight_scaling_factor(module)
|
|
141
|
+
weight_scale_2 = get_weight_scaling_factor_2(module)
|
|
142
|
+
if weight_scale is not None:
|
|
143
|
+
name_to_value["weight_scale"] = weight_scale
|
|
144
|
+
|
|
145
|
+
if weight_scale_2 is not None:
|
|
146
|
+
name_to_value["weight_scale_2"] = weight_scale_2
|
|
147
|
+
|
|
148
|
+
# Getting the input scale
|
|
149
|
+
input_scale = get_activation_scaling_factor(module)
|
|
150
|
+
if input_scale is not None:
|
|
151
|
+
name_to_value["input_scale"] = input_scale
|
|
152
|
+
# TODO (chenhany): support AWQ with pre_quant_scale
|
|
153
|
+
if hasattr(module.input_quantizer, "_pre_quant_scale"):
|
|
154
|
+
raise ValueError("Detect pre_quant_scale! SmoothQuant/AWQ are not yet supported!")
|
|
155
|
+
|
|
156
|
+
if hasattr(module, "output_quantizer"):
|
|
157
|
+
output_scale = get_kv_cache_scaling_factor(module)
|
|
158
|
+
if output_scale is not None:
|
|
159
|
+
name_to_value["output_scale"] = output_scale
|
|
160
|
+
|
|
161
|
+
return name_to_value, qformat, block_size
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class GPTModelExporter:
|
|
165
|
+
"""Megatron Core GPTModel Exporter.
|
|
166
|
+
|
|
167
|
+
The Exporter is created by `export_mcore_gpt_to_hf` to host attributes
|
|
168
|
+
and methods that export a quantized Megatron Core GPTModel to the Hugging
|
|
169
|
+
Face unified checkpoint.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
model: The Megatron Core GPTModel instance.
|
|
173
|
+
pretrained_model_name_or_path: Can be either: the *model id* of a
|
|
174
|
+
pretrained model hosted inside a model repo on huggingface.co; or
|
|
175
|
+
a *directory* containing model weights saved using
|
|
176
|
+
[`~PreTrainedModel.save_pretrained`], e.g., `./my_model_directory/`.
|
|
177
|
+
export_extra_modules: If True, export extra modules like medusa_heads or
|
|
178
|
+
eagle_module. Otherwise, only export the base model.
|
|
179
|
+
dtype: The weights data type to export the unquantized layers.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
def __init__(
|
|
183
|
+
self,
|
|
184
|
+
model: torch.nn.Module,
|
|
185
|
+
pretrained_model_name_or_path: str | os.PathLike | None = None,
|
|
186
|
+
export_extra_modules: bool = False,
|
|
187
|
+
dtype=torch.bfloat16,
|
|
188
|
+
trust_remote_code: bool = True,
|
|
189
|
+
):
|
|
190
|
+
"""Create a GPTModel exporter instance."""
|
|
191
|
+
if not isinstance(model, (GPTModel, MambaModel, LLaVAModel)):
|
|
192
|
+
raise ValueError("Input to GPTModelExport must be a megatron.core.models.GPTModel!")
|
|
193
|
+
|
|
194
|
+
self._state_dict = OrderedDict()
|
|
195
|
+
self._hf_pretrained_model_name = pretrained_model_name_or_path
|
|
196
|
+
self._hf_config = transformers.AutoConfig.from_pretrained(
|
|
197
|
+
pretrained_model_name_or_path, trust_remote_code=trust_remote_code
|
|
198
|
+
)
|
|
199
|
+
# If multimodal, extra the text_config
|
|
200
|
+
self._hf_text_config = getattr(self._hf_config, "text_config", self._hf_config)
|
|
201
|
+
|
|
202
|
+
# Update hf_config
|
|
203
|
+
self._hf_text_config.num_hidden_layers = model.config.num_layers
|
|
204
|
+
self._hf_text_config.hidden_size = model.config.hidden_size
|
|
205
|
+
self._hf_text_config.head_dim = model.config.kv_channels
|
|
206
|
+
self._hf_text_config.num_attention_heads = model.config.num_attention_heads
|
|
207
|
+
self._hf_text_config.num_key_value_heads = model.config.num_query_groups
|
|
208
|
+
self.is_multimodal = isinstance(model, LLaVAModel)
|
|
209
|
+
if not self.is_multimodal:
|
|
210
|
+
self._hf_text_config.intermediate_size = model.config.ffn_hidden_size
|
|
211
|
+
self._hf_quant_config = None
|
|
212
|
+
self._hf_extra_config = None
|
|
213
|
+
self.export_extra_modules = export_extra_modules
|
|
214
|
+
self.is_multimodal = isinstance(model, LLaVAModel)
|
|
215
|
+
self.model = model.language_model if self.is_multimodal else model
|
|
216
|
+
self.dtype = dtype
|
|
217
|
+
self.trust_remote_code = trust_remote_code
|
|
218
|
+
self.arch = self._hf_config.architectures[0]
|
|
219
|
+
# TODO: May modify this later according to what quantization exported ckpt is, currently only support BF16.
|
|
220
|
+
if self.arch == "GptOssForCausalLM":
|
|
221
|
+
if hasattr(self._hf_config, "quantization_config"):
|
|
222
|
+
del self._hf_config.quantization_config
|
|
223
|
+
self.all_rules = self._populate_rule_book()
|
|
224
|
+
self.rules = self.all_rules[self.arch]
|
|
225
|
+
|
|
226
|
+
if not hasattr(model, "_modelopt_state"):
|
|
227
|
+
return
|
|
228
|
+
|
|
229
|
+
for mode, mode_cfg in model._modelopt_state:
|
|
230
|
+
if mode == "medusa" and export_extra_modules:
|
|
231
|
+
medusa_config = {
|
|
232
|
+
"num_medusa_heads": mode_cfg["config"]["medusa_num_heads"],
|
|
233
|
+
"num_medusa_layers": mode_cfg["config"]["medusa_num_layers"],
|
|
234
|
+
}
|
|
235
|
+
self._hf_config.medusa = medusa_config
|
|
236
|
+
self.rules = self.all_rules["MedusaLlamaForCausalLM"]
|
|
237
|
+
|
|
238
|
+
if mode == "eagle" and export_extra_modules:
|
|
239
|
+
is_eagle3 = mode_cfg["config"]["eagle_architecture_config"]["use_aux_hidden_state"]
|
|
240
|
+
|
|
241
|
+
architectures = "LlamaForCausalLMEagle3" if is_eagle3 else "LlamaForCausalLMEagle"
|
|
242
|
+
|
|
243
|
+
self.rules = self.all_rules[architectures]
|
|
244
|
+
|
|
245
|
+
if torch.distributed.get_rank() == torch.distributed.get_world_size() - 1:
|
|
246
|
+
# By default, we use Llama-3.1
|
|
247
|
+
self._hf_extra_config = transformers.AutoConfig.from_pretrained(
|
|
248
|
+
"nvidia/Llama-3.1-8B-Instruct-FP8", trust_remote_code=self.trust_remote_code
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
eagle_config = {
|
|
252
|
+
"use_input_layernorm_in_first_layer": mode_cfg["config"][
|
|
253
|
+
"eagle_architecture_config"
|
|
254
|
+
]["use_input_layernorm_in_first_layer"],
|
|
255
|
+
"use_last_layernorm": mode_cfg["config"]["eagle_architecture_config"][
|
|
256
|
+
"use_last_layernorm"
|
|
257
|
+
],
|
|
258
|
+
"use_mtp_layernorm": mode_cfg["config"]["eagle_architecture_config"][
|
|
259
|
+
"use_mtp_layernorm"
|
|
260
|
+
],
|
|
261
|
+
"use_aux_hidden_state": mode_cfg["config"]["eagle_architecture_config"][
|
|
262
|
+
"use_aux_hidden_state"
|
|
263
|
+
],
|
|
264
|
+
"eagle_aux_hidden_state_layer_ids": model.eagle_config.eagle_aux_hidden_state_layer_ids,
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
eagle_config_update = {
|
|
268
|
+
"architectures": [architectures],
|
|
269
|
+
"head_dim": model.eagle_module.config.kv_channels,
|
|
270
|
+
"hidden_act": self._hf_text_config.hidden_act,
|
|
271
|
+
"hidden_size": self._hf_text_config.hidden_size,
|
|
272
|
+
"intermediate_size": model.eagle_module.config.ffn_hidden_size,
|
|
273
|
+
"max_position_embeddings": self._hf_text_config.max_position_embeddings,
|
|
274
|
+
"num_attention_heads": model.eagle_module.config.num_attention_heads,
|
|
275
|
+
"num_key_value_heads": model.eagle_module.config.num_query_groups,
|
|
276
|
+
"num_hidden_layers": mode_cfg["config"]["eagle_architecture_config"][
|
|
277
|
+
"num_hidden_layers"
|
|
278
|
+
],
|
|
279
|
+
"vocab_size": self._hf_text_config.vocab_size,
|
|
280
|
+
# Unset any special token ids given that the tokenizer can change here.
|
|
281
|
+
"bos_token_id": None,
|
|
282
|
+
"eos_token_id": None,
|
|
283
|
+
"pad_token_id": None,
|
|
284
|
+
"sep_token_id": None,
|
|
285
|
+
# The following attributes are EAGLE specific
|
|
286
|
+
"eagle_config": eagle_config,
|
|
287
|
+
"draft_vocab_size": mode_cfg["config"]["eagle_architecture_config"][
|
|
288
|
+
"draft_vocab_size"
|
|
289
|
+
],
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
self._hf_extra_config.update(eagle_config_update)
|
|
293
|
+
|
|
294
|
+
def save_pretrained(
|
|
295
|
+
self,
|
|
296
|
+
save_directory: str | os.PathLike,
|
|
297
|
+
pretrained_model_name_or_path: str | os.PathLike | None = None,
|
|
298
|
+
):
|
|
299
|
+
"""Save a unified checkpoint which can be deployed by vLLM and TensorRT-LLM.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
save_directory: Directory to which to save. Will be created if it doesn't exist.
|
|
303
|
+
"""
|
|
304
|
+
pp_rank = get_pipeline_model_parallel_rank()
|
|
305
|
+
pp_size = get_pipeline_model_parallel_world_size()
|
|
306
|
+
|
|
307
|
+
# We use the 1st PP rank to handle VLM because vision_models
|
|
308
|
+
# and vision_proj only exist in the first stage.
|
|
309
|
+
is_first_stage_main_rank = pp_rank == 0
|
|
310
|
+
# We use the last PP rank to write the config because
|
|
311
|
+
# medusa_heads and eagle_module only exist in the last stage.
|
|
312
|
+
is_last_stage_main_rank = pp_rank == pp_size - 1
|
|
313
|
+
|
|
314
|
+
# Main export process
|
|
315
|
+
state_dict = self.extra_state_dict if self.export_extra_modules else self.state_dict
|
|
316
|
+
quantization_format = get_quantization_format(self.model)
|
|
317
|
+
quantization = None
|
|
318
|
+
kv_cache_quantization = None
|
|
319
|
+
|
|
320
|
+
if quantization_format in (
|
|
321
|
+
QUANTIZATION_FP8_PB_REAL,
|
|
322
|
+
QUANTIZATION_FP8_PB_WO,
|
|
323
|
+
):
|
|
324
|
+
quantization = quantization_format
|
|
325
|
+
elif quantization_format == QUANTIZATION_FP8:
|
|
326
|
+
quantization = "FP8"
|
|
327
|
+
elif quantization_format == QUANTIZATION_NVFP4:
|
|
328
|
+
quantization = "NVFP4"
|
|
329
|
+
|
|
330
|
+
# We use the last PP rank and the 1st EP rank to write the config because
|
|
331
|
+
# medusa_heads and eagle_module only exist in the last stage.
|
|
332
|
+
if is_last_stage_main_rank:
|
|
333
|
+
if self.export_extra_modules and self._hf_extra_config is not None:
|
|
334
|
+
self._hf_extra_config.save_pretrained(save_directory)
|
|
335
|
+
else:
|
|
336
|
+
self._hf_config.save_pretrained(save_directory)
|
|
337
|
+
try:
|
|
338
|
+
generation_config = transformers.GenerationConfig.from_pretrained(
|
|
339
|
+
self._hf_pretrained_model_name
|
|
340
|
+
)
|
|
341
|
+
generation_config.save_pretrained(save_directory)
|
|
342
|
+
except OSError:
|
|
343
|
+
pass
|
|
344
|
+
try:
|
|
345
|
+
tokenizer = transformers.AutoTokenizer.from_pretrained(
|
|
346
|
+
self._hf_pretrained_model_name
|
|
347
|
+
)
|
|
348
|
+
tokenizer.save_pretrained(save_directory)
|
|
349
|
+
except OSError:
|
|
350
|
+
pass
|
|
351
|
+
except TypeError:
|
|
352
|
+
pass
|
|
353
|
+
try:
|
|
354
|
+
# Load and save preprocessor config from the original model
|
|
355
|
+
processor = AutoProcessor.from_pretrained(
|
|
356
|
+
self._hf_pretrained_model_name, trust_remote_code=self.trust_remote_code
|
|
357
|
+
)
|
|
358
|
+
if hasattr(processor, "image_processor"):
|
|
359
|
+
processor.image_processor.save_pretrained(save_directory)
|
|
360
|
+
except (OSError, ValueError, ImportError):
|
|
361
|
+
pass
|
|
362
|
+
|
|
363
|
+
if is_last_stage_main_rank:
|
|
364
|
+
hf_quant_config = {
|
|
365
|
+
"producer": {
|
|
366
|
+
"name": "modelopt",
|
|
367
|
+
"version": __version__,
|
|
368
|
+
},
|
|
369
|
+
"quantization": {
|
|
370
|
+
"quant_algo": quantization,
|
|
371
|
+
"kv_cache_quant_algo": kv_cache_quantization,
|
|
372
|
+
"exclude_modules": ["lm_head"],
|
|
373
|
+
},
|
|
374
|
+
}
|
|
375
|
+
with open(save_directory + "/hf_quant_config.json", "w") as f:
|
|
376
|
+
json.dump(hf_quant_config, f, indent=4)
|
|
377
|
+
|
|
378
|
+
if (
|
|
379
|
+
is_first_stage_main_rank
|
|
380
|
+
and self.is_multimodal
|
|
381
|
+
and pretrained_model_name_or_path is not None
|
|
382
|
+
):
|
|
383
|
+
hf_checkpoint_path = Path(pretrained_model_name_or_path)
|
|
384
|
+
if not hf_checkpoint_path.is_dir():
|
|
385
|
+
hf_checkpoint_path = tempfile.gettempdir() + "/" + pretrained_model_name_or_path
|
|
386
|
+
if not Path(hf_checkpoint_path).exists():
|
|
387
|
+
snapshot_download(
|
|
388
|
+
repo_id=pretrained_model_name_or_path,
|
|
389
|
+
local_dir=hf_checkpoint_path,
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
safetensors_file = Path(hf_checkpoint_path) / "model.safetensors"
|
|
393
|
+
safetensors_index_file = Path(hf_checkpoint_path) / "model.safetensors.index.json"
|
|
394
|
+
|
|
395
|
+
multimodal_state_dict = {}
|
|
396
|
+
|
|
397
|
+
if safetensors_file.is_file():
|
|
398
|
+
print(f"Loading multimodal components from single file: {safetensors_file}")
|
|
399
|
+
with safe_open(safetensors_file, framework="pt") as f:
|
|
400
|
+
multimodal_keys = [
|
|
401
|
+
key
|
|
402
|
+
for key in f.keys() # noqa: SIM118
|
|
403
|
+
if key.startswith(("multi_modal_projector", "vision_model"))
|
|
404
|
+
]
|
|
405
|
+
for key in tqdm(multimodal_keys, desc="Loading multimodal tensors"):
|
|
406
|
+
multimodal_state_dict[key] = f.get_tensor(key)
|
|
407
|
+
|
|
408
|
+
elif safetensors_index_file.is_file():
|
|
409
|
+
print(f"Loading multimodal components from sharded model: {hf_checkpoint_path}")
|
|
410
|
+
with open(safetensors_index_file) as f:
|
|
411
|
+
safetensors_index = json.load(f)
|
|
412
|
+
|
|
413
|
+
# For multimodal models, vision_model and multi_modal_projector are in the first shard
|
|
414
|
+
all_shard_files = sorted(set(safetensors_index["weight_map"].values()))
|
|
415
|
+
first_shard_file = all_shard_files[0] # e.g., "model-00001-of-00050.safetensors"
|
|
416
|
+
|
|
417
|
+
# Load multimodal components from the first shard file
|
|
418
|
+
safetensors_filepath = Path(hf_checkpoint_path) / first_shard_file
|
|
419
|
+
print(f"Loading multimodal components from {first_shard_file}")
|
|
420
|
+
|
|
421
|
+
with safe_open(safetensors_filepath, framework="pt") as f:
|
|
422
|
+
shard_keys = list(f.keys())
|
|
423
|
+
multimodal_keys_in_shard = [
|
|
424
|
+
k
|
|
425
|
+
for k in shard_keys
|
|
426
|
+
if k.startswith(("multi_modal_projector", "vision_model"))
|
|
427
|
+
]
|
|
428
|
+
|
|
429
|
+
if multimodal_keys_in_shard:
|
|
430
|
+
print(
|
|
431
|
+
f"Found {len(multimodal_keys_in_shard)} multimodal tensors in {first_shard_file}"
|
|
432
|
+
)
|
|
433
|
+
for key in tqdm(
|
|
434
|
+
multimodal_keys_in_shard, desc="Loading multimodal tensors"
|
|
435
|
+
):
|
|
436
|
+
multimodal_state_dict[key] = f.get_tensor(key)
|
|
437
|
+
else:
|
|
438
|
+
print(f"No multimodal components found in {first_shard_file}")
|
|
439
|
+
|
|
440
|
+
else:
|
|
441
|
+
print(f"Warning: No safetensors files found in {hf_checkpoint_path}")
|
|
442
|
+
|
|
443
|
+
print(f"Successfully loaded {len(multimodal_state_dict)} multimodal tensors")
|
|
444
|
+
# Add multimodal components to state_dict
|
|
445
|
+
state_dict.update(multimodal_state_dict)
|
|
446
|
+
|
|
447
|
+
# Barrier to ensure the export_dir has been created.
|
|
448
|
+
torch.distributed.barrier()
|
|
449
|
+
|
|
450
|
+
if self.export_extra_modules:
|
|
451
|
+
if is_last_stage_main_rank:
|
|
452
|
+
save_file(
|
|
453
|
+
state_dict, save_directory + "/model.safetensors", metadata={"format": "pt"}
|
|
454
|
+
)
|
|
455
|
+
torch.distributed.barrier()
|
|
456
|
+
return
|
|
457
|
+
|
|
458
|
+
save_safetensors(state_dict, save_directory)
|
|
459
|
+
|
|
460
|
+
@property
|
|
461
|
+
def state_dict(self):
|
|
462
|
+
"""Return the real quantized state_dict of the base model."""
|
|
463
|
+
if len(self._state_dict) == 0:
|
|
464
|
+
self._get_state_dict()
|
|
465
|
+
return self._state_dict
|
|
466
|
+
|
|
467
|
+
@property
|
|
468
|
+
def extra_state_dict(self):
|
|
469
|
+
if len(self._state_dict) == 0:
|
|
470
|
+
self._get_medusa_heads_state_dict()
|
|
471
|
+
self._get_eagle_module_state_dict()
|
|
472
|
+
return self._state_dict
|
|
473
|
+
|
|
474
|
+
def _populate_rule_book(self):
|
|
475
|
+
all_rules = {}
|
|
476
|
+
|
|
477
|
+
def _custom_mapping_to_lambda(mapping):
|
|
478
|
+
method_map = {
|
|
479
|
+
"name_remapping": self._name_remapping,
|
|
480
|
+
"qkv_slicing": self._qkv_slicing,
|
|
481
|
+
"gated_mlp_slicing": self._gated_mlp_slicing,
|
|
482
|
+
"pack_name_remapping": self._pack_name_remapping,
|
|
483
|
+
"pack_name_remapping_gpt_oss": self._pack_name_remapping_gpt_oss,
|
|
484
|
+
}
|
|
485
|
+
func = method_map[mapping.func_name]
|
|
486
|
+
prefix = mapping.target_name_or_prefix
|
|
487
|
+
func_kwargs = mapping.func_kwargs
|
|
488
|
+
return lambda m, *args: func(m, prefix.format(*args), **func_kwargs)
|
|
489
|
+
|
|
490
|
+
for arch, mappings in all_mcore_hf_export_mapping.items():
|
|
491
|
+
all_rules[arch] = {
|
|
492
|
+
k: _custom_mapping_to_lambda(v) if isinstance(v, CustomModuleMapping) else v
|
|
493
|
+
for (k, v) in mappings.items()
|
|
494
|
+
if isinstance(v, (CustomModuleMapping, bool))
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return all_rules
|
|
498
|
+
|
|
499
|
+
def _get_weight_scales(self, quantized_state: dict[str, Any], qformat: str):
|
|
500
|
+
weight_scale = quantized_state.pop("weight_scale", None)
|
|
501
|
+
weight_scale_2 = quantized_state.pop("weight_scale_2", None)
|
|
502
|
+
|
|
503
|
+
if weight_scale is not None:
|
|
504
|
+
weight_scale = weight_scale.clone().detach()
|
|
505
|
+
if qformat == QUANTIZATION_FP8 and weight_scale.numel() == 1:
|
|
506
|
+
weight_scale = weight_scale.squeeze()
|
|
507
|
+
if weight_scale_2 is not None:
|
|
508
|
+
weight_scale_2 = weight_scale_2.clone().detach()
|
|
509
|
+
|
|
510
|
+
return weight_scale, weight_scale_2
|
|
511
|
+
|
|
512
|
+
def _name_remapping(
|
|
513
|
+
self,
|
|
514
|
+
module: torch.nn.Module | torch.Tensor,
|
|
515
|
+
prefix: str,
|
|
516
|
+
skip_output_scale: bool = True,
|
|
517
|
+
mapping={},
|
|
518
|
+
):
|
|
519
|
+
if isinstance(module, torch.Tensor):
|
|
520
|
+
self._state_dict[prefix] = module
|
|
521
|
+
return
|
|
522
|
+
|
|
523
|
+
name_to_value, qformat, block_size = get_quantized_state(module, self.dtype)
|
|
524
|
+
|
|
525
|
+
weight = name_to_value.pop("weight")
|
|
526
|
+
weight_scale, weight_scale_2 = self._get_weight_scales(name_to_value, qformat)
|
|
527
|
+
|
|
528
|
+
if weight_scale is None:
|
|
529
|
+
self._state_dict[prefix + "weight"] = weight
|
|
530
|
+
else:
|
|
531
|
+
self._state_dict[prefix + "weight"] = to_quantized_weight(
|
|
532
|
+
weight,
|
|
533
|
+
weight_scale,
|
|
534
|
+
qformat,
|
|
535
|
+
weight_scale_2,
|
|
536
|
+
block_size,
|
|
537
|
+
)
|
|
538
|
+
self._state_dict[prefix + "weight_scale"] = weight_scale.detach().clone()
|
|
539
|
+
|
|
540
|
+
if weight_scale_2 is not None:
|
|
541
|
+
if len(weight_scale_2.shape) > 0:
|
|
542
|
+
raise ValueError("weight_scale_2 must be a scalar!")
|
|
543
|
+
self._state_dict[prefix + "weight_scale_2"] = weight_scale_2.detach().clone()
|
|
544
|
+
|
|
545
|
+
for key, val in name_to_value.items():
|
|
546
|
+
if key == "output_scale" and skip_output_scale:
|
|
547
|
+
continue
|
|
548
|
+
else:
|
|
549
|
+
source_key = mapping.get(key, key)
|
|
550
|
+
self._state_dict[prefix + source_key] = val
|
|
551
|
+
|
|
552
|
+
def _gated_mlp_slicing(
|
|
553
|
+
self, module, prefix, gate_proj_name="gate_proj", up_proj_name="up_proj"
|
|
554
|
+
):
|
|
555
|
+
name_to_value, qformat, block_size = get_quantized_state(module, self.dtype)
|
|
556
|
+
|
|
557
|
+
weight = name_to_value.pop("weight")
|
|
558
|
+
weight_scale, weight_scale_2 = self._get_weight_scales(name_to_value, qformat)
|
|
559
|
+
|
|
560
|
+
gate_proj_prefix = prefix + gate_proj_name + "."
|
|
561
|
+
up_proj_prefix = prefix + up_proj_name + "."
|
|
562
|
+
|
|
563
|
+
ffn_hidden_size = module.config.ffn_hidden_size
|
|
564
|
+
gate_proj_weight = weight[:ffn_hidden_size, :]
|
|
565
|
+
up_proj_weight = weight[ffn_hidden_size:, :]
|
|
566
|
+
|
|
567
|
+
if weight_scale is None:
|
|
568
|
+
self._state_dict[gate_proj_prefix + "weight"] = gate_proj_weight
|
|
569
|
+
self._state_dict[up_proj_prefix + "weight"] = up_proj_weight
|
|
570
|
+
else:
|
|
571
|
+
if len(weight_scale.shape) == 0:
|
|
572
|
+
gate_proj_weight_scale = weight_scale.detach().clone()
|
|
573
|
+
up_proj_weight_scale = weight_scale.detach().clone()
|
|
574
|
+
else:
|
|
575
|
+
gate_proj_weight_scale = weight_scale[:ffn_hidden_size]
|
|
576
|
+
up_proj_weight_scale = weight_scale[ffn_hidden_size:]
|
|
577
|
+
self._state_dict[gate_proj_prefix + "weight"] = to_quantized_weight(
|
|
578
|
+
gate_proj_weight,
|
|
579
|
+
gate_proj_weight_scale,
|
|
580
|
+
qformat,
|
|
581
|
+
weight_scale_2,
|
|
582
|
+
block_size,
|
|
583
|
+
)
|
|
584
|
+
self._state_dict[up_proj_prefix + "weight"] = to_quantized_weight(
|
|
585
|
+
up_proj_weight,
|
|
586
|
+
up_proj_weight_scale,
|
|
587
|
+
qformat,
|
|
588
|
+
weight_scale_2,
|
|
589
|
+
block_size,
|
|
590
|
+
)
|
|
591
|
+
self._state_dict[gate_proj_prefix + "weight_scale"] = gate_proj_weight_scale
|
|
592
|
+
self._state_dict[up_proj_prefix + "weight_scale"] = up_proj_weight_scale
|
|
593
|
+
|
|
594
|
+
if weight_scale_2 is not None:
|
|
595
|
+
if len(weight_scale_2.shape) > 0:
|
|
596
|
+
raise ValueError("weight_scale_2 must be a scalar!")
|
|
597
|
+
self._state_dict[gate_proj_prefix + "weight_scale_2"] = weight_scale_2.detach().clone()
|
|
598
|
+
self._state_dict[up_proj_prefix + "weight_scale_2"] = weight_scale_2.detach().clone()
|
|
599
|
+
|
|
600
|
+
# weight and weight_scale have been pop out.
|
|
601
|
+
for key, val in name_to_value.items():
|
|
602
|
+
gate_proj_key = gate_proj_prefix + key
|
|
603
|
+
up_proj_key = up_proj_prefix + key
|
|
604
|
+
if key == "output_scale":
|
|
605
|
+
continue
|
|
606
|
+
else:
|
|
607
|
+
self._state_dict[gate_proj_key] = val.detach().clone()
|
|
608
|
+
self._state_dict[up_proj_key] = val.detach().clone()
|
|
609
|
+
|
|
610
|
+
def _qkv_slicing(
|
|
611
|
+
self,
|
|
612
|
+
module,
|
|
613
|
+
prefix,
|
|
614
|
+
q_proj_name="q_proj",
|
|
615
|
+
k_proj_name="k_proj",
|
|
616
|
+
v_proj_name="v_proj",
|
|
617
|
+
k_scale_name="k_scale",
|
|
618
|
+
v_scale_name="v_scale",
|
|
619
|
+
):
|
|
620
|
+
name_to_value, qformat, block_size = get_quantized_state(module, self.dtype)
|
|
621
|
+
|
|
622
|
+
q_proj_prefix = prefix + q_proj_name + "."
|
|
623
|
+
k_proj_prefix = prefix + k_proj_name + "."
|
|
624
|
+
v_proj_prefix = prefix + v_proj_name + "."
|
|
625
|
+
|
|
626
|
+
config = module.config
|
|
627
|
+
hidden_size = config.hidden_size
|
|
628
|
+
num_query_groups = config.num_query_groups
|
|
629
|
+
head_num = config.num_attention_heads
|
|
630
|
+
head_size = config.kv_channels
|
|
631
|
+
heads_per_group = head_num // num_query_groups
|
|
632
|
+
qkv_total_dim = head_num + 2 * num_query_groups
|
|
633
|
+
|
|
634
|
+
weight = name_to_value.pop("weight")
|
|
635
|
+
|
|
636
|
+
if weight.shape[-1] == 2 * hidden_size:
|
|
637
|
+
print(
|
|
638
|
+
"Parameter linear_qkv.weight has 2x the hidden_size."
|
|
639
|
+
"Set hidden_size to 2x the hidden_size. EAGLE3 is the only known"
|
|
640
|
+
"use case which has this behavior."
|
|
641
|
+
)
|
|
642
|
+
hidden_size = 2 * hidden_size
|
|
643
|
+
|
|
644
|
+
weight = weight.reshape([qkv_total_dim, head_size, hidden_size])
|
|
645
|
+
weight_scale, weight_scale_2 = self._get_weight_scales(name_to_value, qformat)
|
|
646
|
+
|
|
647
|
+
q_slice = torch.cat(
|
|
648
|
+
[
|
|
649
|
+
torch.arange((heads_per_group + 2) * i, (heads_per_group + 2) * i + heads_per_group)
|
|
650
|
+
for i in range(num_query_groups)
|
|
651
|
+
]
|
|
652
|
+
)
|
|
653
|
+
k_slice = torch.arange(heads_per_group, qkv_total_dim, (heads_per_group + 2))
|
|
654
|
+
v_slice = torch.arange(heads_per_group + 1, qkv_total_dim, (heads_per_group + 2))
|
|
655
|
+
## Example of slices
|
|
656
|
+
## 7b: num_query_groups = head_num = 32,
|
|
657
|
+
## q_slice = [0, 3, 6, 9 , ... 90, 93]
|
|
658
|
+
## k_slice = [1, 4, 7, 10, ... 91, 94]
|
|
659
|
+
## v_slice = [2, 5, 8, 11, ... 92, 95]
|
|
660
|
+
## 70b (with GQA): num_query_groups = 8, head_num = 64
|
|
661
|
+
## q_slice = [0, 1, .. 6, 7, 10, 11, .. 16, 17, 20, 21, .. 67, 70, ... 76, 77]
|
|
662
|
+
## k_slice = [8, 18, 28, ... 68, 78]
|
|
663
|
+
## v_slice = [9, 19, 29, ... 69, 79]
|
|
664
|
+
slices = [q_slice, k_slice, v_slice]
|
|
665
|
+
prefixes = [q_proj_prefix, k_proj_prefix, v_proj_prefix]
|
|
666
|
+
|
|
667
|
+
proj_weights = [weight[s].reshape(-1, hidden_size) for s in slices]
|
|
668
|
+
proj_keys = [p + "weight" for p in prefixes]
|
|
669
|
+
|
|
670
|
+
if weight_scale is None:
|
|
671
|
+
for key, weight in zip(proj_keys, proj_weights):
|
|
672
|
+
self._state_dict[key] = weight
|
|
673
|
+
else:
|
|
674
|
+
if len(weight_scale.shape) > 0:
|
|
675
|
+
# AWQ per-block or per-channel scaling
|
|
676
|
+
weight_scale_dtype = weight_scale.dtype
|
|
677
|
+
weight_scale_hidden_size = weight_scale.shape[-1]
|
|
678
|
+
weight_scale = weight_scale.to(dtype=float).reshape(
|
|
679
|
+
[qkv_total_dim, head_size, weight_scale_hidden_size]
|
|
680
|
+
)
|
|
681
|
+
proj_weight_scales = [
|
|
682
|
+
weight_scale[s]
|
|
683
|
+
.reshape(-1, weight_scale_hidden_size)
|
|
684
|
+
.to(dtype=weight_scale_dtype)
|
|
685
|
+
for s in slices
|
|
686
|
+
]
|
|
687
|
+
else:
|
|
688
|
+
# per-tensor scaling
|
|
689
|
+
proj_weight_scales = [
|
|
690
|
+
weight_scale.detach().clone(),
|
|
691
|
+
weight_scale.detach().clone(),
|
|
692
|
+
weight_scale.detach().clone(),
|
|
693
|
+
]
|
|
694
|
+
|
|
695
|
+
for weight, scale, key in zip(proj_weights, proj_weight_scales, proj_keys):
|
|
696
|
+
quantized_weight = to_quantized_weight(
|
|
697
|
+
weight,
|
|
698
|
+
scale,
|
|
699
|
+
qformat,
|
|
700
|
+
weight_scale_2,
|
|
701
|
+
block_size,
|
|
702
|
+
)
|
|
703
|
+
self._state_dict[key] = quantized_weight
|
|
704
|
+
self._state_dict[key + "_scale"] = scale
|
|
705
|
+
|
|
706
|
+
if weight_scale_2 is not None:
|
|
707
|
+
if len(weight_scale_2.shape) > 0:
|
|
708
|
+
raise ValueError("weight_scale_2 must be a scalar!")
|
|
709
|
+
for weight, scale, key in zip(proj_weights, proj_weight_scales, proj_keys):
|
|
710
|
+
self._state_dict[key + "_scale_2"] = weight_scale_2.detach().clone()
|
|
711
|
+
|
|
712
|
+
# weight and weight_scale have been pop out.
|
|
713
|
+
for key, val in name_to_value.items():
|
|
714
|
+
q_proj_key = q_proj_prefix + key
|
|
715
|
+
k_proj_key = k_proj_prefix + key
|
|
716
|
+
v_proj_key = v_proj_prefix + key
|
|
717
|
+
if key == "output_scale":
|
|
718
|
+
self._state_dict[prefix + k_scale_name] = val.detach().clone()
|
|
719
|
+
self._state_dict[prefix + v_scale_name] = val.detach().clone()
|
|
720
|
+
elif key == "bias":
|
|
721
|
+
# Slice bias similar to weight
|
|
722
|
+
bias = val.detach().clone()
|
|
723
|
+
bias = bias.reshape([qkv_total_dim, head_size])
|
|
724
|
+
proj_biases = [bias[s].reshape(-1) for s in slices]
|
|
725
|
+
proj_bias_keys = [q_proj_prefix + key, k_proj_prefix + key, v_proj_prefix + key]
|
|
726
|
+
for bias_tensor, bias_key in zip(proj_biases, proj_bias_keys):
|
|
727
|
+
self._state_dict[bias_key] = bias_tensor
|
|
728
|
+
else:
|
|
729
|
+
self._state_dict[q_proj_key] = val.detach().clone()
|
|
730
|
+
self._state_dict[k_proj_key] = val.detach().clone()
|
|
731
|
+
self._state_dict[v_proj_key] = val.detach().clone()
|
|
732
|
+
|
|
733
|
+
def _pack_name_remapping(self, module, prefix, layer_type=None):
|
|
734
|
+
"""Pack name remapping into one tensor."""
|
|
735
|
+
weight_list = []
|
|
736
|
+
weight_scale_list = []
|
|
737
|
+
weight_scale_2_list = []
|
|
738
|
+
input_scale_list = []
|
|
739
|
+
|
|
740
|
+
for expert in module:
|
|
741
|
+
assert layer_type is not None, "layer_type is required for pack_name_remapping"
|
|
742
|
+
name_to_value, qformat, block_size = get_quantized_state(
|
|
743
|
+
getattr(expert, layer_type), self.dtype
|
|
744
|
+
)
|
|
745
|
+
weight = name_to_value.pop("weight")
|
|
746
|
+
weight_scale, weight_scale_2 = self._get_weight_scales(name_to_value, qformat)
|
|
747
|
+
input_scale = (
|
|
748
|
+
name_to_value.pop("input_scale") if "input_scale" in name_to_value else None
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
weight_list.append(weight)
|
|
752
|
+
weight_scale_list.append(weight_scale)
|
|
753
|
+
weight_scale_2_list.append(weight_scale_2)
|
|
754
|
+
input_scale_list.append(input_scale)
|
|
755
|
+
|
|
756
|
+
merged_weight = torch.stack(weight_list, dim=0)
|
|
757
|
+
|
|
758
|
+
# Transpose the last two dimensions to match HuggingFace format
|
|
759
|
+
# NeMo format: [num_experts, out_features, in_features]
|
|
760
|
+
# HF format: [num_experts, in_features, out_features]
|
|
761
|
+
merged_weight = merged_weight.transpose(-2, -1).contiguous()
|
|
762
|
+
|
|
763
|
+
if weight_scale_2_list[0] is None:
|
|
764
|
+
merged_weight_scale_2 = None
|
|
765
|
+
if weight_scale_list[0] is not None:
|
|
766
|
+
merged_weight_scale = torch.max(torch.stack(weight_scale_list, dim=0), dim=0)[0]
|
|
767
|
+
else:
|
|
768
|
+
merged_weight_scale = None
|
|
769
|
+
else:
|
|
770
|
+
# NVFP4
|
|
771
|
+
merged_weight_scale_2 = torch.max(torch.stack(weight_scale_2_list, dim=0), dim=0)[0]
|
|
772
|
+
merged_weight_scale = torch.stack(weight_scale_list, dim=0)
|
|
773
|
+
# Transpose the scaling factors to match the transposed weights
|
|
774
|
+
merged_weight_scale = merged_weight_scale.transpose(-2, -1).contiguous()
|
|
775
|
+
|
|
776
|
+
if input_scale_list[0] is not None:
|
|
777
|
+
merged_input_scale = torch.max(torch.stack(input_scale_list, dim=0), dim=0)[0]
|
|
778
|
+
else:
|
|
779
|
+
merged_input_scale = None
|
|
780
|
+
|
|
781
|
+
# Save the merged weights
|
|
782
|
+
if merged_weight_scale is None:
|
|
783
|
+
self._state_dict[prefix] = merged_weight
|
|
784
|
+
else:
|
|
785
|
+
self._state_dict[prefix] = to_quantized_weight(
|
|
786
|
+
merged_weight,
|
|
787
|
+
merged_weight_scale,
|
|
788
|
+
qformat,
|
|
789
|
+
merged_weight_scale_2,
|
|
790
|
+
block_size,
|
|
791
|
+
)
|
|
792
|
+
self._state_dict[prefix + "_weight_scale"] = merged_weight_scale
|
|
793
|
+
if merged_weight_scale_2 is not None:
|
|
794
|
+
self._state_dict[prefix + "_weight_scale_2"] = merged_weight_scale_2
|
|
795
|
+
if merged_input_scale is not None:
|
|
796
|
+
self._state_dict[prefix + "_input_scale"] = merged_input_scale
|
|
797
|
+
|
|
798
|
+
def _pack_name_remapping_gpt_oss(self, module, prefix, layer_type=None):
|
|
799
|
+
"""Pack name remapping into one tensor."""
|
|
800
|
+
weight_list = []
|
|
801
|
+
weight_scale_list = []
|
|
802
|
+
weight_scale_2_list = []
|
|
803
|
+
input_scale_list = []
|
|
804
|
+
bias_list = []
|
|
805
|
+
|
|
806
|
+
for expert in module:
|
|
807
|
+
assert layer_type is not None, "layer_type is required for pack_name_remapping"
|
|
808
|
+
name_to_value, qformat, block_size = get_quantized_state(
|
|
809
|
+
getattr(expert, layer_type), self.dtype
|
|
810
|
+
)
|
|
811
|
+
weight = name_to_value.pop("weight")
|
|
812
|
+
bias = name_to_value.pop("bias", None)
|
|
813
|
+
weight_scale, weight_scale_2 = self._get_weight_scales(name_to_value, qformat)
|
|
814
|
+
input_scale = (
|
|
815
|
+
name_to_value.pop("input_scale") if "input_scale" in name_to_value else None
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
weight_list.append(weight)
|
|
819
|
+
weight_scale_list.append(weight_scale)
|
|
820
|
+
weight_scale_2_list.append(weight_scale_2)
|
|
821
|
+
input_scale_list.append(input_scale)
|
|
822
|
+
bias_list.append(bias)
|
|
823
|
+
|
|
824
|
+
merged_weight = torch.stack(weight_list, dim=0)
|
|
825
|
+
|
|
826
|
+
# Transpose the last two dimensions to match HuggingFace format (except for GptOssForCausalLM)
|
|
827
|
+
# NeMo format: [num_experts, out_features, in_features]
|
|
828
|
+
# HF format: [num_experts, in_features, out_features]
|
|
829
|
+
|
|
830
|
+
# TODO: Need to decide if we want to transpose the weight or not.
|
|
831
|
+
merged_weight = merged_weight.transpose(-2, -1).contiguous()
|
|
832
|
+
|
|
833
|
+
# Apply interleaving for GptOssForCausalLM linear_fc1 to match HF format
|
|
834
|
+
if layer_type == "linear_fc1":
|
|
835
|
+
# Megatron has de-interleaved format, need to interleave for HF
|
|
836
|
+
# Pattern: first half -> even indices, second half -> odd indices
|
|
837
|
+
num_experts, in_features, out_features = merged_weight.shape
|
|
838
|
+
half_out = out_features // 2
|
|
839
|
+
|
|
840
|
+
# Create interleaved tensor
|
|
841
|
+
interleaved_weight = torch.zeros_like(merged_weight)
|
|
842
|
+
interleaved_weight[:, :, ::2] = merged_weight[
|
|
843
|
+
:, :, :half_out
|
|
844
|
+
] # First half -> even indices
|
|
845
|
+
interleaved_weight[:, :, 1::2] = merged_weight[
|
|
846
|
+
:, :, half_out:
|
|
847
|
+
] # Second half -> odd indices
|
|
848
|
+
merged_weight = interleaved_weight
|
|
849
|
+
|
|
850
|
+
# Handle bias tensors
|
|
851
|
+
merged_bias = None
|
|
852
|
+
if bias_list[0] is not None:
|
|
853
|
+
merged_bias = torch.stack(bias_list, dim=0)
|
|
854
|
+
|
|
855
|
+
# Apply interleaving for GptOssForCausalLM linear_fc1 bias to match HF format
|
|
856
|
+
if layer_type == "linear_fc1":
|
|
857
|
+
num_experts, bias_len = merged_bias.shape
|
|
858
|
+
half_bias_len = bias_len // 2
|
|
859
|
+
|
|
860
|
+
# Create interleaved bias tensor
|
|
861
|
+
interleaved_bias = torch.zeros_like(merged_bias)
|
|
862
|
+
interleaved_bias[:, ::2] = merged_bias[
|
|
863
|
+
:, :half_bias_len
|
|
864
|
+
] # First half -> even indices
|
|
865
|
+
interleaved_bias[:, 1::2] = merged_bias[
|
|
866
|
+
:, half_bias_len:
|
|
867
|
+
] # Second half -> odd indices
|
|
868
|
+
merged_bias = interleaved_bias
|
|
869
|
+
|
|
870
|
+
if weight_scale_2_list[0] is None:
|
|
871
|
+
merged_weight_scale_2 = None
|
|
872
|
+
if weight_scale_list[0] is not None:
|
|
873
|
+
merged_weight_scale = torch.max(torch.stack(weight_scale_list, dim=0), dim=0)[0]
|
|
874
|
+
else:
|
|
875
|
+
merged_weight_scale = None
|
|
876
|
+
else:
|
|
877
|
+
# NVFP4
|
|
878
|
+
merged_weight_scale_2 = torch.max(torch.stack(weight_scale_2_list, dim=0), dim=0)[0]
|
|
879
|
+
merged_weight_scale = torch.stack(weight_scale_list, dim=0)
|
|
880
|
+
# Transpose the scaling factors to match the transposed weights
|
|
881
|
+
# TODO: Need to decide if we want to transpose the weight or not.
|
|
882
|
+
merged_weight_scale = merged_weight_scale.transpose(-2, -1).contiguous()
|
|
883
|
+
|
|
884
|
+
if input_scale_list[0] is not None:
|
|
885
|
+
merged_input_scale = torch.max(torch.stack(input_scale_list, dim=0), dim=0)[0]
|
|
886
|
+
else:
|
|
887
|
+
merged_input_scale = None
|
|
888
|
+
|
|
889
|
+
# Save the merged weights
|
|
890
|
+
if merged_weight_scale is None:
|
|
891
|
+
# TODO: May need to modify the key name later.
|
|
892
|
+
self._state_dict[prefix] = merged_weight
|
|
893
|
+
else:
|
|
894
|
+
self._state_dict[prefix] = to_quantized_weight(
|
|
895
|
+
merged_weight,
|
|
896
|
+
merged_weight_scale,
|
|
897
|
+
qformat,
|
|
898
|
+
merged_weight_scale_2,
|
|
899
|
+
block_size,
|
|
900
|
+
)
|
|
901
|
+
self._state_dict[prefix + "_weight_scale"] = merged_weight_scale
|
|
902
|
+
if merged_weight_scale_2 is not None:
|
|
903
|
+
self._state_dict[prefix + "_weight_scale_2"] = merged_weight_scale_2
|
|
904
|
+
if merged_input_scale is not None:
|
|
905
|
+
self._state_dict[prefix + "_input_scale"] = merged_input_scale
|
|
906
|
+
|
|
907
|
+
# Save bias tensors if they exist
|
|
908
|
+
if merged_bias is not None:
|
|
909
|
+
# TODO: May need to modify the key name later.
|
|
910
|
+
self._state_dict[prefix + "_bias"] = merged_bias
|
|
911
|
+
|
|
912
|
+
def _get_medusa_heads_state_dict(self):
|
|
913
|
+
medusa_heads = getattr(self.model, "medusa_heads", None)
|
|
914
|
+
if medusa_heads is None:
|
|
915
|
+
return
|
|
916
|
+
|
|
917
|
+
for head_id, head in enumerate(medusa_heads):
|
|
918
|
+
self.rules["medusa_heads.lm_head"](head.lm_head, head_id)
|
|
919
|
+
for layer_id, layer in enumerate(head.medusa_layers):
|
|
920
|
+
self.rules["medusa_heads.medusa_layers.linear"](layer.linear, head_id, layer_id)
|
|
921
|
+
|
|
922
|
+
def _get_eagle_module_state_dict(self):
|
|
923
|
+
eagle_module = getattr(self.model, "eagle_module", None)
|
|
924
|
+
|
|
925
|
+
if eagle_module is None:
|
|
926
|
+
return
|
|
927
|
+
|
|
928
|
+
# if hasattr(self.model, "embedding"):
|
|
929
|
+
# self.rules["word_embeddings"](self.model.embedding.word_embeddings)
|
|
930
|
+
|
|
931
|
+
self.rules["fc"](eagle_module.fc)
|
|
932
|
+
if self.model.eagle_config.use_aux_hidden_state:
|
|
933
|
+
self.rules["enorm"](eagle_module.enorm)
|
|
934
|
+
elif self.model.eagle_config.use_mtp_layernorm:
|
|
935
|
+
self.rules["enorm"](eagle_module.enorm)
|
|
936
|
+
self.rules["hnorm"](eagle_module.hnorm)
|
|
937
|
+
|
|
938
|
+
if self.model.eagle_config.use_last_layernorm:
|
|
939
|
+
self.rules["final_layernorm"](eagle_module.decoder.final_layernorm)
|
|
940
|
+
|
|
941
|
+
if hasattr(self.model.eagle_module, "eagle_output_layer"):
|
|
942
|
+
self.rules["output_layer"](eagle_module.eagle_output_layer)
|
|
943
|
+
if hasattr(self.model.eagle_module, "dt2"):
|
|
944
|
+
self.rules["d2t"](eagle_module.d2t)
|
|
945
|
+
|
|
946
|
+
for layer in eagle_module.decoder.layers:
|
|
947
|
+
layer_id = layer.layer_number - 1
|
|
948
|
+
|
|
949
|
+
if layer_id > 0 or self.model.eagle_config.use_input_layernorm_in_first_layer:
|
|
950
|
+
self.rules["input_layernorm"](layer.input_layernorm, layer_id)
|
|
951
|
+
|
|
952
|
+
if "MLASelfAttention" in str(type(layer.self_attention)):
|
|
953
|
+
if hasattr(layer.self_attention, "linear_q_proj"):
|
|
954
|
+
self.rules["eagle_module.linear_q_proj"](
|
|
955
|
+
layer.self_attention.linear_q_proj, layer_id
|
|
956
|
+
)
|
|
957
|
+
else:
|
|
958
|
+
self.rules["eagle_module.linear_q_down_proj"](
|
|
959
|
+
layer.self_attention.linear_q_down_proj, layer_id
|
|
960
|
+
)
|
|
961
|
+
self.rules["eagle_module.linear_q_layernorm"](
|
|
962
|
+
layer.self_attention.q_layernorm, layer_id
|
|
963
|
+
)
|
|
964
|
+
self.rules["eagle_module.linear_q_up_proj"](
|
|
965
|
+
layer.self_attention.linear_q_up_proj, layer_id
|
|
966
|
+
)
|
|
967
|
+
|
|
968
|
+
self.rules["eagle_module.linear_kv_down_proj"](
|
|
969
|
+
layer.self_attention.linear_kv_down_proj, layer_id
|
|
970
|
+
)
|
|
971
|
+
self.rules["eagle_module.linear_kv_layernorm"](
|
|
972
|
+
layer.self_attention.kv_layernorm, layer_id
|
|
973
|
+
)
|
|
974
|
+
self.rules["eagle_module.linear_kv_up_proj"](
|
|
975
|
+
layer.self_attention.linear_kv_up_proj, layer_id
|
|
976
|
+
)
|
|
977
|
+
else:
|
|
978
|
+
self.rules["linear_qkv"](layer.self_attention.linear_qkv, layer_id)
|
|
979
|
+
|
|
980
|
+
self.rules["linear_proj"](layer.self_attention.linear_proj, layer_id)
|
|
981
|
+
self.rules["pre_mlp_layernorm"](layer.pre_mlp_layernorm, layer_id)
|
|
982
|
+
|
|
983
|
+
if "MoE" in str(type(layer.mlp)):
|
|
984
|
+
self.rules["eagle_module.router"](layer.mlp.router, layer_id)
|
|
985
|
+
if hasattr(layer.mlp, "shared_experts") and layer.mlp.shared_experts is not None:
|
|
986
|
+
self.rules["eagle_module.shared_experts.linear_fc1"](
|
|
987
|
+
layer.mlp.shared_experts.linear_fc1, layer_id
|
|
988
|
+
)
|
|
989
|
+
self.rules["eagle_module.shared_experts.linear_fc2"](
|
|
990
|
+
layer.mlp.shared_experts.linear_fc2, layer_id
|
|
991
|
+
)
|
|
992
|
+
for expert_id, expert in enumerate(layer.mlp.experts.local_experts):
|
|
993
|
+
self.rules["eagle_module.local_experts.linear_fc1"](
|
|
994
|
+
expert.linear_fc1, layer_id, expert_id
|
|
995
|
+
)
|
|
996
|
+
self.rules["eagle_module.local_experts.linear_fc2"](
|
|
997
|
+
expert.linear_fc2, layer_id, expert_id
|
|
998
|
+
)
|
|
999
|
+
else:
|
|
1000
|
+
self.rules["linear_fc1"](layer.mlp.linear_fc1, layer_id)
|
|
1001
|
+
self.rules["linear_fc2"](layer.mlp.linear_fc2, layer_id)
|
|
1002
|
+
|
|
1003
|
+
def _get_state_dict(self):
|
|
1004
|
+
model = self.model
|
|
1005
|
+
|
|
1006
|
+
# Embedding
|
|
1007
|
+
if hasattr(model, "embedding"):
|
|
1008
|
+
self.rules["word_embeddings"](model.embedding.word_embeddings)
|
|
1009
|
+
|
|
1010
|
+
# Final layernorm
|
|
1011
|
+
if hasattr(model.decoder, "final_layernorm") and model.decoder.final_layernorm:
|
|
1012
|
+
self.rules["final_layernorm"](model.decoder.final_layernorm)
|
|
1013
|
+
|
|
1014
|
+
if hasattr(model.decoder, "final_norm") and model.decoder.final_norm:
|
|
1015
|
+
self.rules["final_norm"](model.decoder.final_norm)
|
|
1016
|
+
|
|
1017
|
+
# Output layer
|
|
1018
|
+
if hasattr(model, "output_layer") and not model.share_embeddings_and_output_weights:
|
|
1019
|
+
self.rules["output_layer"](model.output_layer)
|
|
1020
|
+
|
|
1021
|
+
# Decoder layers
|
|
1022
|
+
for layer in model.decoder.layers:
|
|
1023
|
+
layer_id = layer.layer_number - 1
|
|
1024
|
+
|
|
1025
|
+
if isinstance(layer, MambaLayer):
|
|
1026
|
+
if not isinstance(layer.norm, IdentityOp):
|
|
1027
|
+
self.rules["norm"](layer.norm, layer_id)
|
|
1028
|
+
|
|
1029
|
+
self.rules["mixer_norm"](layer.mixer.norm, layer_id)
|
|
1030
|
+
self.rules["A_log"](layer.mixer.A_log, layer_id)
|
|
1031
|
+
self.rules["D"](layer.mixer.D, layer_id)
|
|
1032
|
+
self.rules["dt_bias"](layer.mixer.dt_bias, layer_id)
|
|
1033
|
+
|
|
1034
|
+
self.rules["conv1d"](layer.mixer.conv1d, layer_id)
|
|
1035
|
+
self.rules["in_proj"](layer.mixer.in_proj, layer_id)
|
|
1036
|
+
self.rules["out_proj"](layer.mixer.out_proj, layer_id)
|
|
1037
|
+
|
|
1038
|
+
elif isinstance(layer, TransformerLayer):
|
|
1039
|
+
if not isinstance(layer.input_layernorm, IdentityOp):
|
|
1040
|
+
self.rules["input_layernorm"](layer.input_layernorm, layer_id)
|
|
1041
|
+
|
|
1042
|
+
if not isinstance(layer.self_attention, IdentityOp):
|
|
1043
|
+
if "MLASelfAttention" in str(type(layer.self_attention)):
|
|
1044
|
+
if hasattr(layer.self_attention, "linear_q_proj"):
|
|
1045
|
+
self.rules["linear_q_proj"](
|
|
1046
|
+
layer.self_attention.linear_q_proj, layer_id
|
|
1047
|
+
)
|
|
1048
|
+
else:
|
|
1049
|
+
self.rules["linear_q_down_proj"](
|
|
1050
|
+
layer.self_attention.linear_q_down_proj, layer_id
|
|
1051
|
+
)
|
|
1052
|
+
self.rules["linear_q_layernorm"](
|
|
1053
|
+
layer.self_attention.q_layernorm, layer_id
|
|
1054
|
+
)
|
|
1055
|
+
self.rules["linear_q_up_proj"](
|
|
1056
|
+
layer.self_attention.linear_q_up_proj, layer_id
|
|
1057
|
+
)
|
|
1058
|
+
|
|
1059
|
+
self.rules["linear_kv_down_proj"](
|
|
1060
|
+
layer.self_attention.linear_kv_down_proj, layer_id
|
|
1061
|
+
)
|
|
1062
|
+
self.rules["linear_kv_layernorm"](
|
|
1063
|
+
layer.self_attention.kv_layernorm, layer_id
|
|
1064
|
+
)
|
|
1065
|
+
self.rules["linear_kv_up_proj"](
|
|
1066
|
+
layer.self_attention.linear_kv_up_proj, layer_id
|
|
1067
|
+
)
|
|
1068
|
+
self.rules["linear_proj"](layer.self_attention.linear_proj, layer_id)
|
|
1069
|
+
else:
|
|
1070
|
+
if layer.self_attention.q_layernorm is not None and not isinstance(
|
|
1071
|
+
layer.self_attention.q_layernorm, (IdentityOp, L2Norm)
|
|
1072
|
+
):
|
|
1073
|
+
self.rules["q_layernorm"](layer.self_attention.q_layernorm, layer_id)
|
|
1074
|
+
self.rules["k_layernorm"](layer.self_attention.k_layernorm, layer_id)
|
|
1075
|
+
self.rules["linear_qkv"](layer.self_attention.linear_qkv, layer_id)
|
|
1076
|
+
self.rules["linear_proj"](layer.self_attention.linear_proj, layer_id)
|
|
1077
|
+
if hasattr(layer.self_attention.core_attention, "softmax_offset"):
|
|
1078
|
+
self.rules["softmax_offset"](
|
|
1079
|
+
layer.self_attention.core_attention.softmax_offset, layer_id
|
|
1080
|
+
)
|
|
1081
|
+
|
|
1082
|
+
if not isinstance(layer.pre_mlp_layernorm, IdentityOp):
|
|
1083
|
+
self.rules["pre_mlp_layernorm"](layer.pre_mlp_layernorm, layer_id)
|
|
1084
|
+
|
|
1085
|
+
if not isinstance(layer.mlp, IdentityOp):
|
|
1086
|
+
if "MoE" in str(type(layer.mlp)):
|
|
1087
|
+
self.rules["router"](layer.mlp.router, layer_id)
|
|
1088
|
+
if (
|
|
1089
|
+
hasattr(layer.mlp, "shared_experts")
|
|
1090
|
+
and layer.mlp.shared_experts is not None
|
|
1091
|
+
):
|
|
1092
|
+
self.rules["shared_experts.linear_fc1"](
|
|
1093
|
+
layer.mlp.shared_experts.linear_fc1, layer_id
|
|
1094
|
+
)
|
|
1095
|
+
self.rules["shared_experts.linear_fc2"](
|
|
1096
|
+
layer.mlp.shared_experts.linear_fc2, layer_id
|
|
1097
|
+
)
|
|
1098
|
+
if not self.rules.get("use_packed_local_experts", False):
|
|
1099
|
+
for expert_id, expert in enumerate(layer.mlp.experts.local_experts):
|
|
1100
|
+
self.rules["local_experts.linear_fc1"](
|
|
1101
|
+
expert.linear_fc1, layer_id, expert_id
|
|
1102
|
+
)
|
|
1103
|
+
self.rules["local_experts.linear_fc2"](
|
|
1104
|
+
expert.linear_fc2, layer_id, expert_id
|
|
1105
|
+
)
|
|
1106
|
+
else:
|
|
1107
|
+
# For llama 4, in hf unified checkpoint, all local experts share one scale
|
|
1108
|
+
self.rules["local_experts.linear_fc1"](
|
|
1109
|
+
layer.mlp.experts.local_experts, layer_id
|
|
1110
|
+
)
|
|
1111
|
+
self.rules["local_experts.linear_fc2"](
|
|
1112
|
+
layer.mlp.experts.local_experts, layer_id
|
|
1113
|
+
)
|
|
1114
|
+
else:
|
|
1115
|
+
self.rules["linear_fc1"](layer.mlp.linear_fc1, layer_id)
|
|
1116
|
+
self.rules["linear_fc2"](layer.mlp.linear_fc2, layer_id)
|
|
1117
|
+
else:
|
|
1118
|
+
raise ValueError("Only TransformerLayer or MambaLayer are supported.")
|
|
1119
|
+
|
|
1120
|
+
|
|
1121
|
+
def export_mcore_gpt_to_hf(
|
|
1122
|
+
model: torch.nn.Module,
|
|
1123
|
+
pretrained_model_name_or_path: str | os.PathLike | None = None,
|
|
1124
|
+
export_extra_modules: bool = False,
|
|
1125
|
+
dtype: torch.dtype = torch.float16,
|
|
1126
|
+
export_dir: Path | str = tempfile.gettempdir(),
|
|
1127
|
+
):
|
|
1128
|
+
"""Export Megatron Core GPTModel to unified checkpoint and save to export_dir.
|
|
1129
|
+
|
|
1130
|
+
Args:
|
|
1131
|
+
model: The Megatron Core GPTModel instance.
|
|
1132
|
+
pretrained_model_name_or_path: Can be either: the *model id* of a
|
|
1133
|
+
pretrained model hosted inside a model repo on huggingface.co; or
|
|
1134
|
+
a *directory* containing model weights saved using
|
|
1135
|
+
[`~PreTrainedModel.save_pretrained`], e.g., `./my_model_directory/`.
|
|
1136
|
+
export_extra_modules: If True, export extra modules like medusa_heads or
|
|
1137
|
+
eagle_module. Otherwise, only export the base model.
|
|
1138
|
+
dtype: The weights data type to export the unquantized layers.
|
|
1139
|
+
export_dir: The target export path.
|
|
1140
|
+
"""
|
|
1141
|
+
exporter = GPTModelExporter(
|
|
1142
|
+
model, pretrained_model_name_or_path, export_extra_modules=export_extra_modules, dtype=dtype
|
|
1143
|
+
)
|
|
1144
|
+
exporter.save_pretrained(export_dir, pretrained_model_name_or_path)
|
|
1145
|
+
|
|
1146
|
+
|
|
1147
|
+
def import_mcore_gpt_from_hf(
|
|
1148
|
+
model: torch.nn.Module,
|
|
1149
|
+
pretrained_model_path: str,
|
|
1150
|
+
workspace_dir: str | None = None,
|
|
1151
|
+
dtype: torch.dtype = torch.float16,
|
|
1152
|
+
):
|
|
1153
|
+
"""Import GPTModel state_dict from supported HuggingFace pretrained model path.
|
|
1154
|
+
|
|
1155
|
+
Args:
|
|
1156
|
+
model: The Megatron Core GPTModel instance.
|
|
1157
|
+
pretrained_model_path: A path to a *directory* containing model weights saved using
|
|
1158
|
+
[`~PreTrainedModel.save_pretrained`], e.g., `./my_model_directory/`.
|
|
1159
|
+
dtype: The weights data type to import.
|
|
1160
|
+
"""
|
|
1161
|
+
importer = GPTModelImporter(
|
|
1162
|
+
model, pretrained_model_path, workspace_dir=workspace_dir, dtype=dtype
|
|
1163
|
+
)
|
|
1164
|
+
importer._import_state_dict()
|