|
6 | 6 |
|
7 | 7 | import mlx.core as mx |
8 | 8 | import mlx.nn as nn |
| 9 | +from mlx.nn.layers.distributed import shard_inplace, shard_linear, sum_gradients |
9 | 10 |
|
10 | 11 | from .base import BaseModelArgs, create_attention_mask, scaled_dot_product_attention |
11 | 12 | from .pipeline import PipelineMixin |
@@ -315,13 +316,21 @@ def __init__(self, config: ModelArgs): |
315 | 316 | config=config, intermediate_size=intermediate_size |
316 | 317 | ) |
317 | 318 |
|
| 319 | + self.sharding_group = None |
| 320 | + |
318 | 321 | def __call__(self, x): |
| 322 | + if self.sharding_group is not None: |
| 323 | + x = sum_gradients(self.sharding_group)(x) |
| 324 | + |
319 | 325 | inds, scores = self.gate(x) |
320 | 326 | y = self.switch_mlp(x, inds) |
321 | 327 | y = (y * scores[..., None]).sum(axis=-2) |
322 | 328 | if self.config.n_shared_experts is not None: |
323 | 329 | y = y + self.shared_experts(x) |
324 | 330 |
|
| 331 | + if self.sharding_group is not None: |
| 332 | + y = mx.distributed.all_sum(y, group=self.sharding_group) |
| 333 | + |
325 | 334 | return y |
326 | 335 |
|
327 | 336 |
|
@@ -395,7 +404,8 @@ def __call__( |
395 | 404 | cache[-1].keys = mx.depends(cache[-1].keys, h) |
396 | 405 |
|
397 | 406 | # Broadcast h while keeping it in the graph |
398 | | - h = mx.distributed.all_gather(h)[: h.shape[0]] |
| 407 | + if pipeline_size > 1: |
| 408 | + h = mx.distributed.all_gather(h)[: h.shape[0]] |
399 | 409 |
|
400 | 410 | return self.norm(h) |
401 | 411 |
|
@@ -429,6 +439,62 @@ def sanitize(self, weights): |
429 | 439 | weights[f"{prefix}.mlp.switch_mlp.{m}.{k}"] = mx.stack(to_join) |
430 | 440 | return weights |
431 | 441 |
|
| 442 | + def shard(self, group: Optional[mx.distributed.Group] = None): |
| 443 | + group = group or mx.distributed.init() |
| 444 | + N = group.size() |
| 445 | + for layer in self.model.layers: |
| 446 | + # Shard the self attention |
| 447 | + if layer.self_attn.q_lora_rank is None: |
| 448 | + layer.self_attn.q_proj = shard_linear( |
| 449 | + layer.self_attn.q_proj, "all-to-sharded", group=group |
| 450 | + ) |
| 451 | + else: |
| 452 | + layer.self_attn.q_b_proj = shard_linear( |
| 453 | + layer.self_attn.q_b_proj, "all-to-sharded", group=group |
| 454 | + ) |
| 455 | + layer.self_attn.kv_b_proj = shard_linear( |
| 456 | + layer.self_attn.kv_b_proj, "all-to-sharded", group=group |
| 457 | + ) |
| 458 | + layer.self_attn.o_proj = shard_linear( |
| 459 | + layer.self_attn.o_proj, "sharded-to-all", group=group |
| 460 | + ) |
| 461 | + layer.self_attn.num_heads //= N |
| 462 | + |
| 463 | + # Shard the MLP |
| 464 | + if isinstance(layer.mlp, DeepseekV2MLP): |
| 465 | + layer.mlp.gate_proj = shard_linear( |
| 466 | + layer.mlp.gate_proj, "all-to-sharded", group=group |
| 467 | + ) |
| 468 | + layer.mlp.down_proj = shard_linear( |
| 469 | + layer.mlp.down_proj, "sharded-to-all", group=group |
| 470 | + ) |
| 471 | + layer.mlp.up_proj = shard_linear( |
| 472 | + layer.mlp.up_proj, "all-to-sharded", group=group |
| 473 | + ) |
| 474 | + |
| 475 | + # Shard the MoE. Shard in place since the MoE should be responsible |
| 476 | + # for aggregating the results. |
| 477 | + else: |
| 478 | + layer.mlp.sharding_group = group |
| 479 | + shard_inplace( |
| 480 | + layer.mlp.shared_experts.gate_proj, "all-to-sharded", group=group |
| 481 | + ) |
| 482 | + shard_inplace( |
| 483 | + layer.mlp.shared_experts.down_proj, "sharded-to-all", group=group |
| 484 | + ) |
| 485 | + shard_inplace( |
| 486 | + layer.mlp.shared_experts.up_proj, "all-to-sharded", group=group |
| 487 | + ) |
| 488 | + shard_inplace( |
| 489 | + layer.mlp.switch_mlp.gate_proj, "all-to-sharded", group=group |
| 490 | + ) |
| 491 | + shard_inplace( |
| 492 | + layer.mlp.switch_mlp.down_proj, "sharded-to-all", group=group |
| 493 | + ) |
| 494 | + shard_inplace( |
| 495 | + layer.mlp.switch_mlp.up_proj, "all-to-sharded", group=group |
| 496 | + ) |
| 497 | + |
432 | 498 | @property |
433 | 499 | def layers(self): |
434 | 500 | return self.model.pipeline_layers |
0 commit comments