Qwen2.5是通义千问大模型系列2024年9月发布,参数量从0.5B到72B不等的多个基础模型和指令微调模型。相比Qwen2,Qwen2.5主要带来以下提升:
本仓库包含7B参数的Qwen2.5指令微调模型,具有以下特性:
模型huggingface链接:Qwen2.5-7B-Instruct
本案例使用昇腾A3机器上,基于MindspeedLLM 框架完成Qwen2.5-7B-Instruct模型基于业务数据集的SFT以及Lora训练实践。
Qwen2.5-7B-Instruct在业务数据集上的全参微调以及Lora微调训练与评测使用到的软件以及硬件环境如下:
| 硬件名称 | 配置信息 | 备注 |
|---|---|---|
| 机器型号 | A3 超节点 | |
| 测试集群 | 8卡Pod | 单机 |
| 软件 | 版本 | 部署方式 |
|---|---|---|
| Driver | AscendHDK 25.2.0 | 宿主机 |
| Firmware | AscendHDK 25.2.0 | 宿主机 |
| Python | 3.10.18 | 容器 |
| CANN | 8.2.RC1 | 容器 |
| Torch | 2.6.0 | 容器 |
| Torch_npu | 2.6.0 | 容器 |
| MindSpeed | 2.1.0_core_r0.8.0 | 容器 |
| MindSpeed-LLM | 2.1.0 | 容器 |
| Megatron-LM | core_r0.8.0 | 容器 |
| Docker镜像OS | Ubuntu 20.04.6 | / |
MindspeedLLM 镜像已发布,使用方法请参考:昇腾训练镜像构建指导 中 MindSpeed-LLM镜像章节。
业务数据集是ShareGPT风格数据,因此采用ShareGPT风格数据集处理方法进行预处理。
业务当前提供数据集预处理脚本如下:
# 业务数据集预处理
source /usr/local/Ascend/ascend-toolkit/set_env.sh
cd /usr/local/Ascend/llm-train/MindSpeed-LLM
datapath=***
mkdir ${datapath}/train_data_10k
python ./preprocess_data.py \
--input ${datapath}/train_data_10k.jsonl \
--tokenizer-name-or-path /apps/hw/weights/Qwen2.5-7B \
--output-prefix ${datapath}/train_data_10k/train_data_10k \
--workers 4 \
--log-interval 1000 \
--tokenizer-type PretrainedFromHF \
--handler-name SharegptStyleInstructionHandler \
--prompt-type qwen \
--map-keys '{"messages":"messages", "tags":{"role_tag": "role","content_tag": "content","user_tag": "user","assistant_tag": "assistant","system_tag": "system"}}'关键参数说明:
--input(必须):描述: 输入数据文件的路径,通常是一个jsonl(每行一个JSON对象)文件。
示例:
--input /path/to/data.jsonl
--tokenizer-name-or-path(必须):描述: 用于分词器的预训练模型的名称或路径。可以是Hugging Face模型仓库的ID,也可以是本地包含tokenizer文件的目录。
示例:
--tokenizer-name-or-path Qwen/Qwen1.5-7B或者--tokenizer-name-or-path /path/to/tokenizer
--output-prefix(必须):描述: 输出文件的前缀。预处理后的数据会被保存为多个文件(例如,后缀为.bin和.idx),这个参数指定这些文件的前缀路径。
示例:
--output-prefix /path/to/output/preprocessed_data
--workers(可选,默认值可能为1):描述: 指定数据预处理时使用的进程数(多进程)。更多的进程数可以加快处理速度。
示例:
--workers 4
--log-interval(可选,默认值可能为100):描述: 控制日志打印的频率,例如每处理1000条数据打印一次进度日志。
示例:
--log-interval 1000
--tokenizer-type(必须):描述: 指定tokenizer的类型。这里使用
PretrainedFromHF,表示使用Hugging Face的预训练tokenizer。示例:
--tokenizer-type PretrainedFromHF
--handler-name(必须):描述: 指定数据处理器(handler)的类名。这里使用
SharegptStyleInstructionHandler,这是专门为类似ShareGPT格式(对话格式)的指令数据设计的处理器。该处理器会按照指令模板格式化数据。示例:
--handler-name SharegptStyleInstructionHandler
--prompt-type(必须):描述: 指定提示模板类型。不同的提示模板对应不同的格式化方式。这里指定为
qwen,表示使用Qwen模型所要求的提示格式。示例:
--prompt-type qwen
--map-keys(必须):描述: 一个JSON字符串,用于将输入数据中的字段映射到处理器所需的字段。这个参数允许我们自定义输入JSON中的键名,使得处理器能够正确获取角色、内容等信息。
格式: 一个JSON字典,其中至少包括:
"messages": 输入数据中对话列表的键名(通常是一个列表,包含多个对话回合)
"tags": 一个嵌套字典,用于指定在对话回合中每个字段的键名,例如角色(role)、内容(content)等,以及用户、助手和系统角色的标签。
示例:
--map-keys '{"messages":"messages", "tags":{"role_tag":"role", "content_tag":"content", "user_tag":"user", "assistant_tag":"assistant", "system_tag":"system"}}'说明:在这个例子中,输入数据中对话列表的键名为"messages"。在每一个对话回合中,角色字段的键名为"role",内容字段的键名为"content",而用户、助手和系统消息则分别用"user"、"assistant"和"system"来标识(这些标签用于区分消息发送者)。
注意:由于
--map-keys参数是一个JSON字符串,所以需要用单引号括起来(在shell中),并且确保内部双引号被正确转义或保留(这里使用的是JSON字符串,所以内部用双引号)。
数据预处理脚本会将数据按照相应的数据风格进行解析,并且根据prompt-type所对应的模板构建成为prompt,再转化为token后保存到.bin .idx 文件中。下图是解析后的文件示例。

