LLM 分布式训练框架 | DeepSpeed与Accelerate

文章介绍了deepspeed分布式训练框架,包括与Accelerate的关系、推荐使用deepspeed的理由、基本概念、通信策略、ZeRO技术(Stage0-3和Offload)、混合精度训练、gradientcheckpoint以及如何与transformers库集成。重点讲解了内存优化和不同环境下的通信选择。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

🚀 简单记录下根据网上资料(如Reference中所列)所学到的一些知识,这里主要介绍的是deepspeed分布式训练框架相关概念。

😄小日记:今天太舒服了,早上跑了6km,晚上吃了养生菌菇火锅~

在这里插入图片描述

1、Accelerate和deepspeed的联系

  • Accelerate是PyTorch官方提供的分布式训练工具,而deepspeed是由Microsoft提供的分布式训练工具。
  • 最主要的区别在于支持的模型规模不同,deepspeed支持更大规模的模型。 deepspeed还提供了更多的优化策略和工具,例如ZeRO和Offload等。
  • Accelerate更加稳定和易于使用,适合中小规模的训练任务。
  • ⭐注意:在这里插入代码片Accelerate只支持nvlink,而T4,3090这类显卡是PIX ,检测方式:nvidia-smi topo -m。【我用3090ti跑这个项目:LLaMA-Factory,就跑不了会报错,我怀疑就是这个问题。用deepspeed是ok的】

2、推荐用deepspeed的理由

😄 我更推荐用deepspeed,deepspeed方便了我们在机器有限的情况下来训练、微调大模型,同时它也有很多优秀的性能优化。
目前主流的训练LLM的方式: PyTorch + GPU + DeepSpeed + LLM训练框架

优势:

  • 存储效率:DeepSpeed提供了一种Zero的新型解决方案来减少训练显存的占用,它与传统的数据并行不同,它将模型状态和梯度进行分区来节省大量的显存;
  • 可扩展性:DeepSpeed支持高效的数据并行、模型并行、pipeline并行以及它们的组合,这里也称3D并行;
  • 易用性: 在训练阶段,只需要修改几行代码就可以使pytorch模型使用DeepSpeed。

支持:

DeepSpeed实现了ZeRO论文中描述的所有内容。目前,它提供对以下功能的全面支持:

  • 1、优化器状态分区(ZeRO stage 1)
  • 2、梯度分区(ZeRO stage 2)
  • 3、参数分区(ZeRO stage 3)
  • 4、自定义混合精度训练处理
  • 5、一系列基于CUDA扩展的快速优化器
  • 6、ZeRO-Offload 到 CPU 和 NVMe

⭐ 训练:DeepSpeed ZeRO-2主要用于训练,因为它的特性对推理没有用处。DeepSpeed ZeRO训练支持完整的ZeRO stages 1、2和3,以及ZeRO-Infinity(CPU和NVMe offload)。

⭐ DeepSpeed ZeRO-3可用于训练也可以用于推理,因为它允许将单个GPU无法加载的大模型加载到多个GPU上。
⭐ 推理:DeepSpeed ZeRO推理支持ZeRO stage 3和ZeRO-Infinity。它使用与训练相同的ZeRO协议,但不使用优化器和学习率调度器,只有stage 3与推理相关。更多详细信息请参阅:zero-inference。此外还有DeepSpeed推理 - 这是一种完全不同的技术,它使用张量并行而不是ZeRO(即将推出)。

😄为什么要在仅使用一张 GPU 的情况下使用 DeepSpeed 呢?

  • 1、它具有 ZeRO-offload 功能,可以将一些计算和内存委托给主机的 CPU 和 内存,从而为模型的需求保留更多 GPU 资源 - 例如更大的批处理大小,或启用正常情况下无法容纳的非常大模型。
  • 2、它提供了智能的 GPU 内存管理系统,最小化内存碎片,这再次允许您容纳更大的模型和数据批次。
    虽然接下来我们将详细讨论配置,但在单个 GPU 上通过 DeepSpeed 实现巨大性能提升的关键是在配置文件中至少有以下配置:
{
  "zero_optimization": {
     "stage": 2,
     "offload_optimizer": {
         "device": "cpu",
         "pin_memory": true
     },
     "allgather_partitions": true,
     "allgather_bucket_size": 2e8,
     "reduce_scatter": true,
     "reduce_bucket_size": 2e8,
     "overlap_comm": true,
     "contiguous_gradients": true
  }
}

