Deepspeed多机微调使用和踩坑记录
官方demoDeepSpeedExamples
基本使用
NOTE: deepspeed只需要在一个节点上启动, 它会自动使用ssh根据hostfile的内容去其他节点启动程序。官方演示
多机训练主要有两个步骤:
- 配置ssh免密登陆: 以便deepspeed能访问其他节点然后自动启动其他节点的程序
- 安装pdsh, 如
apt install pdsh
- 配置deepspeed的hostfile: 以便deepspeed确定需要到哪些节点启动,使用多少张显卡
从一个最简单的程序开始
1 | # test.py |
配置ssh免密登陆, 使用如下模板配置~/.ssh/config
文件。其中Host字段的别名要和hostfile对应
1 | Host host1 |
使用ssh-copy-id
挨个传递公钥以完成免密登陆,如ssh-copy-id host1
。
编写hostfile
文件, 格式如下, 第一列和ssh config中的别名对应,slots
表示每个节点有多少张显卡可用, 默认deepspeed会用满节点是所有slot。可以将hostfile
文件拷贝到/job/hostfile
, 这是deepspeed默认的hostfile路径, 也可以手动指定hostfile文件路径。
1 | host1 slots=2 |
最后, 在一个节点上执行deepspeed test.py
就可以启动, 这样的启动方式会自动使用/job/hostfile
。也可以使用--hostfile
手动指定hostfile文件路径: deepspeed --hostfile ./hostfile test.py
。deepspeed会自动填充一些参数, 更多deepspeed的参数用法可以看官方教程。这里主要想表达的就要只需要在一个节点执行这个坑点(不像其他分布式生成要在每个机器上启动, 在所有就绪前会阻塞)。
工程实现版可以参考DeepSpeedExamples, 其中step1就详细示范了怎么做sft。
通过torchrun启动
有些集群平台没有适配直接用deepspeed启动, 只适配了使用pytorch的master/worker启动。所以有必要搞清楚如何使用torchrun启动deepspeed。
原理就是挨个节点执行一下命令, 然是从环境变量中获取LOCAL_RANK
, 因为torchrun不象deepspeed会自动添加--local_rank
参数。其中--node_rank
参数是必要的, 否则会一直等待。rank表示当前节点的id, local rank表示当前节点中单个显卡的id。
1 | torchrun --nproc_per_node=$NPROC_PER_NODE --nnodes=$NNODES --node_rank=${RANK} --master_addr=${MASTER_ADDR} --master_port ${MASTER_PORT} <main.py> |
在代码中添加获取LOCAL_RANK
变量, 这个变量受到--nproc_per_node
影响
1 | import os |
其中$MASTER_ADDR
, $RANK
和$MASTER_PORT
环境变量在集群启动时会自动配好。
PS: torchrun和torch.distributed.launch的一些区别: torch.distributed.launch
会传递--local-rank
参数, 而torchrun
不会。
所以总结一下用deepspeed启动和用torchrun启动的区别:
- deepspeed会根据hostfile自动启动, 默认会使用当前节点的所有显卡; torchrun需要
--nproc_per_node
指定使用多少显卡 - deepspeed在一个节点启动, 通过ssh自动启动其他节点的程序; torchrun需要手动启动每个节点的程序
- deepspeed继承了
torch.distributed.launch
的传统, 会自动给你的程序添加一个--local_rank
参数; torchrun的local_rank
通过LOCAL_RANK
环境变量获取
所以可以说deepspeed就是自动在每个节点上执行了torch.distributed.launch
, 并根据hostfile设置--nproc_per_node
参数。
使用deepspeed官方代码估计所需资源
1 | from transformers import AutoModel; |
e.g. stage3时的llama2-70b的估计。(当然, 实测很不准, 或许是那里没有配置正确?)
1 | Estimated memory needed for params, optim states and gradients for a: |
RuntimeError: Ninja is required to load C++ extensions
如, 可以在代码中加上:
1 | import os |
其中/opt/conda/bin/
是你的cuda环境二进制程序的位置
CPU内存OOM问题
我发了一个issue, 发现用的卡越多使用的CPU内存越多, 及时没开offload。单机单卡的时候CPU内存不会OOM, 当使用单机4卡启动llama2-70b时CPU比GPU先OOM了。