【昇思学习营第六期·五一篇】昇思MindSpore大模型迁移与调优实践
收藏回复举报
【昇思学习营第六期·五一篇】昇思MindSpore大模型迁移与调优实践
发表于2025-05-13 13:10:46
0 查看

昇思MindSpore大模型迁移与调优实践

利用MindSpore Transformers(MindFormers)进行模型的迁移与调优

我们首先来看一下MindSpore的整体框架

true

  • 1.MindSpore的定位:MindSpore是一个AI框架(@或者说深度学习框架),在昇腾软硬件全栈中,向上对接如 MindIE的应用使能,向下对接CANN计算架构。
  • 2.MindSpore和CANN的关系:CANN面向算子开发,MindSpore对接/调用CANN这一层开发出的算子。
  • 3.MindSpore和MindSpore Transformers的关系:MindSpore Transformers是基于MindSpore构建的大模型使能套件,承载了MindSpore?如分布式并行能 力的同时,覆盖预训练、微调、推理、部署全流程,预置多种预训练大模型,微调算法及下游应用,低代码实现原生大模型原生全流程开发。
  • 4.MindSporel的能力:MindSpores分布式并行依托于图编译和对整图的切分,这种方式降低了大模型的开发门槛,又保证了多种并行策略的灵活组合使用。 同时在内存管理上,训练时构造整图,可以获取所有Tensor的生命周期,设计最优内存复用策略。
为何需要做模型迁移:

为适配业界生态,我们经常会遇到将其他框架实现的大模型迁移到MindSpore Transformers的情况,目前常遇到的模型迁移场景主要分为两类: 1.在MindSpore Transformerst中目前已支特的基座模型(如LLāMA)上进行改动,变成适配相应业务场景的模型,工作量及难度较小 2.基于MindSpore Transformers/从头自主完成模型迁移,工作量及难度较大

模型精度调优:
  • 1.精度调优分为有标杆和无标杆的场景,以有标杆场景为例,目标是将基于MindSpore Transformerss实现的模型精度和原框架对齐
  • 2.精度调优常见的对t比项:loss、local norm、global norm
  • 3.精度调优的思路:训练前准备、基础问题排查、长稳问题观察
  • 4.训川练前准备重点关注数据集对济、权重对齐、训练参数对齐,在后续的问题排查中,可重点关注数据类型是否保持一致
  • 5.精度调优工具:MindSpore Dump、Torch Dump、TroubleShooter
开启实战:
  • 安装mindformers
git clone https://gitee.com/hss-shuai/mindformers.git -b feature/r1.3.1/cmb
cd mindformers
bash build.sh
  • 安装mindspore
pip install https://ms-release.obs.cn-north-4.myhuaweicloud.com/2.4.1/MindSpore/unified/aarch64/mindspore-2.4.1-cp39-cp39-linux_aarch64.whl --trusted-host ms-release.obs.cn-north-4.myhuaweicloud.com -i https://pypi.tuna.tsinghua.edu.cn/simple
  • 检查是否安装成功以及是否能调用NPU
python -c "import mindspore;mindspore.set_context(device_target='Ascend');mindspore.run_check()"
pip show mindformers

true

由于计算卡显存的限制,我选择了 Qwen2.5-3B 模型以及 alpaca_data 数据集

数据集预处理(我们首先需要把数据集变为标准的Alpaca 数据集格式)

数据集链接:https://modelscope.cn/datasets/YorickHe/alpaca_data

true

下载数据及后我们用如下脚本将数据集转换为标准格式的Alpaca 数据集格式:

  • 首先将csv格式的文件转换为json格式
import csv
import json

# 输入和输出文件路径
input_csv_path = 'alpaca_data.csv'
output_json_path = 'json_data.json'

# 读取 CSV 并写入标准 Alpaca 格式 JSON
with open(input_csv_path, 'r', encoding='utf-8') as csv_file, open(output_json_path, 'w', encoding='utf-8') as json_file:
    reader = csv.DictReader(csv_file)
    
    for row in reader:
        # 假设列名是 instruction, input, output
        alpaca_entry = {
            "instruction": row.get("instruction", "").strip(),
            "input": row.get("input", "").strip(),
            "output": row.get("output", "").strip()
        }
        json_file.write(json.dumps(alpaca_entry, ensure_ascii=False) + '\n')

print("转换完成,输出文件:", output_json_path)

然后再将json文件转换成标准的Alpaca 数据集格式:

import json

# 输入:jsonl 文件路径
input_file = 'json_data.json'
# 输出:json 数组格式文件路径
output_file = 'converted_output.json'

data = []

with open(input_file, 'r', encoding='utf-8') as f:
    for line in f:
        if line.strip():  # 忽略空行
            data.append(json.loads(line))