3、基本概念

在分布式计算环境中,有几个非常基础的概念需要理解:

  • 节点编号(node_rank:):分配给系统中每个节点的唯一标识符,用于区分不同计算机之间的通信。
  • 全局进程编号(rank):分配给整个系统中的每个进程的唯一标识符,用于区分不同进程之间的通信。
  • 局部进程编号(local_rank):分配给单个节点内的每个进程的唯一标识符,用于区分同一节点内的不同进程之间的通信。
  • 全局总进程数(word_size):在整个系统中运行的所有进程的总数,用于确定可以并行完成多少工作以及需要完成任务所需的资源数量。
  • 主节点(master_ip+master_port):在分布式计算环境中,主节点负责协调所有其他节点和进程的工作,为了确定主节点,我们需要知道它的IP地址和端口号。主节点还负责监控系统状态、处理任务分配和结果汇总等任务,因此是整个系统的关键部分。

4、通信策略

deepspeed 还提供了 mpi、gloo 和 nccl 等通信策略,可以根据具体情况进行选择和配置。

  • mpi 是一种跨节点通信库,常用于 CPU 集群上的分布式训练;
  • gloo 是一种高性能的分布式训练框架,支持 CPU 和 GPU 上的分布式训练;
  • nccl 是 NVIDIA 提供的 GPU 专用通信库,被广泛应用于 GPU 上的分布式训练。

在使用 DeepSpeed 进行分布式训练时,可以根据具体情况选择合适的通信库。通常情况下,如果是在 CPU 集群上进行分布式训练,可以选择 mpi 和 gloo;如果是在 GPU 上进行分布式训练,可以选择 nccl。

5、Zero(ZeRO-Stage3、ZeRO-Offload)

在DeepSpeed下,ZeRO训练支持了完整的ZeRO Stages1, 2和3,以及支持将优化器状态、梯度和模型参数从GPU显存下沉到CPU内存或者硬盘上,实现不同程度的显存节省,以便训练更大的模型。

ZeRO(Zero Redundancy Optimizer)是一种用于大规模训练优化的技术,主要是用来减少内存占用。在大规模训练中,内存占用可以分为 Model StatesActivation两部分 (Activation估计是前向传播时保存下来的各神经元的激活值吧,因为反向传播时计算梯度需要用到) ,而 ZeRO 主要是为了解决 Model States 的内存占用问题。

ZeRO 将模型参数分成了三个部分:Optimizer StatesGradientModel Parameter

  • Optimizer States 是 Optimizer 在进行梯度更新时所需要用到的数据,例如 SGD 中的 Momentum。
  • Gradient 是在反向传播后所产生的梯度信息,其决定了参数的更新方向。
  • Model Parameter 则是模型参数,也就是我们在整个过程中通过数据“学习”的信息。

5.1、ZeRO中不同stage的区别

  • ZeRO-0:禁用所有类型的分片,仅使用 DeepSpeed 作为 DDP (Distributed Data Parallel)

  • ZeRO-1:把优化器状态(optimizer states)分片到每个数据并行的工作进程(每个GPU)下;

  • ZeRO-2:把 优化器状态(optimizer states) + 梯度(gradients) 分片到每个数据并行的工作进程(每个GPU)下;

  • ZeRO-3:把优化器状态(optimizer states) + 梯度(gradients) + 模型参数(parameters) 分片到每个数据并行的工作进程(每个GPU)下。内存减少与数据并行度呈线性关系。例如,在64个GPU(Nd=64)之间进行拆分将产生64倍的内存缩减。通信量有50%的适度增长。

  • ZeRO-Infinity是ZeRO-3的拓展。允许通过使用 NVMe 固态硬盘扩展 GPU 和 CPU 内存来训练大型模型。ZeRO-Infinity 需要启用 ZeRO-3。

⭐ ZeRO-Stage3在deepspeed中通过zero_optimization.stage=0/1/2/3 设置,ZeRO-Offload通过zero_optimization.offload_optimizer.device设置。
⭐ 备注:优化器状态 一般包含FP32 Gradient、FP32 Variance、FP32 Momentum、FP32 Parameters。梯度和模型参数 一般会用FP16就够了,所以占用大头一般是优化器相关的。
所以根据实际硬件资源,选择适合Stage策略即可。如果遇到要跑更大的模型,比如想在3090 24GB下跑13B模型,可能Stage3也OOM跑不起来,此时可以开启Optimizer Offload和Param Offload即可跑起来,但相应的性能会受影响。

