更新时间:2026-02-05 GMT+08:00
分享

准备训练脚本

模型训练使用的训练脚本文件请参见训练脚本(Qwen3-8B)训练脚本(Qwen2.5-VL-32B-Instruct),需要用户参考本章节内容制作sh文件,并上传到OBS桶中。

OBS桶中文件存放目录示例:
obs://verl/verl-a2/run_train_8b.sh
obs://verl/verl-a2/run_qwen3-8b_npu_ma.sh
obs://verl/verl-a2/run_train_32b.sh
obs://verl/verl-a2/run_qwen2_5_vl_32b_npu_ma.sh
表1 训练脚本

训练模型

训练脚本

脚本说明

Qwen3-8B

run_train_8b.sh

训练作业启动脚本,其中包括数据预处理、启动训练任务输出训练日志等功能

run_qwen3-8b_npu_ma.sh

用于启动VeRL强化学习训练任务。

Qwen2.5-VL-32B-Instruct

run_train_32b.sh

训练作业启动脚本,其中包括数据预处理、启动训练任务输出训练日志等功能

run_qwen2_5_vl_32b_npu_ma.sh

用于启动VeRL强化学习训练任务。

路径解释

训练脚本文件中涉及到的路径解释如下:

  • /home/ma-user/work为ModelArts创建训练作业时设置的本地代码目录,用户可以自定义,但是脚本中的路径和创建训练作业时设置的路径必须保持一致。
  • /verl-a2为存放代码、数据集、模型权重的OBS文件夹名称,用户可以自定义。
  • /output_dir为创建训练作业挂载OBS时设置的云上挂载路径,用于保存预处理后的数据、checkpoint、训练日志,用户可以自定义
  • /home/ma-user/verl为镜像预置的verl代码仓,用户无需修改。

参数解释

参数

参数说明

示例值

data.train_files

预处理完成后的训练集

~/dataset/gsm8k/precessed/train.parquet

data.val_files

预处理完成后的验证集

~/dataset/gsm8k/precessed/test.parquet

actor_rollout_ref.model.path

Huggingface模型路径,也可以是容器内路径

~/models/Qwen3-8B

trainer.n_gpus_per_node

每个节点的NPU数量

8

trainer.nnodes

训练所使用的节点数量

2

algorithm.adv_estimator

强化学习策略算法

grpo

data.train_batch_size

单次训练迭代的采样批次大小

16

data.max_prompt_length

最大提示词长度

2048

data.max_response_length

最大回复长度

4096

actor_rollout_ref.rollout.tensor_model_parallel_size

vllm推理时模型切分

2

actor_rollout_ref.nccl_timeout

通信超时时间,根据访存需求酌情设置

7200

actor_rollout_ref.rollout.n

重要性采样次数

2

trainer.save_freq

检查点保存频率,按训练步数计

50

trainer.test_freq

验证频率,按训练步数计

50

trainer.total_epochs

训练轮数,模型将整个训练集完整遍历一遍即为一轮

1

trainer.total_training_steps

训练步数,模型完成一次前向+反向传播+参数更新即为一步

100

更多参数解释请参考VeRL官方文档

训练脚本(Qwen3-8B)

