Skip to content
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
347d450
add tensor parallelism support for non-expert groups
siddharth9820 Jun 29, 2022
4330936
non-expert tensor parallelism - drop tokens before a2a
siddharth9820 Jul 1, 2022
2643c18
support tensor parallelism for non-experts
siddharth9820 Jul 6, 2022
f96e0a0
fix formatting
siddharth9820 Jul 6, 2022
2dfd09c
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 6, 2022
7af3e87
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 7, 2022
1c4e8a7
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 8, 2022
763fb19
migrate code for dropping tokens from megatron
siddharth9820 Jul 12, 2022
0a797fe
change gather function name
siddharth9820 Jul 12, 2022
32063d9
fall back to previous error message
siddharth9820 Jul 12, 2022
b3e2fd8
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 15, 2022
1d2d975
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 19, 2022
ef4feb1
formatting changes
siddharth9820 Jul 19, 2022
ed731d0
change function names
siddharth9820 Jul 19, 2022
7712d90
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 19, 2022
eb6dd0a
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 19, 2022
9b4cd1f
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 20, 2022
fda3714
Merge branch 'master' into moe-tensor-parallelism
tjruwase Jul 20, 2022
7962586
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 20, 2022
8006a1d
fix number of local experts
siddharth9820 Jul 21, 2022
4378112
Merge branch 'moe-tensor-parallelism' of github.com:microsoft/DeepSpe…
siddharth9820 Jul 21, 2022
c9fa997
fix documentation
siddharth9820 Jul 21, 2022
474c932
correct log statement
siddharth9820 Jul 21, 2022
4fe355d
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 22, 2022
555ad4f
roll back ep-size setting code
siddharth9820 Jul 22, 2022
1299a50
Merge branch 'moe-tensor-parallelism' of github.com:microsoft/DeepSpe…
siddharth9820 Jul 22, 2022
ad0a147
add detailed comments
siddharth9820 Jul 22, 2022
e29ddf6
restore function in groupy.py
siddharth9820 Jul 22, 2022
f8469b7
better comments
siddharth9820 Jul 22, 2022
f4f217c
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 25, 2022
baa9812
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 25, 2022
9bdeb41
remove code that changes ep_size and convert it to asserts
siddharth9820 Jul 25, 2022
d34f69c
correct groups
siddharth9820 Jul 25, 2022
01c61b8
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 25, 2022
dd999c0
correction
siddharth9820 Jul 26, 2022
382da2e
add copyright
siddharth9820 Jul 26, 2022
d00c742
correction
siddharth9820 Jul 26, 2022
6a56180
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 26, 2022
da5a688
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 26, 2022
0f3f398
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 26, 2022
725c66b
Merge branch 'master' into moe-tensor-parallelism
awan-10 Jul 26, 2022
ae0030d
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 26, 2022
3d6a136
formatting changes
siddharth9820 Jul 26, 2022
870dfd0
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 26, 2022
c5acd1c
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 27, 2022
a1c470e
Merge branch 'master' into moe-tensor-parallelism
tjruwase Jul 27, 2022
43216ca
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 27, 2022
b6dd6ea
Merge branch 'master' into moe-tensor-parallelism
tjruwase Jul 28, 2022
372c663
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 29, 2022
5379a21
add unit tests
siddharth9820 Jul 29, 2022
50ae30b
small change
siddharth9820 Jul 29, 2022
5f040c8
Merge branch 'master' into moe-tensor-parallelism
siddharth9820 Jul 29, 2022
8dfe33d
remove amp from tests
siddharth9820 Jul 29, 2022
f918175
Merge branch 'moe-tensor-parallelism' of github.com:microsoft/DeepSpe…
siddharth9820 Jul 29, 2022
0afa114
Merge branch 'master' into moe-tensor-parallelism
tjruwase Jul 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions deepspeed/moe/layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ def __init__(self,
noisy_gate_policy: typing.Optional[str] = None,
drop_tokens: bool = True,
use_rts=True,
use_tutel: bool = False):
use_tutel: bool = False,
enable_expert_tensor_parallelism: bool = False):
Comment thread
siddharth9820 marked this conversation as resolved.
"""Initialize an MoE layer.

Arguments:
Expand All @@ -43,20 +44,21 @@ def __init__(self,
drop_tokens (bool, optional): default=True, whether to drop tokens - (setting to False is equivalent to infinite capacity).
use_rts (bool, optional): default=True, whether to use Random Token Selection.
use_tutel (bool, optional): default=False, whether to use Tutel optimizations (if installed).
enable_expert_tensor_parallelism (bool, optional): default=False, whether to use tensor parallelism for experts
"""

super(MoE, self).__init__()