5.2、ZeRO-Offload

ZeRO-Offload: offload指将数据、梯度、优化器状态等下沉到CPU内存或硬盘上。

  • Optimizer Offload: 在Stage2的基础上,把梯度和优化器状态下沉到CPU内存或硬盘。
  • Param Offload: 在Stage3的基础上,把模型参数下沉到CPU内存或硬盘上。

6、deepspeed中的混合精度

混合精度训练是指在训练过程中同时使用FP16(半精度浮点数)和FP32(单精度浮点数)两种精度的技术。使用FP16可以大大减少内存占用,从而可以训练更大规模的模型。但是,由于FP16的精度较低,训练过程中可能会出现梯度消失和模型不稳定的问题。因此,需要使用一些技术来解决这些问题,例如动态精度缩放(Dynamic Loss Scaling)和混合精度优化器(Mixed Precision Optimizer)等。

  • deepspeed提供了混合精度训练的支持,可以通过在配置文件中设置"fp16.enabled": true来启用混合精度训练。在训练过程中,deepspeed会自动将一部分操作转换为FP16格式,并根据需要动态调整精度缩放因子,从而保证训练的稳定性和精度。

  • ⭐ 在使用混合精度训练时,需要注意一些问题,例如梯度裁剪(Gradient Clipping)和学习率调整(Learning Rate Schedule)等。梯度裁剪可以防止梯度爆炸,学习率调整可以帮助模型更好地收敛。因此,在设置混合精度训练时,需要根据具体情况进行选择和配置。

BF16和FP16都是混合精度训练中使用的浮点数表示格式。

  • BF16是一种Brain Floating Point格式,由英特尔提出,可以提供更好的数值稳定性和更高的精度,但需要更多的存储空间。在混合精度训练中,BF16可以作为一种精度更高的替代品,用于一些关键的计算操作,例如梯度累加和权重更新等。使用BF16和FP16一样可以提高模型的训练速度和精度,并减少内存占用。

  • 在 DeepSpeed 中,可以通过在配置文件中设置 "bf16.enabled": true 来启用 BF16 混合精度训练。这将会将一部分操作转换为 BF16 格式,并根据需要动态调整精度缩放因子,从而提高模型的训练速度和精度,并减少内存占用。

7、gradient checkpoint

😄 大型模型在静态和动态方面都很耗资源。首先,它们很难适配 GPU,而且哪怕你把它们放到了设备上,也很难训练,因为批处理大小被迫限制的太小而无法收敛。所以用gradient checkpoint相当于时间换空间。

gradient checkpoint的意思是在反向传播时重新计算深度神经网络的中间值(而通常情况是在前向传播时存储的)。这个策略是用时间(重新计算这些值两次的时间成本)来换空间(提前存储这些值的内存成本)。

  • 具体工作原理是从计算图中省略一些激活值(由前向传播产生,其中这里的”一些“是指可以只省略模型中的部分激活值,折中时间和空间,即前向传播的时候存一个节点释放一个节点,空的那个等需要用的时候再backword的时候重新计算)。这减少了计算图使用的内存,降低了总体内存压力(并允许在处理过程中使用更大的批次大小)。
  • pytorch中对应的函数与实现原理:PyTorch 通过 torch.utils.checkpoint.checkpoint 和 torch.utils.checkpoint.checkpoint_sequential 提供梯度检查点,根据官方文档的 notes,它实现了以下功能,在前向传播时,PyTorch 将保存模型中的每个函数的输入元组。在反向传播过程中,对于每个函数,输入元组和函数的组合以实时的方式重新计算,插入到每个需要它的函数的梯度公式中,然后丢弃(显存中只保存输入数据和函数)。网络计算开销大致相当于每个样本通过模型前向传播开销的两倍。

❓注:神经网络使用的总内存基本上是两个部分的总和,包括静态内存动态内存

  • 静态内存:尽管 PyTorch 模型中内置了一些固定开销,但总的来说几乎完全由模型权重决定。而如今,在生产中使用的现代深度学习模型的总参数在100万到10亿之间。作为参考,一个带 16GB GPU 内存的 NVIDIA T4 的实际限制大约在1-1.5亿个参数之间。

  • 动态内存:在训练模式下,每次通过神经网络的前向传播都为网络中的每个神经元计算一个激活值,这个值随后被存储在所谓的计算图中。必须为批次中的每个单个训练样本存储一个值,因此数量会迅速的累积起来。总成本取决于模型大小和批处理大小,并设置适用于您的GPU内存的最大批处理大小的限制。一开始存储激活的原因是,在反向传播期间计算梯度时需要用到激活。