从HuggingFace下载模型权重Qen2.5-7B-Instruct,下载方式参考HF镜像站,其他模型下载可参考Dense模型、MOE模型和SSM模型文档中对应模型的下载链接。下载完成效果如下:

随着大规模预训练模型的广泛应用,不同的训练框架和硬件平台之间的适配性问题逐渐显现。专有训练框架如MindSpeed-LLM通常采用定制的并行化策略(例如Tensor Parallelism、Pipeline Parallelism)以应对大规模模型训练中的内存和计算瓶颈。随着训练需求和硬件的变化,模型参数的切分策略也需进行相应的调整。然而,跨框架的权重转换往往面临格式不兼容和切分策略不同等挑战。权重转换旨在促进大规模预训练模型在不同训练框架之间的无缝迁移与评估,解决框架间权重格式不兼容及切分策略差异等问题,从而增强模型迁移的灵活性和可扩展性,支持更广泛的应用场景和业务需求。
权重转换实现了 HuggingFace 权重到 Megatron-LM 格式的转换,支持多种并行策略(如张量并行、流水并行等),确保转换后可以在 MindSpeed-LLM 框架下继续训练和推理。
当前采用TP=4,PP=2进行权重切分。权重切分具体代码如下:
# cd MinspeedLLM path
source /usr/local/Ascend/ascend-toolkit/set_env.sh
# 设置需要的权重转换参数
python convert_ckpt.py \
--use-mcore-models \
--model-type GPT \
--load-model-type hf \
--save-model-type mg \
--target-tensor-parallel-size 4 \
--target-pipeline-parallel-size 2 \
--num-layer-list 14, 14
--add-qkv-bias \
--load-dir /***/weights/Qwen2.5-7B-Instruct/ \
--save-dir /***/weights/weight_mg/new_qwen2.5_mcore_tp4_pp2/ \
--tokenizer-model /***/weights/Qwen2.5-7B-Instruct/tokenizer.json \
--model-type-hf llama2 \
--params-dtype bf16| 参数 | 说明 | 可选/必选 |
|---|---|---|
| --target-tensor-parallel-size | TP 切分数量,默认为 1 | 必选 |
| --target-pipeline-parallel-size | PP 切分数量,默认为 1 | 必选 |
| --num-layer-list | 动态PP划分,通过列表指定每个PP Stage的层数,默认为None | 可选 |
| --num-layers-per-virtual-pipeline-stage | VPP划分,指定VPP的每个Stage层数,默认为None | 可选 |
| --target-expert-model-parallel-size | 专家并行,指定专家并行卡数,默认为1 | 可选 |
| --noop-layers | 自定义空层操作,指定在模型某层增加空层,转换后层数为原huggingface模型层数+空层数,默认为None | 可选 |
| --use-mcore-models | 转换为Megatron-Mcore权重,若不指定,则默认转换为Megatron-Legacy权重 | 可选 |
| --model-type-hf | huggingface模型类别,默认为llama2,参考注意 2 | 可选 |
| --tokenizer-model | 需要指明到具体的分词器模型文件,如 tokenizer.model、tokenizer.json、qwen.tiktoken、None等,具体取决于huggingface中词表文件的格式形式 | 必选 |
| --params-dtype | 指定权重转换后的权重精度模式,默认为fp16,如果源格式文件为bf16,则需要对应设置为bf16,影响推理或评估结果 | 必选 |
注意:
VPP和动态PP划分只能二选一
目前支持的模型见 model_cfg.json中“model_mappings”下包含的模型。
关键参数说明:
qwen2.5模型这里的--model-type-hf需要设置为:llama2。参考目前支持的模型 model_cfg.json中“model_mappings”下包含的模型参数映射关系。
执行结果如下:

