Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python support register pass via PassDesc #35602

Merged
merged 5 commits into from Sep 16, 2021

Conversation

Avin0323
Copy link
Contributor

@Avin0323 Avin0323 commented Sep 8, 2021

PR types

New features

PR changes

Others

Describe

PR主要功能:针对fusion等子图替换场景,支持Python侧开发并注册Pass。

背景

  • Pass是指输入一个深度学习计算图Graph,依照一定条件进行修改,输出修改后的Graph的过程;
  • 当前PaddlePadle框架编写Pass代码存在以下问题:
    • 用户需要手写Graph的条件匹配、在Graph上的修改代码;
    • 对Graph操作需要深入底层框架代码,了解Graph的结构,并且知道相关Pass写法;
  • 我们提出了针对fusion等子图替换类Pass的优化方案以支持用户在Python侧开发注册Pass,提升二次开发体验:
    • 用户只需要输入匹配和替换的子图描述,由深度学习框架编写的代码来生成匹配和替换的逻辑,不需要用户对Graph进行匹配和替换操作;
    • API级别的替换,用户可以通过Paddle的Python API构造子图,从而不需要知道Graph的结构,也能写Paddle的Graph Pass代码;

方案设计

子图替换类Pass规则描述

子图替换类型的Pass进行的图变换如下图所示(实线表示数据流,虚线表示变换前后的映射):

image

针对图变换场景,描述一个Pass规则主要需要以下4部分内容:

  1. 匹配子图:用于匹配原计算图的子图;
  2. 替换子图:用于替换匹配子图的子图;
  3. 输入/输出映射:指示图变换后数据映射关系;
  4. 算子属性映射:指示图变换后算子属性的映射关系;

其中匹配/替换子图也可以看做是一个Graph,框架中Graph由ProgramDesc生成,这里我们也采用ProgramDesc表示子图。

针对如上的规则描述,我们使用protobuf创建PassDesc的数据结构来保存,详细内容见paddle/fluid/framework/pass_desc.proto定义文件。

用于Python侧Pass注册

Python侧支持使用两种方式用于开发Pass,类似定义program的方式来定义匹配/替换子图:

  1. 直接使用Paddle Python API;
  2. 使用辅助类型;

提供辅助类型开发的原因在于:

  • 存在部分算子并未开放用户API,辅助类型用于可以“访问”这部分算子;
  • 当替换子图中算子存在属性映射在匹配图中的情况时,辅助类型用于指示这些映射情况;

定义子图通过定义一个函数完成,函数参数为子图的输入数据,返回值为输出数据,一个示例如下:

// 使用Paddle Python API方式
def pattern(x, y1, y2):
        mul1 = paddle.matmul(x, y1)
        mul2 = paddle.matmul(x, y2)
        return mul1, mul2

// 使用辅助类型方式
def pattern(x, y1, y2):
        mul1 = ir.PassDesc.OP.matmul_v2(x, y1)
        mul2 = ir.PassDesc.OP.matmul_v2(x, y2)
        return mul1, mul2
Python侧开发Pass用户接口

Pass注册使用函数装饰器的方式完成,RegisterPass用于修改一个Pass注册函数,将函数名作为Pass类型,用于后续对应该Pass实例。

当定义子图需要指定InputSpec时,通过RegisterPass中添加input_specs参数完成。

Pass注册函数参数为空,返回值为一对或多对匹配、替换子图函数。

注册代码示例如下:

@ir.RegisterPass
def generate_add_n():
    def pattern(x, y, z):
        return paddle.add(paddle.add(x, y), z)

    def replace(x, y, z):
        return paddle.add_n([x, y, z])

    return pattern, replace

@paddle-bot-old
Copy link

paddle-bot-old bot commented Sep 8, 2021

Thanks for your contribution!
Please wait for the result of CI firstly. See Paddle CI Manual for details.

