Repository navigation
[fix] resolve SP/CP gradient issue in fla and qwen35 ckpt conversion #852
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
d1d486a
a7d3e01
e34fbab
181d448
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,6 @@ | |
| from megatron.core.transformer.spec_utils import ModuleSpec | ||
| from megatron.core.transformer.transformer_block import get_num_layers_to_build | ||
| from megatron.core.transformer.transformer_layer import get_transformer_layer_offset | ||
| from transformers import AutoConfig | ||
| from transformers.activations import ACT2FN | ||
|
|
||
| try: | ||
|
|
@@ -16,11 +15,7 @@ | |
| except ImportError: | ||
| pass | ||
|
|
||
| from .hf_attention import HuggingfaceAttention | ||
|
|
||
|
|
||
| def _load_hf_config(hf_checkpoint): | ||
| return AutoConfig.from_pretrained(hf_checkpoint, trust_remote_code=True) | ||
| from .hf_attention import HuggingfaceAttention, _load_hf_config | ||
|
|
||
|
|
||
| def _get_text_config(hf_config): | ||
|
|
@@ -132,6 +127,7 @@ def forward( | |
| initial_state=None, | ||
| output_final_state=False, | ||
| use_qk_l2norm_in_kernel=True, | ||
| cu_seqlens=cu_seqlens, | ||
| ) | ||
|
|
||
| z_shape_og = z.shape | ||
|
|
@@ -209,6 +205,14 @@ def get_qwen3_5_spec(args, config, vp_stage): | |
| hf_config = _load_hf_config(args.hf_checkpoint) | ||
| text_config = _get_text_config(hf_config) | ||
|
|
||
| # Compute layer_types if the config class doesn't expose it | ||
| if not hasattr(text_config, "layer_types"): | ||
| interval = getattr(text_config, "full_attention_interval", 4) | ||
| n = text_config.num_hidden_layers | ||
| text_config.layer_types = [ | ||
| "full_attention" if (i + 1) % interval == 0 else "linear_attention" for i in range(n) | ||
| ] | ||
|
Comment on lines
+209
to
+214
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic for computing layer_types based on full_attention_interval is duplicated here and in miles_plugins/models/qwen3_next.py. This logic should be encapsulated in a shared helper function (possibly within a shared HF config loader) to ensure consistency and reduce code duplication across model implementations. |
||
|
|
||
| for layer_id in range(num_layers_to_build): | ||
| if text_config.layer_types[layer_id + offset] == "linear_attention": | ||
| layer_specs = copy.deepcopy(transformer_layer_spec.layer_specs[layer_id]) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import gc | ||
| import json | ||
| import os | ||
| import shutil | ||
| from functools import wraps | ||
|
|
@@ -20,6 +21,73 @@ | |
| from miles.utils.memory_utils import print_memory | ||
|
|
||
|
|
||
| def _get_hf_weight_names(checkpoint_path: str) -> set[str]: | ||
| index_path = os.path.join(checkpoint_path, "model.safetensors.index.json") | ||
| if os.path.exists(index_path): | ||
| with open(index_path) as f: | ||
| index = json.load(f) | ||
| return set(index.get("weight_map", {}).keys()) | ||
|
|
||
| single_file = os.path.join(checkpoint_path, "model.safetensors") | ||
| if os.path.exists(single_file): | ||
| from safetensors import safe_open | ||
|
|
||
| with safe_open(single_file, framework="pt") as f: | ||
| return set(f.keys()) | ||
|
|
||
| return set() | ||
|
|
||
|
|
||
| def _load_hf_config_with_fallback(checkpoint_path: str): | ||
| """Load HF config with fallback for model types unknown to transformers.""" | ||
| try: | ||
| from transformers import AutoConfig | ||
|
|
||
| return AutoConfig.from_pretrained(checkpoint_path, trust_remote_code=True) | ||
| except (ValueError, KeyError): | ||
| config_path = os.path.join(checkpoint_path, "config.json") | ||
| with open(config_path) as f: | ||
| config_dict = json.load(f) | ||
|
|
||
| dtype_map = {"bfloat16": torch.bfloat16, "float16": torch.float16, "float32": torch.float32} | ||
|
|
||
| def fix_dtype(d): | ||
| if "torch_dtype" in d: | ||
| d["torch_dtype"] = dtype_map.get(d["torch_dtype"], d["torch_dtype"]) | ||
| if "dtype" in d: | ||
| d["dtype"] = dtype_map.get(d["dtype"], d["dtype"]) | ||
|
|
||
| fix_dtype(config_dict) | ||
| ns = type("HFConfig", (), config_dict)() | ||
| if "text_config" in config_dict: | ||
| fix_dtype(config_dict["text_config"]) | ||
| ns.text_config = type("TextConfig", (), config_dict["text_config"])() | ||
| return ns | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
|
|
||
| def _patch_bridge_for_tied_lm_head(bridge, hf_model_path: str): | ||
| """If lm_head is absent in HF weights, fallback output_layer to embeddings.""" | ||
| direct_mapping = getattr(bridge, "_DIRECT_MAPPING", None) | ||
| if not isinstance(direct_mapping, dict): | ||
| return | ||
|
|
||
| output_key = "output_layer.weight" | ||
| if direct_mapping.get(output_key) != "lm_head.weight": | ||
| return | ||
|
|
||
| hf_weight_names = _get_hf_weight_names(hf_model_path) | ||
| if "lm_head.weight" in hf_weight_names: | ||
| return | ||
|
|
||
| embedding_key = direct_mapping.get("embedding.word_embeddings.weight") | ||
| if embedding_key and embedding_key in hf_weight_names: | ||
| direct_mapping[output_key] = embedding_key | ||
| print( | ||
| f"[Patch] lm_head.weight not found in {hf_model_path}; " | ||
| f"fallback map output_layer.weight -> {embedding_key}" | ||
| ) | ||
|
|
||
|
|
||
| def patch_weight_to_mcore_format_preserve_fp32(): | ||
|
|
||
| original_method = Bridge._weight_to_mcore_format | ||
|
|
@@ -130,7 +198,12 @@ def main(): | |
|
|
||
| # Load model | ||
| hf_model_path = args.hf_checkpoint | ||
| bridge = AutoBridge.from_pretrained(hf_model_path, trust_remote_code=True) | ||
| try: | ||
| bridge = AutoBridge.from_pretrained(hf_model_path, trust_remote_code=True) | ||
| except (ValueError, KeyError): | ||
| # Fallback for configs with model_type unknown to installed transformers. | ||
| bridge = AutoBridge.from_config(_load_hf_config_with_fallback(hf_model_path)) | ||
| _patch_bridge_for_tied_lm_head(bridge, hf_model_path) | ||
|
|
||
| # Patch to preserve FP32 precision for _keep_fp32 params | ||
| patch_weight_to_mcore_format_preserve_fp32() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling available_memory() and potentially clear_memory() before every distributed call is extremely expensive and will severely degrade training performance. available_memory queries CUDA memory info via mem_get_info, and clear_memory performs a torch.cuda.synchronize(). Since this context manager wraps almost all collective operations (as seen in the monkey-patching logic on lines 59-81), this adds significant overhead and unnecessary synchronization points to the hot path. This check should be removed from the try block. It is already present in the except block (line 283), which is the appropriate place for diagnostic memory clearing after a failure.