8、DeepSpeed的推理优化

除了训练优化,deepspeed还可以推理优化。
如下图,红色虚线框是以该单位为优化Kernel,对应的数字是优化的效率倍数。
在这里插入图片描述

9、deepspeed搭配transformers库使用

Transformers通过以下两种方式集成了DeepSpeed:

  • 1、通过Trainer集成核心的DeepSpeed功能。这是一种“为您完成一切”式的集成 - 您只需提供自定义配置文件或使用我们的模板配置文件。本文档的大部分内容都集中在这个功能上。【😄下面以trainer集成deepspeed为例】
  • 2、如果您不使用Trainer并希望在自己的Trainer中集成DeepSpeed,那么像from_pretrained和from_config这样的核心功能函数将包括ZeRO stage 3及以上的DeepSpeed的基础部分,如zero.Init。要利用此功能,请阅读有关非Trainer DeepSpeed集成的文档。

下面只是简单举例讲讲大致用法,更多使用讲解请参考参考文献[4]

pip install deepspeed

例如我们可以在transformers的trainer中加入args时,在args里将定义好deepsped的config文件路径传入:
ds_config.json的配置根据实际情况定义,如以下一个ZeRO-2的例子:

⭐注:为防止trainer的参数和ds_config.json里的相同参数配置不一样,建议让trainer来为您做大部分配置。像优化器啊学习率那些,我们就不在ds_config.json里配置了,在trainer的参数里配置好。

{
    "train_batch_size": "auto",
    "train_micro_batch_size_per_gpu": "auto",
    "gradient_accumulation_steps": "auto",
    "gradient_clipping": "auto",
    "zero_allow_untested_optimizer": true,
    "fp16": {
      "enabled": "auto",
      "loss_scale": 0,
      "initial_scale_power": 16,
      "loss_scale_window": 1000,
      "hysteresis": 2,
      "min_loss_scale": 1
    },  
    "zero_optimization": {
      "stage": 2,
      "allgather_partitions": true,
      "allgather_bucket_size": 5e8,
      "reduce_scatter": true,
      "reduce_bucket_size": 5e8,
      "overlap_comm": false,
      "contiguous_gradients": true
    }
  }

train_bash.py如下:

from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    DataCollatorForSeq2Seq,
    TrainingArguments,
    Trainer
)
from peft import (
    LoraConfig,
    TaskType,
    get_peft_model,
    get_peft_model_state_dict,
    set_peft_model_state_dict
)

# 设置训练参数
deepspeed_config = "./ds_config.json" # deepspeed配置文件
training_args = TrainingArguments(
	...
    deepspeed=deepspeed_config, # deepspeed配置文件的位置
)



model = AutoModelForCausalLM.from_pretrained(MODEL_PATH, torch_dtype=torch.float16, device_map=device_map)
# LoRA训练配置,转换模型
lora_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    inference_mode=False,
    r=LORA_R, # LoRA中低秩近似的秩
    lora_alpha=LORA_ALPHA, # 见上文中的低秩矩阵缩放超参数
    lora_dropout=LORA_DROPOUT, # LoRA层的dropout
)
# 
model = get_peft_model(model, lora_config)
model.config.use_cache = False
old_state_dict = model.state_dict
model.state_dict = (
    lambda self, *_, **__: get_peft_model_state_dict(self, old_state_dict())
).__get__(model, type(model))
# 打印模型中的可训练参数
model.print_trainable_parameters()

# 模型训练
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset["train"],
    eval_dataset=tokenized_dataset["validation"],
    data_collator=collate_fn,
    compute_metrics=compute_metrics,
)
trainer.train()

单机多卡跑:
【多机多卡跑应该还有加一些指令,具体参考参考文献[2]】

deepspeed --num_gpus 2 --master_port=9901 ./train_bash.py \
    --deepspeed ds_config.json \
    ... # the arguments of train_bash.py 

