{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 全场景统一架构\n", "\n", "[![查看源文件](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.3.q1/resource/_static/logo_source.svg)](https://gitee.com/mindspore/docs/blob/r2.3.q1/docs/mindspore/source_zh_cn/design/all_scenarios.ipynb)\n", "\n", "MindSpore旨在提供端边云全场景的AI框架。MindSpore可部署于端、边、云不同的硬件环境,满足不同环境的差异化需求,如支持端侧的轻量化部署,支持云侧丰富的训练功能如自动微分、混合精度、模型易用编程等。\n", "\n", "> 云侧包括NVIDIA GPU、Huawei Ascend、Intel x86等,端侧包括Arm、Qualcomm、Kirin等。\n", "\n", "![intro](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.3.q1/docs/mindspore/source_zh_cn/design/images/all_scenarios_intro.png)\n", "\n", "## 全场景重要特性\n", "\n", "MindSpore全场景的几个重要特性:\n", "\n", "1. 端边云统一的C++推理接口,支持算法代码可快速迁移到不同硬件环境执行,如[基于C++接口实现端侧训练](https://mindspore.cn/lite/docs/zh-CN/r2.3.0rc1/quick_start/train_lenet.html)。\n", "2. 模型统一,端云使用相同的模型格式和定义,软件架构一致。MindSpore支持Ascend、GPU、CPU(x86、Arm)等多种硬件的执行,一次训练多处部署使用。\n", "3. 多样化算力支持。提供统一的南向接口,支持新硬件的快捷添加使用。\n", "4. 模型小型化技术,适配不同硬件环境和业务场景的要求,如量化压缩等。\n", "5. 端边云协同技术的快速应用,如[联邦学习](https://mindspore.cn/federated/docs/zh-CN/master/index.html)、[端侧训练](https://mindspore.cn/lite/docs/zh-CN/r2.3.0rc1/use/runtime_train.html)等新技术。\n", "\n", "## 全场景支持模式\n", "\n", "![train-process](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.3.q1/docs/mindspore/source_zh_cn/design/images/all_scenarios_train_process.png)\n", "\n", "如上图所示,在MindSpore上训练出来的模型文件,可通过Serving部署在云服务中执行,也可用过Lite执行在服务器、端侧等设备上。同时Lite支持通过独立工具convert进行模型的离线优化,实现推理时框架的轻量化以及模型执行高性能的目标。\n", "\n", "MindSpore抽象各个硬件下的统一算子接口,因此,在不同硬件环境下,网络模型的编程代码可以保持一致。同时加载相同的模型文件,在MindSpore支持的各个不同硬件上均能有效执行推理。\n", "\n", "推理方面考虑到大量用户使用C++/C编程方式,提供了C++的推理编程接口,相关编程接口在形态上与Python接口的风格较接近。\n", "\n", "同时,通过提供第三方硬件的自定义离线优化注册,第三方硬件的自定义算子注册机制,实现快速对接新的硬件,同时对外的模型编程接口以及模型文件保持不变。\n", "\n", "## 中间表示MindIR\n", "\n", "### 简介\n", "\n", "中间表示(IR)是程序编译过程中介于源语言和目标语言之间的程序表示,以方便编译器进行程序分析和优化,因此IR的设计需要考虑从源语言到目标语言的转换难度,同时考虑程序分析和优化的易用性和性能。\n", "\n", "MindIR是一种基于图表示的函数式IR,其最核心的目的是服务于自动微分变换。自动微分采用的是基于函数式编程框架的变换方法,因此IR采用了接近于ANF函数式的语义。此外,借鉴Sea of Nodes[1]和Thorin[2]的优秀设计,采用了一种基于显性依赖图的表示方式。关于ANF-IR的具体介绍,可以参考[MindSpore IR文法定义](https://www.mindspore.cn/docs/zh-CN/r2.3.0rc1/design/all_scenarios.html#文法定义)。\n", "\n", "在图模式`set_context(mode=GRAPH_MODE)`下运行用MindSpore编写的模型时,若配置中设置了`set_context(save_graphs=1)`,运行时会输出一些图编译过程中生成的一些中间文件,我们称为IR文件。当需要分析更多后端流程相关的ir文件时,可以设置`set_context(save_graphs=True)` 或`set_context(save_graphs=2)`。当需要更多进阶的信息比如可视化计算图,或者更多详细前端ir图时,可以设置`set_context(save_graphs=3)`。当前主要有三种格式的IR文件:\n", "\n", "- ir后缀结尾的IR文件:一种比较直观易懂的以文本格式描述模型结构的文件,可以直接用文本编辑软件查看。\n", "- dot后缀结尾的IR文件:描述了不同节点间的拓扑关系,可以用[graphviz](http://graphviz.org)将此文件作为输入生成图片,方便用户直观地查看模型结构。对于算子比较多的模型,推荐使用可视化组件[MindSpore Insight](https://www.mindspore.cn/mindinsight/docs/zh-CN/r2.3/dashboard.html#计算图可视化)对计算图进行可视化。\n", "\n", "### 文法定义\n", "\n", "ANF是函数式编程中常用且简洁的中间表示,其文法定义如下所示:\n", "\n", "```text\n", " ::= NUMBER | STRING | VAR | BOOLEAN | PRIMOP\n", " | (lambda (VAR …) )\n", " ::= ( …)\n", " | (if )\n", " ::= (let ([VAR ]) ) | | \n", "\n", "```\n", "\n", "ANF中表达式分为原子表达式(aexp)和复合表达式(cexp),原子表达式表示一个常数值或一个变量或一个匿名函数;复合表达式由多个原子表达式复合组成,表示一个匿名函数或原语函数调用,组合的第一个输入是调用的函数,其余输入是调用的参数。\n", "\n", "MindIR文法继承于ANF,其定义如下所示:\n", "\n", "```text\n", " ::= | \n", " ::= Parameter\n", " ::= Scalar | Named | Tensor | Type | Shape\n", " | Primitive | MetaFuncGraph | FuncGraph\n", " ::= ( …)\n", " ::= | \n", "```\n", "\n", "MindIR中的ANode对应于ANF的原子表达式,ANode有两个子类分别为ValueNode和ParameterNode。ValueNode表示常数节点,可承载一个常数值(标量、符号、张量、类型、维度等),也可以是一个原语函数(Primitive)或一个元函数(MetaFuncGraph)或一个普通函数(FuncGraph),因为在函数式编程中函数定义本身也是一个值。ParameterNode是参数节点,表示函数的形参。\n", "\n", "MindIR中CNode对应于ANF的复合表达式,表示一次函数调用。\n", "\n", "在MindSpore自动微分时,会计算ParameterNode和CNode的梯度贡献,并返回最终ParameterNode的梯度,而不计算ValueNode的梯度。\n", "\n", "### 示例\n", "\n", "下面以一段程序作为示例,对比理解MindIR。" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "def func(x, y):\n", " return x / y\n", "\n", "@ms.jit\n", "def test_f(x, y):\n", " a = x - 1\n", " b = a + y\n", " c = b * func(a, b)\n", " return c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "这段Python代码对应的ANF表达为:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```text\n", "lambda (x, y)\n", " let a = x - 1 in\n", " let b = a + y in\n", " let func = lambda (x, y)\n", " let ret = x / y in\n", " ret end in\n", " let %1 = func(a, b) in\n", " let c = b * %1 in\n", " c end\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "对应的MindIR为[ir.dot](https://gitee.com/mindspore/docs/blob/r2.3.q1/docs/mindspore/source_zh_cn/design/images/ir/ir.dot):\n", "\n", "![image1](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.3.q1/docs/mindspore/source_zh_cn/design/images/ir/ir.png)\n", "\n", "在MindIR中,一个函数图(FuncGraph)表示一个普通函数的定义,函数图一般由ParameterNode、ValueNode和CNode组成有向无环图,可以清晰地表达出从参数到返回值的计算过程。在上图中可以看出,python代码中两个函数`test_f`和`func`转换成了两个函数图,其参数`x`和`y`转换为函数图的ParameterNode,每一个表达式转换为一个CNode。CNode的第一个输入链接着调用的函数,例如图中的`add`、`func`、`return`。值得注意的是这些节点均是`ValueNode`,因为它们被理解为常数函数值。CNode的其他输入链接这调用的参数,参数值可以来自于ParameterNode、ValueNode和其他CNode。\n", "\n", "在ANF中每个表达式都用let表达式绑定为一个变量,通过对变量的引用来表示对表达式输出的依赖,而在MindIR中每个表达式都绑定为一个节点,通过节点与节点之间的有向边表示依赖关系。\n", "\n", "### 函数式语义\n", "\n", "MindIR较传统计算图的一个重要特性是不仅可以表达算子之间的数据依赖,还可以表达丰富的函数式语义。\n", "\n", "#### 高阶函数\n", "\n", "在MindIR中,函数的定义是由一个子图来定义,但其本身可以是一个被传递的值,作为其他高阶函数的输入或输出。\n", "例如下面一个简单的示例中,函数`f`作为参数传入了函数`g`,因此函数`g`是一个接收函数输入的高阶函数,函数`f`真正的调用点是在函数`g`内部。" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "@ms.jit\n", "def hof(x):\n", " def f(x):\n", " return x + 3\n", " def g(function, x):\n", " return function(x) * function(x)\n", " res = g(f, x)\n", " return res" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "对应的MindIR为[hof.dot](https://gitee.com/mindspore/docs/blob/r2.3.q1/docs/mindspore/source_zh_cn/design/images/ir/hof.dot):\n", "\n", "![image2](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.3.q1/docs/mindspore/source_zh_cn/design/images/ir/hof.png)\n", "\n", "在实际网络训练脚本中,自动求导泛函`grad`和优化器中常用到的`Partial`和`HyperMap`都是典型的高阶函数。高阶语义极大地提升了MindSpore表达的灵活性和简洁性。\n", "\n", "#### 控制流\n", "\n", "控制流在MindIR中是以高阶函数选择调用的形式表达。这样的形式把控制流转换为高阶函数的数据流,从而使得自动微分算法更加强大。不仅可以支持数据流的自动微分,还可以支持条件跳转、循环和递归等控制流的自动微分。\n", "\n", "下面以一个简单的斐波那契用例来演示说明。" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "@ms.jit\n", "def fibonacci(n):\n", " if n < 1:\n", " return 0\n", " if n == 1:\n", " return 1\n", " return fibonacci(n-1) + fibonacci(n-2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "对应的MindIR为[cf.dot](https://gitee.com/mindspore/docs/blob/r2.3.q1/docs/mindspore/source_zh_cn/design/images/ir/cf.dot):\n", "\n", "![image3](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.3.q1/docs/mindspore/source_zh_cn/design/images/ir/cf.png)\n", "\n", "其中`fibonacci`是顶层函数图,在顶层中有两个函数图被`switch`选择调用。`✓fibonacci`是第一个`if`的True分支,`✗fibonacci`是第一个`if`的False分支。在`✗fibonacci`中被调用的`✓✗fibonacci`是`elif`的True分支,`✗✗fibonacci`是`elif`的False分支。这里需要理解的关键是在MindIR中,条件跳转和递归是以高阶控制流的形式表达的。例如,`✓fibonacci`和`✗fibonacci`是作为`switch`算子的参数传入,`switch`根据条件参数选择哪一个函数作为返回值。因此,`switch`是把输入的函数当成普通的值做了一个二元选择操作,并没有调用,而真正的函数调用是在紧随`switch`后的CNode上完成。\n", "\n", "#### 自由变量和闭包\n", "\n", "闭包(closure)是一种编程语言特性,它指的是代码块和作用域环境的结合。自由变量(free variable)是指在代码块中引用作用域环境中的变量而非局部变量。在MindIR中,代码块是以函数图呈现的,而作用域环境可以理解为该函数被调用时的上下文环境,自由变量的捕获方式是值拷贝而非引用。\n", "\n", "一个典型的闭包用例如下:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "@ms.jit\n", "def func_outer(a, b):\n", " def func_inner(c):\n", " return a + b + c\n", " return func_inner\n", "\n", "@ms.jit\n", "def ms_closure():\n", " closure = func_outer(1, 2)\n", " out1 = closure(1)\n", " out2 = closure(2)\n", " return out1, out2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "对应的MindIR为[closure.dot](https://gitee.com/mindspore/docs/blob/r2.3.q1/docs/mindspore/source_zh_cn/design/images/ir/closure.dot):\n", "\n", "![image4](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.3.q1/docs/mindspore/source_zh_cn/design/images/ir/closure.png)\n", "\n", "在例子中,`a`和`b`是自由变量,因为`func_inner`中变量`a`和`b`是引用的其父图`func_outer`中定义的参数。变量`closure`是一个闭包,它是函数`func_inner`与其上下文`func_outer(1, 2)`的结合。因此,`out1`的结果是4,因为其等价于`1+2+1`,`out2`的结果是5,因为其等价于`1+2+2`。\n", "\n", "### 参考文献\n", "\n", "[1] C. Click and M. Paleczny. A simple graph-based intermediate representation.\n", "SIGPLAN Not., 30:35–49, March 1995.\n", "\n", "[2] Roland Leißa, Marcel Köster, and Sebastian Hack. A graph-based higher-order intermediate\n", "representation. In Proceedings of the 13th Annual IEEE/ACM International Symposium on\n", "Code Generation and Optimization, pages 202–212. IEEE Computer Society, 2015." ] } ], "metadata": { "kernelspec": { "display_name": "MindSpore", "language": "python", "name": "mindspore" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 4 }