当前采用TP=2,PP=1进行权重切分:
# cd MinspeedLLM path
source /usr/local/Ascend/ascend-toolkit/set_env.sh
# 设置需要的权重转换参数
python convert_ckpt.py \
--use-mcore-models \
--model-type GPT \
--load-model-type hf \
--save-model-type mg \
--target-tensor-parallel-size 2 \
--target-pipeline-parallel-size 1 \
--add-qkv-bias \
--load-dir /***/weights/Qwen2.5-7B-Instruct/ \
--save-dir /***/weights/weight_mg/qwen2.5_7b_instruct_mcore_tp2_pp1/ \
--tokenizer-model /***/weights/Qwen2.5-7B-Instruct/tokenizer.json \
--model-type-hf llama2 \
--params-dtype bf16关键参数说明:
qwen2.5模型这里的--model-type-hf需要设置为:llama2。参考目前支持的模型 model_cfg.json中“model_mappings”下包含的模型参数映射关系。
执行结果如下:

全参官方参考链接: 大模型指令微调
# 文件名称scripts/qwen25_7b_instruct/tune_qwen25_7b_full_ptd.sh
set -e
source /usr/local/Ascend/ascend-toolkit/set_env.sh
export CUDA_DEVICE_MAX_CONNECTIONS=1
NPUS_PER_NODE=8
MASTER_ADDR=localhost
MASTER_PORT=1901
NNODES=1
NODE_RANK=0
WORLD_SIZE=$(($NPUS_PER_NODE*$NNODES))
# please fill these path configurations
CKPT_LOAD_DIR="***/weights/weight_mg/qwen2.5_7b_instruct_mcore_tp4_pp2"
CKPT_SAVE_DIR="***/weights/ckpt_record/auto_train_test/qwen2.5_mcore_full_tp4_pp2"
DATA_PATH="***"
TOKENIZER_PATH="***/weights/Qwen2.5-7B-Instruct"
tensorboard_dir="***/outputs/qwen2.5-7b/tensorboard_dir/full"
train_log_path="***/outputs/qwen2.5-7b/logs/tune_mcore_qwen25_7b_full_tp4_pp2_train.log"
TP=4
PP=2
SEQ_LEN=2048
MBS=32
GBS=256
TRAIN_ITERS=200
DISTRIBUTED_ARGS="
--nproc_per_node $NPUS_PER_NODE \
--nnodes $NNODES \
--node_rank $NODE_RANK \
--master_addr $MASTER_ADDR \
--master_port $MASTER_PORT
"
TUNE_ARGS="
--finetune \
--stage sft \
--is-instruction-dataset \
--tokenizer-not-use-fast \
--prompt-type qwen \
--padded-samples \
--variable-seq-lengths \
"
GPT_ARGS="
--use-distributed-optimizer \
--overlap-grad-reduce \
--overlap-param-gather \
--use-mcore-models \
--tensor-model-parallel-size ${TP} \
--pipeline-model-parallel-size ${PP} \
--num-layer-list 14,14 \
--num-layers 28 \
--hidden-size 3584 \
--ffn-hidden-size 18944 \
--num-attention-heads 28 \
--max-position-embeddings ${SEQ_LEN} \
--seq-length ${SEQ_LEN} \
--disable-bias-linear \
--add-qkv-bias \
--group-query-attention \
--num-query-groups 4 \
--use-flash-attn \
--swiglu \
--use-fused-swiglu \
--normalization RMSNorm \
--norm-epsilon 1e-6 \
--use-fused-rmsnorm \
--position-embedding-type rope \
--rotary-base 1000000 \
--use-fused-rotary-pos-emb \
--untie-embeddings-and-output-weights \
--micro-batch-size ${MBS} \
--global-batch-size ${GBS} \
--make-vocab-size-divisible-by 1 \
--padded-vocab-size 152064 \
--tokenizer-type PretrainedFromHF \
--tokenizer-name-or-path ${TOKENIZER_PATH} \
--attention-dropout 0.0 \
--hidden-dropout 0.0 \
--train-iters ${TRAIN_ITERS} \
--lr 1.0e-5 \
--lr-decay-style cosine \
--lr-warmup-fraction 0.1 \
--init-method-std 0.01 \
--weight-decay 0.0 \
--clip-grad 1.0 \
--adam-beta1 0.9 \
--adam-beta2 0.999 \
--initial-loss-scale 4096 \
--no-gradient-accumulation-fusion \
--no-masked-softmax-fusion \
--attention-softmax-in-fp32 \
--bf16 \
--tensorboard-dir $tensorboard_dir \
"
DATA_ARGS="
--data-path $DATA_PATH \
--split 100,0,0
"
CKPT_ARGS="
--no-load-optim \
--no-load-rng \
--no-save-optim \
--no-save-rng \
--seed 1234 \
"
OUTPUT_ARGS="
--log-interval 10 \
--save-interval 1000 \
--eval-interval 1000 \
--eval-iters 0 \
--log-throughput
"
echo "==========Start train=========="
torchrun $DISTRIBUTED_ARGS posttrain_gpt.py \
$GPT_ARGS \
$DATA_ARGS \
$CKPT_ARGS \
$OUTPUT_ARGS \
$TUNE_ARGS \
--load ${CKPT_LOAD_DIR} \
--save ${CKPT_SAVE_DIR} \
--distributed-backend nccl \
| tee $train_log_pathcd ***/MindSpeed-LLM
bash scripts/qwen25_7b_instruct/tune_qwen25_7b_full_ptd.shNPU设备上业务数据集评分结果:

