新手必看!verl多节点训练一键启动实战
1. 引言:为什么选择 verl 进行多节点强化学习训练?
你是否正在为大型语言模型(LLM)的后训练效率低下而烦恼?手动搭建分布式环境、调试通信问题、管理 GPU 资源……这些繁琐的工作不仅耗时,还容易出错。今天,我们来介绍一个真正能“开箱即用”的解决方案——verl。
verl 是由字节跳动火山引擎团队开源的一个专为 LLM 后训练设计的强化学习(RL)框架,也是其HybridFlow 论文的官方实现。它不是另一个玩具级项目,而是面向生产环境打造的高效工具,具备以下核心优势:
- 高吞吐量:无缝集成 vLLM、FSDP 等 SOTA 推理与训练框架,最大化生成和训练速度。
- 灵活并行:支持将 Actor、Critic、Reference 模型灵活分布到不同 GPU 组,资源利用率更高。
- 模块化 API:轻松对接 HuggingFace 模型,无需修改底层代码即可快速迁移。
- 3D-HybridEngine:创新的重分片机制,显著降低训练/推理切换时的通信开销。
本文将带你从零开始,手把手完成 verl 的多节点训练部署,并提供一份可直接运行的slurm_script.sh脚本,真正做到“一键启动”。
2. 环境准备与基础验证
在进入多节点之前,先确保你的单机环境已经正确安装 verl。
2.1 安装与版本检查
打开 Python 环境,执行以下命令:
python导入 verl 并查看版本号:
import verl print(verl.__version__)如果输出类似0.1.0或更高版本,则说明安装成功。若报错,请确认已正确安装依赖包,并建议使用 Ray 最新版(≥2.40),旧版本可能存在兼容性问题。
提示:推荐使用 Conda 创建独立环境,避免依赖冲突。
3. 多节点训练的核心挑战与解决方案
多节点训练最大的难点不在于算法本身,而在于集群管理、任务调度和资源协调。传统方式需要手动启动 Ray 集群、配置网络、提交作业,步骤繁琐且易出错。
verl 提供了两种主流方案:
- 手动 Ray 集群:适合小规模测试或教学场景
- Slurm + Docker/Podman:适合生产级大规模训练
本文重点讲解后者,因为它更贴近真实科研与工业环境。
4. 基于 Slurm 的 verl 多节点训练实战
我们将通过一个完整的slurm_script.sh脚本,实现从容器构建、Ray 集群初始化到数据预处理、模型加载再到正式训练的全流程自动化。
4.1 脚本结构概览
该脚本包含以下关键阶段:
- SLURM 作业配置
- 环境变量设置
- Docker 容器构建与启动
- Ray 多节点集群初始化
- 数据预处理
- 模型加载验证
- PPO 训练任务启动
4.2 SLURM 作业配置详解
#!/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]--nodes=2:使用两个计算节点--gpus-per-node=8:每节点 8 块 GPU,适用于 A100/H100 或 MI300 等高端卡--output和--error:日志输出路径,便于后续排查问题
4.3 关键环境变量设置
为了确保 AMD ROCm 或 NVIDIA CUDA 环境正常工作,必须设置一系列 NCCL/RCCL 相关参数:
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=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注意:
NCCL_IB_HCA需根据实际 InfiniBand 设备名称调整,可通过ibdev2netdev命令查看。
同时,统一 GPU 可见性设置:
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这保证了容器内外 GPU 编号一致,避免设备不可见问题。
4.4 构建并启动 Docker 容器
脚本中使用srun bash -c在每个节点上并行执行容器操作:
srun bash -c " set -e docker image prune -f 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 docker run --rm -d \ -e HYDRA_FULL_ERROR=1 \ --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 "--network host:共享主机网络,简化 Ray 通信--device:挂载 GPU 和 InfiniBand 设备--shm-size 128G:增大共享内存,防止 Ray 对象存储溢出tail -f /dev/null:保持容器常驻运行
4.5 初始化 Ray 多节点集群
这是整个流程中最关键的部分。我们需要在一个节点上启动 Ray Head,其余节点作为 Worker 加入。
获取主节点 IP 地址
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)启动 Head 节点
srun --nodes=1 --ntasks=1 -w "$head_node" \ docker exec "${CONTAINER_NAME}" \ ray start --head --node-ip-address="$head_node_ip" --port=6379 \ --dashboard-port=8266 \ --num-cpus "${SLURM_CPUS_PER_TASK}" --num-gpus "${SLURM_GPUS_PER_NODE}" --block & sleep 10启动 Worker 节点
worker_num=$((SLURM_JOB_NUM_NODES - 1)) for ((i = 1; i <= worker_num; i++)); do node_i=${nodes_array[$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重要:
--block参数确保进程不退出,维持 Ray 守护状态。
4.6 验证 Ray 集群状态
在提交训练任务前,务必验证集群是否正常:
docker exec "${CONTAINER_NAME}" python3 -c ' import ray try: ray.init(address="auto") print("\n=== Ray Cluster Status ===") print(f"Number of nodes: {len(ray.nodes())}") for node in ray.nodes(): print("Node: {}, Status: {}".format(node["NodeManagerHostname"], node["Alive"])) ray.shutdown() print("Ray initialization successful!") except Exception as e: print(f"Ray initialization failed: {str(e)}") '如果输出显示两个节点均处于Alive=True状态,则集群搭建成功。
4.7 数据预处理与模型加载
接下来进行数据准备:
echo "Starting data preprocessing..." docker exec "${CONTAINER_NAME}" \ python3 "examples/data_preprocess/gsm8k.py" "--local_dir" "../data/gsm8k" docker exec "${CONTAINER_NAME}" \ python3 "examples/data_preprocess/math_dataset.py" "--local_dir" "../data/math"然后验证模型能否正常加载(以 Qwen2 系列为例):
echo "Loading model..." docker exec "${CONTAINER_NAME}" \ python3 -c "import transformers; transformers.pipeline('text-generation', model='Qwen/Qwen2-7B-Instruct')"这一步可以提前发现模型下载失败、权限不足等问题。
4.8 启动 PPO 训练任务
最后,使用srun在 Head 节点上启动正式训练:
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.actor.optim.lr=1e-6 \ actor_rollout_ref.rollout.name=vllm \ actor_rollout_ref.rollout.gpu_memory_utilization=0.9 \ critic.optim.lr=1e-5 \ critic.model.path=$MODEL_PATH \ algorithm.kl_ctrl.kl_coef=0.0001 \ 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.nnodes=${SLURM_NNODES} \ trainer.total_epochs=15关键参数说明:
rollout.name=vllm:使用 vLLM 作为推理后端,提升生成速度gpu_memory_utilization=0.9:充分利用显存logger=['console','wandb']:同时输出到控制台和 WandB,便于监控
5. 如何运行这个脚本?
只需将上述完整脚本保存为slurm_script.sh,然后执行:
sbatch slurm_script.sh系统会自动分配资源、启动容器、初始化集群并开始训练。你可以通过以下命令查看日志:
tail -f ../verl_log/slurm-*.out或者访问 Ray Dashboard(默认端口 8266)查看任务详情。
6. 常见问题与调试建议
6.1 Ray 初始化失败
- 检查防火墙是否开放
6379和8266端口 - 确保所有节点时间同步(NTP)
- 查看
/tmp/ray/session_latest/logs/下的日志文件
6.2 GPU 不可见或 ROCm 报错
- 确认
--device /dev/kfd和/dev/dri已正确挂载 - 检查 ROCm 驱动版本与容器镜像是否匹配
- 使用
rocminfo命令验证设备识别情况
6.3 训练过程中 OOM(内存溢出)
- 减小
train_batch_size或ppo_micro_batch_size_per_gpu - 开启 FSDP 参数卸载:
param_offload=True - 增加
--shm-size到 256G 以上
7. 总结:让多节点训练变得简单可靠
通过本文提供的slurm_script.sh脚本,你现在可以:
✅ 一键启动 verl 多节点训练
✅ 自动构建容器环境
✅ 无缝集成 Ray 分布式框架
✅ 支持 AMD ROCm 与 NVIDIA CUDA 双平台
✅ 实现高吞吐、低延迟的 RLHF 训练
verl 的设计理念是“让研究人员专注于算法,而不是运维”。借助这套脚本,即使是刚接触分布式训练的新手,也能快速跑通完整的 PPO 流程。
下一步你可以尝试:
- 替换不同的基础模型(如 Llama3、DeepSeek)
- 调整 RL 超参(KL 系数、学习率等)
- 接入自定义数据集进行微调
记住,强大的工具只是起点,真正的价值在于你如何用它创造出更好的 AI。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。