Skip to content

Commit e41a72c

Browse files
Add ragged all-to-all pipeline correctness coverage
1 parent 85690da commit e41a72c

1 file changed

Lines changed: 66 additions & 29 deletions

File tree

tests/integration/pipeline_parallelism_test.py

Lines changed: 66 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from flax import linen as nn
2525
from flax import nnx
2626
from flax.core import meta
27+
from flax.linen import partitioning as nn_partitioning
2728
import jax
2829
import jax.numpy as jnp
2930
from jax.sharding import Mesh
@@ -154,23 +155,24 @@ def stage_factory(stage_rngs):
154155
return raw_stage_class(config=config, mesh=mesh, model_mode=model_mode, rngs=stage_rngs)
155156

156157
my_pipeline = pipeline.create_pipeline(config=config, layers=stage_factory, mesh=mesh)
157-
init_pipeline_params = my_pipeline.init(
158-
jax.random.PRNGKey(0), inputs, inputs_position, inputs_segmentation, deterministic, model_mode
159-
)
160-
# `get_weight_sharding` is a compact method on `PipelineLinen` (callable as
161-
# `my_pipeline.get_weight_sharding(...)` directly) but on the ToLinen-wrapped NNX
162-
# pipeline it must be invoked inside a bound module context. Use `bind` so the
163-
# same call shape works on both paths.
164-
logical_partition_spec = my_pipeline.bind(init_pipeline_params).get_weight_sharding(
165-
inputs, inputs_position, inputs_segmentation, deterministic, model_mode
166-
)
158+
with jax.set_mesh(mesh), nn_partitioning.axis_rules(config.logical_axis_rules):
159+
init_pipeline_params = my_pipeline.init(
160+
jax.random.PRNGKey(0), inputs, inputs_segmentation, inputs_position, deterministic, model_mode
161+
)
162+
# `get_weight_sharding` is a compact method on `PipelineLinen` (callable as
163+
# `my_pipeline.get_weight_sharding(...)` directly) but on the ToLinen-wrapped NNX
164+
# pipeline it must be invoked inside a bound module context. Use `bind` so the
165+
# same call shape works on both paths.
166+
logical_partition_spec = my_pipeline.bind(init_pipeline_params).get_weight_sharding(
167+
inputs, inputs_segmentation, inputs_position, deterministic, model_mode
168+
)
167169