@Avin0323 Avin0323 changed the title [WIP]python support register pass via PassDesc Python support register pass via PassDesc Sep 14, 2021
paddle/fluid/framework/pass_desc.proto Show resolved Hide resolved
paddle/fluid/framework/pass_desc.proto Show resolved Hide resolved
python/paddle/fluid/ir.py Show resolved Hide resolved
python/paddle/fluid/ir.py Outdated Show resolved Hide resolved
python/paddle/fluid/ir.py Show resolved Hide resolved
python/paddle/fluid/ir.py Outdated Show resolved Hide resolved
Copy link
Member

@zhhsplendid zhhsplendid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@zhhsplendid zhhsplendid merged commit bab39eb into PaddlePaddle:develop Sep 16, 2021
@Avin0323 Avin0323 deleted the py-pass branch September 16, 2021 03:13
zhhsplendid pushed a commit that referenced this pull request Sep 17, 2021
#### 背景

#35602 提供Python侧开发子图替换类Pass的方式:

- 利用Paddle Python API或者辅助类型定义子图program用来匹配/替换图;
- Python侧注册Pass时,将注册函数最终转换为protobuf定义的PassDesc数据形式,供C++侧进行解析完成Pass实例注册。

本PR即为根据PassDesc规则描述解析生成Pass实例。

#### 方案设计

##### Pass规则验证

在以往的Pass开发中,会存在随着算子迭代引发的匹配失效或者错误匹配的问题,该问题可以通过扫描算子支持的参数设置及参数类型等来判断是否应该使用该Pass或者给出提示需要修改Pass代码。

当前Pass开发中提供了算子兼容性OpCompatSensiblePass用于解决上述问题。但同时还存在不足:由于以往Pass开发在运行时才能获取到pattern信息,所以需要在执行Pass时才可以判断。

使用PassDesc表示的Pass可以在执行Pass前验证上述问题,这个过程在VerifyDesc中完成。

##### 根据匹配子图构造pattern

GeneratePass对于图匹配和替换使用GraphPatternDecetor完成,构造匹配pattern实际上就是将对应对象成员PDPattern中添加PDNode和边关系。该过程在函数`InitGeneratePattern`中完成,该函数没有作为GeneratePass的成员方法,主要出于后续可能开发新的Decetor考虑,GeneratePass与Decetor的操作是没有关联的。

初始化pattern主要通过遍历匹配子图program的全部算子实现:

1. 添加当前算子对应PDNode及限制条件(算子类型、属性限制等);
2. 遍历当前算子对应输入并从pattern中尝试获取PDNode:
   - 在pattern中获取到PDNode且为输出节点:表示属于匹配子图的中间节点,将该PDNode设置为中间节点;
   - 在pattern中没有获取到PDNode:添加该输入PDNode并设置作为输入节点;
   - 设置输入到算子的边关系;
3. 遍历当前算子对应输出:
   - 在pattern中获取到PDNode且为输入节点:表示属于匹配子图的中间节点,将该PDNode设置为中间节点;
   - 在pattern中没有获取到PDNode:添加该输入PDNode并设置作为输出节点;
   - 设置算子到输出的边关系;

##### 根据替换子图操作graph

替换子图操作的过程在`GetGenerateRewrite`函数中完成,与`InitGeneratePattern`类似没有作为GeneratePass的成员方法。

生成替换子图操作过程如下:

1. 判断冗余替换子图;
2. 遍历替换子图program的全部算子添加替换子图Node:
   1. 添加当前算子的Node及属性设置;
   2. 遍历当前算子对应输入,添加中间variable节点;
   3. 遍历当前算子对应输出,添加中间variable节点;
   4. 添加输入/输出节点与算子节点的边关系;
3. 删除匹配图中属于中间节点的Node;

##### 优化子图验证

对于替换子图或者替换后的计算图是否可以正确运行等,可以在执行Pass时验证,从而防止在后续执行计算图时出现异常。

当前Pass执行直接修改计算图,验证失败时无法很好的完成还原操作,目前子图验证暂时默认成功,留到后续改进。
niuliling123 pushed a commit to niuliling123/Paddle that referenced this pull request Sep 29, 2021
PR主要功能:针对fusion等子图替换场景,支持Python侧开发并注册Pass。