全参官方参考链接: LoRA 微调简介
# 文件名称 scripts/qwen25_7b_instruct/tune_qwen25_7b_lora_ptd.sh
set -e
export CUDA_DEVICE_MAX_CONNECTIONS=1
source /usr/local/Ascend/ascend-toolkit/set_env.sh
NPUS_PER_NODE=2
MASTER_ADDR=localhost
MASTER_PORT=1902
NNODES=1
NODE_RANK=0
WORLD_SIZE=$(($NPUS_PER_NODE*$NNODES))
# please fill these path configurations
CKPT_LOAD_DIR="***/weights/weight_mg/qwen2.5_7b_instruct_mcore_tp2_pp1"
CKPT_SAVE_DIR="***/weights/ckpt_record/auto_train_test/qwen2.5_mcore_lora_tp2_pp1"
DATA_PATH="***"
TOKENIZER_PATH="***/weights/Qwen2.5-7B-Instruct"
tensorboard_dir="***/outputs/qwen2.5-7b/tensorboard_dir/full"
train_log_path="***/outputs/qwen2.5-7b/logs/tune_mcore_qwen25_7b_lora_tp2_pp1_train.log"
TP=2
PP=1
SEQ_LEN=2048
MBS=16
GBS=64
TRAIN_ITERS=785
DISTRIBUTED_ARGS="
--nproc_per_node $NPUS_PER_NODE \
--nnodes $NNODES \
--node_rank $NODE_RANK \
--master_addr $MASTER_ADDR \
--master_port $MASTER_PORT
"
TUNE_ARGS="
--finetune \
--stage sft \
--is-instruction-dataset \
--tokenizer-not-use-fast \
--prompt-type qwen \
--variable-seq-lengths \
--padded-samples \
--lora-r 16 \
--lora-alpha 32 \
--lora-fusion \
--lora-target-modules linear_qkv linear_proj linear_fc1 linear_fc2
"
GPT_ARGS="
--use-mcore-models \
--use-distributed-optimizer \
--overlap-grad-reduce \
--tensor-model-parallel-size ${TP} \
--pipeline-model-parallel-size ${PP} \
--num-layers 28 \
--hidden-size 3584 \
--ffn-hidden-size 18944 \
--num-attention-heads 28 \
--tokenizer-type PretrainedFromHF \
--tokenizer-name-or-path ${TOKENIZER_PATH} \
--seq-length ${SEQ_LEN} \
--max-position-embeddings ${SEQ_LEN} \
--micro-batch-size ${MBS} \
--global-batch-size ${GBS} \
--make-vocab-size-divisible-by 1 \
--padded-vocab-size 152064 \
--rotary-base 1000000 \
--lr 1.0e-5 \
--lr-warmup-fraction 0.1 \
--train-iters ${TRAIN_ITERS} \
--lr-decay-style cosine \
--untie-embeddings-and-output-weights \
--disable-bias-linear \
--attention-dropout 0.0 \
--init-method-std 0.02 \
--hidden-dropout 0.0 \
--position-embedding-type rope \
--normalization RMSNorm \
--swiglu \
--use-flash-attn \
--weight-decay 0.0 \
--use-rotary-position-embeddings \
--no-masked-softmax-fusion \
--attention-softmax-in-fp32 \
--clip-grad 1.0 \
--adam-beta1 0.9 \
--adam-beta2 0.999 \
--add-qkv-bias \
--initial-loss-scale 4096 \
--no-gradient-accumulation-fusion \
--bf16 \
--group-query-attention \
--num-query-groups 4 \
--norm-epsilon 1e-06 \
--use-fused-rmsnorm \
--use-fused-swiglu \
--use-fused-rotary-pos-emb \
--tensorboard-dir $tensorboard_dir \
"
DATA_ARGS="
--data-path $DATA_PATH \
--split 100,0,0
"
CKPT_ARGS="
--no-load-optim \
--no-load-rng \
--no-save-optim \
--no-save-rng \
--seed 1234 \
"
OUTPUT_ARGS="
--log-interval 10 \
--save-interval 1000 \
--eval-interval 1000 \
--eval-iters 0 \
--log-throughput
"
torchrun $DISTRIBUTED_ARGS posttrain_gpt.py \
$GPT_ARGS \
$DATA_ARGS \
$CKPT_ARGS \
$OUTPUT_ARGS \
$TUNE_ARGS \
--distributed-backend nccl \
--load ${CKPT_LOAD_DIR} \
--save ${CKPT_SAVE_DIR} \
| tee $train_log_pathcd ***/MindSpeed-LLM
bash scripts/qwen25_7b_instruct/tune_qwen25_7b_lora_ptd.shNPU设备上业务数据评分