需要准备2个训练脚本文件run_train_8b.sh和run_qwen3-8b_npu_ma.sh。

  • run_train_8b.sh是训练作业启动脚本,其中包括数据预处理、启动训练任务输出训练日志等功能。run_train_8b.sh脚本启动时,会调用数据预处理脚本gsm8k.py和VeRL训练脚本run_qwen3-8b_npu_ma.sh
    run_train_8b.sh脚本内容具体如下:
    #!/bin/bash
    set -e
    export HCCL_IF_BASE_PORT=61000
    export HCCL_NPU_SOCKET_PORT_RANGE="61000-61050"
    export HCCL_EXEC_TIMEOUT=60000
    export HCCL_CONNECT_TIMEOUT=7200
    unset https_proxy http_proxy proxy ASCEND_RT_VISIBLE_DEVICES
    source /usr/local/Ascend/ascend-toolkit/set_env.sh
    source /usr/local/Ascend/nnal/atb/set_env.sh
    export NNODES=${VC_WORKER_NUM:-1}
    export NGPUS_PER_NODE=${MA_NUM_GPUS:-8}
    export MASTER_ADDR="${VC_WORKER_HOSTS%%,*}"
    export DOMAIN_IP=$(python -c "import socket; print(socket.gethostbyname('${MASTER_ADDR}'))")
    export MASTER_NODE_IP="${DOMAIN_IP}"
    export MASTER_NODE_PORT="6699"
    export USE_OPTIMIZED_MODEL=0
    MAX_WAIT_SECONDS=900
    CHECK_INTERVAL=3
    get_current_ip() {
        local ip
        ip=$(ip -4 addr | grep -v '127.0.0.1' | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -n1 2>/dev/null)
        if [[ -z "$ip" ]]; then
            ip=$(ip -4 addr | grep -v '127.0.0.1' | awk '/inet/ {gsub(/\/.*/,""); print $2}' | head -n1 2>/dev/null)
        fi
        echo "${ip:-}"
    }
    CURRENT_IP=$(get_current_ip)
    if [[ -z "$CURRENT_IP" ]]; then
        echo "无法提取当前节点IP"
        exit 1
    fi
    echo "current ip:$CURRENT_IP,master ip:$MASTER_NODE_IP"
    if [ "$CURRENT_IP" = "$MASTER_NODE_IP" ]; then
        echo "start Ray master node..."
        ray stop --force
        ray start --head --port="$MASTER_NODE_PORT"
        elapsed_seconds=0
        echo "wait $NNODES nodes..."
        while true; do
            current_node_num=$(ray status | grep -c "node_" || echo 0)
            if ! [[ "$current_node_num" =~ ^[0-9]+$ ]]; then
                current_node_num=0
            fi
            echo "current_node_num:$current_node_num"
            if [ "$current_node_num" -ge "$NNODES" ]; then
                echo "all $NNODES workers node is ready."
                break
            fi
            if [ "$elapsed_seconds" -ge "$MAX_WAIT_SECONDS" ]; then
                echo "timeout($MAX_WAIT_SECONDS s),current_node_num:$current_node_num"
                ray stop --force
                exit 1
            fi
            sleep "$CHECK_INTERVAL"
            elapsed_seconds=$((elapsed_seconds + CHECK_INTERVAL))
        done
        echo "start job......"
        RAY_DATA_HOME=${RAY_DATA_HOME:-"/home/ma-user/work/verl-a2"}  # /home/ma-user/work为ModelArts创建训练作业时设置的本地代码目录,用户可以自定义,但是脚本中的路径和创建训练作业时设置的路径必须保持一致。/verl-a2为存放代码、数据集、模型权重的OBS文件夹名称,用户可以自定义。
        OUTPUT_DIR=${OUTPUT_DIR:-"/output_dir/verl-a2"}               # /output_dir为创建训练作业挂载OBS(/verl-a2)时设置的云上挂载路径,用于保存预处理后的数据、checkpoint、训练日志,用户可以自定义
        cd ${RAY_DATA_HOME}
        if [ ! -d "$OUTPUT_DIR/dataset/gsm8k/precessed" ]; then
            python3 gsm8k.py --local_dir "$OUTPUT_DIR/dataset/gsm8k/precessed"
        fi
        cp ${RAY_DATA_HOME}/run_qwen3-8b_npu_ma.sh /home/ma-user/verl/examples/grpo_trainer/run_qwen3-8b_npu_ma.sh
        cd /home/ma-user/verl
        bash examples/grpo_trainer/run_qwen3-8b_npu_ma.sh
    else
        echo "start workers,connect to master:$MASTER_NODE_IP:$MASTER_NODE_PORT"
        ray start \
            --address="${MASTER_NODE_IP}:${MASTER_NODE_PORT}"
    fi
  • run_qwen3-8b_npu_ma.sh脚本是用于启动VeRL强化学习训练任务,需要修改的路径如路径解释
    run_qwen3-8b_npu_ma.sh脚本具体内容如下:
    set -x
    project_name='GRPO-Qwen3'
    exp_name='GRPO-Qwen3-8B-npu'
    gen_tp=2
    RAY_DATA_HOME=${RAY_DATA_HOME:-"/home/ma-user/work/verl-a2"}                     # /home/ma-user/work为ModelArts创建训练作业时设置的本地代码目录,用户可以自定义,但是脚本中的路径和创建训练作业时设置的路径必须保持一致。/verl-a2为存放代码、数据集、模型权重的OBS文件夹名称,用户可以自定义名称。
    OUTPUT_DIR=${OUTPUT_DIR:-"/output_dir/verl-a2"}                                  # /output_dir为创建训练作业挂载OBS(/verl-a2)时设置的云上挂载路径,用于保存预处理后的数据、checkpoint、训练日志,用户可以自定义名称。
    MODEL_PATH=${MODEL_PATH:-"${RAY_DATA_HOME}/models/Qwen3-8B"}                     # .../models/Qwen3-8B为OBS中存放模型的文件夹,用户可以自定义名称。
    CKPTS_DIR=${CKPTS_DIR:-"${OUTPUT_DIR}/ckpts/${project_name}/${exp_name}"}        # .../ckpts为OBS中存放训练输出的文件夹,用户可以自定义名称。
    TRAIN_FILE=${TRAIN_FILE:-"${OUTPUT_DIR}/dataset/gsm8k/precessed/train.parquet"}  # .../dataset/gsm8k为OBS中存放训练数据gsm8k的文件夹,用户可以自定义名称。/precessed/train.parquet,为预处理后的训练数据,用户无需修改。
    TEST_FILE=${TEST_FILE:-"${OUTPUT_DIR}/dataset/gsm8k/precessed/test.parquet"}     # .../dataset/gsm8k为OBS中存放训练数据gsm8k的文件夹,用户可以自定义名称。/precessed/test.parquet,为预处理后的测试数据,用户无需修改。
    python3 -m verl.trainer.main_ppo \
        algorithm.adv_estimator=grpo \
        data.train_files="${TRAIN_FILE}" \
        data.val_files="${TEST_FILE}" \
        data.train_batch_size=16 \
        data.max_prompt_length=2048 \
        data.max_response_length=4096 \
        data.filter_overlong_prompts=True \
        data.truncation='error' \
        actor_rollout_ref.model.path=${MODEL_PATH} \
        actor_rollout_ref.actor.optim.lr=1e-6 \
        actor_rollout_ref.model.use_remove_padding=True \
        actor_rollout_ref.actor.ppo_mini_batch_size=8 \
        actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=1 \
        actor_rollout_ref.actor.use_kl_loss=True \
        actor_rollout_ref.actor.kl_loss_coef=0.001 \
        actor_rollout_ref.actor.kl_loss_type=low_var_kl \
        actor_rollout_ref.actor.entropy_coeff=0 \
        actor_rollout_ref.actor.use_torch_compile=False \
        actor_rollout_ref.nccl_timeout=7200 \
        actor_rollout_ref.ref.use_torch_compile=False \
        actor_rollout_ref.model.enable_gradient_checkpointing=True \
        actor_rollout_ref.actor.fsdp_config.param_offload=True \
        actor_rollout_ref.actor.fsdp_config.optimizer_offload=True \
        actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu=1 \
        actor_rollout_ref.rollout.tensor_model_parallel_size=${gen_tp} \
        actor_rollout_ref.rollout.name=vllm \
        actor_rollout_ref.rollout.gpu_memory_utilization=0.6 \
        actor_rollout_ref.rollout.n=2 \
        actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu=1 \
        actor_rollout_ref.ref.fsdp_config.param_offload=True \
        algorithm.use_kl_in_reward=False \
        trainer.critic_warmup=0 \
        trainer.logger=console \
        trainer.project_name="${project_name}" \
        trainer.experiment_name="${exp_name}" \
        trainer.n_gpus_per_node=${NGPUS_PER_NODE} \
        trainer.nnodes=${NNODES} \
        trainer.default_local_dir=${CKPTS_DIR} \
        trainer.resume_mode=auto \
        actor_rollout_ref.actor.fsdp_config.forward_prefetch=True \
        actor_rollout_ref.ref.fsdp_config.forward_prefetch=True \
        ++actor_rollout_ref.actor.entropy_from_logits_with_chunking=True \
        ++actor_rollout_ref.ref.entropy_from_logits_with_chunking=True \
        trainer.val_before_train=True \
        trainer.save_freq=50 \
        trainer.test_freq=50 \
        trainer.total_epochs=1 \
        trainer.total_training_steps=100 2>&1 | tee ${OUTPUT_DIR}/run_qwen3-8b_npu_ma.log
    
    sleep 20s
    if tail -n 100 ${OUTPUT_DIR}/run_qwen3-8b_npu_ma.log | grep -q 'Final validation metrics'; then
        exit 0
    else
        exit 1
    fi

