Skip to content

Commit 83f7893

Browse files
committed
refactor(exaone): configure provider in bridge
Drop the EXAONE-specific GPTModelProvider subclasses and configure the plain GPTModelProvider returned by the base bridge instead. This follows the Qwen3 provider_bridge pattern and keeps only the custom EXAONE layer spec in the provider module. Signed-off-by: Bias92 <pewpewplay315@gmail.com>
1 parent d5823a4 commit 83f7893

7 files changed

Lines changed: 108 additions & 199 deletions

File tree

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@
3838
)
3939
from megatron.bridge.models.exaone import (
4040
Exaone4Bridge,
41-
Exaone4ModelProvider,
42-
Exaone4ModelProvider1P2B,
4341
)
4442
from megatron.bridge.models.gemma import (
4543
CodeGemmaModelProvider2B,
@@ -231,8 +229,6 @@
231229
"OlMoEBridge",
232230
"OlMoEModelProvider",
233231
"Exaone4Bridge",
234-
"Exaone4ModelProvider",
235-
"Exaone4ModelProvider1P2B",
236232
"NemotronHBridge",
237233
"MambaModelProvider",
238234
"MimoBridge",
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
from megatron.bridge.models.exaone.exaone4_bridge import Exaone4Bridge
2-
from megatron.bridge.models.exaone.exaone4_provider import Exaone4ModelProvider, Exaone4ModelProvider1P2B
32

4-
__all__ = ["Exaone4Bridge", "Exaone4ModelProvider", "Exaone4ModelProvider1P2B"]
3+
__all__ = ["Exaone4Bridge"]

‎src/megatron/bridge/models/exaone/exaone4_bridge.py‎

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"""
3636

3737
import torch
38+
import torch.nn.functional as F
3839
from megatron.core.models.gpt.gpt_model import GPTModel
3940

4041
from megatron.bridge.models.conversion.mapping_registry import MegatronMappingRegistry
@@ -44,7 +45,8 @@
4445
GatedMLPMapping,
4546
QKVMapping,
4647
)
47-
from megatron.bridge.models.exaone.exaone4_provider import Exaone4ModelProvider
48+
from megatron.bridge.models.exaone.exaone4_provider import exaone4_layer_spec
49+
from megatron.bridge.models.gpt_provider import GPTModelProvider
4850
from megatron.bridge.models.hf_pretrained.causal_lm import PreTrainedCausalLM
4951

5052

@@ -57,7 +59,6 @@
5759
@MegatronModelBridge.register_bridge(
5860
source="Exaone4ForCausalLM", # HF architecture string (auto_map / trust_remote_code)
5961
target=GPTModel,
60-
provider=Exaone4ModelProvider,
6162
model_type="exaone4",
6263
)
6364
class Exaone4Bridge(MegatronModelBridge):
@@ -84,8 +85,8 @@ class Exaone4Bridge(MegatronModelBridge):
8485
>>> provider = bridge.to_megatron_provider()
8586
"""
8687

87-
def provider_bridge(self, hf_pretrained: PreTrainedCausalLM) -> Exaone4ModelProvider:
88-
"""Convert HuggingFace EXAONE 4.0 config to Megatron Exaone4ModelProvider.
88+
def provider_bridge(self, hf_pretrained: PreTrainedCausalLM) -> GPTModelProvider:
89+
"""Convert HuggingFace EXAONE 4.0 config to Megatron GPTModelProvider.
8990
9091
Maps HF config fields to Megatron TransformerConfig parameters and sets
9192
EXAONE-specific options including Post-LN, QK norm, and RoPE scaling.
@@ -94,28 +95,25 @@ def provider_bridge(self, hf_pretrained: PreTrainedCausalLM) -> Exaone4ModelProv
9495
hf_pretrained: HuggingFace PreTrainedCausalLM containing the EXAONE config
9596
9697
Returns:
97-
Exaone4ModelProvider configured for EXAONE 4.0 architecture
98+
GPTModelProvider configured for EXAONE 4.0 architecture
9899
"""
99100
hf_config = hf_pretrained.config
100101

101-
provider = Exaone4ModelProvider(
102-
num_layers=hf_config.num_hidden_layers,
103-
hidden_size=hf_config.hidden_size,
104-
ffn_hidden_size=hf_config.intermediate_size,
105-
num_attention_heads=hf_config.num_attention_heads,
106-
num_query_groups=hf_config.num_key_value_heads,
107-
seq_length=hf_config.max_position_embeddings,
108-
init_method_std=hf_config.initializer_range,
109-
layernorm_epsilon=hf_config.rms_norm_eps,
110-
rotary_base=getattr(hf_config, "rope_theta", getattr(hf_config, "rotary_base", 1000000.0)),
111-
make_vocab_size_divisible_by=self.make_vocab_size_divisible_by(hf_config.vocab_size),
112-
share_embeddings_and_output_weights=getattr(hf_config, "tie_word_embeddings", True),
113-
kv_channels=getattr(hf_config, "head_dim", None),
114-
fp16=(self.dtype_from_hf(hf_config, default=torch.float32) == torch.float16),
115-
bf16=(self.dtype_from_hf(hf_config, default=torch.float32) == torch.bfloat16),
116-
params_dtype=self.dtype_from_hf(hf_config, default=torch.float32),
117-
vocab_size=hf_config.vocab_size,
118-
)
102+
provider = super().provider_bridge(hf_pretrained)
103+
104+
# EXAONE-specific architecture settings. Size-dependent fields are
105+
# populated by the shared HF config mapping in the base bridge.
106+
provider.normalization = "RMSNorm"
107+
provider.activation_func = F.silu
108+
provider.gated_linear_unit = True
109+
provider.position_embedding_type = "rope"
110+
provider.add_bias_linear = False
111+
provider.add_qkv_bias = False
112+
provider.qk_layernorm = True
113+
provider.hidden_dropout = 0.0
114+
provider.attention_dropout = 0.0
115+
provider.transformer_layer_spec = exaone4_layer_spec
116+
provider.autocast_dtype = torch.bfloat16
119117

120118
# RoPE scaling for EXAONE 4.0 (llama3-style)
121119
hf_rope_scaling = getattr(hf_config, "rope_scaling", None)
@@ -131,11 +129,11 @@ def provider_bridge(self, hf_pretrained: PreTrainedCausalLM) -> Exaone4ModelProv
131129
return provider
132130

133131
@classmethod
134-
def megatron_to_hf_config(cls, provider: Exaone4ModelProvider) -> dict:
135-
"""Convert Megatron Exaone4ModelProvider config to HuggingFace config dict.
132+
def megatron_to_hf_config(cls, provider: GPTModelProvider) -> dict:
133+
"""Convert Megatron GPTModelProvider config to HuggingFace config dict.
136134
137135
Args:
138-
provider: Exaone4ModelProvider with EXAONE configuration
136+
provider: GPTModelProvider with EXAONE configuration
139137
140138
Returns:
141139
Dictionary of HuggingFace Exaone4Config parameters
@@ -202,22 +200,20 @@ def mapping_registry(self) -> MegatronMappingRegistry:
202200
# =====================================================================
203201
# Composite Mappings (require concatenation/splitting)
204202
# =====================================================================
205-
mapping_list.extend(
206-
[
207-
# QKV: Merge separate Q, K, V projections into single QKV matrix
208-
QKVMapping(
209-
megatron_param="decoder.layers.*.self_attention.linear_qkv.weight",
210-
q="model.layers.*.self_attn.q_proj.weight",
211-
k="model.layers.*.self_attn.k_proj.weight",
212-
v="model.layers.*.self_attn.v_proj.weight",
213-
),
214-
# Gated MLP: Merge gate and up projections into single FC1 matrix
215-
GatedMLPMapping(
216-
megatron_param="decoder.layers.*.mlp.linear_fc1.weight",
217-
gate="model.layers.*.mlp.gate_proj.weight",
218-
up="model.layers.*.mlp.up_proj.weight",
219-
),
220-
]
221-
)
203+
mapping_list.extend([
204+
# QKV: Merge separate Q, K, V projections into single QKV matrix
205+
QKVMapping(
206+
megatron_param="decoder.layers.*.self_attention.linear_qkv.weight",
207+
q="model.layers.*.self_attn.q_proj.weight",
208+
k="model.layers.*.self_attn.k_proj.weight",
209+
v="model.layers.*.self_attn.v_proj.weight",
210+
),
211+
# Gated MLP: Merge gate and up projections into single FC1 matrix
212+
GatedMLPMapping(
213+
megatron_param="decoder.layers.*.mlp.linear_fc1.weight",
214+
gate="model.layers.*.mlp.gate_proj.weight",
215+
up="model.layers.*.mlp.up_proj.weight",
216+
),
217+
])
222218

223219
return MegatronMappingRegistry(*mapping_list)

‎src/megatron/bridge/models/exaone/exaone4_provider.py‎

Lines changed: 0 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,10 @@
3030
established by Gemma2 bridge.
3131
"""
3232

33-
from dataclasses import dataclass
34-
from typing import Callable
35-
36-
import torch
37-
import torch.nn.functional as F
3833
from megatron.core.extensions.transformer_engine import TEColumnParallelLinear
3934
from megatron.core.fusions.fused_bias_dropout import get_bias_dropout_add
4035
from megatron.core.transformer import (
4136
ModuleSpec,
42-
TransformerConfig,
4337
TransformerLayer,
4438
TransformerLayerSubmodules,
4539
)
@@ -97,81 +91,3 @@ def exaone4_layer_spec(config: "GPTModelProvider") -> ModuleSpec: # noqa: ARG00
9791
mlp_bda=get_bias_dropout_add,
9892
),
9993
)
100-
101-
102-
# =============================================================================
103-
# EXAONE 4.0 Model Providers
104-
# =============================================================================
105-
106-
107-
@dataclass
108-
class Exaone4ModelProvider(GPTModelProvider):
109-
"""Base configuration for EXAONE 4.0 models (LG AI Research).
110-
111-
Architecture features:
112-
- Pure Post-LayerNorm (no input_layernorm / pre_feedforward_layernorm)
113-
- QK RMSNorm after Q/K projection
114-
- GQA (Grouped Query Attention)
115-
- SwiGLU activation
116-
- RoPE with llama3-style scaling
117-
- Tied word embeddings (inherits share_embeddings_and_output_weights=True from GPTModelProvider)
118-
"""
119-
120-
# Architecture defaults common across EXAONE 4.0 model sizes
121-
normalization: str = "RMSNorm"
122-
activation_func: Callable = F.silu
123-
gated_linear_unit: bool = True
124-
position_embedding_type: str = "rope"
125-
add_bias_linear: bool = False
126-
add_qkv_bias: bool = False
127-
qk_layernorm: bool = True # EXAONE 4.0 uses QK RMSNorm
128-
hidden_dropout: float = 0.0
129-
attention_dropout: float = 0.0
130-
131-
# Custom layer spec for Post-LN architecture
132-
transformer_layer_spec: ModuleSpec | Callable[["GPTModelProvider"], ModuleSpec] = exaone4_layer_spec
133-
134-
# Dtype defaults
135-
autocast_dtype: torch.dtype = torch.bfloat16
136-
params_dtype: torch.dtype = torch.bfloat16
137-
bf16: bool = True
138-
139-
140-
@dataclass
141-
class Exaone4ModelProvider1P2B(Exaone4ModelProvider):
142-
"""Configuration for EXAONE 4.0 1.2B.
143-
144-
Model: LGAI-EXAONE/EXAONE-4.0-1.2B
145-
- 30 layers, 2048 hidden, 32 attention heads, 8 KV heads
146-
- Full attention only (no sliding window / hybrid attention)
147-
- RoPE: theta=1M, llama3 scaling factor=16, original_max_pos=8192
148-
- Vocab: 102,400 tokens, Context: 65,536 tokens
149-
"""
150-
151-
num_layers: int = 30
152-
hidden_size: int = 2048
153-
ffn_hidden_size: int = 4096
154-
num_attention_heads: int = 32
155-
num_query_groups: int = 8
156-
kv_channels: int = 64
157-
seq_length: int = 65536
158-
vocab_size: int = 102400
159-
rotary_base: float = 1000000.0
160-
layernorm_epsilon: float = 1e-5
161-
init_method_std: float = 0.02
162-
163-
# RoPE scaling (llama3-style)
164-
rope_scaling: bool = True
165-
rope_scaling_factor: float = 16.0
166-
rope_scaling_low_freq_factor: float = 1.0
167-
rope_scaling_high_freq_factor: float = 4.0
168-
rope_scaling_original_max_position_embeddings: int = 8192
169-
170-
171-
# TODO: Add Exaone4ModelProvider32B when 32B model details are confirmed
172-
# The 32B model introduces hybrid attention (LLLG pattern: 3 local + 1 global)
173-
# with sliding_window_pattern and layer_types configuration.
174-
# This will require:
175-
# - Layer-wise attention type branching (local vs global)
176-
# - RoPE disable for global attention layers
177-
# - Sliding window size configuration per layer

