|
|
@@ -29,6 +29,81 @@ __all__ = [ |
|
|
|
] |
|
|
|
|
|
|
|
class DeepSpeedDriver(TorchDDPDriver): |
|
|
|
""" |
|
|
|
实现 ``deepspeed`` 分布式训练的 ``Driver``。 |
|
|
|
|
|
|
|
.. note:: |
|
|
|
|
|
|
|
您在绝大多数情况下不需要自己使用到该类,通过向 ``Trainer`` 传入正确的参数,您可以方便快速地部署您的分布式训练; |
|
|
|
|
|
|
|
``DeepSpeedDriver`` 目前支持的三种启动方式: |
|
|
|
|
|
|
|
1. 用户自己不进行任何操作,直接使用我们的 ``Trainer``,这时是由我们自己使用 ``open_subprocesses`` 拉起多个进程, |
|
|
|
然后 ``DeepSpeedDriver`` 自己通过调用 ``deepspeed.initialize`` 来初始化模型和同心组;(情况 A) |
|
|
|
|
|
|
|
.. code-block:: |
|
|
|
|
|
|
|
trainer = Trainer( |
|
|
|
... |
|
|
|
driver='deepspeed', |
|
|
|
device=[0, 1] |
|
|
|
) |
|
|
|
trainer.run() |
|
|
|
|
|
|
|
通过运行 ``python train.py`` 启动; |
|
|
|
|
|
|
|
2. 用户同样不在 ``Trainer`` 之外初始化 ``deepspeed``,但是用户自己使用 ``python -m torch.distributed.launch`` 拉起来创建多个进程,这时我们仍旧 |
|
|
|
会通过调用 ``model.initialize`` 来初始化 ``ddp`` 的通信组;(情况 B) |
|
|
|
|
|
|
|
.. code-block:: |
|
|
|
|
|
|
|
trainer = Trainer( |
|
|
|
... |
|
|
|
driver='deepspeed', |
|
|
|
device=None |
|
|
|
) |
|
|
|
trainer.run() |
|
|
|
|
|
|
|
通过运行 ``deepspeed train.py`` 启动; |
|
|
|
|
|
|
|
3. 用户自己在外面初始化 ``deepspeed``,并且通过 ``deepspeed train.py`` 拉起,这时无论是多个进程的拉起和通信组的建立 |
|
|
|
都由用户自己操作,我们只会在 ``driver.setup`` 的时候对 ``DeepSpeedDriver`` 设置一些必要的属性值;(情况 C) |
|
|
|
|
|
|
|
.. code-block:: |
|
|
|
|
|
|
|
import deepspeed |
|
|
|
|
|
|
|
# 初始化 |
|
|
|
model, _, _, _ = deepspeed.initialize(model, ...) |
|
|
|
|
|
|
|
trainer = Trainer( |
|
|
|
... |
|
|
|
driver='deepspeed', |
|
|
|
device=None |
|
|
|
) |
|
|
|
trainer.run() |
|
|
|
|
|
|
|
通过运行 ``deepspeed train.py`` 启动; |
|
|
|
|
|
|
|
:param model: 传入给 ``Trainer`` 的 ``model`` 参数; |
|
|
|
:param parallel_device: 用于分布式训练的 ``gpu`` 设备; |
|
|
|
:param is_pull_by_torch_run: 标志当前的脚本的启动是否由 ``python -m torch.distributed.launch`` 启动的; |
|
|
|
:param fp16: 是否开启 fp16 训练; |
|
|
|
:param deepspeed_kwargs: |
|
|
|
* *strategy* -- 使用 ZeRO 优化的策略,默认为 ``deepspeed``;目前仅支持以下值: |
|
|
|
|
|
|
|
* ``deepspeed`` -- 使用 ZeRO 的第二阶段,等同于 ``deepspeed_stage_2``; |
|
|
|
* ``deepspeed_stage_1`` -- 使用 ZeRO 的第一阶段,仅将 ``optimizer`` 的状态分散到不同设备上; |
|
|
|
* ``deepspeed_stage_2`` -- 使用 ZeRO 的第二阶段,将 ``optimizer`` 和**梯度**分散到不同设备上; |
|
|
|
* ``deepspeed_stage_2_offload`` -- 使用 ZeRO 的第二阶段,并且借助 cpu 的内存来进一步节约显存; |
|
|
|
* ``deepspeed_stage_3`` -- 使用 ZeRO 的第三阶段,将 ``optimizer`` 、**梯度**和**模型**分散到不同设备上; |
|
|
|
* ``deepspeed_stage_3_offload`` -- 使用 ZeRO 的第三阶段,并且借助 cpu 的内存来进一步节约显存; |
|
|
|
* ``deepspeed_stage_3_offload_nvme`` -- 使用 ZeRO 的第三阶段,并且借助 NVMe 硬盘来进一步节约显存; |
|
|
|
* *logging_level* -- ``deepspeed`` 库的日志等级,默认为 **logging.ERROR**; |
|
|
|
* *config* -- ``deepspeed`` 的各项设置;**FastNLP** 允许用户传入自己的设置以增强灵活性,但这会使参数 |
|
|
|
中的 ``optimizer`` 、``strategy`` 、 ``fp16`` 等失效,即当这个参数存在时,**FastNLP** 会用该参数覆盖 |
|
|
|
其它的设置; |
|
|
|
""" |
|
|
|
# TODO fp16 load_config |
|
|
|
def __init__( |
|
|
|
self, |
|
|
@@ -36,11 +111,13 @@ class DeepSpeedDriver(TorchDDPDriver): |
|
|
|
parallel_device: Union[List["torch.device"], "torch.device"], |
|
|
|
is_pull_by_torch_run = False, |
|
|
|
fp16: bool = False, |
|
|
|
deepspeed_kwargs: Dict = {}, |
|
|
|
**kwargs |
|
|
|
): |
|
|
|
assert _NEED_IMPORT_DEEPSPEED, "Deepspeed is not imported." |
|
|
|
# assert not dist.is_initialized(), "DeepSpeedDriver does not support initialize distributed by user." |
|
|
|
TorchDriver.__init__(self, model=model, fp16=False, **kwargs) |
|
|
|
kwargs.pop("torch_kwargs", None) |
|
|
|
self._ds_kwargs = deepspeed_kwargs |
|
|
|
TorchDriver.__init__(self, model=model, fp16=False, torch_kwargs=deepspeed_kwargs, **kwargs) |
|
|
|
self.fp16 = fp16 |
|
|
|
|
|
|
|
# 如果用户自己在外面初始化 DDP,那么其一定是通过 python -m torch.distributed.launch 拉起的; |
|
|
@@ -108,7 +185,6 @@ class DeepSpeedDriver(TorchDDPDriver): |
|
|
|
"to 1 for deepspeed configuration.") |
|
|
|
self.train_micro_batch_size = 1 |
|
|
|
|
|
|
|
self._ds_kwargs = kwargs.get("deepspeed_kwargs", {}) |
|
|
|
self.strategy = self._ds_kwargs.get("strategy", "deepspeed") |
|
|
|
deepspeed_logging_level = self._ds_kwargs.get("logging_level", logging.ERROR) |
|
|
|
deepspeed.utils.logging.logger.setLevel(deepspeed_logging_level) |
|
|
@@ -125,7 +201,7 @@ class DeepSpeedDriver(TorchDDPDriver): |
|
|
|
准备分布式环境,该函数主要做以下两件事情: |
|
|
|
|
|
|
|
1. 开启多进程,每个 gpu 设备对应单独的一个进程; |
|
|
|
2. 每个进程将模型迁移到自己对应的 ``gpu`` 设备上;然后使用 ``DistributedDataParallel`` 包裹模型; |
|
|
|
2. 使用 ``deepspeed.initialize`` 包裹模型; |
|
|
|
""" |
|
|
|
if len(self.optimizers) != 1: |
|
|
|
raise ValueError("Multi optimizers is not supported for `DeepSpeedDriver` right now.") |
|
|
@@ -160,15 +236,15 @@ class DeepSpeedDriver(TorchDDPDriver): |
|
|
|
self.open_subprocess() |
|
|
|
self.global_rank = self.local_rank # rank 一定是通过环境变量去获取的; |
|
|
|
deepspeed.init_distributed("nccl", distributed_port=self.master_port) |
|
|
|
# 用户在这个 trainer 前面又初始化了一个 trainer,并且使用的是 TorchDDPDriver; |
|
|
|
# 用户在这个 trainer 前面又初始化了一个 trainer,并且使用的是 DeepSpeedDriver; |
|
|
|
else: |
|
|
|
# 如果 `dist.is_initialized() == True`,那么说明 TorchDDPDriver 在之前已经初始化并且已经 setup 过一次,那么我们需要保证现在 |
|
|
|
# 使用的(即之后的)TorchDDPDriver 的设置和第一个 TorchDDPDriver 是完全一样的; |
|
|
|
# 如果 `dist.is_initialized() == True`,那么说明 DeepSpeedDriver 在之前已经初始化并且已经 setup 过一次,那么我们需要保证现在 |
|
|
|
# 使用的(即之后的)DeepSpeedDriver 的设置和第一个 DeepSpeedDriver 是完全一样的; |
|
|
|
pre_num_processes = int(os.environ[FASTNLP_DISTRIBUTED_CHECK]) |
|
|
|
if pre_num_processes != len(self.parallel_device): |
|
|
|
raise RuntimeError( |
|
|
|
"Notice you are using `TorchDDPDriver` after one instantiated `TorchDDPDriver`, it is not" |
|
|
|
"allowed that your second `TorchDDPDriver` has a new setting of parameters " |
|
|
|
"Notice you are using `DeepSpeedDriver` after one instantiated `DeepSpeedDriver`, it is not" |
|
|
|
"allowed that your second `DeepSpeedDriver` has a new setting of parameters " |
|
|
|
"`num_nodes` and `num_processes`.") |
|
|
|
self.world_size = dist.get_world_size() |
|
|
|
self.global_rank = dist.get_rank() |
|
|
@@ -302,7 +378,7 @@ class DeepSpeedDriver(TorchDDPDriver): |
|
|
|
保存当前 driver 的模型到 folder 下。 |
|
|
|
|
|
|
|
:param filepath: 保存到哪个文件夹; |
|
|
|
:param only_state_dict: 是否只保存权重; |
|
|
|
:param only_state_dict: 是否只保存权重;在 ``DeepSpeedDriver`` 中该参数无效; |
|
|
|
:return: |
|
|
|
""" |
|
|
|
# deepspeed engine 要求在每个 rank 都调用 save_checkpoint,故去掉了 rank_zero_call 装饰器 |
|
|
@@ -325,7 +401,7 @@ class DeepSpeedDriver(TorchDDPDriver): |
|
|
|
从 folder 中加载权重并赋值到当前 driver 的模型上。 |
|
|
|
|
|
|
|
:param filepath: 加载权重或模型的路径 |
|
|
|
:param load_state_dict: 保存的内容是否只是权重。 |
|
|
|
:param load_state_dict: 保存的内容是否只是权重;在 ``DeepSpeedDriver`` 中该参数无效; |
|
|
|
:param kwargs: |
|
|
|
:return: |
|
|
|
""" |
|
|
|