with open(output_file, 'w', encoding='utf-8') as f:
    json.dump(data, f, ensure_ascii=False, indent=4)

print(f"转换完成,共转换 {len(data)} 条记录,输出文件:{output_file}")

true

最终需要的数据集格式:

true

  • 然后再进行alpaca 数据预处理
python mindformers/research/qwen2_5/alpaca_converter.py \
--data_path ./dataset/alpaca_data/converted_output.json \
--output_path ./dataset/alpaca_data/alpaca-data-messages.json

# 参数说明
data_path:   输入下载的文件路径
output_path: 输出文件的保存路径

true

  • 然后再进行数据预处理和Mindrecord数据生成
python research/qwen2_5/qwen2_5_preprocess.py \
 --dataset_type 'qa' \
 --input_glob /home/ma-user/work/dataset/alpaca_data/alpaca-data-messages.json \
 --vocab_file /home/ma-user/work/model/Qwen2.5-3B/vocab.json \
 --merges_file /home/ma-user/work/model/Qwen2.5-3B/merges.txt \
 --seq_length 4096 \
 --output_file /home/ma-user/work/dataset/alpaca_data/alpaca-messages.mindrecord
 
 
  # 参数说明
dataset_type: 预处理数据类型
input_glob:   转换后的alpaca的文件路径
vocab_file:   vocab.json文件路径
merges_file:  merges.txt文件路径
seq_length:   输出数据的序列长度
output_file:  输出文件的保存路径

注意,在运行这一步时可能会报错:

true

  • 解决办法:

  • 在终端输入:

export PYTHONPATH=$PYTHONPATH:/home/ma-user/work/mindformers/research/

然后进入到:mindformers下运行脚本:

python research/qwen2_5/qwen2_5_preprocess.py \
 --dataset_type 'qa' \
 --input_glob /home/ma-user/work/dataset/alpaca_data/alpaca-data-messages.json \
 --vocab_file /home/ma-user/work/model/Qwen2.5-3B/vocab.json \
 --merges_file /home/ma-user/work/model/Qwen2.5-3B/merges.txt \
 --seq_length 4096 \
 --output_file /home/ma-user/work/dataset/alpaca_data/alpaca-messages.mindrecord
 
 # 参数说明
dataset_type: 预处理数据类型
input_glob:   转换后的alpaca的文件路径
vocab_file:   vocab.json文件路径
merges_file:  merges.txt文件路径
seq_length:   输出数据的序列长度
output_file:  输出文件的保存路径

true

  • 然后就是进行torch权重转mindspore权重
python research/qwen2_5/convert_weight.py \
--torch_ckpt_dir /home/ma-user/work/model/Qwen2.5-3B/ \
--mindspore_ckpt_path /home/ma-user/work/model/Qwen2.5-3B/transform.ckpt

# 参数说明:
torch_ckpt_dir:      预训练权重文件所在的目录, 此参数必须
mindspore_ckpt_path: 转换后的输出文件存放路径, 默认为'./transform.ckpt'

true

  • 开始训练(此时我们需要使用单卡)
python run_mindformer.py --config research/qwen2_5/pretrain_qwen2_5_3b_4k.yaml --run_mode.train

但是yaml文件中的对应配置文件需要你自己写入:

# dataset
train_dataset: &train_dataset
  data_loader:
    type: MindDataset
    dataset_dir: "/home/ma-user/work/dataset/alpaca_data/alpaca-messages.mindrecord"
    shuffle: False
    num_samples: 64
  input_columns: ["input_ids"]
  num_parallel_workers: 8
  python_multiprocessing: False
  drop_remainder: True
  batch_size: 1
  repeat: 1
  numa_enable: False
  prefetch_size: 1
train_dataset_task:
  type: CausalLanguageModelDataset
  dataset_config: *train_dataset
  
  
checkpoint_name_or_path: "/home/ma-user/work/model/Qwen2.5-3B/qwen2_5_3b.ckpt"
  • 可以看到已经在单卡上跑起来了

true

true

  • 下面是我的完整yaml配置文件(路径需要改为自己的)
seed: 42
output_dir: './output'
run_mode: 'train'
use_parallel: True
resume_training: False
only_save_strategy: False

load_checkpoint: ''
auto_trans_ckpt: False  # If true, auto transform load_checkpoint to load in distributed model

# trainer config
trainer:
  type: CausalLanguageModelingTrainer
  model_name: 'qwen2_5_3b'

# runner config
runner_config:
  epochs: 3
  batch_size: 4
  sink_mode: True
  sink_size: 1
  gradient_accumulation_steps: 1

