Skip to content

Commit 53bea08

Browse files
committed
models: add Apertus / Apertus 1.5 bridges
Upstream 0.6.x has no Apertus support. This adds: - models/apertus/: ApertusBridge for ApertusForCausalLM, plus MCoreXIELU (a Megatron-side wrapper of transformers' XIELUActivation that requires the CUDA xielu kernel and TP-sums its alpha grads) and get_apertus_decoder_block_spec (RMSNorm q/k-layernorm + MCoreXIELU MLP activation). - models/apertus1p5/: Apertus1p5Bridge for the multimodal Apertus1p5ForConditionalGeneration -- hyperparameters read from config.text_config, HF weight keys under model.language_model.*, pruned-LM-head handling (output_vocab_size < vocab_size), vision/audio tokenizer towers left unmapped (target is a text-only GPTModel). - models/__init__.py: import both (the @register_bridge decorator only fires on import) and add to __all__. - hf_pretrained/safe_config_loader.py: replace the filelock around AutoConfig.from_pretrained with contextlib.nullcontext -- fcntl.flock raises ENOLCK/ESTALE in-container on CSCS Lustre and the config is written once by rank 0 and read-only afterward.
1 parent 51885cf commit 53bea08

6 files changed

Lines changed: 492 additions & 1 deletion

File tree

‎src/megatron/bridge/models/__init__.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
# limitations under the License.
1414

1515
# Import model providers for easy access
16+
from megatron.bridge.models.apertus import ApertusBridge
17+
from megatron.bridge.models.apertus1p5 import Apertus1p5Bridge
1618
from megatron.bridge.models.bailing import (
1719
BailingMoeV2Bridge,
1820
)
@@ -206,6 +208,8 @@
206208
"ReplicatedMapping",
207209
"RowParallelMapping",
208210
"AutoMapping",
211+
"ApertusBridge",
212+
"Apertus1p5Bridge",
209213
"BailingMoeV2Bridge",
210214
# DeepSeek Models
211215
"DeepSeekV2Bridge",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from megatron.bridge.models.apertus.apertus_bridge import ApertusBridge
2+
3+
__all__ = ["ApertusBridge"]
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
from __future__ import annotations
2+
3+
import torch
4+
from megatron.core.models.gpt.gpt_layer_specs import get_gpt_decoder_block_spec
5+
from megatron.core.models.gpt.gpt_model import GPTModel
6+
from megatron.core.transformer.spec_utils import get_submodules
7+
from megatron.core.utils import is_torch_min_version
8+
from transformers import ApertusForCausalLM
9+
from transformers.activations import XIELUActivation
10+
11+
from megatron.bridge.models.conversion.mapping_registry import MegatronMappingRegistry
12+
from megatron.bridge.models.conversion.model_bridge import MegatronModelBridge
13+
from megatron.bridge.models.conversion.param_mapping import (
14+
AutoMapping,
15+
ColumnParallelMapping,
16+
QKVMapping,
17+
ReplicatedMapping,
18+
)
19+
from megatron.bridge.models.conversion.utils import unwrap_model
20+
21+
_ROPE_DEFAULTS = {
22+
"rope_type": "llama3",
23+
"original_max_position_embeddings": 8192,
24+
"low_freq_factor": 1.0,
25+
"high_freq_factor": 4.0,
26+
}
27+
28+
29+
class MCoreXIELU(XIELUActivation):
30+
def __init__(self, *, config):
31+
super().__init__(dtype=config.params_dtype, with_vector_loads=False)
32+
if self._xielu_cuda_obj is None:
33+
raise RuntimeError("CUDA xIELU is required. Install rubber-duck-debug/xielu.")
34+
self.to(
35+
device=torch.device("cpu")
36+
if getattr(config, "use_cpu_initialization", False)
37+
else torch.device("cuda", torch.cuda.current_device())
38+
)
39+
self.alpha_p.sum_gradients_across_tp_domain = True
40+
self.alpha_n.sum_gradients_across_tp_domain = True
41+
self._sync_runtime_scalars()
42+
self.register_load_state_dict_post_hook(lambda m, _: m._sync_runtime_scalars())
43+
44+
@torch.no_grad()
45+
def _sync_runtime_scalars(self):
46+
"""Refresh CUDA-kernel host caches after beta/eps buffers change."""
47+
if getattr(self.beta, "is_meta", False) or getattr(self.eps, "is_meta", False):
48+
return
49+
self._beta_scalar = float(self.beta.detach().cpu().float().item())
50+
self._eps_scalar = float(self.eps.detach().cpu().float().item())
51+
52+
53+
def get_apertus_decoder_block_spec(config, vp_stage=None, pp_rank=None):
54+
assert is_torch_min_version("2.4.0a0"), "Torch RMSNorm requires PyTorch >= 2.4"
55+
block_spec = get_gpt_decoder_block_spec(
56+
config, use_transformer_engine=True, normalization="RMSNorm", vp_stage=vp_stage, pp_rank=pp_rank
57+
)
58+
for layer in block_spec.layer_specs:
59+
attn = layer.submodules.self_attention.submodules
60+
attn.q_layernorm = attn.k_layernorm = (lambda config, hidden_size, eps=1e-5, **_: torch.nn.RMSNorm(hidden_size, eps=eps))
61+
get_submodules(layer.submodules.mlp).activation_func = MCoreXIELU
62+
return block_spec
63+
64+
65+
@MegatronModelBridge.register_bridge(source=ApertusForCausalLM, target=GPTModel, model_type="apertus")
66+
class ApertusBridge(MegatronModelBridge):
67+
@classmethod
68+
def hf_to_megatron_activation(cls, hidden_act: str):
69+
if hidden_act != "xielu":
70+
return super().hf_to_megatron_activation(hidden_act)
71+
return lambda _: (_ for _ in ()).throw(RuntimeError("expected MCoreXIELU"))
72+
73+
def provider_bridge(self, hf_pretrained):
74+
provider = super().provider_bridge(hf_pretrained)
75+
config = hf_pretrained.config
76+
rope = {**(getattr(config, "rope_scaling", None) or {}), **(getattr(config, "rope_parameters", None) or {})}
77+
rope_type = rope.get("rope_type", rope.get("type", "llama3"))
78+
factor = float(rope.get("factor", 1.0))
79+
theta = float(rope.get("rope_theta", getattr(config, "rope_theta", 10000.0)))
80+
if config.hidden_act != "xielu":
81+
raise ValueError(f"Expected hidden_act='xielu', got {config.hidden_act!r}")
82+
if config.attention_bias:
83+
raise ValueError("Apertus attention_bias=True is unsupported")
84+
if rope_type != "llama3":
85+
raise ValueError(f"Unsupported Apertus RoPE type: {rope_type!r}")
86+
87+
provider.apertus_rope_scaling = {
88+
"rope_type": rope_type,
89+
"type": rope_type,
90+
"factor": factor,
91+
"original_max_position_embeddings": int(rope.get("original_max_position_embeddings", 8192)),
92+
"low_freq_factor": float(rope.get("low_freq_factor", 1.0)),
93+
"high_freq_factor": float(rope.get("high_freq_factor", 4.0)),
94+
}
95+
provider.normalization = "RMSNorm"
96+
provider.qk_layernorm = True
97+
provider.gated_linear_unit = False
98+
provider.use_te_activation_func = False
99+
provider.bias_activation_fusion = False
100+
provider.add_bias_linear = False
101+
provider.add_qkv_bias = False
102+
provider.hidden_dropout = 0.0
103+
provider.rotary_interleaved = False
104+
provider.position_embedding_type = "rope"
105+
provider.rotary_base = theta
106+
provider.rope_scaling = True
107+
provider.rope_scaling_factor = factor
108+
provider.transformer_layer_spec = get_apertus_decoder_block_spec
109+
return provider
110+
111+
def load_weights_hf_to_megatron(self, hf_pretrained, megatron_model, allowed_mismatched_params=None):
112+
models = super().load_weights_hf_to_megatron(
113+
hf_pretrained, megatron_model, allowed_mismatched_params=allowed_mismatched_params
114+
)
115+
[
116+
m._sync_runtime_scalars()
117+
for model in unwrap_model(models)
118+
for m in model.modules()
119+
if isinstance(m, MCoreXIELU)
120+
]
121+
return models
122+
123+
@classmethod
124+
def megatron_to_hf_config(cls, provider):
125+
config = super().megatron_to_hf_config(provider)
126+
theta = float(provider.rotary_base)
127+
rope = {
128+
**(getattr(provider, "apertus_rope_scaling", None) or _ROPE_DEFAULTS),
129+
"factor": float(provider.rope_scaling_factor),
130+
}
131+
rope["rope_type"] = rope["type"] = rope.get("rope_type", rope.get("type", "llama3"))
132+
config.update(
133+
hidden_act="xielu",
134+
attention_bias=False,
135+
rope_theta=theta,
136+
rope_scaling=rope,
137+
rope_parameters={**rope, "rope_theta": theta},
138+
)
139+
return config
140+
141+
def mapping_registry(self) -> MegatronMappingRegistry:
142+
L, H = "decoder.layers.*", "model.layers.*"
143+
auto = {
144+
"embedding.word_embeddings.weight": "model.embed_tokens.weight",
145+
"output_layer.weight": "lm_head.weight",
146+
"decoder.final_layernorm.weight": "model.norm.weight",
147+
f"{L}.self_attention.linear_proj.weight": f"{H}.self_attn.o_proj.weight",
148+
f"{L}.self_attention.q_layernorm.weight": f"{H}.self_attn.q_norm.weight",
149+
f"{L}.self_attention.k_layernorm.weight": f"{H}.self_attn.k_norm.weight",
150+
f"{L}.mlp.linear_fc2.weight": f"{H}.mlp.down_proj.weight",
151+
}
152+
repl = {
153+
f"{L}.self_attention.linear_qkv.layer_norm_weight": f"{H}.attention_layernorm.weight",
154+
f"{L}.mlp.linear_fc1.layer_norm_weight": f"{H}.feedforward_layernorm.weight",
155+
**{f"{L}.mlp.activation_func.{n}": f"{H}.mlp.act_fn.{n}" for n in ("alpha_p", "alpha_n", "beta", "eps")},
156+
}
157+
qkv = QKVMapping(
158+
f"{L}.self_attention.linear_qkv.weight",
159+
q=f"{H}.self_attn.q_proj.weight",
160+
k=f"{H}.self_attn.k_proj.weight",
161+
v=f"{H}.self_attn.v_proj.weight",
162+
)
163+
qkv._tp_mapping = ColumnParallelMapping(qkv.megatron_param, qkv.megatron_param)
164+
return MegatronMappingRegistry(
165+
*(AutoMapping(m, h) for m, h in auto.items()),
166+
*(ReplicatedMapping(m, h) for m, h in repl.items()),
167+
qkv,
168+
ColumnParallelMapping(f"{L}.mlp.linear_fc1.weight", f"{H}.mlp.up_proj.weight"),
169+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from megatron.bridge.models.apertus1p5.apertus1p5_bridge import Apertus1p5Bridge
2+
3+
__all__ = ["Apertus1p5Bridge"]

0 commit comments

Comments
 (0)