self.use_residual = use_residual
self.ep_size = min(
ep_size,
num_experts) # the ep size should be less than the number of experts
self.enable_expert_tensor_parallelism = enable_expert_tensor_parallelism
assert num_experts % ep_size == 0, f"Number of experts ({num_experts}) should be divisible by expert parallel size ({ep_size})"
self.ep_size = ep_size
self.expert_group_name = f"ep_size_{self.ep_size}"
self.num_experts = num_experts
self.num_local_experts = 1 if num_experts < ep_size else num_experts // ep_size
self.num_local_experts = num_experts // self.ep_size

log_dist(
f'Creating MoE layer with num_experts: {num_experts} | num_local_experts: {self.num_local_experts} | expert_parallel_size: {ep_size}',
f'Creating MoE layer with num_experts: {num_experts} | num_local_experts: {self.num_local_experts} | expert_parallel_size: {self.ep_size}',
[0])

assert noisy_gate_policy is None or noisy_gate_policy in ['None', 'Jitter', 'RSample'], \
Expand Down Expand Up @@ -91,9 +93,12 @@ def _create_process_groups(self):
print(
f"No existing process group found, creating a new group named: {self.expert_group_name}"
)
if groups.mpu is None:
if (groups.mpu is None) or (not self.enable_expert_tensor_parallelism):
# Condition 1 - no groups.mpu means no tensor parallelism
# Condition 2 - disabling expert tensor parallelism on purpose
groups._create_expert_and_data_parallel(self.ep_size)
else:
# expert tensor parallelism is enabled
groups._create_expert_data_and_model_parallel(self.ep_size,
mpu=groups.mpu)
# Set the group handle for the MOELayer (deepspeed_moe) object
Expand Down
16 changes: 16 additions & 0 deletions deepspeed/moe/sharded_moe.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from torch import Tensor
from torch.nn import Module
import torch.nn.functional as F
from deepspeed.utils import groups

if TYPE_CHECKING:
Base = Module[Tensor]
Expand Down Expand Up @@ -520,6 +521,15 @@ def forward(self, *input: Tensor, **kwargs: Any) -> Tensor:
if self.wall_clock_breakdown:
self.timers('falltoall').start()

if groups._get_expert_model_parallel_world_size() == 1:
# If the non-expert is tensor-parallel, it will create
# duplicate tokens on the tensor-parallel ranks.
# Since our experts are not tensor-parallel, these duplicates
# need to be dropped to ensure correctness.
# this also doubles up as a communication optimization as we are
# reducing the all-to-all communication volume.
dispatched_input = groups.drop_tokens(dispatched_input, dim=1)

dispatched_input = _AllToAll.apply(self.ep_group, dispatched_input)

if self.wall_clock_breakdown:
Expand Down Expand Up @@ -548,6 +558,12 @@ def forward(self, *input: Tensor, **kwargs: Any) -> Tensor:
-1,
d_model)

if groups._get_expert_model_parallel_world_size() == 1:
# the dropped duplicate tokens need to be gathered on each
# tensor parallel rank again for the tensor-parallel
# non-expert of the next layer.
expert_output = groups.gather_tokens(expert_output, dim=1)

if self.use_tutel:
combined_output = self._tutel_dispatcher.decode(expert_output.view(E * C, M))
else:
Expand Down
38 changes: 32 additions & 6 deletions deepspeed/utils/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

from deepspeed.utils import log_dist
from deepspeed.utils.exceptions import DeprecatedException
from deepspeed.utils.mappings import _GatherTokens, _DropTokens

# Expert parallel group that the current rank belongs to.
_EXPERT_PARALLEL_GROUP = {}
Expand All @@ -37,6 +38,8 @@
_WORLD_GROUP = None
# global object to maintain mpu object if passed by a Megatron client
mpu = None
# global object that stores tensor parallel world size for experts
expert_tensor_parallel_world_size = 1


# Deprecated groups initialize function.
Expand Down Expand Up @@ -104,7 +107,7 @@ def _create_model_parallel(model_parallel_size_):
return _DATA_PARALLEL_GROUP, _MODEL_PARALLEL_GROUP


def _create_expert_and_data_parallel(ep_size):
def _create_expert_and_data_parallel(expert_parallel_size_):
"""
Create expert and data parallel groups.

Expand All @@ -119,11 +122,12 @@ def _create_expert_and_data_parallel(ep_size):
"""
assert dist.is_initialized()

log_dist(f'Creating expert and data parallel groups with size {ep_size}', ranks=[0])
log_dist(
f'Creating expert and data parallel groups with size {expert_parallel_size_}',
ranks=[0])
world_size = dist.get_world_size()
rank = dist.get_rank()

expert_parallel_size_ = min(ep_size, world_size)
_ensure_divisibility(world_size, expert_parallel_size_)

group_name = f"ep_size_{expert_parallel_size_}"
Expand Down Expand Up @@ -221,11 +225,17 @@ def _create_expert_data_and_model_parallel(expert_parallel_size_, mpu):
assert dist.is_initialized(), "dist is not initialized"
model_parallel_size_ = mpu.get_model_parallel_world_size()

