Files
paddlepaddle--paddle/python/paddle/distributed/auto_parallel/fully_shard.py
T
2026-07-13 12:40:42 +08:00

200 lines
7.5 KiB
Python

# Copyright (c) 2025 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 copy
import os
from types import MethodType
import paddle
import paddle.distributed as dist
from paddle.autograd import PyLayer
from .auto_dp_utils import in_auto_dp_mode
from .fully_shard_fusion import FullyShardFusion
def shard_accumulators(parameters_and_grads, optimizer, target_block):
if getattr(optimizer, "_has_sharded_accumulators", False):
return
optimizer._has_sharded_accumulators = True
for param, _ in parameters_and_grads:
optimizer._create_accumulators(
target_block,
[param],
)
target_name = param.name
if param.name in optimizer._master_weights.keys():
master_weight = optimizer._master_weights[param.name]
target_name = master_weight.name
for key in optimizer._accumulators.keys():
accumulator = optimizer._accumulators[key][target_name]
if accumulator.is_dist():
continue
origin_accumulator_name = accumulator.name
if 'beta' not in key:
placements = copy.deepcopy(param.placements)
else:
placements = [
dist.Replicate()
for _ in range(len(param.process_mesh.shape))
]
optimizer._accumulators[key][target_name] = dist.shard_tensor(
accumulator,
mesh=param.process_mesh,
placements=placements,
)
optimizer._accumulators[key][
target_name
].name = origin_accumulator_name
def _finish_update_impl(self, block, parameters_and_grads):
if not isinstance(parameters_and_grads, list):
parameters_and_grads = parameters_and_grads['params']
for param, _ in parameters_and_grads:
param.main_grad = None
optimizer._finish_update = MethodType(_finish_update_impl, optimizer)
class FullyShardAuto:
def __init__(
self,
model,
mesh,
enable_tensor_fusion_and_overlap=True,
fsdp_unit_layers=None,
moe_layers_name=None,
):
if enable_tensor_fusion_and_overlap:
FullyShardFusion(model, mesh, fsdp_unit_layers, moe_layers_name)
else:
self.model = model
self.mesh = mesh
# use first dims as sharding axis
self._shard_fn = dist.ShardingStage3(0, self.mesh)
for param in self.model.parameters():
param._need_shard_auto = True
self._shard_fn._shard_parameter(param)
if not in_auto_dp_mode():
self._shard_fn._register_hook_for_param_grad(param)
if in_auto_dp_mode():
self._register_comm_hook(self.model)
os.environ["skip_sharding3_output_reshard"] = "1"
def _register_comm_hook(self, model):
def _pre_forward_hook(sublayers):
@paddle.autograd.no_grad()
def gather_comm(*_):
dp_axis = dist.auto_parallel.get_mesh().dim_names.index('dp')
for key, param in sublayers._parameters.items():
if param.placements[dp_axis] != dist.Replicate():
new_placements = copy.deepcopy(param.placements)
new_placements[dp_axis] = dist.Replicate()
replicate_param = dist.reshard(
param, param.process_mesh, new_placements
)
param.get_tensor()._share_data_with(
replicate_param.get_tensor()
)
return gather_comm
def _post_forward_hook(sublayers):
@paddle.autograd.no_grad()
def shard_comm(*_):
dp_axis = dist.auto_parallel.get_mesh().dim_names.index('dp')
for key, param in sublayers._parameters.items():
if (
param.trainable
and param.placements[dp_axis] == dist.Replicate()
):
new_placements = copy.deepcopy(param.placements)
new_placements[dp_axis] = dist.Shard(dp_axis)
shard_param = dist.reshard(
param, param.process_mesh, new_placements
)
param.get_tensor()._share_data_with(
shard_param.get_tensor()
)
return shard_comm
def _post_backward_hook(param):
def shard_comm(grad):
dp_axis = dist.auto_parallel.get_mesh().dim_names.index('dp')
if param.placements[dp_axis] == dist.Replicate():
new_placements = copy.deepcopy(param.placements)
new_placements[dp_axis] = dist.Shard(dp_axis)
shard_param = dist.reshard(
param, param.process_mesh, new_placements
)
param.get_tensor()._share_data_with(
shard_param.get_tensor()
)
return grad
param.register_hook(shard_comm)
# register forward hooks
for name, sublayers in model.named_sublayers(include_self=True):
sublayers.register_forward_pre_hook(_pre_forward_hook(sublayers))
sublayers.register_forward_post_hook(_post_forward_hook(sublayers))
# register backward hooks
for param in model.parameters():
if param.trainable:
_post_backward_hook(param)
# register layer hooks for param sync in tie weights
self._register_layer_hooks(model)
def _register_layer_hooks(self, layer, name="last_layer"):
def _forward_post_hook(layer, inputs, outputs):
return LayerHook.apply(
outputs,
layer=layer,
)
if layer.parameters(include_sublayers=False):
layer.register_forward_post_hook(_forward_post_hook)
for name, sub_layer in layer.named_children():
self._register_layer_hooks(sub_layer, name)
class LayerHook(PyLayer):
@staticmethod
def forward(ctx, inputs, layer):
ctx.layer = layer
return inputs
@staticmethod
def backward(ctx, *args):
layer = ctx.layer
dp_axis = dist.auto_parallel.get_mesh().dim_names.index('dp')
for param in layer.parameters(include_sublayers=False):
if (
param.trainable
and param.placements[dp_axis] != dist.Replicate()
):
new_placements = copy.deepcopy(param.placements)
new_placements[dp_axis] = dist.Replicate()
replicate_param = dist.reshard(
param, param.process_mesh, new_placements
)
param.get_tensor()._share_data_with(
replicate_param.get_tensor()
)
return args