MindSpeed MindSpore后端迁移开发指南
0 概述
当前大模型相关业务发展迅速,AI框架PyTorch因其编程友好受到业界大多数大模型训练、推理软件的青睐,华为昇腾也提供了基于PyTorch的昇腾MindSpeed + 昇腾NPU训练解决方案。为此,MindSpore推出了动态图方案以及动态图API接口,使用户也可以像使用PyTorch一样使用MindSpore AI框架。当前华为昇腾MindSpeed也已支持接入MindSpore AI框架作为后端引擎,打造华为全栈解决方案,使用户在友好编程的同时,也享受到华为全栈软硬结合带来的极致性能体验。
昇腾社区已经提供了MindSpeed迁移开发指导。本指南侧重提供MindSpeed MindSpore后端的迁移开发指导,帮助用户快速地将大模型训练从PyTorch后端迁移至MindSpore后端。
在介绍迁移开发前,先简要介绍MindSpore动态图和API适配工具MSAdapter,供用户了解MindSpore后端和PyTorch后端的差异,以启发用户在模型迁移开发遇到问题时进行问题排查。
MindSpore动态图介绍
MindSpore 动态图模式又称PyNative模式。相比之前版本的小算子拼接方案,当前版本采用了pybind算子直调的方式,即正向算子执行直接调用底层算子接口,极大地减少了单算子执行的流程开销和数据结构转换开销,在性能上有较大提升。MindSpore动态图模式仍然是基于MindSpore的基本机制实现,因此,其与PyTorch动态图仍然存在部分机制上的差异,以下进行简要阐述。
自动微分机制差异
神经网络的训练主要使用反向传播算法,自动微分是各个AI框架实现反向传播的核心机制。PyTorch使用动态计算图,在代码执行时立即运算,正反向计算图在每次前向传播时动态构建;PyTorch反向微分是命令式反向微分,符合面向对象编程的使用习惯。
MindSpore使用函数式自动微分的设计理念,提供了更接近数学语义的自动微分接口grad和value_and_grad. 与PyTorch的自动微分Tensor.backward机制不同,MindSpore需要针对需要自动微分的函数对象调用grad接口获取函数微分,并指定需要求导的输入的位置索引。grad和value_and_grad接口的使用详见 mindspore.grad 和 mindspore.value_and_grad.
自定义算子
与PyTorch类似的,MindSpore动态图模式也支持了自定义算子接入,用户可以参考基于CustomOpBuilder的自定义算子了解如何进行自定义算子接入。
动态图API接口差异
尽管MindSpore动态图API接口的目标是与PyTorch API保持一致,但由于框架机制等原因,部分MindSpore动态图API接口可能在参数、输入、输出、逻辑功能和特定场景等方面与PyTorch APIs存在一定差异,具体差异情况详见PyTorch与MindSpore API映射表。
MSAdapter工具介绍
MSAdapter是一款MindSpore生态适配工具,在不改变用户原有使用习惯下,将PyTorch/JAX等三方框架代码快速迁移到MindSpore生态上,帮助用户高效使用昇腾算力。该工具的基本原理是使用MindSpore动态图算子来实现PyTorch API接口,由于框架的差异性,部分接口仍存在差异或者不支持,具体支持列表详见torch接口支持列表。
1 软件安装
为了便于用户理解和选择合适的MindSpeed版本,我们提供了详细的版本配套表,如表1所示。 该表详细列出了MindSpeed版本与对应的MindSpore版本及CANN版本之间的匹配关系,确保用户能够根据自身软件环境准确选择相匹配的版本,以实现最优的性能与功能支持。
| MindSpeed版本 | 2.2.0_core_r0.12.1 |
| MindSpeed代码分支名称 | master:配套Megatron-LM的core_v0.12.1分支 |
| CANN版本 | CANN 8.3.RC1 |
| MindSpore版本 | 2.7.1 |
| MSAdapter版本 | master |
| Python版本 | Python3.9.x, Python3.10.x |
安装操作
- 安装依赖的软件
| 软件 | 版本 |
|---|---|
| 昇腾NPU驱动 | 建议下载并安装左侧软件,具体请参见《CANN 软件安装指南》 |
| 昇腾NPU固件 | |
| Toolkit(开发套件) | |
| Kernels(算子包) | |
| NNAL(Ascend Transformer Boost加速库) | |
| MindSpore框架 | 建议下载并安装左侧软件,具体参见《MindSpore 安装指南》 |
| MSAdapter插件 | 建议下载并安装左侧软件,具体参见《MSAdapter 安装指南》 |
-
下载MindSpeed-Core-MS源码master分支,执行一键适配。
git clone https://gitee.com/ascend/MindSpeed-Core-MS.git -b r0.4.0 cd MindSpeed-Core-MS根据使用场景,执行以下命令之一:
大语言模型:MindSpeed-LLM
source auto_convert.sh llm多模态模型:MindSpeed-MM
source auto_convert.sh mm强化学习:MindSpeed-RL
source auto_convert_rl.sh说明: MindSpeed-Core-MS源码提供了一键适配,用户无需再手动拉取MindSpeed等仓库源码,具体使用见README。
2 MindSpeed PyTorch模型迁移
对于MindSpeed PyTorch后端已支持但MindSpore后端不支持的模型,用户只需针对PyTorch后端提供的模型shell脚本进行少量适配修改,即可迁移至MindSpore后端。shell脚本适配的主体原则如下:
-
启动命修改为
msrun,并加入--ai-framework mindspore参数; 以下是一个启动命令适配的示例。适配前(PTA):
python -m torch.distributed.launch $DISTRIBUTED_ARGS pretrain_gpt.py \ $GPT_ARGS \ $DATA_ARGS \ $OUTPUT_ARGS \ $MLA_ARGS \ $ROPE_ARGS \ $MOE_ARGS \ $MTP_ARGS \ $DUALPIPE_ARGS \ $MEM_ARGS \ --distributed-backend nccl \ --save $CKPT_SAVE_DIR \ --load $CKPT_LOAD_DIR \ | tee logs/pretrain_deepseek3_671b_4k_ptd.log适配后(MA):
msrun $DISTRIBUTED_ARGS $basepath/pretrain_gpt.py \ $GPT_ARGS \ $DATA_ARGS \ $OUTPUT_ARGS \ $MLA_ARGS \ $ROPE_ARGS \ $MOE_ARGS \ $MTP_ARGS \ $MEM_ARGS \ --distributed-backend nccl \ --save $CKPT_SAVE_DIR \ --load $CKPT_LOAD_DIR \ --ai-framework mindspore \ 2>&1 | tee logs/ms_pretrain_deepseek3_671b_4k_ptd.log -
DISTRIBUTED_ARGS参数适配;torchrun命令使用的分布式参数包括--nproc_per_node, --nnodes, --node_rank, --master_addr, --master_port,切换为msrun后,分布式参数名需要适配调整。msrun使用的分布式参数和torchrun的分布式参数关系见下表,各参数含义和使用详见msrun启动:msrun 分布式参数 与torchrun分布式参数的关系 --local_worker_num =nproc_per_node--worker_num =nproc_per_node*nnodes--master_addr =master_addr--master_port =master_port--node_rank =node_rank--log_dir / --join / --cluster_time_out / --bind_core / 以下是一个
DISTRIBUTED_ARGS参数适配示例。适配前(PTA):
# PyTorch后端启动脚本 NPUS_PER_NODE=8 MASTER_ADDR=localhost #主节点IP MASTER_PORT=6000 NNODES=64 NODE_RANK=0 WORLD_SIZE=$(($NPUS_PER_NODE*$NNODES)) DISTRIBUTED_ARGS=" --nproc_per_node $NPUS_PER_NODE \ --nnodes $NNODES \ --node_rank $NODE_RANK \ --master_addr $MASTER_ADDR \ --master_port $MASTER_PORT "适配后(MA):
# MindSpore后端启动脚本 NPUS_PER_NODE=8 MASTER_ADDR=localhost #主节点IP MASTER_PORT=9110 NNODES=64 NODE_RANK=0 WORLD_SIZE=$(($NPUS_PER_NODE*$NNODES)) DISTRIBUTED_ARGS=" --master_addr $MASTER_ADDR \ --node_rank $NODE_RANK \ --worker_num $WORLD_SIZE \ --local_worker_num $NPUS_PER_NODE \ --master_port $MASTER_PORT \ --log_dir=msrun_log \ --join=False \ --cluster_time_out=300 \ --bind_core=True \ " -
确认PyTorch shell脚本中的特性开关所对应的特性在MindSpore后端已支持。若MindSpore后端未支持,我们建议在启动脚本中关闭这些特性。
完成上述启动shell脚本适配后,用户即可尝试使用脚本拉起模型任务。
3 MindSpeed MindSpore进阶开发
本章节提供MindSpeed MindSpore后端的进阶开发,帮助用户在MindSpore后端下进行新模型开发或者接入自定义算子,提升开发效率。
新模型开发
在基于MindSpeed MindSpore后端进行新模型开发时,用户可以像使用torch API一样使用MSAdapter提供的API接口在MindSpeed中开发新模型,但请注意部分接口的差异(见torch接口支持列表)。
若用户开发的新模型不涉及自定义加速优化特性,则基于MindSpore后端的开发与基于PyTorch后端的开发基本无差异。但若用户的新模型涉及到
自定义加速优化特性开发、特别是涉及到自定义反向微分时,则需要特别关注反向微分的定义方式。由于MindSpore采用的函数式自动微分机制,MSAdapter暂时无法支持用户以tensor.backward()方式完成与tensor有关的操作/函数的全链条微分,仍需使用MindSpore的grad接口。
自定义算子接入
当前MindSpeed MindSpore通过MSAdapter接入自定义算子,并通过patch机制进行函数替换使得模型训练时可以使用自定义算子。因此,接入自定义算子可以参照如下流程:
-
- 在MSAdapter中利用MindSpore的动态图模式自定义算子机制实现自定义算子接入;
-
- 在MindSpeed中实现patch替换。
下面以计算通信融合算子MATMUL_ALL_REDUCE为例介绍自定算子接入的开发流程(背景见计算通信并行CoC特性说明)。
-
- MSAdapter中接入自定义算子。
MATMUL_ALL_REDUCE融合算子是硬件使能软件CANN提供的加速算子,需要利用MindSpore的自定义算子机制实现接入,以使Pyhon侧能够调用。
- 首先在
MSAdapter/csrc/atb_ops目录下新增lcal_coc.cpp文件,实现matmul_all_reduce函数并使用PyBind11将该函数注册成为Python模块接口。在开发C++算子时需要特别注意算子的输入输出。
#include <vector> #include "ms_extension.h" #include "atb_common.h" using LinearParallelParam = atb::infer::LinearParallelParam; namespace atb { template <> struct HashOpParam<LinearParallelParam> { void operator()(const LinearParallelParam ¶m) const { add_param_to_buf("transWeight", param.transWeight); add_param_to_buf("rank", param.rank); add_param_to_buf("rankSize", param.rankSize); add_param_to_buf("rankRoot", param.rankRoot); add_param_to_buf("hasResidual", param.hasResidual); add_param_to_buf("backend", param.backend); add_param_to_buf("commMode", param.commMode); add_param_to_buf("type", param.type); add_param_to_buf("keepIntermediate", param.keepIntermediate); add_param_to_buf("commDomain", param.commDomain); } }; } // namespace atb void matmul_all_reduce(const BaseTensorPtr &input1, const BaseTensorPtr &input2, const std::optional<BaseTensorPtr> &biasOpt, BaseTensorPtr &output, int rank, int rankSize, const std::string &commDomain) { const BaseTensorPtr &bias = biasOpt.has_value() ? biasOpt.value() : nullptr; LinearParallelParam param; bool transB = (input1->shape()[1] != input2->shape()[0]); param.transWeight = transB; param.rank = rank; param.rankSize = rankSize; param.rankRoot = 0; param.hasResidual = biasOpt.has_value(); param.backend = "lcoc"; param.commMode = atb::infer::CommMode::COMM_MULTI_PROCESS; param.type = atb::infer::LinearParallelParam::ParallelType::LINEAR_ALL_REDUCE; param.keepIntermediate = false; param.commDomain = commDomain; auto op = atb::OpParamCache<LinearParallelParam>::getInstance().getOperation(param, "MatMulAllReduce"); BaseTensorPtrList inputPara = {input1, input2}; if (biasOpt.has_value()) { inputPara.push_back(bias); } MsRunAtbOp(op, inputPara, {output}); } PYBIND11_MODULE(MS_EXTENSION_NAME, m) { m.def("matmul_all_reduce", &matmul_all_reduce, "matmul_all_reduce on ascend device", pybind11::arg("input1"), pybind11::arg("input2"), pybind11::arg("biasOpt"), pybind11::arg("output"), pybind11::arg("rank"), pybind11::arg("rankSize"), pybind11::arg("commDomain")); }- 然后在MSAdapter的
mindtorch/torch_npu子模块中利用AtbOpBuilder(本质上是使用MindSporeCustomOpBuilder)对外提供mindtorch.torch_npu.matmul_all_reduce接口。
def matmul_all_reduce(input1, input2, bias, output, rank, tp_size, comm_domain): return builder.load("lcal_coc").matmul_all_reduce(input1, input2, bias, output, rank, tp_size, comm_domain) - MSAdapter中接入自定义算子。
-
- 在MindSpeed中进行patch替换(MindSpeed Torch后端已接入计算通信融合算子
matmul_all_reduce,在MindSpore后端时对其进行替换)。 以MindSpeed-LLM为例,在mindspeed-llm/mindspore/mindspore_adaptor.py的MindSporeAdaptation中加入patch替换逻辑:
from mindspeed.mindspore.ops.lcal_functional import matmul_all_reduce MegatronAdaptation.register('mindspeed.ops.lcal_functional.CoCOperations.matmul_all_reduce', matmul_all_reduce) - 在MindSpeed中进行patch替换(MindSpeed Torch后端已接入计算通信融合算子