在之前博客进行了简单的训练尝试:https://www.dong-blog.fun/post/2060
在本博客,将会深入进行多机多卡训练,以及调整训练奖励函数。
之前构建了镜像: docker build . -t kevinchina/deeplearning:r1
展开代码FROM hiyouga/verl:ngc-th2.6.0-cu126-vllm0.8.4-flashinfer0.2.2-cxx11abi0 WORKDIR /workplace RUN git clone https://github.com/hiyouga/EasyR1.git WORKDIR /workplace/EasyR1 RUN pip install -e . -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple RUN pip install swanlab -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
多机多卡的rbma通信条件在这个镜像也具备: apt-get install libibverbs1 -y
verl 依托于 Ray, 所以多机多卡使用需要使用Ray进行训练,Ray和torchrun的原理也差不多。Ray在master节点启动head,在子节点启动服务,然后由Ray统一任务调度。
所以官网教程有:
展开代码Start the Ray head node. 启动 Ray 主节点。 ray start --head --port=6379 --dashboard-host=0.0.0.0 Start the Ray worker node and connect to the head node. 启动 Ray 工作节点并连接到头节点。 ray start --address=<head_node_ip>:6379 Check the Ray resource pool. 检查 Ray 资源池。 ray status Run training script on the Ray head node only. 运行训练脚本仅在 Ray 主节点上。 bash examples/qwen2_5_vl_7b_geo3k_grpo.sh
在一般的多机多卡调度里,有这三个环境变量:
展开代码${MASTER_ADDR} ${MASTER_PORT} ${RANK}
这是一个用于分布式训练的Bash脚本,主要功能是配置和启动Ray集群来运行Qwen2-5-VL-7B模型的训练任务。脚本首先设置分布式训练参数(如节点数、GPU数量、主节点IP等),然后根据当前节点的RANK值区分主节点和工作节点:主节点启动Ray head服务并等待所有工作节点连接,工作节点则连接到主节点。当集群就绪后,主节点会启动训练脚本(qwen2_5_vl_7b_grpo_train.sh),同时集成了SwanLab实验跟踪工具,支持云端和本地训练日志记录。脚本最后通过sleep infinity保持Ray服务持续运行,确保分布式训练环境稳定。该脚本特别适用于多节点GPU集群上的大规模模型训练场景:
bash展开代码#!/bin/bash
cp /config/qwen2_5_vl_7b_grpo_train.sh /workplace/EasyR1/examples/
# 参数设置(可以改成从环境变量读取)
TOTAL_NODES=2 # 总节点数
GPU_PER_NODE=8 # 每台机器的 GPU 数量
#RAY_HEAD_IP="${MASTER_ADDR}" # 主节点 IP
RAY_PORT="${MASTER_PORT}" # Ray 默认端口
NODE_RANK="${RANK}" # 当前节点 rank(0 是主节点)
RAY_HEAD_IP=$(getent hosts "${MASTER_ADDR}" | awk '{print $1}')
echo "RAY_HEAD_IP: $RAY_HEAD_IP"
echo "NODE_RANK: $NODE_RANK"
echo "TOTAL_NODES: $TOTAL_NODES"
echo "GPU_PER_NODE: $GPU_PER_NODE"
echo "RAY_PORT: $RAY_PORT"
export SWANLAB_API_KEY=xxxxx # 设置在线跟踪模式API
export SWANLAB_LOG_DIR=/swanlab_log # 设置本地日志存储路径
export SWANLAB_MODE=cloud # 包含四种模式:cloud云端跟踪模式(默认)、cloud-only仅云端跟踪本地不保存文件、local本地跟踪模式、disabled完全不记录用于debug
# 检查是否是主节点
if [ "$NODE_RANK" -eq 0 ]; then
echo "Starting Ray HEAD node on $(hostname -I | awk '{print $1}')"
ray start --head --port="$RAY_PORT" --dashboard-host=0.0.0.0 --num-gpus="$GPU_PER_NODE"
sleep 20
else
sleep 10
echo "Starting Ray WORKER node #$NODE_RANK, connecting to $RAY_HEAD_IP:$RAY_PORT"
ray start --address="$RAY_HEAD_IP:$RAY_PORT" --num-gpus="$GPU_PER_NODE"
fi
# 等待所有节点加入(主节点检查)
if [ "$NODE_RANK" -eq 0 ]; then
echo "Waiting for all nodes to join..."
while true; do
echo "Checking connected nodes..."
LIVE_NODES=$(ray status | grep -c " 1 node_")
echo "Current connected nodes: $LIVE_NODES / $TOTAL_NODES"
# 打印ray status的完整输出以便调试
echo "Ray cluster status:"
ray status
if [ "$LIVE_NODES" -eq "$TOTAL_NODES" ]; then
echo "All $TOTAL_NODES nodes connected successfully!"
break
fi
echo "Waiting 5 seconds before next check..."
sleep 5
done
# 在主节点上启动训练脚本
echo "Launching training script..."
echo "Setting up SwanLab environment variables..."
export SWANLAB_API_KEY=pM7Xvs5OS2EeXPO5gKXfJ # 设置在线跟踪模式API
export SWANLAB_LOG_DIR=/swanlab_log # 设置本地日志存储路径
export SWANLAB_MODE=cloud # 包含四种模式:cloud云端跟踪模式(默认)、cloud-only仅云端跟踪本地不保存文件、local本地跟踪模式、disabled完全不记录用于debug
echo "Starting training with qwen2_5_vl_7b_grpo_train.sh..."
cd /workplace/EasyR1 && bash examples/qwen2_5_vl_7b_grpo_train.sh
fi
# 保持 Ray 运行(防止脚本退出)
sleep infinity
训练脚本/config/qwen2_5_vl_7b_grpo_train.sh:
bash展开代码#!/bin/bash
set -x
export PYTHONUNBUFFERED=1
export SWANLAB_API_KEY=xxxxx # 设置在线跟踪模式API
export SWANLAB_LOG_DIR=/swanlab_log # 设置本地日志存储路径
export SWANLAB_MODE=cloud # 包含四种模式:cloud云端跟踪模式(默认)、cloud-only仅云端跟踪本地不保存文件、local本地跟踪模式、disabled完全不记录用于debug
MODEL_PATH=/Qwen2.5-VL-3B-Instruct
python3 -m verl.trainer.main \
config=examples/config.yaml \
data.train_files=hiyouga/geometry3k@train \
data.val_files=hiyouga/geometry3k@test \
worker.actor.model.model_path=${MODEL_PATH} \
trainer.experiment_name=qwen2_5_vl_7b_geo_grpo \
trainer.logger=['console','swanlab'] \
trainer.n_gpus_per_node=8 \
trainer.nnodes=2
本文作者:Dong
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!