
# 准备训练脚本
在MindSpeed-LLM中**不同类型的模型** 的权重转化、数据处理、微调任务脚本**有所差异** ，具体请参考[MindSpeed-LLM/docs/quick_start.md · Ascend/MindSpeed-LLM - AtomGit \| GitCode](https://gitcode.com/Ascend/MindSpeed-LLM/tree/2.2.0/examples/mcore) 下每种模型具体的脚本example。
以Qwen3系列模型的训练任务为例，分别准备对应模型的训练脚本，并上传至OBS桶中。具体脚本如下：
- ckpt_convert_qwen3_hf2mcore.sh：权重转化脚本，由hf格式转化为mg格式的权重，脚本示例参考[权重转化脚本（hf2mg）]。
- data_convert_qwen3_instruction.sh：数据处理脚本，脚本示例参考[数据处理脚本]。
- tune_qwen3_32b_4K_full_ptd.sh：微调任务执行脚本，脚本示例参考[微调任务执行脚本]
- ckpt_convert_qwen3_mcore2hf.sh：权重转换脚本，由mg格式转化为hf格式的权重，脚本示例参考[权重转换脚本（mg2hf）]。
- run_distributed_task.sh：训练作业启动脚本，执行时会调用上述所有脚本。多机训练场景下，通过run_distributed_task.sh脚本使得每个节点之间可以互相获取节点IP地址并设置MASTER_PORT。脚本示例参考[训练作业启动脚本]。
#### 脚本中公共参数解释
sh脚本中参数详解如下：
- ${output_dir} ：训练或者格式转化的输出路径。配置obs路径到输出的环境变量output_dir，例如/home/ma-user/modelarts/outputs/output_dir_0。
- ${model_path}：训练模型的路径，根据此参数配置TOKENIZER_PATH参数。
- ${dataset_path} 待处理的数据集的路径。
 
如直接复制本脚本，请注意不同操作系统下换行符的差异。可以使用dos2unix xxx.sh命令将脚本内换行符转换为linux系统的换行符。
以**Qwen3-32B** 为例，准备如下脚本，其中脚本中的注释仅为说明参数含义，实际使用时请**去除** ，**在每行的\\后不要留有空格**。
 #### 权重转化脚本（hf2mg）
**ckpt_convert_qwen3_hf2mcore.sh**脚本内容如下，脚本中需要修改的参数请参见脚本中的注释。
其中的环境变量统一在**run_distributed_task.sh训练任务启动总脚本** 进行设置。
```
# 修改 ascend-toolkit 路径
export CUDA_DEVICE_MAX_CONNECTIONS=1
source /usr/local/Ascend/ascend-toolkit/set_env.sh
python /home/ma-user/MA_Turbo/src/open_source/MindSpeed-LLM/convert_ckpt.py \ #镜像预装的MindSpeed-LLM的convert代码位置,无需修改
    --use-mcore-models \
    --model-type GPT \
    --load-model-type hf \
    --save-model-type mg \
    --target-tensor-parallel-size 8 \
    --target-pipeline-parallel-size 2 \
    --spec mindspeed_llm.tasks.models.spec.qwen3_spec layer_spec \
    --load-dir "$TOKENIZER_PATH" \                                   #模型的具体位置，在run_distributed_task.sh中统一设置
    --save-dir "$CKPT_LOAD_DIR" \                   #保存转化完成后的权重位置，在run_distributed_task.sh中统一设置
    --tokenizer-model "$TOKENIZER_PATH/tokenizer.json"  \             #tokenizer.json位置，变量部分在run_distributed_task.sh中统一设置
    --params-dtype bf16 \
    --model-type-hf qwen3
```
 #### 数据处理脚本
**data_convert_qwen3_instruction.sh**脚本内容如下，脚本中需要修改的参数请参见脚本中的注释。
其中的环境变量统一在**run_distributed_task.sh训练任务启动总脚本** 进行设置。
```
# 请按照您的真实环境修改 set_env.sh 路径
source /usr/local/Ascend/ascend-toolkit/set_env.sh
mkdir -p "${output_dir}/finetune_dataset" #保持不变
python /home/ma-user/MA_Turbo/src/open_source/MindSpeed-LLM/preprocess_data.py \ #镜像预装的MindSpeed-LLM的convert代码位置，无需修改
    --input ${dataset_path}/alpaca_gpt4_data.json \                              #待处理数据集的路径
    --tokenizer-name-or-path "$TOKENIZER_PATH" \                          #用于处理数据集的模型路径，在run_distributed_task.sh中统一设置
    --output-prefix "$DATA_PATH" \                      #输出的数据数据集路径+格式,在run_distributed_task.sh中统一设置
    --handler-name AlpacaStyleInstructionHandler \    #如更换格式需要同步修改
    --tokenizer-type PretrainedFromHF \
    --workers 4 \
    --log-interval 1000 \
    --enable-thinking true \
    --prompt-type qwen3
```
 #### 微调任务执行脚本
**tune_qwen3_32b_4K_full_ptd.sh** 脚本内容如下，脚本中需要修改的参数请参见脚本中的注释。
```
#!/bin/bash
export HCCL_CONNECT_TIMEOUT=7200   # 最大超时时间，可以根据实际情况调整
export HCCL_EXEC_TIMEOUT=7200
export CUDA_DEVICE_MAX_CONNECTIONS=1
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
# =========================================================
# 1. 接收外部环境变量 (如果外部未传递，则使用冒号后的默认值)
此处环境变量建议在run_distributed_task.sh 中设置覆盖
# =========================================================
NPUS_PER_NODE=${NPUS_PER_NODE:-8}
MASTER_ADDR=${MASTER_ADDR:-"localhost"}
MASTER_PORT=${MASTER_PORT:-6000}
NNODES=${NNODES:-1}
NODE_RANK=${NODE_RANK:-0}
WORLD_SIZE=$(($NPUS_PER_NODE*$NNODES))
# 路径配置也改为接收环境变量，此处环境变量建议在run_distributed_task.sh 中设置覆盖
CKPT_LOAD_DIR=${CKPT_LOAD_DIR:-"${output_dir}/model_weights/qwen3_mcore/"}    #默认参考值，run_distributed_task.sh中覆盖
CKPT_SAVE_DIR=${CKPT_SAVE_DIR:-"${output_dir}/ckpt/qwen3-32b/"}               #默认参考值，run_distributed_task.sh中覆盖
DATA_PATH=${DATA_PATH:-"${output_dir}/finetune_dataset/alpaca"}               #默认参考值，run_distributed_task.sh中覆盖
TOKENIZER_PATH=${TOKENIZER_PATH:-"${model_path}/Qwen3-32B/"}                  #默认参考值，run_distributed_task.sh中覆盖
# =========================================================
# 2. 训练参数 
# =========================================================
TP=8     #与ckpt_convert_qwen3_hf2mcore.sh中保持一致
PP=2     #与ckpt_convert_qwen3_hf2mcore.sh中保持一致
MBS=1
GBS=32
TRAIN_ITERS=50
SEQ_LENGTH=4096
DISTRIBUTED_ARGS="
    --nproc_per_node $NPUS_PER_NODE \
    --nnodes $NNODES \
    --node_rank $NODE_RANK \
    --master_addr $MASTER_ADDR \
    --master_port $MASTER_PORT
"
OPTIMIZE_ARGS="
    --use-flash-attn \
    --use-fused-rotary-pos-emb \
    --use-rotary-position-embeddings \
    --use-fused-swiglu \
    --use-fused-rmsnorm \
    --no-masked-softmax-fusion \
    --use-distributed-optimizer
"
TRAIN_ARGS="
    --micro-batch-size 1 \
    --global-batch-size 16 \
    --lr 1.25e-6 \
    --lr-decay-style cosine \
    --min-lr 1.25e-7 \
    --weight-decay 1e-1 \
    --lr-warmup-fraction 0.01 \
    --attention-dropout 0.0 \
    --init-method-std 0.01 \
    --hidden-dropout 0.0 \
    --clip-grad 1.0 \
    --adam-beta1 0.9 \
    --adam-beta2 0.95 \
    --initial-loss-scale 4096 \
    --seed 42 \
    --bf16 \
    --train-iters ${TRAIN_ITERS} \
    --seq-length ${SEQ_LENGTH} \
    --no-shared-storage
"
MODEL_PARALLEL_ARGS="
    --tensor-model-parallel-size ${TP} \
    --pipeline-model-parallel-size ${PP}
"
GPT_ARGS="
    --use-mcore-models \
    --spec mindspeed_llm.tasks.models.spec.qwen3_spec layer_spec \
    --kv-channels 128 \
    --qk-layernorm \
    --tokenizer-name-or-path ${TOKENIZER_PATH} \
    --max-position-embeddings ${SEQ_LENGTH} \
    --num-layers 64 \
    --hidden-size 5120 \
    --ffn-hidden-size 25600 \
    --num-attention-heads 64 \
    --tokenizer-type PretrainedFromHF \
    --make-vocab-size-divisible-by 1 \
    --padded-vocab-size 151936 \
    --rotary-base 1000000 \
    --untie-embeddings-and-output-weights \
    --disable-bias-linear \
    --position-embedding-type rope \
    --normalization RMSNorm \
    --swiglu \
    --attention-softmax-in-fp32 \
    --no-gradient-accumulation-fusion \
    --group-query-attention \
    --num-query-groups 8
"
DATA_ARGS="
    --data-path $DATA_PATH \
    --split 100,0,0
"
OUTPUT_ARGS="
    --load ${CKPT_LOAD_DIR} \
    --save ${CKPT_SAVE_DIR} \
    --log-interval 1 \
    --save-interval ${TRAIN_ITERS} \
    --eval-interval ${TRAIN_ITERS} \
    --eval-iters 0 \
    --no-load-optim \
    --no-load-rng
"
TUNE_ARGS="
    --finetune \
    --stage sft \
    --is-instruction-dataset \
    --tokenizer-not-use-fast \
    --prompt-type qwen3 \
    --variable-seq-lengths
"
# 运行 torchrun，注意日志文件名加入了 NODE_RANK 以防冲突
mkdir -p "$CHECK_LOG_DIR" #校验日志，无需修改
set -o pipefail             #开启pipefail
torchrun $DISTRIBUTED_ARGS /home/ma-user/MA_Turbo/src/open_source/MindSpeed-LLM/posttrain_gpt.py \  #镜像预装的MindSpeed-LLM的启动代码位置，无需修改
    $GPT_ARGS \
    $DATA_ARGS \
    $OUTPUT_ARGS \
    $OPTIMIZE_ARGS \
    $TRAIN_ARGS \
    $TUNE_ARGS \
    $MODEL_PARALLEL_ARGS \
    --distributed-backend nccl \
    2>&1 | tee "$CHECK_LOG_PATH" && \
    echo "Training completes" >> "$CHECK_LOG_PATH"  #补充完成标志到日志中，环境变量在run_distributed_task.sh中统一设置，无需修改
sleep 10s
tail -n 500 "$CHECK_LOG_PATH" | grep -q -E "Training completes" || exit 1 #异常状态校验
```
 #### 权重转换脚本（mg2hf）
ckpt_convert_qwen3_mcore2hf.sh脚本内容如下，脚本中需要修改的参数请参见脚本中的注释。
其中的环境变量统一在**run_distributed_task.sh训练任务启动总脚本**进行设置。
转换成功后的权重保存目录下**仅包含模型权重文件** ，不会生成config.json模型配置文件和tokenizer.model、vocab.json等词表文件。**run_distributed_task.sh**脚本中会帮助将config.json模型配置文件和tokenizer.model、vocab.json等词表文件复制到转化后的模型文件夹中。
hf2mg格式转换的时候使用convert_ckpt.py，在此处也是用convert_ckpt.py来进行mg2hf。
具体参数说明如下注释。**使用时请去除注释和'\\'后所有的空格。**
```
# 修改 ascend-toolkit 路径
source /usr/local/Ascend/ascend-toolkit/set_env.sh
export CUDA_DEVICE_MAX_CONNECTIONS=1
mkdir -p "$CHECK_LOG_DIR"
set -o pipefail
python /home/ma-user/MA_Turbo/src/open_source/MindSpeed-LLM/convert_ckpt.py \ #镜像预装的MindSpeed-LLM的convert代码位置,无需修改。
    --use-mcore-models \
    --model-type GPT \
    --load-model-type mg \
    --save-model-type hf \
    --target-tensor-parallel-size 1 \#均为1，无需修改
    --target-pipeline-parallel-size 1 \#均为1，无需修改
    --spec mindspeed_llm.tasks.models.spec.qwen3_spec layer_spec \
    --load-dir "$CKPT_SAVE_DIR" \#训练完成时的ckpt保存位置，即为run_distributed_task.sh中的CKPT_SAVE_DIR
    --save-dir "$TOKENIZER_PATH" \#原始的hf格式的模型路径，即为run_distributed_task.sh中的TOKENIZER_PATH
    --params-dtype bf16 \
    --model-type-hf qwen3 \
    2>&1 | tee "$CHECK_LOG_PATH" && \
    echo "Convert mg2hf completes" >> "$CHECK_LOG_PATH"  #补充完成标志到日志中
sleep 10s
tail -n 500 "$CHECK_LOG_PATH" | grep -q -E "Convert mg2hf completes"  || exit 1 #异常状态校验，注意日志名称一致
```
 #### 训练作业启动脚本
**run_distributed_task.sh** 脚本内容如下，脚本中需要修改的参数请参见脚本中的注释。
```
#!/bin/bash
set -e
# =========================
# 1. 基础环境与网络配置
# =========================
export HCCL_CONNECT_TIMEOUT=7200   # 最大超时时间，可以根据实际情况调整
export HCCL_EXEC_TIMEOUT=7200
export HCCL_IF_BASE_PORT=61000
export HCCL_NPU_SOCKET_PORT_RANGE="61000-61050"
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
# 自动获取本机 IP
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)
# 解析 ModelArts 环境变量,自动获取NPU数量和Nodes数量
export NNODES=${VC_WORKER_NUM:-1}
export NPUS_PER_NODE=${MA_NUM_GPUS:-8}
export MASTER_PORT=6000
# 自动计算 NODE_RANK 和 MASTER_ADDR
if [ "$NNODES" -gt 1 ]; then
    MASTER_ADDR_RAW="${VC_WORKER_HOSTS%%,*}"
    export MASTER_ADDR=$(python3 -c "import socket; print(socket.gethostbyname('${MASTER_ADDR_RAW}'))")
    export NODE_RANK=$(python3 -c "import os, socket; hosts = os.environ.get('VC_WORKER_HOSTS', '').split(','); cur='${CURRENT_IP}'; print(next((i for i, h in enumerate(hosts) if socket.gethostbyname(h.strip()) == cur), 0))")
    echo "[INFO] Multi-Node: Master=$MASTER_ADDR, Rank=$NODE_RANK, Total=$NNODES"
else
    export MASTER_ADDR="localhost"
    export NODE_RANK=0
    echo "[INFO] Single-Node Mode"
fi
# =========================
# 2. 定义全局路径 (供子脚本使用，会覆盖微调脚本中的相应参数)
# =========================
export CKPT_LOAD_DIR="${output_dir}/model_weights/qwen3_mcore/"  #之前转换的mg格式路径
export CKPT_SAVE_DIR="${output_dir}/ckpt/qwen3-32b/"              #ckpt 保存路径
export DATA_PATH="${output_dir}/finetune_dataset/alpaca"         #处理后的微调数据集路径+格式（此处为alpaca），此处结尾无"/"
export TOKENIZER_PATH="${model_path}/Qwen3-32B/"                 #模型路径
export TRAINED_HF_MODELS="${output_dir}/trained_hf_models/"      #训练完成后mg格式转化为hf格式的模型路径
export CHECK_LOG_DIR="/home/ma-user/logs"                        #容器内校验log所在文件夹，容器内路径非挂载路径，无需修改，此处结尾无"/"
export CHECK_LOG_FILE="tune_qwen3_32b_full_ptd.log"              #容器内校验log名称，可以根据模型和任务自行设置名称
export CHECK_LOG_PATH="${CHECK_LOG_DIR}/${CHECK_LOG_FILE}"       #容器内校验log总路径，容器内文件，无需修改
# 确保目录存在
mkdir -p "$CKPT_SAVE_DIR"
mkdir -p "$CHECK_LOG_DIR" #校验日志
# =========================
# 3. 权重转换（hf2mg）与数据处理
# =========================
echo " Starting Checkpoint & Data Conversion..."
# 执行权重转换
echo " Running ckpt_convert_qwen3_hf2mcore.sh..."
bash ckpt_convert_qwen3_hf2mcore.sh                   #本模型的权重转换脚本
# 执行数据处理
echo " Running data_convert_qwen3_instruction.sh..."
bash data_convert_qwen3_instruction.sh                #本模型的数据处理脚本
echo " Preparation Done. Creating flag file."
# =========================
# 4. 启动分布式训练 (所有节点都执行)
# =========================
# 检查是否存在 r+ 模式（OBS挂载需保持本内容）
FILE_PATH="/home/ma-user/MA_Turbo/src/open_source/MindSpeed-LLM/mindspeed_llm/tasks/preprocess/decoder_packed_mtf_dataset.py"
if grep -q "mmap_mode='r+'" "$FILE_PATH"; then
    # 仅针对包含 mmap_mode='r+' 的行进行精准替换
    sed -i.bak "s/\(mmap_mode=['\"]\)\(r+\)\(['\"]\)/\1r\3/g" "$FILE_PATH"
else
    echo "未检测到 mmap_mode='r+'，跳过操作。"
fi
echo " Starting Training Script on Rank $NODE_RANK..."
# 执行修改后的训练脚本
bash tune_qwen3_32b_4K_full_ptd.sh                #本模型的微调训练脚本
# =========================
# 5. 权重转换（mg2hf）与保存模型
# =========================
# 仅在主节点执行权重转换（mg2hf）
if [ "$NODE_RANK" -eq 0 ]; then
    echo "[Master] Master. Doing Convert ckpt from mcore to hf models"
    mkdir -p "$TRAINED_HF_MODELS"
    find "$TOKENIZER_PATH" -type f ! -name "*.safetensors" -exec cp {} "$TRAINED_HF_MODELS" \;  #复制原始模型的config.json模型配置文件和tokenizer.model、vocab.json等词表文件
    bash ckpt_convert_qwen3_mcore2hf.sh                                                         #执行mg2hf权重转化脚本
    mv "$TOKENIZER_PATH/mg2hf/"* "$TRAINED_HF_MODELS/"                                          #将转化后权重移动到OBS中的相应位置
    echo "All steps completes" >> "$CHECK_LOG_PATH"
else
    echo "[Worker $NODE_RANK] Task finished. Waiting for Master to complete conversion..."
    echo "All steps completes" >> "$CHECK_LOG_PATH"
fi
sleep 10s
tail -n 500 "$CHECK_LOG_PATH" | grep -q -E "All steps completes"  && exit 0 || exit 1 #异常状态校验反馈给平台，环境变量无需修改
```
将以上脚本放在自己的OBS桶内的相应位置。