168170
# Create a dummy scalar loss function so we may take the gradient wrt weights
169171
def pipeline_parallelism_dummy_loss_extra(
170172
params,
171173
inputs,
172-
inputs_position,
173174
inputs_segmentation,
175+
inputs_position,
174176
deterministic,
175177
model_mode,
176178
dummy_targets,
@@ -179,8 +181,8 @@ def pipeline_parallelism_dummy_loss_extra(
179181
outputs = my_pipeline.apply(
180182
params,
181183
inputs,
182-
inputs_position,
183184
inputs_segmentation,
185+
inputs_position,
184186
deterministic,
185187
model_mode,
186188
logical_partition_spec=logical_partition_spec,
@@ -192,7 +194,7 @@ def pipeline_parallelism_dummy_loss_extra(
192194
pipeline_parallelism_dummy_loss_extra, logical_partition_spec=logical_partition_spec
193195
)
194196

195-
def regular_sequential_layers(params, inputs, inputs_position, inputs_segmentation, deterministic, model_mode):
197+
def regular_sequential_layers(params, inputs, inputs_segmentation, inputs_position, deterministic, model_mode):
196198
def get_cur_layer_params(params, layer_idx):
197199
def get_cur_layer_params_arr(leaf):
198200
# Reshape layers into a linear list of layers, e.g. [repeat, stage] into [layers]
@@ -213,34 +215,35 @@ def get_cur_layer_params_arr(leaf):
213215
for layer in range(config.num_decoder_layers):
214216
cur_layer_params = get_cur_layer_params(params, layer)
215217
cur_layer_params["params"] = cur_layer_params["params"]["layers"]
216-
if config.num_pipeline_repeats > 1 and config.num_layers_per_pipeline_stage > 1:
218+
if config.num_pipeline_repeats > 1:
217219
cur_layer_params["params"] = meta.remove_axis(
218220
cur_layer_params["params"], 0, {nn.PARTITION_NAME: "circular_repeats"}
219221
)
220-
cur_layer_params["params"] = meta.remove_axis(cur_layer_params["params"], 0, {nn.PARTITION_NAME: "layers"})
222+
cur_layer_params["params"] = meta.remove_axis(cur_layer_params["params"], 0, {nn.PARTITION_NAME: "layers"})
221223
reg_layer_activations, _ = single_pipeline_stage.apply(
222-
cur_layer_params, reg_layer_activations, inputs_position, inputs_segmentation, deterministic, model_mode
224+
cur_layer_params, reg_layer_activations, inputs_segmentation, inputs_position, deterministic, model_mode
223225
)
224226
return reg_layer_activations
225227

226228
def regular_sequential_layers_dummy_loss(
227-
params, inputs, inputs_position, inputs_segmentation, deterministic, model_mode, dummy_targets
229+
params, inputs, inputs_segmentation, inputs_position, deterministic, model_mode, dummy_targets
228230
):
229-
outputs = regular_sequential_layers(params, inputs, inputs_position, inputs_segmentation, deterministic, model_mode)
231+
outputs = regular_sequential_layers(params, inputs, inputs_segmentation, inputs_position, deterministic, model_mode)
230232
loss = jnp.linalg.norm(outputs - dummy_targets)
231233
return loss
232234

233-
assert_same_output_and_grad(
234-
regular_sequential_layers_dummy_loss,
235-
pipeline_parallelism_dummy_loss,
236-
init_pipeline_params,
237-
inputs,
238-
inputs_segmentation,
239-
inputs_position,
240-
deterministic,
241-
model_mode,
242-
dummy_targets,
243-
)
235+
with jax.set_mesh(mesh), nn_partitioning.axis_rules(config.logical_axis_rules):
236+
assert_same_output_and_grad(
237+
regular_sequential_layers_dummy_loss,
238+
pipeline_parallelism_dummy_loss,
239+
init_pipeline_params,
240+
inputs,
241+
inputs_segmentation,
242+
inputs_position,
243+
deterministic,
244+
model_mode,
245+
dummy_targets,
246+
)
244247

245248
def assert_pipeline_same_output_and_grad(self, config, single_pipeline_stage_class=None):
246249
"""
@@ -342,6 +345,40 @@ def test_circular_deepseek_megablox_same_output_and_grad(self):
342345
config, single_pipeline_stage_class=deepseek.DeepSeekMoELayerToLinen
343346
)
344347

348+
@pytest.mark.tpu_only
349+
def test_deepseek_ragged_a2a_ep_same_output_and_grad(self):
350+
config = pyconfig.initialize(
351+
[sys.argv[0], get_test_config_path()],
352+
enable_checkpointing=False,
353+
enable_goodput_recording=False,
354+
run_name="deepseek_ragged_a2a_ep",
355+
dtype="bfloat16",
356+
weight_dtype="bfloat16",
357+
matmul_precision="high",
358+
max_target_length=128,
359+
base_emb_dim=256,
360+
ici_pipeline_parallelism=2,
361+
ici_expert_parallelism=2,
362+
allow_split_physical_axes=True,
363+
base_num_decoder_layers=2,
364+
num_pipeline_microbatches=4,
365+
per_device_batch_size=4,
366+
num_experts=4,
367+
num_experts_per_tok=2,
368+
shared_experts=1,
369+
megablox=True,
370+
sparse_matmul=True,
371+
capacity_factor=-1,
372+
decoder_block="deepseek",
373+
attention_type="mla",
374+
base_moe_mlp_dim=256,
375+
base_mlp_dim=256,
376+
)
377+
self.assert_pipeline_same_output_and_grad(
378+
config,
379+
single_pipeline_stage_class=deepseek.DeepSeekMoELayerToLinen,
380+
)
381+
345382
@pytest.mark.tpu_only
346383
def test_circular_ag_once(self):
347384
# 2 stages, 8 microbatches, all gather once

0 commit comments

Comments
 (0)