开源之夏-MindSpore CPU算子全流程开发指导
博客目录如下:
一、项目概述
1. 开源之夏活动介绍
2. 昇思MindSpore介绍
3. CPU正向算子开发介绍
二、ParallelConcat算子介绍
1. MindSpore ParallelConcat算子介绍
2. TensorFlow ParallelConcat算子案例
3. MindSpore相似Concat算子案例
三、CPU算子开发流程
1. 算子开发文件
2. 算子python侧前端接口开发
3. 算子c++侧前端接口开发
4. 算子cpu_kernel底层核函数开发
5. 交付件准备
四、参考文献
一、 项目概述
1. 开源之夏活动介绍
开源之夏(英文简称 OSPP)是开源软件供应链点亮计划的系列暑期活动,它由中国科学院软件研究所与openEuler社区共同举办,旨在鼓励在校学生积极参与开源软件的开发维护,促进优秀开源软件社区的蓬勃发展,培养和发掘更多优秀的开发者。活动联合国内外各大开源社区,针对重要开源软件的开发与维护提供项目任务,并面向全球高校学生开放报名。
2. 昇思Mindspore介绍
MindSpore是一个全场景深度学习框架,旨在实现易开发、高效执行、全场景覆盖三大目标,其中易开发表现为API友好、调试难度低,高效执行包括计算效率、数据预处理效率和分布式训练效率,全场景则指框架同时支持云、边缘以及端侧场景。Mindspore欢迎每一个开发者都参与到的社区里,为全场景AI框架MindSpore添砖加瓦!具体了解可以点击一下链接进行跳转:
Mindspore 官网 :可以全方位了解MindSpore,包括安装、教程、文档、社区、资源下载和资讯栏目等。
①MindSpore Gitee:fork仓库后即可随时跟踪MindSpore最新进展,参与issues讨论、提交代码。
②MindSpore Github :Gitee的MindSpore代码镜像,习惯用github的开发者可以在这里进行MindSpore的学习。
MindSpore 论坛:在论坛中与其他开发者交流,共同学习,共同成长!
针对本次开源之夏活动,昇思MindSpore社区共设置了21个项目(包含1个Kube-Edge社区集成项目),难度包含基础与进阶,适合不同层次的同学参与。每个任务均有专业导师提供1v1专业指导,开发者可以登录项目页了解详细信息,也可以直接联系项目导师进行咨询。
3. CPU正向算子开发介绍
算子是MindSpore深度学习框架中的一个基本组件,例如常见的卷积操作、激活函数、全连接、批归一化等,示例可见官网。MindSpore框架中的算子包括算子前端和算子后端,算子前端是指提供用户使用的低阶算子Python API,后端指对用户不可见的算子底层实现逻辑。MindSpore支持CPU、GPU和Ascend三种硬件设备。其中,Ascend是指昇腾AI处理器,根据设备差异有多个不同的算子后端实现,运行在Ascend上的包括AI CPU算子和AI Core算子。
CPU算子开发的目的,就是要在MindSpore框架中通过调用算子API来使用运行在CPU硬件平台上的相应算子。
(1) CPU正向算子开发的整体流程如下:
(2) CPU正向算子开发涉及文件如下:
(3) CPU正向算子开发目标:
MindSpore CPU算子开发的基本目标是与友商竞品完成对齐,包括功能、规格、精度和性能等方面。如果前端用户资料不足以明确算子功能或其他相关信息,可参考PyTorch或TensorFlow的算子实现,具体资料和源码链接如下:
PyTorch官方文档:https://pytorch.org/docs/stable/index.html
PyTorch中文文档:https://pytorch-cn.readthedocs.io/zh/latest
PyTorch官方源码:https://github.com/pytorch/pytorch
TensorFlow官方文档:https://tensorflow.google.cn/install
TensorFlow中文文档:https://tensorflow.google.cn/
TensorFlow官方源码:https://github.com/tensorflow
二、 ParallelConcat算子介绍
1. MindSpore ParallelConcat算子介绍
(1) 算子功能
该算子的功能为沿着第一个维度上拼接包含N个张量的列表(所有张量的第一维度必须为1)。ParallelConcat算子和Concat算子的功能非常类似,而ParallelConcat与Concat之间的区别就在于,Concat要求在操作开始之前计算所有输入,但不要求在构图期间知道各个输入Tensor的shape。而ParallelConcat算子会在输入Tensor可用时将其片段先行复制到输出中,在某些情况下这样可以提供性能优势。
该算子的前端接口如下:
昇思 Mindspore ParallelConcat 算子 接口 文档
(2) 算子输入与输出
上图为ParallelConcat算子的输入与输出信息,从官方文档可知算子的输入和输出存在以下要求:
① 输入 "values" 为一个元组tuple或者列表list。
② 输入 "values" 的长度必须大于等于1,不能为空。
③ 输入 "values" 中的元素均为Tensor类型并且必须具有相同的数据类型和shape。
④ 输入 "values" 中的所有Tensor的第一个维度必须为1。
⑤ 输出 "y" 也是一个Tensor并且与输入 "values" 具有相同的数据类型。
⑥ 输出 "y" 的第一个维度取决于输入"values" 的长度。
⑦ 输出 "y" 的其他维度与输入 "values" 中任一Tensor的其他维度相同。
(3) 算子实现逻辑
在清楚了算子的功能和对于输入输出的限制后,可以简要概括算子的实现逻辑如下:
① Python侧前端接口获取输入 "values" 。
② 在C++侧接口的InferType函数中对输入 "values" 的类型和数据类型进行校验:如果输入 "values" 中不全为Tensor或存在数据类型不完全一致的情况则抛出异常;若无异常则推导出输出 "y" 具有和输入 "values" 一样的数据类型。
③ 在C++侧接口的InferShape函数中对于输入 "values" 的shape进行校验:首先获取输入 "values" 的长度N以及其中第一个元素(Tensor[0])的shape并判断其第一个维度是否为1。再遍历输入 "values" 中剩余的所有Tensor(1,2,…,N-1)的shape,将其与Tensor[0]的shape进行对比,如果存在任一Tensor的shape与Tensor[0]的shape不相同的情况则抛出异常;如果无异常则将输入 "values" 的长度N作为输出 "y" 的shape的第一个维度的大小(N,),同时将Tensor0的shape除去第一维后的剩余维度(,*…)作为输出 "y" 的后续维度。最后判断当前算子是否处于动态shape推导阶段,如果是则需要获取获取输入 "values" 的最大shape和最小shape,并且同时返回推导出的输出 "y" shape (N,*…)和最大最小shape;如果不是则直接返回输出 "y" 的shape(N,*…)。
④ 在C++侧的cpu_kernel底层函数中获取输出 "y" 的内存地址out_addr和输入 "values" 中每个Tensor的内存地址列表input_addr_list,之后遍历每一个输入"values"中的Tensor,并将其数值复制到out_addr中,最后返回输出 "y" 的内存地址。此时输出 "y" 为一个Tensor,其dtype为输入 "vlaues" 中任一Tensor的dtype,其shape第一维度为 "values" 的长度,剩余维度与 "values" 中任一Tensor的其他维度相同,数值为输入 "values" 中所有Tensor在第一维度拼接后的结果。
最终M indSpore ParallelConcat 算子逻辑流程图 如下(见下一页):
(4) 算子异常处理
① 输入 "values" 不是一个Tensor的tuple或list,返回:
" TypeError: For ParallelConcat, the input "values" must be a list or tuple of Tensors. But got: xxx."
② 输入 "values" 中Tensor的数据类型不属于[float16, float32, float64, int16, int32]
返回:
" TypeError: For ParallelConcat, the type of the tensor in input "values" must be in
[float16, float32, float64, int16, int32], but got xxx."
③ 输入 "values" 中所有Tensor的数据类型不完全相同,返回:
" TypeError: For ParallelConcat, the type of the Tensor in input "values" must be the same, but got Tensor[i] with type xxx and Tensor[j] with type yyy."
④ 输入 "values" 的长度小于1,返回:
" ValueError: For ParallelConcat, the length of input "values" must be greater or equal to 1, but got length xxx."
⑤ 输入 "values" 中任一Tensor的第一个维度不为1,返回:
" ValueError: For ParallelConcat, the first dimension of all Tensors in the input "values" must be 1, but got Tensor[i]‘s first dimension x."
⑥ 输入 "values" 中所有Tensor的shape不完全相同,返回:
" ValueError: For ParallelConcat, the shape of the Tensor in input "values" must be the same, but got Tensor[i] with shape xxx and Tensor[j] with shape yyy."
MindSpore ParallelConcat算子逻辑流程图
2. TensorFlow友商ParallelConcat算子案例
(1) 算子功能
沿第一维连接 N 张量的列表。接口文档如下:
(2) 算子输入与输出
可以看出TensorFlow的输入和输出均与MindSpore一致,额外的输入Shape实际上也不是必须的参数。
(3) 算子源码实现
由于算子源码包含多个文件,并且代码量较大,故此处只列举核心代码,其余代码可参考TensorFlow 官方代码仓库 :
① 算子前端Python接口定义文件:
依据TensorFlow官网的ParallelConcat接口文档查找arrary_ops.py文件未找到ParallelConcat前端接口,但是在Stack算子的接口定义中调用了parallel_concat算子,而其所属算子文件已更改,并且查阅相关资料后得知gen_array_ops文件是在编译时生成的文件,因此此处不过多介绍:
② 算子C++侧输出推导文件
输入输出设置:
InferType:
判断 "values" 中所有Tensor的dtype是否相同:
判断 "values" 中所有Tensor的dtype是否为支持的数据类型:
InferShape:
判断输入 "values" 的长度是否大于等于1:
判断输入Tensor的第一个维度是否为1:
判断输入 "values" 中所有Tensor的Shape是否相同:
③ 算子底层实现文件
④ 算子反向实现文件
经查阅文档和源码,ParallelConcat算子无反向
⑤ 算子Python接口单元测试文件
3. MindSpore相似Concat算子案例
(1) 算子功能
在指定轴上拼接输入Tensor,输入的是一个tuple,其元素秩相同,即。将给定的轴设为,并且,输入元素的数量设为。对于第个数据,的shape为,是第个元素的第个维度。则输出tensor的shape为:
接口文档如下:
(2) 算子输入与输出
从表中可以看出Concat支持所有的数据类型,同时比ParallelConcat算子多了一个输入属性 "axis" 其数值用于指定Tensor进行拼接的维度。由此我们可以看出,实际上ParallelConcat算子就是Concat算子在axis=0,输入 "values" 中所有Tensor 的第一个维度均为1且输入数据dtype=[int16,int32,float16,float32,float64]情况下的特例,因此我们完全可以参照Concat算子的源码进行开发,并且可以做一定的简化。这也就是为什么Issue中说ParallelConcat在某些情况下可以提供性能优势的原因。
(3) 算子源码实现
由于算子源码包含多个文件,并且代码量较大,故此处只列举核心代码,其余代码可参考Concat 已合入代码 PR :
① 算子前端Python接口定义文件:
② 算子C++侧输出推导文件
InferType:
判断 "values" 是否为一个tuple或list:
判断 "values" 中所有Tensor的dtype是否相同:
判断 "values" 中所有Tensor的dtype是否为支持的数据类型:
InferShape:
判断输入 "values" 的长度是否大于等于1
判断输入Tensor的第一个维度是否为1:
Concat算子对于输入Tensor的第一个维度无要求,因此ParallelConcat算子需要添加这一检验。
判断输入 "values" 中所有Tensor的Shape是否相同:
Concat算子对于输入Tensor的第一个维度无要求,因此ParallelConcat算子需要添加这一检验。
③ 算子底层实现文件
④ 算子反向实现文件
经查阅文档和源码,ParallelConcat算子无反向
⑤ 算子Python接口单元测试文件
未查询到Concat算子的接口单元测试文件,因此ParallelConcat算子需要添加这一文件。
三、 算子开发流程
本章会先对本次ParallelConcat算子需要开发的文件进行一个简要介绍,然后参照Concat算子,以伪代码的形式详细阐述ParallelConcat算子各个文件的开发思路和实现逻辑。
1. 算子开发文件
本次ParallelConcat算子需要开发的文件以及内容简介如下:
(1) 算子前端Python接口定义文件
Python侧算子原语都定义在MindSpore仓的mindspore/python/mindspore/ops/operations/文件夹中,根据算子的不同功能分类定义在不同的python文件中。具体而言ParallelConcat算子属于array ops,因此需要定义在array_ops.py文件中。注册完算子,需要在 mindspore/pyth on/mindspore/ops/operations/__init__.py 中对应的算子类别中添加算子名。
① mindspore/ops/operations/array_ops.py文件
• 定义算子名;
• 编写初始化_init_方法。
② mindspore/ops/operations/_init_.py文件
• 导入算子接口。
(2) 算子C++侧输出推导文件
每个算子有各自的特定功能,算子对输入的shape和type可能有特定的要求,输入数据通过算子计算以后也可能会使shape和dype改变。因此,我们需要了解算子的功能和大致计算逻辑,对 每个算子输入进行合理校验,以及对输出的shape和type给出推理结果。
① mindspore/core/ops/parallel_concat.h文件
• 声明算子类与接口函数;
• 定义字符串常量表达式,内容为算子名称;
• 定义类的构造函数,添加输入输出的Name;
• 定义类的析构函数;
• 宏定义MIND_API_BASE_MEMBER ()。
• 定义算子推理函数。
② mindspore/core/ops/parallel_concat.cc文件
• 实现类外算子推理函数;
• 实现InferShape()函数,对输入的Shape进行校验并推导输出Shape;
• 实现InferType()函数,对输入的Type进行校验并推导输出Type;
• 宏定义REGISTER_PRIMITIVE_EVAL_IMPL用于注册算子推理函数;
③ mindspore/core/base/core_ops.h文件
• 定义PrimitivePtr。
(3) 算子底层实现文件
① mindspore/ccsrc/plugin/device/cpu/parallel_concat_cpu_kernel.h文件
• 注册算子,CPU算子类继承于NativeCpuKernelMod,并创建在mindspore/kernel 命名空间下;
• 定义构造函数和析构函数;
• 声明InitKernel和Launch函数;
• 使用override 定义类保护函数GetOpSupport,用于框架获取可支持的数据类型。
② mindspore/ccsrc/plugin/device/cpu/parallel_concat_cpu_kernel.cc文件
算子实现,算子具体实现类是继承自 NativeCpuKernelMod 类的子类,需根据算子实际情况,重写 InitKernel ()、和 Launch()等方法,主要功能如下:
• 实现InitKernel ()函数,进行算子输入输出 shape 和属性值的获取、format 转换,并根据 shape 信息,计算输入输出以及 workspace 的内存需求。
• 实现Launch()函数,完成算子核心功能的调用执行。
• 实现LaunchKernel()模板函数,实现算子的计算逻辑。
• 实现GetOpSupport()函数,注册算子所支持的数据类型,对于各种可变类型组合,需要进行补齐,详细方法可以参考当前库里源码。
• MS_KERNEL_FACTORY_REG注册宏将算子类注册到工厂中。
(4) 算子反向实现文件
经查阅文档和源码,ParallelConcat算子无反向
(5) 算子Python接口单元测试文件
① tests/ut/python/ops/test_ops.py文件
新增算子在tests/ut/python/ops/test_ops.py中添加算子ut用例。文件中区分了test_case_math_ops、test_case_nn_ops、test_case_array_ops和test_case_other_ops等用例列表,具体而言ParallelConcat算子在test_case_array_ops用例列表中添加。
2. 算子python侧前端接口开发
① mindspore/ops/operations/array_ops.py文件
• 定义算子名:
• 编写初始化_init_方法:
② mindspore/ops/operations/_init_.py文件
• 导入算子接口。
3. 算子C++侧前端接口开发
① mindspore/core/ops/parallel_concat.h文件
• 声明算子类与接口函数:
• 定义字符串常量表达式,内容为算子名称:
• 定义类的构造函数,添加输入输出的Name:
• 定义类的析构函数:
• 宏定义MIND_API_BASE_MEMBER ():
• 定义算子推理函数:
② mindspore/core/ops/parallel_concat.cc文件
• 实现类外算子推理函数:
• 实现InferShape()函数,对输入的Shape进行校验并推导输出Shape:
• 实现InferType()函数,对输入的Type进行校验并推导输出Type:
• 宏定义REGISTER_PRIMITIVE_EVAL_IMPL用于注册算子推理函数:
③ mindspore/core/base/core_ops.h文件
• 定义PrimitivePtr:
4. 算子cpu_kernel底层核函数开发
① mindspore/ccsrc/plugin/device/cpu/parallel_concat_cpu_kernel.h文件
• 注册算子,CPU算子类继承于NativeCpuKernelMod,并创建在mindspore/kernel 命名空间下:
• 定义构造函数和析构函数:
• 声明InitKernel和Launch函数:
• 使用override 定义类保护函数GetOpSupport,用于框架获取可支持的数据类型:
② mindspore/ccsrc/plugin/device/cpu/parallel_concat_cpu_kernel.cc文件
算子实现,算子具体实现类是继承自 NativeCpuKernelMod 类的子类,需根据算子实际情况,重写 InitKernel ()、和 Launch()等方法,主要功能如下:
• 实现InitKernel ()函数,进行算子输入输出 shape 和属性值的获取、format 转换,并根据 shape 信息,计算输入输出以及 workspace 的内存需求:
• 实现Launch()函数,完成算子核心功能的调用执行:
• 实现LaunchKernel()模板函数,实现算子的计算逻辑:
• 实现GetOpSupport()函数,注册算子所支持的数据类型,对于各种可变类型组合,需要进行补齐,详细方法可以参考当前库里源码:
• MS_KERNEL_FACTORY_REG注册宏将算子类注册到工厂中:
5. 交付件准备
(1) 算子接入设计文档
算子接入设计文档主要包括算子Python侧接口描述与功能介绍,算子输入输出参数说明,算子Infer推导流程,算子异常处理。这些内容本申请书均已包含,故只需要稍加修改即可。
(2) so文件
MindSpore CPU正向算子开发需要交付算子的运行so文件,具体而言ParallelConcat算子so文件为:libcpu_kernels_ParallelConcat.so,该文件需要向导师领取,再将其上传到测试服务器中,经过编译后会自动将其存储到conda/envs/xxx/lib/python3.7/site-packages/mindspore /lib/路径下,以支持该算子在CPU环境下的运行和测试。
(3) doctest
doctest是用于检测算子接口注释是否符合规范以及样例Example输出是否正确,其具体流程如下:
① 进入自己conda环境中的mindspore包:
cd /xxx/xxx/.conda/envs/xxx/lib/python3.7/site-packages/mindspore
② 在当前路径传入conftest.py文件
③ 修改conftest.py文件中导入的operation类型:
④ 进入算子python侧前端接口定义目录:
cd /xxx/xxx/.conda/envs/xxx/lib/python3.7/site -packages/mindspore/ops/operations
⑤ 在当前目录下传入需要doctest的算子接口文件array_ops_parallel_concat.py:
⑥ 将ParallelConcat算子的前端定义复制粘贴到上面创建的array_ops_ parallel_concat.py文件中,并且要增加支持的平台类型
⑦ 在此路径下执行doctest命令:
pytest --disable-warnings -vra --doctest-modules -o doctest_optionflags=NORMALIZE_ WHITESPACE --tb=long array_ops_ parallel_concat.py
⑧ 如果通过则输出绿色passed,如果报错则根据报错信息修改注释
(4) 接口注释网页自验证
接口注释网页自验证是为了能够直观检查算子合入后的前端接口文档是否存在错误以及是否满足规范。其具体流程可参照:接口注释网页自验证指南
其具体流程如下:
① 下载MindSpore Docs仓代码:这里注意,由于官方指南为旧版本,因此其中一些文件路径对应不上,因此建议clone旧版的Docs仓代码,例如:
git clone -b r1.6 https://gitee.com/mindspore/docs.git
② 进入api目录,安装该目录下requirements.txt文件中的依赖项:
cd docs/mindspore/api
pip install -r requirements.txt
③ 在docs仓中添加新增算子接口名:
ops接口在docs/mindspore/api/source_zh_cn/api_python/operations.rst文件中添加。需要注意算子对应功能的类别,具体而言ParallelConcat算子需要在Array Operators中增加一行新增接口mindspore.ops.ParallelConcat:
④ 在api目录下执行如下命令,完成后会新建build_zh_cn/html目录,该目录中存放了生成后的文档网页:
make html
⑤ 下载完整的build_zh_cn/html包,并使用浏览器打开index.html文件在其中找到mindspore.ops.ParallelConcatt算子的前端接口文件并截图保存。
(5) 测试报告
测试报告主要依照第5小节的测试用例来编写,需要包括测试用例的数量,测试用例覆盖的shape和dtype情况,并且需要包含Graph静态图和Pynative动态图两个模式的测试结果。最后需要填写测试报告表,对于表中列举的输入,输出以及属性测试结果进行填写,并在备注栏附加测试用例名,方便测试人员对照测试。
四、 参考文献
[1] 开源之夏-昇思MindSpore CPU正向算子开发:ParallelConcat项目详情页:
https://summer-ospp.ac.cn/#/org/prodetail/221cb0311
[2] MindSpore官网:
[3] MindSpore CPU算子开发指南:
[4] MindSpore官方Gitee代码托管仓库:
https://gitee.com/mindspore/mindspore
[5] MindSpore官方Github代码托管仓库:
https://github.com/mindspore-ai/mindspore
[6] MindSpore官方论坛:
https://bbs.huaweicloud.com/forum/forum-1076-1.html
[7] PyTorch官方文档:
https://pytorch.org/docs/stable/index.html
[8] PyTorch中文文档:
https://pytorch-cn.readthedocs.io/zh/latest
[9] PyTorch官方Github代码托管仓库:
https://github.com/pytorch/pytorch
[10] TensorFlow官方文档:
https://tensorflow.google.cn/versions
[11] TensorFlow中文文档:
[12] TensorFlow官方Github代码托管仓库:
[13] MindSpore ParallelConcat算子接口文档:
https://www.mindspore.cn/docs/api_python/ops/mindspore.ops.ParallelConcat.html
[14] MindSpore ParallelConcat GPU算子开发Issue:
https://gitee.com/mindspore/mindspore/issues/I4ZZSB?from=project-issue
[15] MindSpore ParallelConcat GPU算子开发PR:
https://gitee.com/mindspore/mindspore/pulls/41299
[16] MindSpore ParallelConcat TBE算子开发PR:
https://gitee.com/mindspore/min d spore/pulls/2811/files
https://gitee.com/mindspore/mind s pore/pulls/3029/files
[17] MindSpore Concat算子接口文档:
[18] MindSpore Concat AICPU算子开发Issue:
https://gitee.com/mindspore/mindspore/issues/I48O7Z?from=project-issue
[19] MindSpore Concat AICPU算子开发PR:
https://gitee.com/mindspore/mindspore/pulls/26772/files
[20] MindSpore CPU算子交付件模板下载链接:
[21] MindSpore CPU算子交付件doctest以及网页接口自验证指南:
[22] MindSpore CPU算子交付件动态shape测试dynamic_test测试用例编写指南:
[23] MindSpore CPU算子代码与交付件自验证checklist链接:
- 点赞
- 收藏
- 关注作者
评论(0)