1313# limitations under the License.
1414from __future__ import annotations
1515
16- import os
1716from 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
2320if TYPE_CHECKING :
24- from . configuration_utils import DistributedConfig
21+ import torch . nn as nn
2522
2623if 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 ?
0 commit comments