191 lines
6.6 KiB
Python
191 lines
6.6 KiB
Python
# Copyright (c) 2023 PaddlePaddle Authors. 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 paddle
|
|
import paddle.distributed as dist
|
|
from paddle.distributed import fleet
|
|
from paddle.distributed.fleet.utils.sequence_parallel_utils import (
|
|
_check_environment_for_overlap,
|
|
)
|
|
from paddle.framework import core
|
|
|
|
from paddlenlp.transformers.llama.modeling_auto import get_mesh
|
|
|
|
|
|
def is_fused_matmul_bias_supported():
|
|
if paddle.is_compiled_with_cuda() and not paddle.is_compiled_with_rocm() or paddle.is_compiled_with_xpu():
|
|
return hasattr(core.eager.ops.legacy, "fused_gemm_epilogue")
|
|
else:
|
|
return False
|
|
|
|
|
|
ipp = None
|
|
id2ipp = {}
|
|
|
|
paddle_nn_functional_linear = paddle.nn.functional.linear
|
|
if is_fused_matmul_bias_supported():
|
|
paddle_incubate_nn_functional_fused_linear = paddle.incubate.nn.functional.fused_linear
|
|
|
|
|
|
# modify from Paddle/python/paddle/distributed/auto_parallel/moe_utils.py
|
|
def _dist_reshape(
|
|
dist_tensor,
|
|
global_shape,
|
|
mesh,
|
|
placements,
|
|
):
|
|
local_tensor = dist_tensor._local_value()
|
|
tgt_global_shape = [dist_tensor.shape[0] * dist_tensor.shape[1], dist_tensor.shape[2]]
|
|
tgt_local_shape = [local_tensor.shape[0] * local_tensor.shape[1], local_tensor.shape[2]]
|
|
|
|
place = paddle.framework._current_expected_place()
|
|
place = paddle.framework._get_paddle_place(place)
|
|
|
|
local_tensor = local_tensor.reshape(tgt_local_shape)
|
|
|
|
if placements[1].is_shard():
|
|
new_placements = [dist.Shard(0), dist.Shard(1)]
|
|
else:
|
|
new_placements = [dist.Shard(0), dist.Replicate()]
|
|
|
|
out = paddle.Tensor(
|
|
local_tensor,
|
|
dims=tgt_global_shape,
|
|
process_mesh=mesh,
|
|
placements=new_placements,
|
|
place=place,
|
|
)
|
|
out.stop_gradient = dist_tensor.stop_gradient
|
|
return out
|
|
|
|
|
|
if is_fused_matmul_bias_supported():
|
|
origin_linear = paddle.incubate.nn.functional.fused_linear
|
|
else:
|
|
origin_linear = paddle.nn.functional.linear
|
|
|
|
|
|
class FusedLinearWithReduceScatter(paddle.autograd.PyLayer):
|
|
@staticmethod
|
|
def forward(ctx, x, weight, bias=None, name=None):
|
|
global ipp
|
|
input_parallel = dist.reshard(
|
|
x,
|
|
get_mesh(ipp),
|
|
[dist.Shard(1), dist.Replicate()],
|
|
)
|
|
y = origin_linear(input_parallel, weight, bias)
|
|
ctx.save_for_backward(weight, bias, input_parallel)
|
|
|
|
return y
|
|
|
|
@staticmethod
|
|
def backward(ctx, dy):
|
|
weight, bias, input_parallel = ctx.saved_tensor()
|
|
|
|
# compute dx
|
|
if dy.dtype == weight.dtype:
|
|
dinput_parallel = paddle.matmul(dy, weight, transpose_y=True)
|
|
else:
|
|
dinput_parallel = paddle.matmul(dy, paddle.cast(weight, dtype=dy.dtype), transpose_y=True)
|
|
|
|
hcg = fleet.get_hybrid_communicate_group()
|
|
model_parallel_group = hcg.get_model_parallel_group()
|
|
parallelism = model_parallel_group.nranks
|
|
|
|
assert (
|
|
dinput_parallel.shape[0] % parallelism == 0
|
|
), f"Input sequence length {dinput_parallel.shape[0]} can't be divided exactly by sequence parallelism {parallelism}"
|
|
|
|
# reduce-scatter dx
|
|
dx_global_shape = dinput_parallel.shape
|
|
dx_global_shape[0] = dx_global_shape[0] // parallelism
|
|
dinput_parallel_local = dinput_parallel._local_value()
|
|
dx_local_shape = dinput_parallel_local.shape
|
|
dx_local_shape[0] = dx_local_shape[0] // parallelism
|
|
dx_local = paddle.empty(shape=dx_local_shape, dtype=dinput_parallel.dtype)
|
|
task = dist.stream.reduce_scatter(
|
|
dx_local,
|
|
dinput_parallel_local,
|
|
op=dist.ReduceOp.SUM,
|
|
group=model_parallel_group,
|
|
sync_op=False,
|
|
)
|
|
|
|
# compute dw and dbias
|
|
_check_environment_for_overlap()
|
|
dy = _dist_reshape(dy, [-1, dy.shape[-1]], dy.process_mesh, dy.placements)
|
|
input_parallel = _dist_reshape(
|
|
input_parallel, [-1, input_parallel.shape[-1]], input_parallel.process_mesh, input_parallel.placements
|
|
)
|
|
dw = paddle.matmul(
|
|
input_parallel,
|
|
dy,
|
|
transpose_x=True,
|
|
)
|
|
if bias is None:
|
|
task.wait()
|
|
place = paddle.framework._current_expected_place()
|
|
place = paddle.framework._get_paddle_place(place)
|
|
|
|
dx = paddle.Tensor(
|
|
dx_local,
|
|
dims=dx_global_shape,
|
|
process_mesh=dinput_parallel.process_mesh,
|
|
placements=[dist.Shard(1), dist.Shard(0)],
|
|
place=place,
|
|
)
|
|
dx.stop_gradient = dx.stop_gradient
|
|
return dx, dw
|
|
else:
|
|
dbias = paddle.sum(dy, axis=0)
|
|
task.wait()
|
|
place = paddle.framework._current_expected_place()
|
|
place = paddle.framework._get_paddle_place(place)
|
|
|
|
dx = paddle.Tensor(
|
|
dx_local,
|
|
dims=dx_global_shape,
|
|
process_mesh=dinput_parallel.process_mesh,
|
|
placements=[dist.Shard(1), dist.Shard(0)],
|
|
place=place,
|
|
)
|
|
dx.stop_gradient = dx.stop_gradient
|
|
return dx, dw, dbias
|
|
|
|
|
|
def forward_pre_hook(layer, input):
|
|
paddle.nn.functional.linear = FusedLinearWithReduceScatter.apply
|
|
if is_fused_matmul_bias_supported():
|
|
paddle.incubate.nn.functional.fused_linear = FusedLinearWithReduceScatter.apply
|
|
global ipp, id2ipp
|
|
ipp = id2ipp[id(layer)]
|
|
|
|
|
|
def forward_post_hook(layer, input, output):
|
|
paddle.nn.functional.linear = paddle_nn_functional_linear
|
|
if is_fused_matmul_bias_supported():
|
|
paddle.incubate.nn.functional.fused_linear = paddle_incubate_nn_functional_fused_linear
|
|
|
|
|
|
def mock_layers_sp_async_reduce_scatter(model):
|
|
global ipp, id2ipp
|
|
for name, layer in model.named_sublayers():
|
|
if name.endswith("self_attn") or name.startswith("mlp"):
|
|
ipp = layer.ipp
|
|
for n in ["qkv_proj", "q_proj", "k_proj", "v_proj", "gate_up_fused_proj", "gate_proj", "up_proj"]:
|
|
if name.endswith(n):
|
|
id2ipp[id(layer)] = ipp
|
|
layer.register_forward_pre_hook(forward_pre_hook)
|
|
layer.register_forward_post_hook(forward_post_hook)
|