导语
本部分内容来自于Huggingface的Accelerate库的官方文档,并添加了一些个人的笔记。
简介
🤗Accelerate是一个库,只需添加四行代码,就可以在任何分布式配置中运行相同的PyTorch代码!简而言之,大规模的训练和推理变得简单、高效和适应性强。
+ from accelerate import Accelerator
+ accelerator = Accelerator()
+ model, optimizer, training_dataloader, scheduler = accelerator.prepare(
+ model, optimizer, training_dataloader, scheduler
+ )
for batch in training_dataloader:
optimizer.zero_grad()
inputs, targets = batch
inputs = inputs.to(device)
targets = targets.to(device)
outputs = model(inputs)
loss = loss_function(outputs, targets)
+ accelerator.backward(loss)
optimizer.step()
scheduler.step()
建立在torch_xla和torch.distributed上。🤗Accelerate负责这些繁重的工作,所以用户不需要编写任何自定义代码来适应这些平台。转换现有代码库以利用DeepSpeed,执行完全分片的数据并行,并自动支持混合精度训练!
这段代码可以通过Accelerate的CLI界面在任何系统上启动:
accelerate launch {my_script.py}
安装与配置
在开始之前,需要设置环境、安装适当的包并配置🤗Accelerate。🤗Accelerate在Python 3.7+上进行了测试。
安装Accelerate
提供三种安装的方式:
Pip
pip install accelerate
Conda
conda install -c conda-forge accelerate
Source
每天都有尚未发布的新功能被添加进来。要自己尝试,请从GitHub存储库安装:
pip install git+https://github.com/huggingface/accelerate
如果您正在为库做贡献,或者希望在运行代码时使用源代码并查看实时结果,可以从存储库的本地克隆版本安装可编辑版本:
git clone https://github.com/huggingface/accelerate
cd accelerate
pip install -e .
配置Accelerate
安装后,我们需要配置🤗Accelerate,以便为训练设置当前系统。要做到这一点,请运行以下程序并回答提示给您的问题:
accelerate config
我在自己使用的服务器中的配置信息如下:
(llama) jiexing@houyi:~$ accelerate config
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------In which compute environment are you running?
This machine
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Which type of machine are you using?
multi-GPU
How many different machines will you use (use more than 1 for multi-node training)? [1]: 1
Do you wish to optimize your script with torch dynamo?[yes/NO]:no
Do you want to use DeepSpeed? [yes/NO]: yes
Do you want to specify a json file to a DeepSpeed config? [yes/NO]: no
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------What should be your DeepSpeed's ZeRO optimization stage?
3
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Where to offload optimizer states?
cpu
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Where to offload parameters?
cpu
How many gradient accumulation steps you're passing in your script? [1]: 1
Do you want to use gradient clipping? [yes/NO]: no
Do you want to save 16-bit model weights when using ZeRO Stage-3? [yes/NO]: no
Do you want to enable `deepspeed.zero.Init` when using ZeRO Stage-3 for constructing massive models? [yes/NO]: no
How many GPU(s) should be used for distributed training? [1]:8
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Do you wish to use FP16 or BF16 (mixed precision)?
bf16
accelerate configuration saved at /home/jiexing/.cache/huggingface/accelerate/default_config.yaml
如果要编写一个不包括DeepSpeed配置或在tpu上运行等选项的基本配置,可以快速运行:
python -c "from accelerate.utils import write_basic_config; write_basic_config(mixed_precision='fp16')"
🤗Accelerate将自动利用可用gpu的最大数量,并设置混合精度模式。
要检查配置是否正常,运行:
accelerate env
我的机器上输出示例如下所示,它描述了一台机器上的八个gpu,使用混合精度:
(llama) jiexing@houyi:~$ accelerate env
- `Accelerate` version: 0.17.1
- Platform: Linux-5.4.0-144-generic-x86_64-with-glibc2.31
- Python version: 3.9.12
- Numpy version: 1.24.2
- PyTorch version (GPU?): 2.0.0+cu117 (True)
- `Accelerate` default config:
- compute_environment: LOCAL_MACHINE
- distributed_type: DEEPSPEED
- mixed_precision: bf16
- use_cpu: False
- num_processes: 8
- machine_rank: 0
- num_machines: 1
- rdzv_backend: static
- same_network: True
- main_training_function: main
- deepspeed_config: {'gradient_accumulation_steps': 1, 'offload_optimizer_device': 'cpu', 'offload_param_device': 'cpu', 'zero3_init_flag': False, 'zero3_save_16bit_model': False, 'zero_stage': 3}
- fsdp_config: {}
- megatron_lm_config: {}
- downcast_bf16: no
- tpu_use_cluster: False
- tpu_use_sudo: False
- tpu_env: []
- dynamo_config: {}
在配置时,需要设置FP16或者BF16,它们的相关简介如下:
- BF16是对FP32单精度浮点数截断数据,即用8bit 表示指数,7bit 表示小数。
- FP16半精度浮点数,用5bit 表示指数,10bit 表示小数;
- 与32位相比,采用BF16/FP16吞吐量可以翻倍,内存需求可以减半。但是这两者精度上差异不一样,BF16 可表示的整数范围更广泛,但是尾数精度较小;FP16 表示整数范围较小,但是尾数精度较高。
主要用法
要在你自己的脚本中使用🤗Accelerate,你必须改变四件事:
- 导入Accelerator主类并实例化一个Accelerator对象:
from accelerate import Accelerator
accelerator = Accelerator()
这应该尽可能早地写在训练脚本中,因为它将初始化分布式训练所需的一切。你不需要指明你所处的环境类型(一台有GPU的机器,一台有多个GPU的机器,几台有多个GPU或一个TPU的机器),库会自动检测到这一点。
- 删除对模型和输入数据的.to(device)或.cuda()调用。accelerator对象将为您处理这个问题,并为您将所有这些对象放在正确的设备上。如果你知道你在做什么,你可以留下那些.to(device)调用,但你应该使用加速器对象提供的设备:accelerator.device。
要完全禁用自动设备放置,在初始化加速器时传递device_placement=False。
- 将所有与训练相关的对象(optimizer, model, training dataloader, learning rate scheduler)传递给prepare()方法。这将确保为训练做好一切准备。
model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare(
model, optimizer, train_dataloader, lr_scheduler
)
特别是,训练数据加载器将在所有可用的gpu/TPU核上进行分片,以便每个gpu/TPU核看到训练数据集的不同部分。此外,所有进程的随机状态将在每次迭代开始时通过数据加载器进行同步,以确保数据以相同的方式进行洗牌(如果您决定使用shuffle=True或任何类型的随机采样器)。
训练的实际批处理大小将是使用的设备数量乘以你在脚本中设置的批处理大小:例如在4个gpu上训练,批处理大小为16集,创建训练数据加载器时将以实际批处理大小为64进行训练。
或者,您可以在创建初始化加速器时使用split_batch=True选项,在这种情况下,批处理大小将始终保持不变,无论您是在1、2、4还是64个gpu上运行脚本。
在开始实际的训练循环之前,应该在创建所有用于训练的对象后立即执行此指令。
只有在每个优化器步骤中需要对调度程序进行步进时,才应该将学习率调度程序传递给prepare()。
当你使用这种方法时,你的训练数据加载器可能会改变长度:如果你在X个gpu上运行,它的长度将除以X(因为你的实际批处理大小将乘以X),除非你设置split_batches=True。
任何使用训练数据加载器长度的指令(例如,如果您想记录总训练步数)都应该在调用prepare()之后执行。
您完全可以单独将数据加载器发送到prepare(),但最好将模型和优化器一起发送到prepare()。
您可能想要也可能不想将验证数据加载器发送到prepare(),这取决于您是否想要运行分布式计算(参见下面)。
- 将loss.backward()替换为accelerator.backward(loss)。
你都准备好了!通过所有这些更改,您的脚本将运行在本地机器以及多个gpu或TPU上!您可以使用自己喜欢的工具来启动分布式训练,也可以使用🤗Accelerate启动器。
分布式验证
如果将验证数据加载器排除在prepare()方法之外,则可以在训练脚本中执行常规计算。在这种情况下,您需要手动将输入数据放在accelerator.device上。
要执行分布式计算,请将验证数据加载器发送到prepare()方法:
validation_dataloader = accelerator.prepare(validation_dataloader)
至于你的training dataloader,这意味着(如果你在多个设备上运行你的脚本)每个设备只能看到部分评估数据。你需要把你的预测组合在一起。使用gather_for_metrics()方法很容易做到这一点。
for inputs, targets in validation_dataloader:
predictions = model(inputs)
# Gather all predictions and targets
all_predictions, all_targets = accelerator.gather_for_metrics((predictions, targets))
# Example of use with a *Datasets.Metric*
metric.add_batch(all_predictions, all_targets)
类似于training dataloader,通过prepare()传递验证数据加载器可能会改变它:如果你在X个gpu上运行,它的长度将除以X(因为你的实际批处理大小将乘以X),除非你设置split_batches=True。
使用训练数据加载器长度的任何指令(例如,如果需要总训练步数来创建学习率调度器)都应该在调用prepare()之后。
数据集末尾的一些数据可能会被复制,因此批处理可以在所有工作者中平均分配。因此,度量应该通过gather_for_metrics()方法计算,以便在收集数据时自动删除重复的数据。
如果出于某种原因,您不希望自动执行此操作,则可以使用gather()来跨所有进程收集数据,并且可以手动执行此操作。
gather()和gather_for_metrics()方法要求每个进程上的张量大小都相同。如果在每个进程上有不同大小的张量(例如在批处理中动态填充到最大长度时),您应该使用pad_across_processes()方法跨进程将张量填充到最大大小。
参考
- Huggingface Accelerate, huggingface.co/docs/accele…
- BF16 与 FP16 在模型上哪个精度更高呢, zhuanlan.zhihu.com/p/449345588