CoTyle / lakonlab /parallel /fsdp2_wrapper.py
liuhuijie03
update
f0395ef
Raw
History Blame Contribute Delete
6.29 kB
# Copyright (c) 2025 Hansheng Chen
import importlib
import torch
import torch.nn as nn
import torch.distributed as dist
try:
from torch.distributed.fsdp import fully_shard, MixedPrecisionPolicy
except:
pass
from mmcv.parallel.scatter_gather import scatter_kwargs
from mmcv.parallel import MODULE_WRAPPERS
def get_module_object(path):
module_path, attribute = path.rsplit('.', 1)
module = importlib.import_module(module_path)
return getattr(module, attribute)
@MODULE_WRAPPERS.register_module()
class FSDP2Wrapper(nn.Module):
def __init__(
self,
module,
wrap_frozen_modules=False,
ignore_frozen_parameters=False,
param_dtype='bfloat16',
reduce_dtype='float32',
fsdp_modules=None,
exclude_keys=(),
hybrid_sharding=True,
**kwargs):
super().__init__()
self.module = module
fsdp_kwargs = kwargs
self.param_dtype = getattr(torch, param_dtype)
self.reduce_dtype = getattr(torch, reduce_dtype)
if hybrid_sharding:
global_world_size = dist.get_world_size()
num_devices_per_node = torch.cuda.device_count()
mesh = dist.init_device_mesh(
'cuda',
(global_world_size // num_devices_per_node, num_devices_per_node),
mesh_dim_names=('replicate', 'shard'))
fsdp_kwargs.update(mesh=mesh)
if fsdp_modules is not None:
assert isinstance(fsdp_modules, (list, tuple))
fsdp_modules = tuple([get_module_object(m) for m in fsdp_modules])
self.to_fsdp(
wrap_frozen_modules,
ignore_frozen_parameters,
exclude_keys,
fsdp_modules=fsdp_modules,
**fsdp_kwargs)
def to_fsdp(self,
wrap_frozen_modules=False,
ignore_frozen_parameters=False,
exclude_keys=(),
fsdp_modules=(),
**kwargs):
for name, module in self.module._modules.items():
if name in exclude_keys or next(module.parameters(), None) is None:
module = module.cuda()
elif all(not p.requires_grad for p in module.parameters()):
if wrap_frozen_modules:
for submodule in module.modules():
if isinstance(submodule, fsdp_modules):
fsdp_kwargs = kwargs.copy()
fsdp_kwargs.update(
mp_policy=MixedPrecisionPolicy(
param_dtype=self.param_dtype,
reduce_dtype=self.reduce_dtype))
fully_shard(submodule, **fsdp_kwargs)
fsdp_kwargs = kwargs.copy()
fsdp_kwargs.update(
mp_policy=MixedPrecisionPolicy(
param_dtype=self.param_dtype,
reduce_dtype=self.reduce_dtype,
cast_forward_inputs=False))
fully_shard(module, **fsdp_kwargs)
else:
module = module.cuda()
else:
if ignore_frozen_parameters:
ignored_params = []
for p in module.parameters():
if not p.requires_grad:
p.data = p.data.cuda()
ignored_params.append(p)
else:
ignored_params = None
for submodule in module.modules():
if isinstance(submodule, fsdp_modules):
fsdp_kwargs = kwargs.copy()
fsdp_kwargs.update(
mp_policy=MixedPrecisionPolicy(
param_dtype=self.param_dtype,
reduce_dtype=self.reduce_dtype))
if ignored_params is not None: # requires torch >= 2.7
fsdp_kwargs.update(ignored_params=ignored_params)
fully_shard(submodule, **fsdp_kwargs)
fsdp_kwargs = kwargs.copy()
fsdp_kwargs.update(
mp_policy=MixedPrecisionPolicy(
param_dtype=self.param_dtype,
reduce_dtype=self.reduce_dtype,
cast_forward_inputs=False))
if ignored_params is not None: # requires torch >= 2.7
fsdp_kwargs.update(ignored_params=ignored_params)
fully_shard(module, **fsdp_kwargs)
self.module._modules[name] = module
def scatter(self, inputs, kwargs, device_ids):
"""Scatter function.
Args:
inputs (Tensor): Input Tensor.
kwargs (dict): Args for
``mmcv.parallel.scatter_gather.scatter_kwargs``.
device_ids (int): Device id.
"""
return scatter_kwargs(inputs, kwargs, device_ids)
def forward(self, *inputs, **kwargs):
"""Forward function.
Args:
inputs (tuple): Input data.
kwargs (dict): Args for
``mmcv.parallel.scatter_gather.scatter_kwargs``.
"""
inputs, kwargs = self.scatter(inputs, kwargs, [torch.cuda.current_device()])
return self.module(*inputs[0], **kwargs[0])
def train_step(self, *inputs, **kwargs):
"""Train step function.
Args:
inputs (Tensor): Input Tensor.
kwargs (dict): Args for
``mmcv.parallel.scatter_gather.scatter_kwargs``.
"""
inputs, kwargs = self.scatter(inputs, kwargs, [torch.cuda.current_device()])
output = self.module.train_step(*inputs[0], **kwargs[0])
return output
def val_step(self, *inputs, **kwargs):
"""Validation step function.
Args:
inputs (tuple): Input data.
kwargs (dict): Args for ``scatter_kwargs``.
"""
inputs, kwargs = self.scatter(inputs, kwargs, [torch.cuda.current_device()])
output = self.module.val_step(*inputs[0], **kwargs[0])
return output