背景
Pass是指输入一个深度学习计算图Graph,依照一定条件进行修改,输出修改后的Graph的过程;
当前PaddlePadle框架编写Pass代码存在以下问题:
用户需要手写Graph的条件匹配、在Graph上的修改代码;
对Graph操作需要深入底层框架代码,了解Graph的结构,并且知道相关Pass写法;
我们提出了针对fusion等子图替换类Pass的优化方案以支持用户在Python侧开发注册Pass,提升二次开发体验:
用户只需要输入匹配和替换的子图描述,由深度学习框架编写的代码来生成匹配和替换的逻辑,不需要用户对Graph进行匹配和替换操作;
API级别的替换,用户可以通过Paddle的Python API构造子图,从而不需要知道Graph的结构,也能写Paddle的Graph Pass代码
niuliling123 pushed a commit to niuliling123/Paddle that referenced this pull request Sep 29, 2021
#### 背景

PaddlePaddle#35602 提供Python侧开发子图替换类Pass的方式:

- 利用Paddle Python API或者辅助类型定义子图program用来匹配/替换图;
- Python侧注册Pass时,将注册函数最终转换为protobuf定义的PassDesc数据形式,供C++侧进行解析完成Pass实例注册。

本PR即为根据PassDesc规则描述解析生成Pass实例。

#### 方案设计

##### Pass规则验证

在以往的Pass开发中,会存在随着算子迭代引发的匹配失效或者错误匹配的问题,该问题可以通过扫描算子支持的参数设置及参数类型等来判断是否应该使用该Pass或者给出提示需要修改Pass代码。

当前Pass开发中提供了算子兼容性OpCompatSensiblePass用于解决上述问题。但同时还存在不足:由于以往Pass开发在运行时才能获取到pattern信息,所以需要在执行Pass时才可以判断。

使用PassDesc表示的Pass可以在执行Pass前验证上述问题,这个过程在VerifyDesc中完成。

##### 根据匹配子图构造pattern

GeneratePass对于图匹配和替换使用GraphPatternDecetor完成,构造匹配pattern实际上就是将对应对象成员PDPattern中添加PDNode和边关系。该过程在函数`InitGeneratePattern`中完成,该函数没有作为GeneratePass的成员方法,主要出于后续可能开发新的Decetor考虑,GeneratePass与Decetor的操作是没有关联的。

初始化pattern主要通过遍历匹配子图program的全部算子实现:

1. 添加当前算子对应PDNode及限制条件(算子类型、属性限制等);
2. 遍历当前算子对应输入并从pattern中尝试获取PDNode:
   - 在pattern中获取到PDNode且为输出节点:表示属于匹配子图的中间节点,将该PDNode设置为中间节点;
   - 在pattern中没有获取到PDNode:添加该输入PDNode并设置作为输入节点;
   - 设置输入到算子的边关系;
3. 遍历当前算子对应输出:
   - 在pattern中获取到PDNode且为输入节点:表示属于匹配子图的中间节点,将该PDNode设置为中间节点;
   - 在pattern中没有获取到PDNode:添加该输入PDNode并设置作为输出节点;
   - 设置算子到输出的边关系;

##### 根据替换子图操作graph

替换子图操作的过程在`GetGenerateRewrite`函数中完成,与`InitGeneratePattern`类似没有作为GeneratePass的成员方法。

生成替换子图操作过程如下:

1. 判断冗余替换子图;
2. 遍历替换子图program的全部算子添加替换子图Node:
   1. 添加当前算子的Node及属性设置;
   2. 遍历当前算子对应输入,添加中间variable节点;
   3. 遍历当前算子对应输出,添加中间variable节点;
   4. 添加输入/输出节点与算子节点的边关系;
3. 删除匹配图中属于中间节点的Node;

##### 优化子图验证

对于替换子图或者替换后的计算图是否可以正确运行等,可以在执行Pass时验证,从而防止在后续执行计算图时出现异常。

当前Pass执行直接修改计算图,验证失败时无法很好的完成还原操作,目前子图验证暂时默认成功,留到后续改进。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants