征服 MindSpore(一): IR 图再多也吃得下!

  1. 开场白
  2. 访谈环节
    1. 为什么我们总是找用户拿 IR 图?
    2. 基于 IR 图的简单问题分析
    3. 更进一步 - 探索 IR图
    4. IR 的功能是什么?
  3. 自由提问环节
  4. 大腿带躺环节
    1. 功能分析
    2. 功能实现
      1. 基于脚手架搭建项目
      2. 实现语法高亮
      3. 实现定义查找
      4. 实现引用查找
  5. 免责声明
  6. 致谢

开场白

主持人致辞】: 大家好,欢迎大家莅临本期的《重生之大腿带我征服 mindspore》栏目。今天,我们很荣幸地邀请到并行组的大佬-->首座带领我们阅读关于IR图信息的学习和实践,欢迎首座!!
首座】: 谢谢大家~ 呵呵呵。
观众】:掌声鼓励!
============【快速进入正题】 ==========

访谈环节

为什么我们总是找用户拿 IR 图?

问题1】: 首座,咱们定位问题的时候,老找用户拿 IR 图,那这个IR图到底是什么呢?
首座】 :IR 图本质上是描述源代码的程序表示,在 mindspore 中,其以静态单赋值形式(SSA)的形式表现。
具体到 mindspore 来说,就是描述咱们写的程序经过程序解析后的中间形态,通过这个中间形态,可以看到源代码被转换成什么样子。也就是说,我们可以从这个 IR 图上看到算子(计算、通信)的输入和输出,以便和源程序的逻辑进行比对,看是否符合咱们的预期。

【主持人】:哦哦,那我明白了,其实就是把咱们的源程序转换成一种通用的形式,所以它的逻辑肯定要和源程序一致。
【首座】:是的。

=============== 我是分割线 =================

基于 IR 图的简单问题分析

============【举例分析】============

问题2】:咱们的观众很多都是工程师,擅长从问题入手,首座你可以举一个使用 IR 图进行问题分析的例子吗?
首座】 :呵呵呵,让我浅浅思考一下。既然我来自并行组,我就举一个简单关于并行的简单报错吧。

这是一个由 Matmul 构建的简单网络,其两个参数由输入传入。

class MyMatMul(nn.Cell):
    def __init__(self):
        super().__init__()
        self.net = ops.MatMul().shard(((1, 1), (1, 1)))
 
    def construct(self, input1, input2):
        output = self.net(input1, input2)
        return output
 
input1 = ms.Tensor(np.random.random([24, 48]), ms.float16)
input2 = ms.Tensor(np.random.random([48, 36]), ms.float16)
 
net = MyMatMul()
output = net(input1, input2)
print(output)

执行该脚本出现如下报错:

ValueError: For 'MatMul' the input dimensions must be equal, but got 'x1_col': 48 and 'x2_row': 384.

查看 IR 图:

cke_254217.png

【首座】可以看到, MatMulNet construct 对应程序中的 construct 部分,但是两个参数的输入偷偷变成了原来的 8倍,这不符合咱们的预期啊! 这是为什么呢?
【观众】:mindspore 又出 bug 了 --> 笑声响起)

【主持人】: 请大家请安静,咳咳咳,首座还没说完呢。

=========== 【首座继续】 ==========

这是因为在 mindspore 中,假定从数据集获取的网络 shape 为 (A,B)(A,B), 其需要从数据集中取 (A×N,B)(A×N,B)条数据,而每张卡都取(A,B)(A,B)条数据。 这里 MatMul 的输入就是被当成了数据输入,进行了扩增,从而导致了 Tensor 变成原来的 N 倍,这里的 N 就是 卡数。

这里的解决方案就是让数据导入策略不使用数据并行导入,或者把Matmul 算子设置为 Parameter。这里给出第一种解决方法,改成 Parameter 的方式就留给大家做个探索把~。
ms.set_auto_parallel_context(parallel_mode=ms.ParallelMode.SEMI_AUTO_PARALLEL, full_batch=True)

【主持人、观众】:哦~
【某观众】:这是 bug 还是 feature?
【某观众】:嗯?还要做作业?

【首座】:(内心 OS:我靠,想做作业是吧,多给你2题)

=============== 我是分割线 =================

更进一步 - 探索 IR图

【问题3】首座刚才带我们做了一点热身,但是大家看起来才刚刚入门,首座能不能提供更详细的一些入门资料,帮助大家理解呢?
【首座】没问题

在座的各位都是攻城狮,都有自己擅长的编程语言,每个编程语言有自己的语法,IR 图也是。

MindIR 是一种基于图表示的函数式IR,其最核心的目的是服务于自动微分变换。而自动微分采用的是基于函数式编程框架的变换方法,ANF是函数式编程中常用且简洁的中间表示。借鉴于ANF 语法,MindIR 的文法长这样:

<ANode> ::= <ValueNode> | <ParameterNode>
<ParameterNode> ::= Parameter
<ValueNode> ::= Scalar | Named | Tensor | Type | Shape | Primitive | MetaFuncGraph | FuncGraph 
<CNode> ::= (<AnfNode> …)
<AnfNode> ::= <CNode> | <ANode>

左侧可以简单地理解为父类,右侧是它的子类。--》除了第4条。
第4条表示CNode 是一个包含多个 AnfNode 的列表。

  • MindIR中 CNode 对应于ANF的复合表达式,表示一次函数调用

    • 像MatMul 的计算,其就是一个函数调用。第一个参数是 MatMul 这个算子,第二个、第三个参数是输入,其他输入则是相关属性。 ---》 这里调通之前的用例就可以拿到这个 IR

      cke_265631.png

  • MindIR中的 Anode 对应于ANF的原子表达式,ANode有两个子类分别为 ValueNode 和 ParameterNode。

    • ValueNode表示常数节点,可承载一个常数值(标量、符号、张量、类型、维度等),也可以是一个原语函数(Primitive)或一个元函数(MetaFuncGraph)或一个普通函数(FuncGraph),因为在函数式编程中函数定义本身也是一个值。
    • ParameterNode 是参数节点,表示函数的形参。

=============== 我是分割线 =================

IR 的功能是什么?

【问题】首座,现在大家对 IR 图的概念有一定的了解,但是有同学在存图后,发现对应目录下有很多 ir,这些 ir 图的功能分别是什么呢?
【首座】这个问题问得很好,我们组内很多新员工也有这样的疑问。首先,大家在存图的时候是设置的图模式,另外一个模式是 Pynative。 图模式的好处之一就是能够在编译的时候,获取到完整的图信息,以便我们根据整图结构进行更好地优化,而在动态图的时候,就无法拿到这些信息。

那么,对于图进行处理,就需要分为多个阶段,不同的阶段会对图进行相关的处理。这里只说几个大家关注度比较高的(其实是其他的阶段,作者不懂)

  • ParseAction: 解析用户脚本,这对应了 parse_xxx.ir
  • abstact specialize: 这里会对用户脚本中不同算子的 abstract 进行推导,abstract包含(shape/type/value)。在 Python/C++ 侧的算子就有 InferShape 和 InferType 等函数用于实现该功能。
  • inline:在网络脚本中,通常我们是以 Cell 为单位进行解析,那么这个图的信息太多,因此这里会把若干张小图 inline 成若干个大图。
  • optimize: 这里主要做的是设备无关的相关优化,设备无关指的是通用类优化,和不同的后端(如ascend、gpu)无关。这里会执行大量的 pass 对整图结构进行调整,用于优化性能。并行的相关处理也在这个阶段,大家跑多机多卡的时候,通常都会挂在这个阶段。
    • 由于并行调整幅度比较大,所以在执行前后分别会保存 step_parallel_begin 和 step_parallel_end 这两张图,用于比对关于图的调整是否正确。

      cke_272184.png


      大家看上面的这些截图,其实这些图前面的序号就是按照不同阶段的执行顺序。

自由提问环节

============= 【提问环节】 ============【问题一】:首座,您好,我是小来。Mindspore 主要推行的是静态图模式,而torch 类似动态图模式,我想请问下静态图相较于动态图,它的优劣势分别是什么?

【首座】:优势大概是这样的:静态图能够获得图的全局信息,包含节点的顺序和数据类型,可以有针对性的进行优化,这里的优化可以是算子执行顺序的调整、内存优化等,最终的目的都是为了更快的执行速度。

劣势主要还是在于开发/调试方面:

  • 首先,torch 是单算子下发执行的,调测效率比较高。例如,torch 需要打印数据的时候,可以直接 print 对应的数据,而 mindspore 在这方面支持还比较有限(据可靠消息,在最新的 2.3 版本上已经支持了)。
  • 其次,静态图里面的图,意味着固定的 pattern,这也就意味着自由度受限。如果出来一种新模式,有比较大的概率可能是无法处理的。所以这就对 静态图框架的开发者有较高的要求,如果相关的 pass 没有处理好一些 corner case,可能会出现意料之外的错误。

【接着提问】:那是不是基于动态图开发,然后转换为静态图训练或者部署?这样鱼与熊掌兼得啊!

【首座】:是的,咳咳咳,但是你知道的,理想与现实总是会存在 gap 的。据我所知,在2.3及后续版本,会逐步聚焦易用性的开发,构建好生态,敬请大家期待。

大腿带躺环节

本章节,大腿带我们实现基于 IR 图的解析功能

功能分析

在 IR 图中,主要有如下定义:

  • Parameter: 以 %para 开头
  • 算子:以 %开头
  • 子图:通常以数字开头

cke_278740.png

如上图所示,%22 就是被标记的 Load 算子,它用于加载 para69 开头的 embedding_table。

如下图所示,在 subgraph instance 这个属性后跟着的就是子图的名称 43_CR_CR_model_pangu_alpha_PanGUAlphaWithLoss_construct_27676, 其使用通常以 call @43_CR_CR_model_pangu_alpha_PanGUAlphaWithLoss_construct_27676 的形式存在

cke_285431.png

对于某一个参数/算子/子图,查找它的定义和引用是一个很常见的需求。
例如,在并行的 pass 中,需要确认算子 shape 是否符合预期,如果不符合预期,就需要沿着当前算子,找到第一个不符合预期的算子,分析根因。

查找引用则是需要确认当前参数/算子/子图的调用,常用于特性开发的情况。

另外,IR 图是没有颜色的,而在平时编程时,是会对变量、函数等做高亮,方便识别。因此对于 IR 图,我们需要对关注的信息进行高亮,方便快速识别, 这些包含:

  • 变量: 参数
  • 函数: 算子名、子图
  • 关键字:call、Partial
  • 其他: 算子信息(Shape,type)

功能实现

基于脚手架搭建项目

这里主要基于官方脚手架进行基础搭建

注意,这里还需要安装 npm。

当前 IR 图只支持 .ir 类型,我们需要在 package.json 文件的 contributes 中添加 languages,来支持 .ir 文件,否则无法触发。

cke_292210.png

这里还需要配置 language-configuration.json:

{
    "comments": {
        // symbol for single line comment
        "lineComment": "//",
        // symbol for a block comment
        "blockComment": ["/*", "*/"]
    },
    "brackets": [
        ["{", "}"],
        ["[", "]"],
        ["(", ")"]
    ],
    "autoClosingPairs": [
        { "open": "{", "close": "}" },
        { "open": "[", "close": "]" },
        { "open": "(", "close": ")" },
        { "open": "\"", "close": "\"" },
        { "open": "'", "close": "'" }
    ],
    "surroundingPairs": [
        { "open": "{", "close": "}" },
        { "open": "[", "close": "]" },
        { "open": "(", "close": ")" },
        { "open": "\"", "close": "\"" },
        { "open": "'", "close": "'" }
    ]
}

实现语法高亮

实现定义查找
实现引用查找

免责声明

本文中提到的人物对话【纯属虚构】的,如有雷同,纯属巧合,请【勿对号入座】。

感谢本期特邀嘉宾:首座
感谢本栏目的常驻嘉宾:xxx、xxx。
感谢以下工作人员:我自己。
感谢观众。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值