训练脚本(Qwen2.5-VL-32B-Instruct

需要准备2个训练脚本文件run_train_32b.sh和run_qwen3-8b_npu_ma.sh。
  • run_train_32b.sh是训练作业启动脚本,其中包括数据预处理、启动训练任务输出训练日志等功能。run_train_32b.sh脚本启动时,会调用数据预处理脚本geometry3k.py和VeRL训练脚本run_qwen2_5_vl_32b_npu_ma.sh。
    run_train_32b.sh脚本内容具体如下:
    #!/bin/bash
    set -e
    export HCCL_IF_BASE_PORT=61000
    export HCCL_NPU_SOCKET_PORT_RANGE="61000-61050"
    export HCCL_EXEC_TIMEOUT=60000
    export HCCL_CONNECT_TIMEOUT=7200
    unset https_proxy http_proxy proxy ASCEND_RT_VISIBLE_DEVICES
    source /usr/local/Ascend/ascend-toolkit/set_env.sh
    source /usr/local/Ascend/nnal/atb/set_env.sh
    # 按照实际情况修改
    export NNODES=${VC_WORKER_NUM:-2}
    export NGPUS_PER_NODE=${MA_NUM_GPUS:-8}
    export MASTER_ADDR="${VC_WORKER_HOSTS%%,*}"
    export DOMAIN_IP=$(python -c "import socket; print(socket.gethostbyname('${MASTER_ADDR}'))")
    export MASTER_NODE_IP="${DOMAIN_IP}"
    export MASTER_NODE_PORT="6699"
    export USE_OPTIMIZED_MODEL=0
    MAX_WAIT_SECONDS=900
    CHECK_INTERVAL=3
    get_current_ip() {
        local ip
        ip=$(ip -4 addr | grep -v '127.0.0.1' | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -n1 2>/dev/null)
        if [[ -z "$ip" ]]; then
            ip=$(ip -4 addr | grep -v '127.0.0.1' | awk '/inet/ {gsub(/\/.*/,""); print $2}' | head -n1 2>/dev/null)
        fi
        echo "${ip:-}"
    }
    CURRENT_IP=$(get_current_ip)
    if [[ -z "$CURRENT_IP" ]]; then
        echo "无法提取当前节点IP"
        exit 1
    fi
    echo "current ip:$CURRENT_IP,master ip:$MASTER_NODE_IP"
    if [ "$CURRENT_IP" = "$MASTER_NODE_IP" ]; then
        echo "start Ray master node..."
        ray stop --force
        ray start --head --port="$MASTER_NODE_PORT"
        elapsed_seconds=0
        echo "wait $NNODES nodes..."
        while true; do
            current_node_num=$(ray status | grep -c "node_" || echo 0)
            if ! [[ "$current_node_num" =~ ^[0-9]+$ ]]; then
                current_node_num=0
            fi
            echo "current_node_num:$current_node_num"
            if [ "$current_node_num" -ge "$NNODES" ]; then
                echo "all $NNODES workers node is ready."
                break
            fi
            if [ "$elapsed_seconds" -ge "$MAX_WAIT_SECONDS" ]; then
                echo "timeout($MAX_WAIT_SECONDS s),current_node_num:$current_node_num"
                ray stop --force
                exit 1
            fi
            sleep "$CHECK_INTERVAL"
            elapsed_seconds=$((elapsed_seconds + CHECK_INTERVAL))
        done
        echo "start job......"
        RAY_DATA_HOME=${RAY_DATA_HOME:-"/home/ma-user/work/verl-a2"}  # /home/ma-user/work为ModelArts创建训练作业时设置的本地代码目录,用户可以自定义,但是脚本中的路径和创建训练作业时设置的路径必须保持一致。/verl-a2为存放代码、数据集、模型权重的OBS文件夹名称,用户可以自定义。
        OUTPUT_DIR=${OUTPUT_DIR:-"/output_dir/verl-a2"}               # /output_dir为创建训练作业挂载OBS(/verl-a2)时设置的云上挂载路径,用于保存预处理后的数据、checkpoint、训练日志,用户可以自定义
        cd ${RAY_DATA_HOME}
        if [ ! -d "$OUTPUT_DIR/dataset/geometry3k/precessed" ]; then
            python3 geometry3k.py --local_dir "$OUTPUT_DIR/dataset/geometry3k/precessed"
        fi
        cp ${RAY_DATA_HOME}/run_qwen2_5_vl_32b_npu_ma.sh /home/ma-user/verl/examples/grpo_trainer/run_qwen2_5_vl_32b_npu_ma.sh
        cd /home/ma-user/verl
        bash examples/grpo_trainer/run_qwen2_5_vl_32b_npu_ma.sh
    else
        echo "start workers,connect to master:$MASTER_NODE_IP:$MASTER_NODE_PORT"
        ray start \
            --address="${MASTER_NODE_IP}:${MASTER_NODE_PORT}"
    fi
  • run_qwen2_5_vl_32b_npu_ma.sh脚本是用于启动VeRL强化学习训练任务,需要修改的路径如路径解释

    runqwen2_5_vl_32b_npu_ma.sh脚本具体内容如下:

    set -x
    project_name='GRPO-Qwen2_5'
    exp_name='GRPO-Qwen2_5-VL-32B-npu'
    RAY_DATA_HOME=${RAY_DATA_HOME:-"/home/ma-user/work/verl-a2"}                          # /home/ma-user/work为ModelArts创建训练作业时设置的本地代码目录,用户可以自定义,但是脚本中的路径和创建训练作业时设置的路径必须保持一致。/verl-a2为存放代码、数据集、模型权重的OBS文件夹名称,用户可以自定义名称。
    OUTPUT_DIR=${OUTPUT_DIR:-"/output_dir/verl-a2"}                                      # /output_dir为创建训练作业挂载OBS(/verl-a2)时设置的云上挂载路径,用于保存预处理后的数据、checkpoint、训练日志,用户可以自定义名称。
    MODEL_PATH=${MODEL_PATH:-"${RAY_DATA_HOME}/models/Qwen2.5-VL-32B-Instruct"}           # .../models/Qwen2.5-VL-32B-Instruct为OBS中存放模型的文件夹,用户可以自定义名称。
    CKPTS_DIR=${CKPTS_DIR:-"${OUTPUT_DIR}/ckpts/${project_name}/${exp_name}"}             # .../ckpts为OBS中存放训练输出的文件夹,用户可以自定义名称。
    TRAIN_FILE=${TRAIN_FILE:-"${OUTPUT_DIR}/dataset/geometry3k/precessed/train.parquet"}  # .../dataset/geometry3k为OBS中存放训练数据geometry3k的文件夹,用户可以自定义名称。/precessed/train.parquet,为预处理后的训练数据,用户无需修改。
    TEST_FILE=${TEST_FILE:-"${OUTPUT_DIR}/dataset/geometry3k/precessed/test.parquet"}     # .../dataset/geometry3k为OBS中存放训练数据geometry3k的文件夹,用户可以自定义名称。/precessed/test.parquet,为预处理后的测试数据,用户无需修改。
    ENGINE=${1:-vllm}
    # Some models are optimized by vllm ascend. While in some case, e.g. rlhf training, 
    # the optimized model may not be suitable. In this case, set this value to 0 to disable the optimized model.
    export USE_OPTIMIZED_MODEL=0
    python3 -m verl.trainer.main_ppo \
        algorithm.adv_estimator=grpo \
        data.train_files="${TRAIN_FILE}" \
        data.val_files="${TEST_FILE}" \
        data.train_batch_size=16 \
        data.max_prompt_length=2048 \
        data.max_response_length=4096 \
        data.filter_overlong_prompts=True \
        data.truncation='error' \
        data.image_key=images \
        actor_rollout_ref.model.path="${MODEL_PATH}" \
        actor_rollout_ref.actor.optim.lr=1e-6 \
        actor_rollout_ref.model.use_remove_padding=True \
        actor_rollout_ref.actor.ppo_mini_batch_size=16 \
        actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu=1 \
        actor_rollout_ref.actor.use_kl_loss=True \
        actor_rollout_ref.actor.kl_loss_coef=0.01 \
        actor_rollout_ref.actor.kl_loss_type=low_var_kl \
        actor_rollout_ref.actor.entropy_coeff=0 \
        actor_rollout_ref.actor.use_torch_compile=False \
        actor_rollout_ref.nccl_timeout=7200 \
        actor_rollout_ref.model.enable_gradient_checkpointing=True \
        actor_rollout_ref.actor.fsdp_config.param_offload=True \
        actor_rollout_ref.actor.fsdp_config.optimizer_offload=True \
        actor_rollout_ref.rollout.log_prob_micro_batch_size_per_gpu=1 \
        actor_rollout_ref.rollout.tensor_model_parallel_size=8 \
        actor_rollout_ref.rollout.name=$ENGINE \
        +actor_rollout_ref.rollout.engine_kwargs.vllm.disable_mm_preprocessor_cache=True \
        actor_rollout_ref.rollout.gpu_memory_utilization=0.6 \
        actor_rollout_ref.rollout.enable_chunked_prefill=False \
        actor_rollout_ref.rollout.enforce_eager=True \
        actor_rollout_ref.rollout.free_cache_engine=True \
        actor_rollout_ref.rollout.n=2 \
        actor_rollout_ref.ref.log_prob_micro_batch_size_per_gpu=1 \
        actor_rollout_ref.ref.fsdp_config.param_offload=True \
        algorithm.use_kl_in_reward=False \
        trainer.critic_warmup=0 \
        trainer.logger=console \
        trainer.project_name="${project_name}" \
        trainer.experiment_name="${exp_name}" \
        trainer.n_gpus_per_node=${NGPUS_PER_NODE} \
        trainer.nnodes=${NNODES} \
        trainer.default_local_dir=${CKPTS_DIR} \
        trainer.save_freq=50 \
        trainer.test_freq=50 \
        trainer.total_epochs=1 \
        trainer.total_training_steps=100 2>&1 | tee ${OUTPUT_DIR}/run_qwen2_5_vl_32b_npu_ma.log
    
    sleep 20s
    if tail -n 100 ${OUTPUT_DIR}/run_qwen2_5_vl_32b_npu_ma.log | grep -q 'Final validation metrics'; then
        exit 0
    else
        exit 1
    fi

相关文档