‎tests/functional_tests/models/exaone/test_exaone4_provider.py‎

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,51 @@
1313
# limitations under the License.
1414

1515
import torch
16+
import torch.nn.functional as F
1617
from transformers import PretrainedConfig
1718

1819
from megatron.bridge.models.conversion.auto_bridge import AutoBridge
19-
from megatron.bridge.models.exaone import Exaone4ModelProvider1P2B
20+
from megatron.bridge.models.exaone.exaone4_provider import exaone4_layer_spec
21+
from megatron.bridge.models.gpt_provider import GPTModelProvider
2022
from tests.functional_tests.utils import compare_provider_configs
2123

2224

25+
def make_expected_exaone4_1p2b_provider():
26+
provider = GPTModelProvider(
27+
num_layers=30,
28+
hidden_size=2048,
29+
ffn_hidden_size=4096,
30+
num_attention_heads=32,
31+
num_query_groups=8,
32+
kv_channels=64,
33+
seq_length=65536,
34+
vocab_size=102400,
35+
rotary_base=1000000.0,
36+
layernorm_epsilon=1e-5,
37+
init_method_std=0.02,
38+
normalization="RMSNorm",
39+
activation_func=F.silu,
40+
gated_linear_unit=True,
41+
position_embedding_type="rope",
42+
add_bias_linear=False,
43+
add_qkv_bias=False,
44+
qk_layernorm=True,
45+
hidden_dropout=0.0,
46+
attention_dropout=0.0,
47+
transformer_layer_spec=exaone4_layer_spec,
48+
autocast_dtype=torch.bfloat16,
49+
params_dtype=torch.bfloat16,
50+
bf16=True,
51+
fp16=False,
52+
rope_scaling=True,
53+
rope_scaling_factor=16.0,
54+
)
55+
provider.rope_scaling_low_freq_factor = 1.0
56+
provider.rope_scaling_high_freq_factor = 4.0
57+
provider.rope_scaling_original_max_position_embeddings = 8192
58+
return provider
59+
60+
2361
class TestExaone4ProviderMapping:
2462
"""Test EXAONE 4.0 config-only AutoBridge provider mapping."""
2563

@@ -53,7 +91,7 @@ def test_bridge_vs_predefined_provider_config_from_config_only(self):
5391
converted_provider = bridge.to_megatron_provider(load_weights=False)
5492
converted_provider.finalize()
5593

56-
predefined_provider = Exaone4ModelProvider1P2B()
94+
predefined_provider = make_expected_exaone4_1p2b_provider()
5795
predefined_provider.finalize()
5896

5997
compare_provider_configs(converted_provider, predefined_provider, "exaone4-config-only")

0 commit comments

Comments
 (0)