权重转换实现了 Megatron-LM 权重到 HuggingFace 格式的转换,支持多种并行策略(如张量并行、流水并行等)。转换过程中,模型的权重会被适配为 HuggingFace 的标准格式,确保可以在 HuggingFace 环境下继续进行训练和推理。
Megatron-LM权重转换到Huggingface格式参考链接
# 文件名 scripts/qwen25_7b_instruct/ckpt_convert_qwen25_mcore2hf_4_full.sh
source /usr/local/Ascend/ascend-toolkit/set_env.sh
python convert_ckpt.py \
--model-type GPT \
--load-model-type mg \
--save-model-type hf \
--model-type-hf llama2 \
--use-mcore-models \
--add-qkv-bias \
--load-dir ***/weights/ckpt_record/auto_train_test/qwen2.5_mcore_full_tp4_pp2 \
--target-tensor-parallel-size 1 \
--target-pipeline-parallel-size 1 \
--save-dir ***/weights/Qwen2.5-7B-Instruct # <-- 需要填入原始HF模型路径,新权重会存于 ***/weights/Qwen2.5-7B-Instruct/mg2hf/
mv ***/weights/Qwen2.5-7B-Instruct/mg2hf/ $mg2hf_pathcd ***/MindSpeed-LLM
bash scripts/qwen25_7b_instruct/ckpt_convert_qwen25_mcore2hf_4_full.shGPU精度基线:93.20%,当前评分:93.9%。