runner_wrapper:
  type: MFTrainOneStepCell
  scale_sense:
    type: DynamicLossScaleUpdateCell
    loss_scale_value: 1
    scale_factor: 1
    scale_window: 1000
  use_clip_grad: True
  max_grad_norm: 1.0

# optimizer
optimizer:
  type: AdamW
  betas: [0.9, 0.95]
  eps: 1.e-8
  weight_decay: 0.1

lr_schedule:
  type: WarmUpStableDecayLR
  learning_rate: 3.e-4
  lr_end: 0
  warmup_ratio: 0.1
  decay_start_ratio: 0.9
  total_steps: -1 # -1 means it will load the total steps of the dataset


# dataset
train_dataset: &train_dataset
  data_loader:
    type: MindDataset
    dataset_dir: "/home/ma-user/work/dataset/alpaca_data/alpaca-messages.mindrecord"
    shuffle: False
    num_samples: 64
  input_columns: ["input_ids"]
  num_parallel_workers: 8
  python_multiprocessing: False
  drop_remainder: True
  batch_size: 1
  repeat: 1
  numa_enable: False
  prefetch_size: 1
train_dataset_task:
  type: CausalLanguageModelDataset
  dataset_config: *train_dataset

# parallel context config
parallel:
  full_batch: True
  enable_parallel_optimizer: True
  parallel_optimizer_config:
    gradient_accumulation_shard: False
    parallel_optimizer_threshold: 64

# default parallel of device num = 32 910B
parallel_config:
  data_parallel: 1
  model_parallel: 1
  pipeline_stage: 1
  context_parallel: 1
  use_seq_parallel: True
  micro_batch_num: 1
  vocab_emb_dp: False
# when model parallel is greater than 1, we can set micro_batch_interleave_num=2, that may accelerate the train process.

# recompute config
recompute_config:
  recompute: True
  select_recompute: False
  # select_recompute:
  #   'feed_forward\.mul': [3]
  #   'feed_forward\.w1\.activation\.silu': [3]
  #   'feed_forward\.w1\.reshape': [3]
  #   'feed_forward\.w2\.reshape': [3]
  parallel_optimizer_comm_recompute: False
  mp_comm_recompute: True
  recompute_slice_activation: False

# callbacks
callbacks:
  - type: CheckpointMonitor
    prefix: "qwen2_5"
    save_checkpoint_steps: 2000
    keep_checkpoint_max: 1
    save_ckpt_with_optim_param: True
    async_save: False

# mindspore context init config
context:
  jit_config:
    jit_level: "O1"
  memory_optimize_level: "O1"
  mode: 0 #0--Graph Mode; 1--Pynative Mode
  graph_kernel_flags: "--enable_cluster_ops=MatMul --online_tuning=1"
  max_device_memory: "59GB"
  device_id: 0

# model config
model:
  model_config:
    #use_attn_mask_compression: True
    type: LlamaConfig
    batch_size: 1
    seq_length: 4096
    hidden_size: 2048
    num_layers: 36
    num_heads: 16
    n_kv_heads: 2
    vocab_size: 151936
    initializer_range: 0.02
    intermediate_size: 11008
    qkv_has_bias: True
    rms_norm_eps: 1.0e-6
    theta: 1000000.0
    emb_dropout_prob: 0.0
    eos_token_id: 151643
    pad_token_id: 151643
    bos_token_id: 151643
    compute_dtype: "bfloat16"
    layernorm_compute_type: "float32"
    softmax_compute_type: "float32"
    rotary_dtype: "float32"
    param_init_type: "float32"
    use_past: False
    use_flash_attention: True
    use_past_shard: False
    offset: 0
    checkpoint_name_or_path: "/home/ma-user/work/model/Qwen2.5-3B/qwen2_5_3b.ckpt"
    repetition_penalty: 1.05
    max_decode_length: 1024
    top_k: 0
    top_p: 0.8
    do_sample: False
    extend_method: "None" # support "None", "PI", "NTK"
    fine_grain_interleave: 1
    qkv_concat: false
    # configuration items copied from Qwen
    rotary_pct: 1.0
    rotary_emb_base: 1000000
    tie_word_embeddings: True

  arch:
    type: LlamaForCausalLM

# metric
metric:
  type: PerplexityMetric

思考:在这个过程中,用mindformers微调的过程还是与其他主流框架有不小的异同的,我认为mindformers适合的群体是专业的开发者,对性能要求有着严格追求的群体,说白了就是上手难度不小,如果要是让我去选择的话我会选择mindnlp来进行微调。但是其优化的效果确实不错,与mindnlp对比,能够减小一部分显存的占用率。

建议:如果可以的话,我觉得可以做一个webui来进行微调,这样更加方便也更加直观。mindnlp也是一样的。

我要发帖子