准备训练脚本
在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桶内的相应位置。