准备训练脚本
在MindSpeed-LLM中不同类型的模型的权重转化、数据处理、微调任务脚本有所差异,具体请参考MindSpeed-LLM/docs/quick_start.md · Ascend/MindSpeed-LLM - AtomGit | GitCode 下每种模型具体的脚本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。脚本示例参考训练作业启动脚本。
脚本中公共参数解释
- ${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脚本内容如下,脚本中需要修改的参数请参见脚本中的注释。
# 修改 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脚本内容如下,脚本中需要修改的参数请参见脚本中的注释。
# 请按照您的真实环境修改 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 微调任务执行脚本
#!/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 #异常状态校验,注意日志名称一致 训练作业启动脚本
#!/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桶内的相应位置。