多节点训练
上次更新时间:2025 年 10 月 6 日。
作者:Xibin Wu,Yusheng Su。
选项 1:手动启动
设置多节点 Ray 集群
使用
ray start --head --dashboard-host=0.0.0.0启动主节点。您需要关注两个地址:
GCS 地址:
ray start --address=<address>,工作节点应连接到此地址。Dashboard 地址:
<address>:8265,您应该将作业提交到集群。
使用上面获取的
ray start --address=<address>启动工作节点。
现在,您应该通过
ray status看到集群有两个节点。
此外,您可以通过上面获取的地址在浏览器中访问仪表板。
可能需要配置防火墙规则才能访问仪表板,如有问题,请联系您的网络管理员。
将作业提交到 Ray 集群
使用上面获取的仪表板地址将 Ray 作业提交到集群。
ray job submit --address="http://127.0.0.1:8265" \
--runtime-env=verl/trainer/runtime_env.yaml \
--no-wait \
-- \
python3 -m verl.trainer.main_ppo \
trainer.n_gpus_per_node=8 \
trainer.nnodes=2 \
...
然后,您可以使用以下命令检查作业状态:
ray job list:列出提交到集群的所有作业。
ray job logs <Submission ID>:查询作业的日志。
ray job status <Submission ID>:查询作业的状态。
ray job stop <Submission ID>:请求停止作业。
ray job list | grep submission_id | grep JobStatus | grep RUNNING | grep -oP ‘raysubmit_[^’'’”]+’ | head -n 1:获取正在运行的作业的最新提交 ID。
ray job logs <Submission ID> –follow:向 ray job logs 命令添加
--follow参数以启用连续日志流。
您还可以从
/tmp/ray/session_latest/logs/访问驱动程序/任务/Actor 日志,驱动程序日志是job-driver-raysubmit_<Submission ID>.log。我们强烈建议您从仪表板查看多节点训练中的作业详情,因为它提供了更结构化的方式来查看作业信息。
选项 2:通过 SkyPilot 在 Kubernetes 或云上启动
Note
SkyPilot 示例配置文件已包含在 examples/skypilot/ 目录中:
verl-ppo.yaml- 使用 GSM8K 数据集的 PPO 训练verl-grpo.yaml- 使用 MATH 数据集的 GRPO 训练verl-multiturn-tools.yaml- 多轮工具使用训练
有关详细使用说明,请参阅 SkyPilot 示例 README。
步骤 1:设置 SkyPilot
SkyPilot 支持不同的云,我们以 GCP 为例。 安装 skypilot
conda create -y -n sky python=3.10
conda activate sky
pip install "skypilot[gcp]"
conda install -c conda-forge google-cloud-sdk
gcloud init
# 如果您没有凭据文件,请运行此命令。
# 这将生成 ~/.config/gcloud/application_default_credentials.json。
gcloud auth application-default login
# 检查 GCP 凭据是否已正确设置。
sky check gcp
步骤 2:准备数据集
git clone https://github.com/volcengine/verl.git
cd examples/data_preprocess
python3 gsm8k.py --local_save_dir ~/data/gsm8k
步骤 3:使用 SkyPilot 提交作业
创建一个 SkyPilot YAML 文件
verl-cluster.yml,内容如下:
workdir: . 将同步当前目录下的所有数据到远程集群。
resources:
accelerators: L4:1 # 每个节点有 1 个 L4 GPU
image_id: docker:verlai/verl:base-verl0.5-cu126-cudnn9.8-torch2.7.0-fa2.7.4
memory: 64+ # 每个节点有 64 GB 内存
ports: 8265 # Ray 仪表板暴露的端口
num_nodes: 2 # 集群大小
# --------------- 工作目录同步 (workdir) ---------------
# 定义要同步到远程集群的本地工作目录。
# 这里,'.' 表示同步 sky submit 命令当前运行所在目录。
workdir: .
# --------------- (secrets) ---------------
secrets:
## 您的 wandb API 密钥 ##
WANDB_API_KEY: null
# --------------- 文件挂载/数据上传 (file_mounts) ---------------
# 如果您的数据集(gsm8k 文件夹)是本地的,则需要将其上传到远程集群。
file_mounts:
# 远程路径(相对于远程用户的家目录):本地路径
# /remote/dir1/file: /local/dir1/file
data/gsm8k: ~/data/gsm8k
# --------------- 环境设置 (setup) ---------------
# 在远程集群的每个节点上运行的命令,用于设置环境(例如,安装依赖项)。这些命令直接在 Docker 内部运行。
setup: |
rm -rf verl
git clone https://github.com/volcengine/verl.git
cd verl
pip3 install -v -e .[vllm]
# --------------- 运行命令 (run) ---------------
# 在远程集群上执行的实际任务命令。
# 此脚本将首先启动 Ray 集群(在 Head 和 Worker 节点上执行不同的 ray start 命令)。
# 然后,您的训练脚本将仅在 Head 节点上运行 (SKYPILOT_NODE_RANK == 0)。
run: |
# 获取 Head 节点的 IP 和节点总数(由 SkyPilot 注入的环境变量)。
head_ip=`echo "$SKYPILOT_NODE_IPS" | head -n1`
num_nodes=`echo "$SKYPILOT_NODE_IPS" | wc -l` # 这里 num_nodes 应该等于 2。
# 登录 wandb
python3 -c "import wandb; wandb.login(relogin=True, key='$WANDB_API_KEY')"
# 根据节点角色启动 Ray(Head=0,Worker>0)。
# 此逻辑是标准的 Ray 集群启动脚本。
if [ "$SKYPILOT_NODE_RANK" == "0" ]; then
# Head 节点启动 Ray Head。
echo "Starting Ray head node..."
# 检查 Ray Head 是否已在运行,以避免重复启动。
ps aux | grep ray | grep 6379 &> /dev/null || ray start --head --disable-usage-stats \
--port=6379 \
--dashboard-host=0.0.0.0 \
--dashboard-port=8265
# 等待所有工作节点加入集群。
while [ $(ray nodes | grep NODE_ID | wc -l) -lt $num_nodes ]; do
echo "Waiting for all nodes to join... ($(ray nodes | grep NODE_ID | wc -l)/$num_nodes)"
sleep 5
done
# Head 节点执行训练脚本。
echo "Executing training script on head node..."
python3 -m verl.trainer.main_ppo \
data.train_files=data/gsm8k/train.parquet \
data.val_files=data/gsm8k/test.parquet \
data.train_batch_size=256 \
data.max_prompt_length=512 \
data.max_response_length=256 \
actor_rollout_ref.model.path=Qwen/Qwen2.5-0.5B-Instruct \
actor_rollout_ref.actor.optim.lr=1e-6 \
actor_rollout_ref.actor.ppo_mini_batch_size=64 \
actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=4 \
actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu=8 \
actor_rollout_ref.rollout.tensor_model_parallel_size=1 \
actor_rollout_ref.rollout.name=vllm \
actor_rollout_ref.rollout.gpu_memory_utilization=0.4 \
actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu=4 \
critic.optim.lr=1e-5 \
critic.model.path=Qwen/Qwen2.5-0.5B-Instruct \
critic.ppo_micro_batch_size_per_gpu=4 \
algorithm.kl_ctrl.kl_coef=0.001 \
trainer.logger=['console','wandb'] \
trainer.val_before_train=False \
trainer.default_hdfs_dir=null \
trainer.n_gpus_per_node=1 \
trainer.nnodes=2 \
trainer.save_freq=20 \
trainer.test_freq=20 \
trainer.total_epochs=2 \
trainer.project_name=verl_examples \
trainer.experiment_name=experiment_name_gsm8k
else
# 等待 Ray Head 启动。
sleep 10 # 增加等待时间以确保 Head 启动完成。
# Worker 节点启动 Ray Worker。
echo "Starting Ray worker node..."
# 检查 Ray Worker 是否已在运行,以避免重复启动。
ps aux | grep ray | grep $head_ip:6379 &> /dev/null || ray start --address $head_ip:6379 --disable-usage-stats
# 在 `ray start` 之后添加 sleep,以确保 Ray 有足够的时间成为守护进程
sleep 5 # 确保 Worker 成功连接到 Head。
fi
# 这里没有向 Worker 节点添加任何命令;Worker 的主要任务是启动 Ray 并等待 Head 节点分配任务。
echo "Node setup and Ray start script finished for rank $SKYPILOT_NODE_RANK."
export WANDB_API_KEY=<your-wandb-api-key>
sky launch -c verl --secret WANDB_API_KEY verl-cluster.yml
在 GCP 上检查集群
检查 Ray Dashboard
我们可以通过 GCP 主节点在 RAY Dashboard 上看到集群:
`console
$ sky status --endpoint 8265 verl
1.2.3.4:8265
`
检查模型的检查点
# 登录主节点
ssh verl
# 全局步数会变化。请从训练日志中查找正确路径。
cd ~/sky_workdir/checkpoints/verl_examples/gsm8k/
# 然后列出内容以找到检查点,例如:
ls -R .
选项 3:通过 Slurm 启动
Ray 为用户提供了`这个 <https://docs.ray.io/en/latest/cluster/vms/user-guides/community/slurm.html>`_ 官方教程,用于在 Slurm 之上启动 Ray 集群。我们已在多节点设置下,在 Slurm 集群上使用以下步骤验证了 GSM8K 示例。
[可选] 如果您的集群支持 Apptainer 或 Singularity 并且您希望使用它们,请将 verl 的 Docker 镜像转换为 Apptainer 镜像。或者,使用集群上可用的包管理器设置环境,或使用您可用的其他容器运行时(例如,通过 Slurm 的 OCI 支持)。
apptainer pull /your/dest/dir/vemlp-th2.4.0-cu124-vllm0.6.3-ray2.10-te1.7-v0.0.3.sif docker://verlai/verl:vemlp-th2.4.0-cu124-vllm0.6.3-ray2.10-te1.7-v0.0.3
按照 GSM8K 示例 准备数据集和模型检查点。
修改 examples/slurm/ray_on_slurm.slurm 以反映您集群的专属信息。
使用 sbatch 将作业脚本提交到 Slurm 集群。
请注意,Slurm 集群的设置可能会有所不同。如果遇到任何问题,请参考 Ray 的 Slurm 用户指南 以了解常见注意事项。
如果您更改了 Slurm 资源规格,请务必在必要时更新作业脚本中的环境变量。
选项 4:通过 dstack 启动
dstackai/dstack 是一个开源容器编排器,可简化跨云提供商和本地环境的分布式训练,而无需使用 K8S 或 Slurm。
先决条件
一旦 dstack 安装 完成,请使用 dstack init 将目录初始化为一个仓库。
mkdir myproject && cd myproject
dstack init
创建一个 fleet (集群)
在提交分布式训练作业之前,请创建一个 dstack fleet (集群)。
运行 Ray 集群任务
创建 cluster (集群) 后,定义一个 Ray 集群任务,例如在 ray-cluster.dstack.yml 中:
type: task
name: ray-verl-cluster
nodes: 2
env:
- WANDB_API_KEY
- PYTHONUNBUFFERED=1
- CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7
image: whatcanyousee/verl:ngc-cu124-vllm0.8.5-sglang0.4.6-mcore0.12.0-te2.2
commands:
- git clone https://github.com/volcengine/verl
- cd verl
- pip install --no-deps -e .
- pip install hf_transfer hf_xet
- |
if [ $DSTACK_NODE_RANK = 0 ]; then
python3 examples/data_preprocess/gsm8k.py --local_save_dir ~/data/gsm8k
python3 -c "import transformers; transformers.pipeline('text-generation', model='Qwen/Qwen2.5-7B-Instruct')"
ray start --head --port=6379;
else
ray start --address=$DSTACK_MASTER_NODE_IP:6379
fi
# 暴露 Ray 仪表板端口
ports:
- 8265
resources:
gpu: 80GB:8
shm_size: 128GB
# 将检查点保存在实例上
volumes:
- /checkpoints:/checkpoints
现在,如果您通过 dstack apply 运行此任务,它将自动将 Ray 的仪表板端口转发到 localhost:8265。
dstack apply -f ray-cluster.dstack.yml
只要 dstack apply 正在运行,您就可以使用 localhost:8265 提交 Ray 作业以供执行。
提交 Ray 作业
在提交 Ray 作业之前,请确保在本地安装 ray:
pip install ray
现在,您可以将训练作业提交到 Ray 集群,该集群可以通过 localhost:8265 访问:
$ RAY_ADDRESS=http://localhost:8265
$ ray job submit \
-- python3 -m verl.trainer.main_ppo \
data.train_files=/root/data/gsm8k/train.parquet \
data.val_files=/root/data/gsm8k/test.parquet \
data.train_batch_size=256 \
data.max_prompt_length=512 \
data.max_response_length=256 \
actor_rollout_ref.model.path=Qwen/Qwen2.5-7B-Instruct \
actor_rollout_ref.actor.optim.lr=1e-6 \
actor_rollout_ref.actor.ppo_mini_batch_size=64 \
actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=4 \
actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu=8 \
actor_rollout_ref.rollout.tensor_model_parallel_size=1 \
actor_rollout_ref.rollout.gpu_memory_utilization=0.4 \
actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu=4 \
critic.optim.lr=1e-5 \
critic.model.path=Qwen/Qwen2.5-7B-Instruct \
critic.ppo_micro_batch_size_per_gpu=4 \
algorithm.kl_ctrl.kl_coef=0.001 \
trainer.project_name=ppo_training \
trainer.experiment_name=qwen-2.5-7B \
trainer.val_before_train=False \
trainer.n_gpus_per_node=8 \
trainer.nnodes=2 \
trainer.default_local_dir=/checkpoints \
trainer.save_freq=10 \
trainer.test_freq=10 \
trainer.total_epochs=15 2>&1 | tee verl_demo.log \
trainer.resume_mode=disable
有关 dstack 如何工作的更多详细信息,请查阅其 文档。
如何调试?
Ray Distributed Debugger VSCode 扩展(推荐)
从 Ray 2.39 开始,Anyscale 推出了 Ray Distributed Debugger VSCode 扩展。请按照扩展的安装说明进行操作,然后使用您之前获取的仪表板 URL 添加您的集群。
先决条件。
确保已安装以下组件(有关更多详细信息,请参阅扩展的 README):
Visual Studio Code
ray[default] >= 2.9.1
debugpy >= 1.8.0
环境变量。
要启用事后调试,请设置:
export RAY_DEBUG_POST_MORTEM=1
注意
在启动 Ray 之前,请务必删除任何旧的标志:
RAY_DEBUG=legacy
–ray-debugger-external
配置断点。在代码中设置 breakpoint() 并将作业提交到集群。然后,扩展将显示断点信息。
在远程函数中插入 breakpoint() 调用。
将作业提交到集群。
扩展将检测活动的断点并在 VSCode 中显示它们。
启动调试器。
直接从命令行运行您的作业(不要使用 launch.json):
python job.py附加到断点。
一旦进程命中第一个 breakpoint(),请点击 VSCode 侧边栏中的 Ray Distributed Debugger 图标以附加调试器。
使用多个 breakpoint() 进行调试。
对于每个后续任务,首先断开当前调试器会话,然后再次单击扩展图标以附加到下一个断点。
旧版 Ray Debugger
Ray 有一个内置的旧版 debugger,允许您调试分布式应用程序。要启用调试器,请使用
RAY_DEBUG=legacy和--ray-debugger-external启动 Ray 集群。
# 启动主节点
RAY_DEBUG=legacy ray start --head --dashboard-host=0.0.0.0 --ray-debugger-external
# 启动工作节点
RAY_DEBUG=legacy ray start --address='10.124.46.192:6379' --ray-debugger-external
在代码中设置断点,并将作业提交到集群。然后运行
ray debug等待断点:
AMD 集群上的多节点训练
如果您想在 AMD 集群上使用 Docker/Podman 容器通过 slurm 进行多节点训练,可以使用以下脚本。
如果您在使用 AMD GPU 运行 verl 时遇到任何问题,请联系 Yusheng Su。
Note
在以下脚本中,您需要使用
podman或docker。我们稍后将发布 apptainer 脚本。如果您想使用
podman,只需将脚本中的docker替换为podman。
该脚本包含以下步骤:
SLURM 配置
环境设置
Docker/Podman 容器设置
Ray 集群初始化
数据预处理
模型设置
训练启动
slurm_script.sh
#!/bin/bash
#SBATCH --job-name=verl-ray-on-slurm
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=2
#SBATCH --mem=200G
#SBATCH --time=30-00:00:00
#SBATCH --gpus-per-node=8
#SBATCH --cpus-per-task=28
#SBATCH --output=../verl_log/slurm-%j.out
#SBATCH --error=../verl_log/slurm-%j.err
#SBATCH --nodelist=gpu-[0,1]
# 加载必要的模块
### 运行此设置
# [Cluster]: 使用 docker
# docker pull docker.io/rocm/vllm:rocm6.2_mi300_ubuntu20.04_py3.9_vllm_0.6.4
##########################################################################
###以下设置应在不同的项目和集群中配置###
##########################################################################
### 项目
CONTAINER_NAME="multinode_verl_training"
IMG="verl.rocm"
DOCKERFILE="docker/Dockerfile.rocm"
# echo $PWD
verl_workdir="${HOME}/projects/verl_upstream"
export TRANSFORMERS_CACHE="${HOME}/.cache/huggingface"
export HF_HOME=$TRANSFORMERS_CACHE
### 集群网络设置
export NCCL_DEBUG=TRACE
export GPU_MAX_HW_QUEUES=2
export TORCH_NCCL_HIGH_PRIORITY=1
export NCCL_CHECKS_DISABLE=1
# export NCCL_IB_HCA=rdma0,rdma1,rdma2,rdma3,rdma4,rdma5,rdma6,rdma7
export NCCL_IB_HCA=mlx5_0,mlx5_1,mlx5_2,mlx5_3,mlx5_4,mlx5_5,mlx5_8,mlx5_9
export NCCL_IB_GID_INDEX=3
export NCCL_CROSS_NIC=0
export CUDA_DEVICE_MAX_CONNECTIONS=1
export NCCL_PROTO=Simple
export RCCL_MSCCL_ENABLE=0
export TOKENIZERS_PARALLELISM=false
export HSA_NO_SCRATCH_RECLAIM=1
##########################################################################
### 用于 rocm 和训练脚本
export HIP_VISIBLE_DEVICES=0,1,2,3,4,5,6,7
export ROCR_VISIBLE_DEVICES=$HIP_VISIBLE_DEVICES
export CUDA_VISIBLE_DEVICES=$HIP_VISIBLE_DEVICES
# 构建并启动 Docker 容器
srun bash -c "
# 遇到任何错误时退出
set -e
# 清理悬空镜像(标签为 <none> 的镜像)
docker image prune -f
# 需要先拉取 docker
docker pull docker.io/rocm/vllm:rocm6.2_mi300_ubuntu20.04_py3.9_vllm_0.6.4
if ! docker images --format "{{.Repository}}:{{.Tag}}" | grep -q "${IMG}"; then
echo \"Building ${IMG} image...\"
docker build -f \"${DOCKERFILE}\" -t \"${IMG}\" .
else
echo \"${IMG} image already exists, skipping build\"
fi
# 删除旧容器(如果存在)
docker rm \"${CONTAINER_NAME}\" 2>/dev/null || true
# 检查网络设备
ibdev2netdev
# 启动 docker
docker run --rm -d \
-e HYDRA_FULL_ERROR=1 \
-e HIP_VISIBLE_DEVICES=${HIP_VISIBLE_DEVICES} \
-e ROCR_VISIBLE_DEVICES=${ROCR_VISIBLE_DEVICES} \
-e CUDA_VISIBLE_DEVICES=${CUDA_VISIBLE_DEVICES} \
-e NCCL_DEBUG=${NCCL_DEBUG} \
-e GPU_MAX_HW_QUEUES=${GPU_MAX_HW_QUEUES} \
-e TORCH_NCCL_HIGH_PRIORITY=${TORCH_NCCL_HIGH_PRIORITY} \
-e NCCL_CHECKS_DISABLE=${NCCL_CHECKS_DISABLE} \
-e NCCL_IB_HCA=${NCCL_IB_HCA} \
-e NCCL_IB_GID_INDEX=${NCCL_IB_GID_INDEX} \
-e NCCL_CROSS_NIC=${NCCL_CROSS_NIC} \
-e CUDA_DEVICE_MAX_CONNECTIONS=${CUDA_DEVICE_MAX_CONNECTIONS} \
-e NCCL_PROTO=${NCCL_PROTO} \
-e RCCL_MSCCL_ENABLE=${RCCL_MSCCL_ENABLE} \
-e TOKENIZERS_PARALLELISM=${TOKENIZERS_PARALLELISM} \
-e HSA_NO_SCRATCH_RECLAIM=${HSA_NO_SCRATCH_RECLAIM} \
-e TRANSFORMERS_CACHE=${TRANSFORMERS_CACHE} \
-e HF_HOME=${HF_HOME} \
--network host \
--device /dev/dri \
--device /dev/kfd \
--device /dev/infiniband \
--group-add video \
--cap-add SYS_PTRACE \
--security-opt seccomp=unconfined \
--privileged \
-v \${HOME}:\${HOME} \
-v \${HOME}/.ssh:/root/.ssh \
-w "${verl_workdir}" \
--shm-size 128G \
--name \"${CONTAINER_NAME}\" \
\"${IMG}\" \
tail -f /dev/null
echo \"Container setup completed\"
"
# (可选):如果您不想使用 root 模式,并希望将自己分配为用户
# 请将 `-e HOST_UID=$(id -u)` 和 `-e HOST_GID=$(id -g)` 添加到上面的 docker 启动脚本中。
### Ray 在训练前启动节点
# 获取节点名称
nodes_array=($(scontrol show hostnames "$SLURM_JOB_NODELIST" | tr '\n' ' '))
head_node=${nodes_array[0]}
head_node_ip=$(srun --nodes=1 --ntasks=1 -w "$head_node" hostname --ip-address)
# 如果检测到主节点 IP 中存在空格字符,我们将
# 将其转换为 ipv4 地址。此步骤是可选的。
if [[ "$head_node_ip" == *" "* ]]; then
IFS=' ' read -ra ADDR <<<"$head_node_ip"
if [[ ${#ADDR[0]} -gt 16 ]]; then
head_node_ip=${ADDR[1]}
else
head_node_ip=${ADDR[0]}
fi
echo "检测到 IPV6 地址。我们将 IPV4 地址分割为 $head_node_ip"
fi
port=6379
ip_head=$head_node_ip:$port
export ip_head
echo "Head IP: $ip_head"
# 确保我们在 Ray 初始化之前设置了环境变量
# 打印所有环境变量
printenv
echo "在 $head_node 启动 HEAD"
srun --nodes=1 --ntasks=1 -w "$head_node" \
docker exec "${CONTAINER_NAME}" \
ray start --head --node-ip-address="$head_node_ip" --port=$port \
--dashboard-port=8266 \
--num-cpus "${SLURM_CPUS_PER_TASK}" --num-gpus "${SLURM_GPUS_PER_NODE}" --block &
# 可选,但在某些 Ray 版本 < 1.0 中可能有用。
sleep 10
# 工作节点以外的节点数量
worker_num=$((SLURM_JOB_NUM_NODES - 1))
for ((i = 1; i <= worker_num; i++)); do
node_i=${nodes_array[$i]}
echo "调试:在节点 node_i = ${node_i} 上启动工作节点"
if [ -z "$node_i" ]; then
echo "错误:工作节点 $i 的节点名称为空"
continue
fi
echo "在 $node_i 启动 WORKER $i"
srun --nodes=1 --ntasks=1 -w "$node_i" \
docker exec "${CONTAINER_NAME}" \
ray start --address "$ip_head" --num-cpus "${SLURM_CPUS_PER_TASK}" --num-gpus "${SLURM_GPUS_PER_NODE}" --block &
sleep 5
done
# Ray 初始化测试(检查以上执行过程中是否有错误)
echo "在 slurm 节点上测试 Ray 初始化..."
docker exec "${CONTAINER_NAME}" python3 -c '
import ray
try:
ray.init(address="auto")
print("\n=== Ray 集群状态 ===")
print(f"节点数: {len(ray.nodes())}")
for node in ray.nodes():
print("节点: {}, 状态: {}".format(node["NodeManagerHostname"], node["Alive"]))
# print(f"节点: {node}")
ray.shutdown()
print("Ray 初始化成功!")
except Exception as e:
print(f"Ray 初始化失败: {str(e)}")
'
echo "=== Ray 测试完成 ==="
######
# 运行数据预处理
echo "开始数据预处理..."
docker exec "${CONTAINER_NAME}" \
python3 "examples/data_preprocess/gsm8k.py" "--local_save_dir" "../data/gsm8k"
echo "开始数据预处理..."
docker exec "${CONTAINER_NAME}" \
python3 "examples/data_preprocess/math_dataset.py" "--local_dir" "../data/math"
train_files="../data/gsm8k/train.parquet"
val_files="../data/gsm8k/test.parquet"
# 下载并测试模型
echo "加载模型..."
docker exec "${CONTAINER_NAME}" \
python3 -c "import transformers; transformers.pipeline('text-generation', model='Qwen/Qwen2-7B-Instruct')"
MODEL_PATH="Qwen/Qwen2-7B-Instruct"
# 在 pipeline 测试后设置模型路径
MODEL_PATH="Qwen/Qwen2.5-0.5B-Instruct"
echo "== 数据和模型加载完成 =="
echo "开始训练..."
docker exec "${CONTAINER_NAME}" \
python3 -c "import transformers; transformers.pipeline('text-generation', model='Qwen/Qwen2-7B-Instruct')"
MODEL_PATH="Qwen/Qwen2-7B-Instruct"
PYTHONUNBUFFERED=1 srun --overlap --nodes=${SLURM_NNODES} --ntasks=1 -w "$head_node" \
docker exec "${CONTAINER_NAME}" \
python3 -m verl.trainer.main_ppo \
data.train_files=$train_files \
data.val_files=$val_files \
data.train_batch_size=1024 \
data.max_prompt_length=1024 \
data.max_response_length=1024 \
actor_rollout_ref.model.path=$MODEL_PATH \
actor_rollout_ref.model.enable_gradient_checkpointing=False \
actor_rollout_ref.actor.optim.lr=1e-6 \
actor_rollout_ref.model.use_remove_padding=True \
actor_rollout_ref.actor.ppo_mini_batch_size=256 \
actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=8 \
actor_rollout_ref.model.enable_gradient_checkpointing=True \
actor_rollout_ref.actor.fsdp_config.param_offload=False \
actor_rollout_ref.actor.fsdp_config.optimizer_offload=False \
actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu=16 \
actor_rollout_ref.rollout.tensor_model_parallel_size=2 \
actor_rollout_ref.rollout.name=vllm \
actor_rollout_ref.rollout.gpu_memory_utilization=0.9 \
actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu=16 \
actor_rollout_ref.ref.fsdp_config.param_offload=True \
critic.optim.lr=1e-5 \
critic.model.use_remove_padding=True \
critic.model.path=$MODEL_PATH \
critic.model.enable_gradient_checkpointing=False \
critic.ppo_micro_batch_size_per_gpu=8 \
critic.model.fsdp_config.param_offload=False \
critic.model.fsdp_config.optimizer_offload=False \
algorithm.kl_ctrl.kl_coef=0.0001 \
trainer.critic_warmup=0 \
trainer.logger='["console","wandb"]' \
trainer.project_name='verl_example' \
trainer.experiment_name='Qwen2.5-32B-Instruct_function_rm' \
trainer.n_gpus_per_node=${SLURM_GPUS_PER_NODE} \
trainer.val_before_train=False \
trainer.nnodes=${SLURM_NNODES} \
trainer.save_freq=-1 \
trainer.test_freq=10 \
trainer.total_epochs=15
使用上面的 slurm_script.sh 运行多节点训练
只需 sbatch 您的 slurm_script.sh
sbatch slurm_script.sh