lora权重转换参考链接
注意:
Lora在权重转换前,需要将Config.json中的数据类型改为float16,推理时采用fp16加载,否则会有精度误差。
# 文件名 scripts/qwen25_7b_instruct/ckpt_convert_qwen25_mcore2hf_lora.sh
source /usr/local/Ascend/ascend-toolkit/set_env.sh
python convert_ckpt.py \
--model-type GPT \
--use-mcore-models \
--load-model-type mg \
--save-model-type hf \
--load-dir /***/weights/weight_mg/qwen2.5_7b_instruct_mcore_tp2_pp1 \
--lora-load ***/weights/ckpt_record/auto_train_test/qwen2.5_mcore_lora_tp2_pp1 \
--lora-r 16 \
--lora-alpha 32 \
--lora-target-modules linear_qkv linear_proj linear_fc1 linear_fc2 \
--target-tensor-parallel-size 1 \
--target-pipeline-parallel-size 1 \
--add-qkv-bias \
--save-dir ***/weights/Qwen2.5-7B-Instruct # <-- 需要填入原始HF模型路径,新权重会存于 ***/weights/Qwen2.5-7B-Instruct/mg2hf/
mg2hf_path="***/hw/weights/ckpt_record/mg2hf/Qwen25_7B_Instruct_mcore2hf"
[ -d $mg2hf_path ] && rm -r $mg2hf_path
mv /apps/shared_storage/hw_test/hw/weights/Qwen2.5-7B-Instruct/mg2hf $mg2hf_pathcd /***/MindSpeed-LLM
bash scripts/qwen25_7b_instruct/ckpt_convert_qwen25_mcore2hf_lora.shGPU精度基线:88.70%,当前评分:88.8%。

由下表的测试结果可知,在业务数据集上,当前全参微调和Lora微调均获得较好的评分,相比基线评分都有所提升。
| 微调方式 | GPU精度基线 | NPU评分 | NPU误差 | GPU评分 | GPU误差 |
|---|---|---|---|---|---|
| 全参微调 | 93.20% | 93.90% | +0.70% | 93.90% | +0.70% |
| Lora微调 | 88.70% | 88.80% | +0.10% | 88.80% | +0.10% |