⭐注:如果您需要在特定的 GPU 上运行,而不是 GPU 0,则无法使用 CUDA_VISIBLE_DEVICES 来限制可用 GPU 的可见范围。相反,您必须使用以下语法。【在这个例子中,我们告诉 DeepSpeed 使用 GPU 1(第二个 GPU)。】

deepspeed --include localhost:1 examples/pytorch/translation/run_translation.py ...

Reference

[1] deepspeed github: https://github.com/microsoft/DeepSpeed
[2] deepspeed官网:https://www.deepspeed.ai/
[3] deepspeed官方文档:https://deepspeed.readthedocs.io/en/latest/index.html
[4] transformers与deepspeed的集成使用教程:https://huggingface.co/docs/transformers/main/zh/main_classes/deepspeed
[5] 大模型训练之框架篇
[6] 用ZeRO训练大模型原理解析及参数含义解释

### 回答1: 这个错误提示说,内置函数或方法对象没有'numpy'属性。也就是说,你在尝试使用numpy库时,使用的变量名可能内置函数重名了。请检查你的代码,将冲突的变量名更改为其他名称,或者使用全名(如numpy.xxx)调用numpy库中的函数。 ### 回答2: 在使用Python的NumPy库时,如果遇到“attributeerror: 'builtin_function_or_method' object has no attribute 'numpy'”的错误,这通常是因为Python解释器无法找到正确的NumPy库。Python解释器通过查找sys.path中包含的目录,来找到并导入所需的库。 可能的原因之一是NumPy库没有被正确安装或安装失败,在这种情况下,需要重新安装NumPy库并确保安装过程中不出现任何错误。 另一种可能的原因是Python解释器无法找到正确的NumPy库位置。在这种情况下,可以尝试通过以下几种方法解决问题: 1. 手动添加NumPy库的路径到sys.path中:可以通过以下代码,手动添加NumPy库的路径到sys.path中: ``` import sys sys.path.append('<Path to NumPy installation>') ``` 其中,'<Path to NumPy installation>'是NumPy库的安装路径。 2. 检查Python环境变量是否正确设置:可能是由于Python环境变量没有正确设置,导致Python解释器无法找到正确的NumPy库位置。可以在命令行中运行以下命令,检查Python环境变量是否设置正确: ``` python -c "import numpy; print(numpy.__file__)" ``` 如果输出结果中包含正确的NumPy库路径,则说明环境变量设置正确,否则需要重新设置Python环境变量。 3. 卸载并重新安装Python和NumPy库:如果以上方法仍然无法解决问题,可以尝试卸载Python和NumPy库,并重新安装它们。 综上所述,遇到“attributeerror: 'builtin_function_or_method' object has no attribute 'numpy'”的错误,可以通过重新安装或手动添加NumPy库路径到sys.path中,检查Python环境变量设置是否正确,或卸载并重新安装Python和NumPy库等方法来解决问题。 ### 回答3: 在Python中,当我们使用某个包或库时,我们需要先将该包或库导入到我们的代码中,然后才能使用其中的函数或方法。如果我们忘记导入该包或库,或者导入方式错误,就会出现类似以上提到的错误信息: "AttributeError: 'builtin_function_or_method' object has no attribute 'numpy'" 这个错误的意思是说:我们使用了名为“numpy”的方法或属性,但是解释器却找不到这个方法或属性。这可能有以下几种可能性: 1. 没有将numpy库导入到代码中,代码中的numpy方法是找不到的。 2. 导入numpy库的方式不正确,解释器无法正确识别numpy库中的方法。 3. numpy库未正确安装,缺少必要的文件或配置。 针对这些可能性,我们可以采取以下措施来解决该错误: 1. 导入numpy库:在Python中,使用import语句将某个库导入到代码中。例如,我们可以使用以下代码导入numpy库,并在代码中使用其方法: import numpy a = numpy.array([1,2,3]) 2. 检查导入发生错误的原因:如果出现以上错误,我们可以检查导入方式是否正确,例如拼写是否正确,有没有缺失关键字等。 3. 重新安装numpy库:如果以上两个方法无效,我们可以尝试重新安装numpy库,确保其正确完成安装并且文件和配置完整。可以使用以下命令重新安装: pip install --upgrade numpy 总之,对于以上错误,我们需要仔细检查代码中有没有正确导入numpy库,并确保numpy库已经正确地安装。也可以在网络上寻找其他有关于这个常见错误的解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

#苦行僧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值