Skip to content

Commit 8abfa62

Browse files
committed
Constrain OLMoDDP pipeline schedules
1 parent b1c6d1e commit 8abfa62

4 files changed

Lines changed: 88 additions & 16 deletions

File tree

src/olmo_core/train/train_module/transformer/config.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,15 @@ def build(
552552
"""
553553
from .ddp_train_module import OLMoDDPTrainModule
554554

555+
if self.pp_config is not None and self.pp_config.schedule not in {
556+
PipelineScheduleType.custom_interleaved_1F1B,
557+
PipelineScheduleType.custom_1F1B_V,
558+
}:
559+
raise OLMoConfigurationError(
560+
"OLMoDDPTrainModule pipeline parallelism requires a custom pipeline schedule; "
561+
f"got {self.pp_config.schedule.value!r}"
562+
)
563+
555564
kwargs = self.as_dict(exclude_none=True, recurse=False)
556565

557566
if (state_dict_save_opts := kwargs.pop("state_dict_save_opts", None)) is not None:

src/olmo_core/train/train_module/transformer/ddp_train_module.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1911,7 +1911,6 @@ def eval_batch(
19111911
lm_output = self.run_pipeline_eval(
19121912
input_ids,
19131913
labels,
1914-
batch_num_tokens_for_loss=None,
19151914
ignore_index=self.label_ignore_index,
19161915
loss_reduction="none",
19171916
**model_kwargs,
Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
11
from types import SimpleNamespace
2+
from typing import Any, cast
23
from unittest.mock import Mock
34

45
from olmo_core.train.callbacks.batch_size_scheduler import BatchSizeSchedulerCallback
56
from olmo_core.train.train_module import OLMoDDPTrainModule
67

78

8-
def test_batch_size_change_rebuilds_olmo_ddp_pipeline_schedule():
9+
def test_batch_size_change_rebuilds_olmo_ddp_pipeline_schedule(monkeypatch):
910
train_module = OLMoDDPTrainModule.__new__(OLMoDDPTrainModule)
10-
train_module.rebuild_train_pp_schedule = Mock()
11+
rebuild_schedule = Mock()
12+
monkeypatch.setattr(train_module, "rebuild_train_pp_schedule", rebuild_schedule)
1113
train_module.optim = None
1214
train_module.scheduler = None
1315
data_loader = SimpleNamespace(global_batch_size=8)
1416
callback = BatchSizeSchedulerCallback()
15-
callback.trainer = SimpleNamespace(
16-
train_module=train_module,
17-
data_loader=data_loader,
18-
callbacks={},
17+
callback.trainer = cast(
18+
Any,
19+
SimpleNamespace(
20+
train_module=train_module,
21+
data_loader=data_loader,
22+
callbacks={},
23+
),
1924
)
2025

2126
callback._update_batch_size_and_lr(16)
2227

23-
train_module.rebuild_train_pp_schedule.assert_called_once_with(16)
28+
rebuild_schedule.assert_called_once_with(16)
2429
assert data_loader.global_batch_size == 16

src/test/train/train_module/transformer/ddp_train_module_test.py

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
"""Tests for :class:`OLMoDDPTrainModule` config and construction."""
22

3+
import contextlib
34
from types import SimpleNamespace
4-
from typing import Optional
5+
from typing import Any, Optional, cast
56

67
import pytest
78
import torch
89
import torch.distributed as dist
910

1011
from olmo_core.config import DType
11-
from olmo_core.distributed.parallel import DataParallelType
12+
from olmo_core.distributed.parallel import DataParallelType, PipelineScheduleType
1213
from olmo_core.exceptions import OLMoConfigurationError
1314
from olmo_core.nn.attention import AttentionConfig, AttentionType
1415
from olmo_core.nn.ddp.block import OLMoDDPTransformerBlockConfig
@@ -58,6 +59,22 @@ def test_moe_v2_train_module_config_roundtrips_with_parallelism():
5859
assert restored.pp_config is not None and restored.pp_config.degree == 2
5960

6061

62+
def test_olmo_ddp_pipeline_parallelism_requires_custom_schedule():
63+
model = _tiny_model_config().build(init_device="cpu")
64+
config = OLMoDDPTrainModuleConfig(
65+
rank_microbatch_size=512,
66+
max_sequence_length=512,
67+
optim=OLMoDDPOptimizerConfig(lr=1e-3),
68+
pp_config=TransformerPipelineParallelConfig(
69+
degree=2,
70+
schedule=PipelineScheduleType.interleaved_1F1B,
71+
),
72+
)
73+
74+
with pytest.raises(OLMoConfigurationError, match="requires a custom pipeline schedule"):
75+
config.build(model, device=torch.device("cpu"), eval_only=True)
76+
77+
6178
def _tiny_model_config(
6279
*,
6380
d_model: int = 64,
@@ -345,12 +362,13 @@ def __init__(self, **kwargs):
345362
built_with.update(kwargs)
346363

347364
train_module = OLMoDDPTrainModule.__new__(OLMoDDPTrainModule)
348-
train_module._trainer = SimpleNamespace(dp_process_group=None)
349-
train_module._pp_config = SimpleNamespace(
350-
schedule="schedule", forward_pull_ahead_extra_activations=None
365+
train_module._trainer = cast(Any, SimpleNamespace(dp_process_group=None))
366+
train_module._pp_config = TransformerPipelineParallelConfig(
367+
degree=2,
368+
schedule=PipelineScheduleType.custom_interleaved_1F1B,
351369
)
352-
train_module._pp_stages = [object()]
353-
train_module.model_parts = [object()]
370+
train_module._pp_stages = cast(Any, [object()])
371+
train_module.model_parts = cast(Any, [object()])
354372
train_module.rank_microbatch_size = 4
355373
train_module.world_mesh = {"dense": {"pp": object()}}
356374
monkeypatch.setattr(
@@ -373,7 +391,7 @@ def test_broadcast_pp_eval_output_reconstructs_on_non_final_rank(monkeypatch):
373391
None,
374392
]
375393
train_module = OLMoDDPTrainModule.__new__(OLMoDDPTrainModule)
376-
train_module.pp_group = object()
394+
train_module.pp_group = cast(Any, object())
377395
train_module.pp_group_rank = 0
378396
train_module.pp_final_stage_rank = 1
379397
train_module.device = torch.device("cpu")
@@ -405,6 +423,47 @@ def fake_broadcast(tensor, **kwargs):
405423
assert torch.equal(actual, wanted)
406424

407425

426+
def test_pipeline_eval_does_not_forward_training_only_loss_kwarg(monkeypatch):
427+
train_module = OLMoDDPTrainModule.__new__(OLMoDDPTrainModule)
428+
train_module._cp_config = None
429+
train_module._tp_config = None
430+
train_module._pp_config = TransformerPipelineParallelConfig(
431+
degree=2,
432+
schedule=PipelineScheduleType.custom_interleaved_1F1B,
433+
)
434+
train_module.model_parts = cast(Any, [torch.nn.Identity()])
435+
train_module.label_ignore_index = -100
436+
input_ids = torch.ones((2, 3), dtype=torch.long)
437+
labels = input_ids.clone()
438+
microbatch_output = LMOutputWithLoss(
439+
logits=torch.ones((2, 3, 4)),
440+
loss=torch.ones((2, 3)),
441+
ce_loss=torch.ones((2, 3)),
442+
z_loss=None,
443+
)
444+
forwarded_kwargs = {}
445+
446+
monkeypatch.setattr(
447+
train_module,
448+
"_prepare_batch",
449+
lambda batch, labels: (input_ids, labels, {}),
450+
)
451+
monkeypatch.setattr(train_module, "_eval_batch_context", contextlib.nullcontext)
452+
453+
def fake_run_pipeline_eval(input_ids, labels, **kwargs):
454+
del input_ids, labels
455+
forwarded_kwargs.update(kwargs)
456+
return [[microbatch_output]]
457+
458+
monkeypatch.setattr(train_module, "run_pipeline_eval", fake_run_pipeline_eval)
459+
monkeypatch.setattr(train_module, "_broadcast_pp_eval_output", lambda output: output)
460+
461+
output = train_module.eval_batch({}, labels)
462+
463+
assert output is not None
464+
assert "batch_num_tokens_for_loss" not in forwarded_kwargs
465+
466+
408467
def _run_global_lb_group_is_stage_local():
409468
train_module = OLMoDDPTrainModule.__new__(OLMoDDPTrainModule)
410469
train_module.world_mesh = {}

0 commit comments

Comments
 (0)