跳到主要内容

RL训练:Qwen3-30B-A3B

1. 演练目标与架构

本节基于四机32卡MTT S5000完成Qwen3-30B-A3B-Instruct模型的强化学习(RL)训练闭环,使用GRPO算法,基于VeRL强化学习框架,和Megatron训练框架、SGLang推理框架。

2. 前置条件

2.1 硬件环境

本例使用四台GPU服务器,所下所示。

节点IP地址GPU数量角色
node12510.10.142.1258×MTT S5000Master/Head
node7110.10.134.718×MTT S5000Worker
node11610.10.142.1168×MTT S5000Worker
node11910.10.142.1198×MTT S5000Worker

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.parquetAIME 2024竞赛题验证集
aime_2025.parquetAIME 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=1MoE训练并行策略
Rollout并行TP=2SGLang推理并行
Ref并行EP=8参考模型并行
batch_size128全局批次大小
max_response_length32768最大生成长度(32K)
n8GRPO样本数
算法GRPO使用GRPO算法
gpu_memory_utilization0.7SGLang显存利用率

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_mfuActor模型算力利用率20-35%
throughput吞吐(tokens/s)500-700/节点
reward平均奖励逐渐提升
response_length平均响应长度根据任务变化
running-reqSGLang运行请求数40-60
token usageSGLang 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分钟
显存占用~60-70GB/卡
SGLang KV Cache~20-30%
并发请求数45-55/节点

性能特点

  • 32K长序列导致Rollout阶段耗时较长(~25-35分钟)
  • MoE模型的EP=8配置使专家分布更均匀
  • 显存压力主要来自32K KV Cache,约占用15-20GB/卡

文档版本: v1.0 创建日期: 2026-03-23 适用范围: Qwen3-30B-A3B-Instruct 四机32卡RL训练


附录:已知问题与修复记录

问题1:Actor 调试代码硬编码路径

现象: 训练在 Actor 更新阶段报错:

RuntimeError: Parent directory /mnt/seed-program-nas/001688/kechun.wu/0226/tmp_pts does not exist.

原因megatron_actor.py 中存在硬编码的调试保存路径,用于保存中间结果供调试分析。

解决方案

修改 /data/datapool/verl/verl/workers/actor/megatron_actor.py, 找到以下代码块(约第523-525行)并注释掉:

# 修改前
if torch.distributed.get_rank() in [0,1,2]:
p = '/mnt/seed-program-nas/001688/kechun.wu/0226/tmp_pts/rank0_pgloss_actor_rank{}.pt'.format(torch.distributed.get_rank())
torch.save([log_prob,old_log_prob,advantages,advantages,response_mask],p)

# 修改后
# DEBUG: Commented out for production training
# if torch.distributed.get_rank() in [0,1,2]:
# p = "/data/datapool/verl/tmp/rank0_pgloss_actor_rank{}.pt".format(torch.distributed.get_rank())
# torch.save([log_prob,old_log_prob,advantages,advantages,response_mask],p)

建议

  • 生产环境训练建议注释掉调试代码
  • 如需调试,可修改为本地路径并创建对应目录

问题2:ray_trainer.py 中的硬编码路径

现象: 训练在第一个 step 的 rollout 完成后报错:

RuntimeError: Parent directory /mnt/seed-program-nas/001688/kechun.wu/0226 does not exist.

原因ray_trainer.py 第 1504-1507 行有硬编码的调试保存路径。

解决方案

修改 /data/datapool/verl/verl/trainer/ppo/ray_trainer.py,注释掉调试代码:

# 修改前
p = '/mnt/seed-program-nas/001688/kechun.wu/0226/30b_gen_32k_no_graph_amthink_128*8.pt'
#p = '/mnt/seed-program-nas/001688/kechun.wu/0226/gen_data/30b_gen_32k_graph+test.pt'
p = '/mnt/seed-program-nas/001688/kechun.wu/0226/30b_gen_32k_graph_amthink_128*8.pt'
torch.save(batch,p)

# 修改后
# DEBUG: Commented out for production training
# p = '/mnt/seed-program-nas/001688/kechun.wu/0226/30b_gen_32k_no_graph_amthink_128*8.pt'
# #p = '/mnt/seed-program-nas/001688/kechun.wu/0226/gen_data/30b_gen_32k_graph+test.pt'
# p = '/mnt/seed-program-nas/001688/kechun.wu/0226/30b_gen_32k_graph_amthink_128*8.pt'
# torch.save(batch,p)

所有硬编码路径修复总结

修复了以下文件中的研发环境硬编码路径:

文件行号修复内容
megatron_actor.py~523-525注释掉 torch.save(pgloss...)
ray_trainer.py~1504-1507注释掉 torch.save(batch...)