Skip to content

Commit f8534f6

Browse files
authored
Merge pull request #457 from Modalities/moe_rf
MoE Refactoring
2 parents ee2c5dc + daea062 commit f8534f6

8 files changed

Lines changed: 132 additions & 144 deletions

File tree

‎src/modalities/loss_functions.py‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,39 @@ def _parse_arguments(
8787
return labels, lm_logits
8888

8989

90+
class MoECrossEntropyLoss(Loss):
91+
"""Cross entropy loss with optional MoE auxiliary losses from model layers."""
92+
93+
def __init__(
94+
self,
95+
target_key: str,
96+
prediction_key: str,
97+
model,
98+
tag: str = "MoECrossEntropyLoss",
99+
):
100+
super().__init__(tag)
101+
self.target_key = target_key
102+
self.prediction_key = prediction_key
103+
self.model = model
104+
self.loss_fun = CrossEntropyLoss(reduction="mean")
105+
106+
def __call__(self, forward_batch: InferenceResultBatch) -> torch.Tensor:
107+
labels = forward_batch.get_targets(self.target_key)
108+
lm_logits = forward_batch.get_predictions(self.prediction_key)
109+
110+
labels = labels.to(lm_logits.device)
111+
loss = self.loss_fun(
112+
lm_logits.contiguous().view(-1, lm_logits.size(-1)),
113+
labels.contiguous().long().view(-1),
114+
)
115+
116+
for layer in self.model.layers.values():
117+
if hasattr(layer, "aux_loss") and layer.aux_loss is not None:
118+
loss = loss + layer.aux_loss.to(loss.dtype)
119+
120+
return loss
121+
122+
90123
def nce_loss(
91124
embedding1: torch.Tensor, embedding2: torch.Tensor, device: torch.device, is_asymmetric: bool, temperature: float
92125
) -> torch.Tensor:

‎src/modalities/models/model_factory.py‎

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import itertools
66
import json
77
import time
8+
import warnings
89
from dataclasses import asdict, dataclass
910
from functools import partial
1011
from pathlib import Path
@@ -18,6 +19,7 @@
1819
from torch.distributed.fsdp import FSDPModule as FSDP2
1920
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP1
2021
from torch.distributed.fsdp import ShardingStrategy
22+
from torch.distributed.tensor import DTensor
2123
from torch.distributed.tensor.parallel import (
2224
ColwiseParallel,
2325
PrepareModuleInput,
@@ -41,9 +43,10 @@
4143
TransformerMLP,
4244
)
4345
from modalities.models.model import ActivationType
46+
from modalities.models.parallelism.expert_parallelism import ExpertParallel
4447
from modalities.nn.model_initialization.initialization_if import ModelInitializationIF
4548
from modalities.running_env.env_utils import FSDP2MixedPrecisionSettings, MixedPrecisionSettings
46-
from modalities.running_env.fsdp.device_mesh import ParallelismDegrees
49+
from modalities.running_env.fsdp.device_mesh import ParallelismDegrees, get_mesh_for_parallelism_method
4750
from modalities.running_env.fsdp.fsdp_auto_wrapper import FSDPTransformerAutoWrapPolicyFactory
4851
from modalities.training.activation_checkpointing.activation_checkpointing import (
4952
ActivationCheckpointing,
@@ -253,6 +256,95 @@ def get_fsdp2_wrapped_model(
253256
)
254257
return model
255258

259+
@staticmethod
260+
def get_ep_wrapped_model(
261+
model,
262+
block_names: list[str],
263+
device_mesh: DeviceMesh,
264+
mixed_precision_settings: FSDP2MixedPrecisionSettings,
265+
) -> nn.Module:
266+
"""Apply expert parallel wrapping for selected MoE blocks."""
267+
block_types = []
268+
missing_block_names = []
269+
for name in block_names:
270+
block_type = get_module_class_from_name(model, name)
271+
if block_type is None:
272+
missing_block_names.append(name)
273+
else:
274+
block_types.append(block_type)
275+
276+
if len(missing_block_names) > 0 and (
277+
not dist.is_available() or not dist.is_initialized() or dist.get_rank() == 0
278+
):
279+
warnings.warn(
280+
f"Could not resolve some requested MoE block names and they will be ignored: {missing_block_names}",
281+
stacklevel=2,
282+
)
283+
284+
block_types = tuple(block_types)
285+
286+
if len(block_types) == 0:
287+
raise ValueError(f"None of the requested MoE block names were found: {block_names}")
288+
289+
ep_mesh = get_mesh_for_parallelism_method(device_mesh, ParallelismDegrees.EP)
290+
target_dtype = mixed_precision_settings.param_dtype.value
291+
292+
wrapped_blocks = 0
293+
for module in model.modules():
294+
if isinstance(module, block_types):
295+
if hasattr(module, "experts"):
296+
ep_target = module
297+
elif (ffn := getattr(module, "ffn", None)) is not None and hasattr(ffn, "experts"):
298+
ep_target = ffn
299+
else:
300+
raise ValueError(
301+
f"Module {type(module).__name__} has no EP-compatible experts location. "
302+
"Expected `experts` or `ffn.experts`."
303+
)
304+
305+
if getattr(ep_target, "_ep_enabled", False):
306+
continue
307+
308+
experts = ep_target.experts
309+
missing = [a for a in ("w1", "w2") if not hasattr(experts, a)]
310+
if missing:
311+
raise ValueError(
312+
f"Module {type(ep_target).__name__}.experts is not grouped-experts compatible. "
313+
f"Missing: {missing}"
314+
)
315+
if experts.w1.ndim != 3 or experts.w2.ndim != 3:
316+
raise ValueError(
317+
f"Expected grouped expert parameters with ndim=3. Got w1.ndim={experts.w1.ndim}, "
318+
f"w2.ndim={experts.w2.ndim}"
319+
)
320+
321+
ep_target._ep_mesh = ep_mesh
322+
ep_target._ep_group = ep_mesh.get_group()
323+
ep_target._ep_size = ep_mesh.size()
324+
ep_target._ep_rank = ep_mesh.get_local_rank()
325+
326+
ep_target.experts = ExpertParallel()._apply(ep_target.experts, ep_mesh)
327+
setattr(ep_target.experts, "_ep_enabled", True)
328+
329+
for pname, p in list(ep_target.experts._parameters.items()):
330+
if isinstance(p, DTensor) and p.dtype != target_dtype:
331+
local = p.to_local().to(target_dtype)
332+
ep_target.experts._parameters[pname] = nn.Parameter(
333+
DTensor.from_local(local, p.device_mesh, p.placements, run_check=False),
334+
requires_grad=p.requires_grad,
335+
)
336+
337+
wrapped_blocks += 1
338+
339+
if wrapped_blocks == 0:
340+
raise ValueError(f"No blocks matched the requested types: {[t.__name__ for t in block_types]}")
341+
342+
model._ep_wrapped = True
343+
model._ep_mesh = ep_mesh
344+
model._ep_num_wrapped_blocks = wrapped_blocks
345+
346+
return model
347+
256348
@staticmethod
257349
def get_weight_initialized_model(model: nn.Module, model_initializer: ModelInitializationIF) -> nn.Module:
258350
"""
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
from modalities.models.moe.loss_functions import MoECrossEntropyLoss
2-
from modalities.models.moe.model_factory import get_ep_wrapped_model
1+
from modalities.loss_functions import MoECrossEntropyLoss
32
from modalities.models.moe.qwen_model import QwenModel, QwenModelConfig
43

54
__all__ = [
65
"MoECrossEntropyLoss",
76
"QwenModel",
87
"QwenModelConfig",
9-
"get_ep_wrapped_model",
108
]

‎src/modalities/models/moe/loss_functions.py‎

Lines changed: 0 additions & 39 deletions
This file was deleted.

‎src/modalities/models/moe/model_factory.py‎

Lines changed: 0 additions & 96 deletions
This file was deleted.

‎src/modalities/registry/components.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
ProgressSubscriberFactory,
8686
ResultsSubscriberFactory,
8787
)
88-
from modalities.loss_functions import CLMCrossEntropyLoss
88+
from modalities.loss_functions import CLMCrossEntropyLoss, MoECrossEntropyLoss
8989
from modalities.models.coca.coca_model import CoCa, CoCaConfig
9090
from modalities.models.coca.collator import CoCaCollateFnConfig, CoCaCollatorFn
9191
from modalities.models.components.layer_norms import (
@@ -99,8 +99,6 @@
9999
from modalities.models.gpt2.llama3_like_initialization import Llama3Initializer, Llama3InitializerConfig
100100
from modalities.models.huggingface.huggingface_model import HuggingFacePretrainedModel, HuggingFacePretrainedModelConfig
101101
from modalities.models.model_factory import GPT2ModelFactory, ModelFactory
102-
from modalities.models.moe.loss_functions import MoECrossEntropyLoss
103-
from modalities.models.moe.model_factory import get_ep_wrapped_model
104102
from modalities.models.moe.qwen_model import QwenModel, QwenModelConfig
105103
from modalities.models.parallelism.pipeline_parallelism import ComponentSelectorFromPipeline, PipelineFactory
106104
from modalities.models.parallelism.pipeline_parallelism_configs import (
@@ -196,7 +194,7 @@ class ComponentEntity:
196194
# models
197195
ComponentEntity("model", "gpt2", GPT2ModelFactory.get_gpt2_model, GPT2LLMConfig),
198196
ComponentEntity("model", "moe", QwenModel, QwenModelConfig),
199-
ComponentEntity("model", "ep_wrapped", get_ep_wrapped_model, EPWrappedModelConfig),
197+
ComponentEntity("model", "ep_wrapped", ModelFactory.get_ep_wrapped_model, EPWrappedModelConfig),
200198
ComponentEntity(
201199
"model", "gpt2_tp", maybe_model_list(GPT2ModelFactory.get_gpt2_tensor_parallelized_model), GPT2ModelTPConfig
202200
),

‎tests/dataloader/distributed/test_distributed_multidim_dataloader.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ def _test_dataloader_produces_different_samples_in_different_dp_ranks(process_id
9393
tensor_parallel_degree=2,
9494
pipeline_parallel_degree=2,
9595
context_parallel_degree=1,
96+
expert_parallel_degree=1,
9697
enable_loss_parallel=False,
9798
world_size=world_size,
9899
)
@@ -117,6 +118,7 @@ def _test_dataloader_produces_same_samples_in_connected_non_dp_ranks(process_id:
117118
tensor_parallel_degree=2,
118119
pipeline_parallel_degree=2,
119120
context_parallel_degree=1,
121+
expert_parallel_degree=1,
120122
enable_loss_parallel=False,
121123
world_size=world_size,
122124
)

‎tests/models/moe/test_loss_functions.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from torch.nn import CrossEntropyLoss
33

44
from modalities.batch import InferenceResultBatch
5-
from modalities.models.moe.loss_functions import MoECrossEntropyLoss
5+
from modalities.loss_functions import MoECrossEntropyLoss
66

77

88
class DummyLayer:

0 commit comments

Comments
 (0)