global expert_tensor_parallel_world_size
expert_tensor_parallel_world_size = model_parallel_size_

world_size = dist.get_world_size()
rank = dist.get_rank()
dp_world_size = mpu.get_data_parallel_world_size()
dp_rank = mpu.get_data_parallel_rank()

_ensure_divisibility(world_size, model_parallel_size_)
_ensure_divisibility(dp_world_size, expert_parallel_size_)

log_dist(
f"Creating deepspeed groups with model parallel size {model_parallel_size_}, expert parallel size {expert_parallel_size_}, world size {world_size}, dp world size {dp_world_size}",
[0])
Expand All @@ -236,9 +246,6 @@ def _create_expert_data_and_model_parallel(expert_parallel_size_, mpu):
_DATA_PARALLEL_GROUP = mpu.get_data_parallel_group()
_MODEL_PARALLEL_GROUP = mpu.get_model_parallel_group()

expert_parallel_size_ = min(expert_parallel_size_, dp_world_size)
_ensure_divisibility(world_size, expert_parallel_size_)

group_name = f"ep_size_{expert_parallel_size_}"
Comment thread
siddharth9820 marked this conversation as resolved.

# Only create groups if they don't already exist
Expand Down Expand Up @@ -386,3 +393,22 @@ def _get_data_parallel_rank():
if mpu is not None:
return mpu.get_data_parallel_rank()
return dist.get_rank(group=_get_data_parallel_group())


def _get_expert_model_parallel_world_size():
global expert_tensor_parallel_world_size
return expert_tensor_parallel_world_size


def gather_tokens(input_, dim=0):
global mpu
if mpu is None: # no tensor parallelism for non-experts
return input_
return _GatherTokens.apply(input_, dim)


def drop_tokens(input_, dim=0):
global mpu
if mpu is None: # no tensor parallelism for non-experts
return input_
return _DropTokens.apply(input_, dim)
95 changes: 95 additions & 0 deletions deepspeed/utils/mappings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
'''
Comment thread
siddharth9820 marked this conversation as resolved.
Copyright 2022 The Microsoft DeepSpeed Team
'''

# The file has been adapted from the following Megatron-LM file:
# https://github.com/NVIDIA/Megatron-LM/blob/main/megatron/mpu/mappings.py
# Git commit hash: 9dc3c42a84aa656f583703cf8b6b4f79f712b796
# We retain the following copyright from the original files:

# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import torch
import deepspeed


def _gather_tokens(input_, dim=0):
"""Gather tensors and concatenate them along a dimension"""
mpu = deepspeed.utils.groups.mpu
if mpu.get_tensor_model_parallel_world_size() == 1:
return input_

input_ = input_.contiguous()
# Size and dimension.
rank = mpu.get_tensor_model_parallel_rank()

tensor_list = [
torch.empty_like(input_)
for _ in range(mpu.get_tensor_model_parallel_world_size())
]
tensor_list[rank] = input_
deepspeed.comm.all_gather(tensor_list,
input_,
group=mpu.get_tensor_model_parallel_group())

# Note: torch.cat already creates a contiguous tensor.
output = torch.cat(tensor_list, dim=dim).contiguous()

return output


def _drop_tokens(input_, dim=0):
"""Divide a tensor among the tensor parallel ranks"""
mpu = deepspeed.utils.groups.mpu
if mpu.get_tensor_model_parallel_world_size() == 1:
return input_
total_chunks = mpu.get_tensor_model_parallel_world_size()
this_chunk = mpu.get_tensor_model_parallel_rank()
assert input_.shape[dim] % total_chunks == 0, f"input dimension {dim} ({input_.shape[dim]}) is not divisible by tensor parallel world size ({total_chunks})"
chunk_size = input_.shape[dim] // total_chunks

return torch.narrow(input_, dim, this_chunk * chunk_size, chunk_size)


class _GatherTokens(torch.autograd.Function):
"""All gather tokens among the tensor parallel ranks"""
@staticmethod
def symbolic(graph, input_, dim):
return _gather_tokens(input_, dim)

@staticmethod
def forward(ctx, input_, dim):
ctx.dim = dim
return _gather_tokens(input_, dim)

@staticmethod
def backward(ctx, grad_output):
return _drop_tokens(grad_output, ctx.dim), None


class _DropTokens(torch.autograd.Function):
"Divide tokens equally among the tensor parallel ranks"

@staticmethod
def symbolic(graph, input_, dim):
return _drop_tokens(input_, dim)

@staticmethod
def forward(ctx, input_, dim):
ctx.dim = dim
return _drop_tokens(input_, dim)

@staticmethod
def backward(ctx, input_):
return _gather_tokens(input_, ctx.dim), None