RL训练:Qwen3-30B-A3B
1. 演练目标与架构
本节基于四机32卡MTT S5000完成Qwen3-30B-A3B-Instruct模型的强化学习(RL)训练闭环,使用GRPO算法,基于VeRL强化学习框架,和Megatron训练框架、SGLang推理框架。
2. 前置条件
2.1 硬件环境
本例使用四台GPU服务器,所下所示。
| 节点 | IP地址 | GPU数量 | 角色 |
|---|---|---|---|
| node125 | 10.10.142.125 | 8×MTT S5000 | Master/Head |
| node71 | 10.10.134.71 | 8×MTT S5000 | Worker |
| node116 | 10.10.142.116 | 8×MTT S5000 | Worker |
| node119 | 10.10.142.119 | 8×MTT S5000 | Worker |
2.2 共享存储
四机通过NFS共享 /data/datapool/ 目录,包含:
- VeRL代码:
/data/datapool/verl - 模型:
/data/datapool/models/Qwen3-30B-A3B-Instruct-2507(HF格式),/data/datapool/models/Qwen3-30B-A3B-Instruct-2507-MCore(MCore格式) - 数据:
/data/datapool/verl/musa_examples/data
3. 环境准备
3.1 四机容器创建
在四台宿主机上分别执行:
# 创建容器
sudo docker create \
--privileged \
--env MTHREADS_VISIBLE_DEVICES=all \
--net host \
-v /data/:/data/ \
--name train-rl \
registry.mthreads.com/mcctest/training-suite:v2.1.5-musa-4.3.7 \
sleep infinity
# 启动容器
sudo docker start train-rl
# 启动SSH服务
docker exec train-rl service ssh restart
四机容器需要配置好ssh免密登录,这里不再赘述。
3.2 创建四机hostfile
在 node125 容器内创建:
cat > /data/datapool/verl/musa_examples/hostfile << 'EOF'
10.10.142.125 slots=8
10.10.134.71 slots=8
10.10.142.116 slots=8
10.10.142.119 slots=8
EOF
4. 准备训练数据
训练数据已在 /data/datapool/verl/musa_examples/data/ 目录中预置:
| 文件 | 说明 | 用途 |
|---|---|---|
amt_math17k_d_qweninstruct.parquet | 数学推理训练数据(17K条) | 训练集 |
aime_2024.parquet | AIME 2024竞赛题 | 验证集 |
aime_2025.parquet | AIME 2025竞赛题 | 验证集 |
# 检查数据文件
ls -la /data/datapool/verl/musa_examples/data/
5. 模型准备
5.1 下载Qwen3-30B-A3B模型
# 安装modelscope
pip install modelscope
# 下载模型(如需要)
modelscope download --model Qwen/Qwen3-30B-A3B-Instruct --local_dir /data/datapool/models/Qwen3-30B-A3B-Instruct-2507
5.2 模型格式转换(HF → MCore)
Qwen3-30B-A3B需要转换为Megatron MCore格式才能训练。
创建转换脚本:
cat > /data/datapool/verl/musa_examples/convert_qwen3_30b_to_mcore.sh << 'EOF'
#!/bin/bash
set -x
# 设置路径
export MEGATRON_PATH=/home/Megatron-LM
export VERL_PATH=/data/datapool/verl
export PYTHONPATH=${MEGATRON_PATH}:${VERL_PATH}:$PYTHONPATH
# 模型路径
HF_MODEL_PATH='/data/datapool/models/Qwen3-30B-A3B-Instruct-2507'
DIST_CKPT_PATH='/data/datapool/models/Qwen3-30B-A3B-Instruct-2507-MCore'
# 创建输出目录
mkdir -p $DIST_CKPT_PATH
# 执行转换
python -u /data/datapool/verl/scripts/converter_hf_to_mcore.py \
--hf_model_path $HF_MODEL_PATH \
--output_path $DIST_CKPT_PATH
echo "转换完成!MCore格式模型保存在: $DIST_CKPT_PATH"
EOF
chmod +x /data/datapool/verl/musa_examples/convert_qwen3_30b_to_mcore.sh
执行转换:
cd /data/datapool/verl/musa_examples
bash convert_qwen3_30b_to_mcore.sh
转换完成后验证:
ls -lh /data/datapool/models/Qwen3-30B-A3B-Instruct-2507-MCore/
# 应看到 ~57GB 的分片文件(2个shard)
6. 配置修改
6.1 runtime_env.yaml
确保已添加MCCL网络配置:
6.2 关键代码修复(硬编码路径)
参考"附录:已知问题与修复记录",确保已修复以下硬编码路径:
| 文件 | 修复内容 |
|---|---|
megatron_actor.py (~523行) | 注释掉调试 torch.save |
ray_trainer.py (~1504行) | 注释掉调试 torch.save |
7. 创建四机训练脚本
创建 /data/datapool/verl/musa_examples/qwen3-30b-instruct_grpo_32gpu.sh:
#!/bin/bash
set -x
# =============================================================================
# Qwen3-30B-A3B GRPO 训练脚本 - 四机32卡版本
# =============================================================================
# MCCL 网络配置(四机必需)
export MCCL_SOCKET_IFNAME=bond0
export GLOO_SOCKET_IFNAME=bond0
export MCCL_IB_GID_INDEX=3
export MCCL_IB_TIMEOUT=20
export MCCL_IB_RETRY_CNT=20
export MCCL_CROSS_NIC=1
# -----------------------------------------------------------------------------
# 1. 模型和数据路径配置
# -----------------------------------------------------------------------------
HF_MODEL_PATH=/data/datapool/models/Qwen3-30B-A3B-Instruct-2507
DIST_CKPT_PATH=/data/datapool/models/Qwen3-30B-A3B-Instruct-2507-MCore
DATASET_PATH="/data/datapool/verl/musa_examples/data"
train_files=$DATASET_PATH/amt_math17k_d_qweninstruct.parquet
test_files="['$DATASET_PATH/aime_2024.parquet','$DATASET_PATH/aime_2025.parquet']"
# -----------------------------------------------------------------------------
# 2. Verl路径配置
# -----------------------------------------------------------------------------
export VERL_PATH=/data/datapool/verl
CONFIG_PATH=$VERL_PATH/verl/trainer/config
# -----------------------------------------------------------------------------
# 3. 训练参数配置(MoE模型专用)
# -----------------------------------------------------------------------------
use_dynamic_bsz=True
max_prompt_length=1024
max_response_length=32768 # 32K长度
actor_ppo_max_token_len=$(((max_prompt_length + max_response_length) * 1))
infer_ppo_max_token_len=$(((max_prompt_length + max_response_length) * 1))
# -----------------------------------------------------------------------------
# 4. Runtime环境和Hostfile配置
# -----------------------------------------------------------------------------
runtime_env=./runtime_env.yaml
HOSTFILE="hostfile"
HEAD_IP=$(head -n 1 "$HOSTFILE" | awk '{print $1}')
# 禁用profiler(避免研发环境硬编码路径报错)
export GLOBAL_PROFILER_TOOL=null
# -----------------------------------------------------------------------------
# 5. 启动Ray并提交训练任务
# -----------------------------------------------------------------------------
RAY_ADDRESS="http://${HEAD_IP}:8265" ray job submit \
--runtime-env=$runtime_env \
--no-wait \
-- python3 -u -m verl.trainer.main_ppo \
--config-path="$CONFIG_PATH" \
--config-name='ppo_megatron_trainer_demo.yaml'\
algorithm.adv_estimator=grpo \
data.train_files=$train_files \
data.val_files="$test_files" \
data.train_batch_size=128 \
data.max_prompt_length=$max_prompt_length \
data.max_response_length=$max_response_length \
data.filter_overlong_prompts=True \
data.prompt_key=prompt \
data.truncation='error' \
actor_rollout_ref.model.path=$HF_MODEL_PATH \
actor_rollout_ref.model.enable_activation_offload=True \
actor_rollout_ref.model.enable_gradient_checkpointing=True \
actor_rollout_ref.actor.optim.lr=4e-6 \
actor_rollout_ref.actor.optim.lr_warmup_steps=10 \
actor_rollout_ref.actor.optim.lr_decay_style=constant \
actor_rollout_ref.actor.optim.weight_decay=0.01 \
actor_rollout_ref.actor.ppo_mini_batch_size=128 \
actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=1 \
actor_rollout_ref.actor.profiler.enable=False \
actor_rollout_ref.actor.megatron.use_mbridge=False \
actor_rollout_ref.actor.megatron.vanilla_mbridge=False \
# Actor并行策略: PP=4, EP=8, TP=1
actor_rollout_ref.actor.megatron.pipeline_model_parallel_size=4 \
actor_rollout_ref.actor.megatron.tensor_model_parallel_size=1 \
actor_rollout_ref.actor.megatron.expert_model_parallel_size=8 \
actor_rollout_ref.actor.megatron.expert_tensor_parallel_size=1 \
actor_rollout_ref.actor.megatron.use_dist_checkpointing=True \
actor_rollout_ref.actor.megatron.dist_checkpointing_path=$DIST_CKPT_PATH \
# 内存优化配置
actor_rollout_ref.actor.megatron.param_offload=True \
actor_rollout_ref.actor.megatron.grad_offload=True \
actor_rollout_ref.actor.megatron.optimizer_offload=True \
actor_rollout_ref.actor.megatron.sequence_parallel=True \
# MoE专用配置
+actor_rollout_ref.actor.megatron.override_transformer_config.moe_token_dispatcher_type="alltoall" \
+actor_rollout_ref.actor.megatron.override_transformer_config.moe_router_dtype=fp32 \
+actor_rollout_ref.actor.megatron.override_transformer_config.moe_enable_deepep=False \
# 重计算配置
+actor_rollout_ref.actor.megatron.override_transformer_config.recompute_granularity=full \
+actor_rollout_ref.actor.megatron.override_transformer_config.recompute_method=block \
+actor_rollout_ref.actor.megatron.override_transformer_config.recompute_num_layers=48 \
+actor_rollout_ref.actor.megatron.override_transformer_config.num_layers_in_last_pipeline_stage=3 \
# 优化器配置
+actor_rollout_ref.actor.optim.override_optimizer_config.use_precision_aware_optimizer=True \
+actor_rollout_ref.actor.optim.override_optimizer_config.optimizer_cpu_offload=True \
# 融合优化
+actor_rollout_ref.actor.megatron.override_transformer_config.apply_rope_fusion=True \
+actor_rollout_ref.actor.megatron.override_transformer_config.masked_softmax_fusion=True \
actor_rollout_ref.actor.use_dynamic_bsz=${use_dynamic_bsz} \
actor_rollout_ref.model.use_remove_padding=True \
# Ref配置
actor_rollout_ref.ref.log_prob_use_dynamic_bsz=${use_dynamic_bsz} \
actor_rollout_ref.rollout.log_prob_use_dynamic_bsz=${use_dynamic_bsz} \
actor_rollout_ref.actor.ppo_max_token_len_per_gpu=${actor_ppo_max_token_len} \
actor_rollout_ref.ref.log_prob_max_token_len_per_gpu=${infer_ppo_max_token_len} \
actor_rollout_ref.rollout.log_prob_max_token_len_per_gpu=${infer_ppo_max_token_len} \
# Rollout配置
actor_rollout_ref.rollout.calculate_log_probs=True \
+actor_rollout_ref.rollout.engine_kwargs.sglang.disable_radix_cache=True \
+actor_rollout_ref.rollout.engine_kwargs.sglang.disable_overlap_schedule=True \
+actor_rollout_ref.rollout.engine_kwargs.sglang.disable_custom_all_reduce=True \
+actor_rollout_ref.rollout.engine_kwargs.sglang.chunked_prefill_size=-1 \
actor_rollout_ref.actor.clip_ratio_low=0.0 \
actor_rollout_ref.actor.clip_ratio_high=0.0 \
actor_rollout_ref.actor.clip_ratio_c=1.001 \
actor_rollout_ref.actor.use_kl_loss=False \
actor_rollout_ref.actor.kl_loss_coef=0.0 \
actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu=1 \
actor_rollout_ref.rollout.tensor_model_parallel_size=2 \
actor_rollout_ref.rollout.expert_parallel_size=1 \
actor_rollout_ref.rollout.name=sglang \
actor_rollout_ref.rollout.gpu_memory_utilization=0.7 \
actor_rollout_ref.rollout.n=8 \
actor_rollout_ref.rollout.temperature=1.0 \
actor_rollout_ref.rollout.top_k=-1 \
actor_rollout_ref.rollout.top_p=1 \
actor_rollout_ref.rollout.val_kwargs.temperature=0.6 \
actor_rollout_ref.rollout.val_kwargs.n=8 \
actor_rollout_ref.rollout.free_cache_engine=True \
# Ref Megatron配置
actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu=1 \
actor_rollout_ref.ref.megatron.pipeline_model_parallel_size=1 \
actor_rollout_ref.ref.megatron.tensor_model_parallel_size=1 \
actor_rollout_ref.ref.megatron.expert_model_parallel_size=8 \
actor_rollout_ref.ref.megatron.use_dist_checkpointing=True \
actor_rollout_ref.ref.megatron.dist_checkpointing_path=$DIST_CKPT_PATH \
actor_rollout_ref.ref.megatron.sequence_parallel=False \
algorithm.use_kl_in_reward=False \
trainer.critic_warmup=0 \
trainer.logger='["console","tensorboard"]' \
trainer.project_name='verl_grpo_qwen3_30b' \
trainer.experiment_name='Qwen3-30B-A3B_megatron_sglang' \
trainer.n_gpus_per_node=8 \
trainer.val_before_train=False \
trainer.nnodes=4 \
trainer.save_freq=150 \
trainer.test_freq=5 \
trainer.total_epochs=1
赋予执行权限:
chmod +x /data/datapool/verl/musa_examples/qwen3-30b-instruct_grpo_32gpu.sh
7.1 关键配置说明
| 配置项 | 值 | 说明 |
|---|---|---|
| Actor并行 | PP=4, EP=8, TP=1 | MoE训练并行策略 |
| Rollout并行 | TP=2 | SGLang推理并行 |
| Ref并行 | EP=8 | 参考模型并行 |
| batch_size | 128 | 全局批次大小 |
| max_response_length | 32768 | 最大生成长度(32K) |
| n | 8 | GRPO样本数 |
| 算法 | GRPO | 使用GRPO算法 |
| gpu_memory_utilization | 0.7 | SGLang显存利用率 |
8. 启动四机训练
8.1 停止现有Ray进程
在四机容器内分别执行:
ray stop --force
或使用脚本批量执行:
for host in node125 node71 node116 node119; do
ssh $host "ray stop --force"
done
8.2 启动Ray集群
在 node125 容器内执行:
cd /data/datapool/verl/musa_examples
bash setup_ray.sh
setup_ray.sh 脚本内容:
#!/bin/bash
HOSTFILE="hostfile"
HEAD_IP=$(head -n 1 "$HOSTFILE" | awk '{print $1}')
COUNT=0
while IFS= read -r line; do
IP=$(echo "$line" | awk '{print $1}')
echo "正在连接 $IP..."
ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 "root@$IP" << EOF
if [ $COUNT -eq 0 ]; then
ray start --head --dashboard-host=0.0.0.0
else
ray start --address=${HEAD_IP}:6379
fi
EOF
COUNT=$((COUNT + 1))
echo "已完成 $IP 的配置"
echo "----------------------------------------"
done < "$HOSTFILE"
echo "所有机器配置完成!"
验证集群状态:
ray status
# 应看到 4 个节点,32 个 GPU
8.3 启动训练
在 node125 容器内执行:
cd /data/datapool/verl/musa_examples
nohup bash qwen3-30b-instruct_grpo_32gpu.sh > nohup_30b_32gpu.log 2>&1 &
9. 监控四机训练
9.1 Ray集群监控
# 查看Ray集群状态
ray status
# 查看任务状态
ray job list
ray job status <job_id>
# 查看实时日志
ray job logs <job_id> --follow
9.2 关键监控指标
| 指标 | 说明 | 正常范围 |
|---|---|---|
| step | 训练步数 | 递增 |
| actor_mfu | Actor模型算力利用率 | 20-35% |
| throughput | 吞吐(tokens/s) | 500-700/节点 |
| reward | 平均奖励 | 逐渐提升 |
| response_length | 平均响应长度 | 根据任务变化 |
| running-req | SGLang运行请求数 | 40-60 |
| token usage | SGLang KV Cache使用率 | 0.20-0.35 |
9.3 SGLang监控
Rollout阶段观察SGLang日志:
# 典型日志输出
Decode batch. #running-req: 50, #token: 2300000, token usage: 0.27, ...
# gen throughput (token/s): 620.15, #queue-req: 0
指标解读:
running-req: 当前正在生成的请求数(45-55为正常)token usage: KV Cache使用率(反映32K长序列的显存压力)gen throughput: 单节点生成吞吐量(580-650 tokens/s为正常)
10. 性能参考
基于 Qwen3-30B-A3B 在四机 32 卡 MTT S5000 上的训练性能(32K长度):
| 指标 | 数值 |
|---|---|
| Actor MFU | ~20-30% |
| 单节点Throughput | ~550-650 tokens/s |
| 每step时间 | ~30-40分钟 |