Skip to content

Commit cae39c9

Browse files
committed
add very dumb layers splitting
1 parent bdeaaba commit cae39c9

3 files changed

Lines changed: 75 additions & 27 deletions

File tree

src/transformers/distributed/mixin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@
2828
from ..utils.hub import create_and_tag_model_card
2929
from .configuration_utils import DistributedConfig
3030
from .fsdp import is_fsdp_managed_module
31-
from .pipeline_parallel import initialize_pipeline_parallelism
3231
from .utils import (
3332
_get_torch_distributed_rank,
3433
_is_torch_distributed_initialized,
3534
distribute_model,
3635
gather_full_state_dict,
3736
initialize_fully_sharded_data_parallelism,
37+
initialize_pipeline_parallelism,
3838
save_model_checkpoint_distributed,
3939
)
4040

src/transformers/distributed/pipeline_parallel.py

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,45 +13,61 @@
1313
# limitations under the License.
1414
from __future__ import annotations
1515

16-
import os
1716
from typing import TYPE_CHECKING
1817

19-
from ..utils import is_torch_available, is_torch_greater_or_equal
20-
from .utils import _ensure_torch_distributed
21-
18+
from ..utils import is_torch_available
2219

2320
if TYPE_CHECKING:
24-
from .configuration_utils import DistributedConfig
21+
import torch.nn as nn
2522

2623
if is_torch_available():
2724
import torch
25+
import torch.nn as nn
26+
27+
28+
class PPMissingLayer(nn.Identity):
29+
"""A placeholder layer for missing layers in a pipeline parallel model."""
30+
31+
def __init__(self, *args, **kwargs):
32+
super().__init__()
33+
34+
def forward(self, *args, **kwargs):
35+
"""Return the first arg from args or the first value from kwargs."""
36+
return args[0] if args else next(iter(kwargs.values()))
37+
38+
39+
def apply_pipeline_parallelism(model: nn.Module, device_mesh: torch.distributed.device_mesh.DeviceMesh) -> nn.Module:
40+
"""Naive even split of ``base_model.layers`` across PP ranks."""
41+
pp_size = device_mesh.size()
42+
if pp_size <= 1:
43+
return model
44+
45+
pp_rank = device_mesh.get_local_rank()
46+
is_first_rank = pp_rank == 0
47+
is_last_rank = pp_rank == pp_size - 1
2848

49+
base_model = getattr(model, model.base_model_prefix)
50+
layers = base_model.layers
51+
num_layers = len(layers)
2952

30-
def initialize_pipeline_parallelism(
31-
distributed_config: DistributedConfig,
32-
):
33-
if not is_torch_greater_or_equal("2.5"):
34-
raise OSError("Pipeline parallelism with DistributedConfig requires `torch>=2.5`.")
53+
layers_per_rank = num_layers // pp_size
54+
start_layer = pp_rank * layers_per_rank
55+
end_layer = num_layers if is_last_rank else start_layer + layers_per_rank
3556

36-
device_type = torch._C._get_accelerator().type
37-
_ensure_torch_distributed(device_type)
57+
if not is_first_rank:
58+
base_model.embed_tokens = PPMissingLayer()
3859

39-
world_size = torch.distributed.get_world_size()
40-
pp_size = distributed_config.pp_size
41-
if world_size != pp_size:
42-
raise RuntimeError(f"world_size ({world_size}) must be equal to pp_size ({pp_size})")
60+
for i in range(num_layers):
61+
if i < start_layer or i >= end_layer:
62+
layers[i] = PPMissingLayer()
4363

44-
if device_type != "cpu":
45-
local_rank = int(os.environ.get("LOCAL_RANK", 0))
46-
getattr(torch, device_type).set_device(local_rank)
47-
device_map = torch.device(device_type, local_rank)
48-
else:
49-
device_map = torch.device(device_type)
50-
64+
if not is_last_rank:
65+
base_model.norm = PPMissingLayer()
66+
model.lm_head = PPMissingLayer()
5167

52-
assert world_size == pp_size, f"world_size ({world_size}) must be equal to pp_size ({pp_size})"
53-
mesh = torch.distributed.init_device_mesh(device_type, (pp_size,), mesh_dim_names=("pp",))
68+
model._pp_rank = pp_rank
69+
model._pp_size = pp_size
70+
return model
5471

55-
return device_map, mesh
5672

5773
#TODO(3outeille): probably have to introduce pipeline_communicate here ?

src/transformers/distributed/utils.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from ..integrations.tensor_parallel import apply_tensor_parallelism
2020
from ..utils import is_torch_available, is_torch_greater_or_equal
2121
from .fsdp import apply_fully_sharded_data_parallelism
22+
from .pipeline_parallel import apply_pipeline_parallelism
2223

2324

2425
if TYPE_CHECKING:
@@ -153,6 +154,34 @@ def initialize_fully_sharded_data_parallelism(distributed_config: DistributedCon
153154
return device_map, mesh
154155

155156

157+
def initialize_pipeline_parallelism(
158+
distributed_config: DistributedConfig,
159+
):
160+
if not is_torch_greater_or_equal("2.5"):
161+
raise OSError("Pipeline parallelism with DistributedConfig requires `torch>=2.5`.")
162+
163+
device_type = torch._C._get_accelerator().type
164+
_ensure_torch_distributed(device_type)
165+
166+
world_size = torch.distributed.get_world_size()
167+
pp_size = distributed_config.pp_size
168+
if world_size != pp_size:
169+
raise RuntimeError(f"world_size ({world_size}) must be equal to pp_size ({pp_size})")
170+
171+
if device_type != "cpu":
172+
local_rank = int(os.environ.get("LOCAL_RANK", 0))
173+
getattr(torch, device_type).set_device(local_rank)
174+
device_map = torch.device(device_type, local_rank)
175+
else:
176+
device_map = torch.device(device_type)
177+
178+
179+
assert world_size == pp_size, f"world_size ({world_size}) must be equal to pp_size ({pp_size})"
180+
mesh = torch.distributed.init_device_mesh(device_type, (pp_size,), mesh_dim_names=("pp",))
181+
182+
return device_map, mesh
183+
184+
156185
def distribute_model(
157186
model,
158187
distributed_config: DistributedConfig,
@@ -172,6 +201,9 @@ def distribute_model(
172201
elif distributed_config.fsdp_size > 1:
173202
fsdp_mesh = device_mesh["fsdp"] if device_mesh.ndim > 1 else device_mesh
174203
model = apply_fully_sharded_data_parallelism(model, fsdp_mesh)
204+
elif distributed_config.pp_size > 1:
205+
pp_mesh = device_mesh["pp"] if device_mesh.ndim > 1 else device_mesh
206+
model = apply_pipeline_parallelism(model, pp_mesh)
175207

176208
return model
177209

0 commit comments

Comments
 (0)