TVM/Relay 的 PartitionGraph()(mod) 函数讨论整理
TVM/Relay 的图形分区功能。以下简单示例,错误信息。
PartitionGraph() 函数指定图形是用带有 AnnotateTarget([“target”]) 函数的目标注释的。编写了以下示例,以便能够将“add”运算符划分为一个单独的功能函数(使用relay模式语言,或遍历 AST,将add划分为一个单独的relay函数),试图了解 PartitionGraph() 如何在简单情况下工作。
这是代码:
graph_type =1
def _register_external_op_helper(op_name, supported=True):
@tvm.ir.register_op_attr(op_name, "target.special")
def _func_wrapper(attrs, args):
return supported
return _func_wrapper
_register_external_op_helper("add")
_register_external_op_helper("subtract")
if graph_type == 1:
# this is test case for graph type 1
print("Graph type 1")
# graph 1: true branch
x1 = relay.var('x', shape=(10, 1))
y1 = relay.var('y', shape=(10, 1))
# graph 2: false branch
x2 = relay.var('x', shape=(10, 1))
y2 = relay.var('y', shape=(10, 1))
f1 = relay.op.add(x1, y1)
f2 = relay.op.multiply(x2, y2)
cond = relay.var('c')
result = relay.If(cond, true_branch=f1, false_branch=f2)
f = relay.Function([], result)
mod = tvm.IRModule({"main": f})
mod = relay.transform.AnnotateTarget(["special"])(mod) # ==> It GIVES ERROR here
mod = relay.transform.PartitionGraph()(mod) #
这是错误信息。
Graph type 1
Traceback (most recent call last):
File "C:Program FilesJetBrainsPyCharm 2020.1.2pluginspythonhelperspydevpydevd.py", line 1438, in _exec
pydev_imports.execfile(file, globals, locals) # execute the script
File "C:Program FilesJetBrainsPyCharm 2020.1.2pluginspythonhelperspydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
exec(compile(contents+" ", file, 'exec'), glob, loc)
File "C:/repos/tvm23/tvm/graph_opt/subgraph/PartitionGraphTry.py", line 48, in <module>
mod = relay.transform.AnnotateTarget(["special"])(mod) # Output: Figure 2
File "C: epos vm23 vmpython vmir ransform.py", line 127, in __call__
return _ffi_transform_api.RunPass(self, mod)
File "C: epos vm23 vmpython vm\_ffi\_ctypespacked_func.py", line 237, in __call__
raise get_last_ffi_error()
tvm._ffi.base.TVMError: Traceback (most recent call last):
File "C: epos vm23 vmsrcirmodule.cc", line 192
TVMError: Check failed: fv.size() == 0 (5 vs. 0) : There are free variables: [Var(c, ty=TensorType([], bool)), Var(x, ty=TensorType([10, 1], float32)), Var(y, ty=TensorType([10, 1], float32)), Var(x, ty=TensorType([10, 1], float32)), Var(y, ty=TensorType([10, 1], float32))] in function: #[version = "0.0.5"]
fn () -> Tensor[(10, 1), float32] {
free_var %c: bool;
if (%c) {
free_var %x: Tensor[(10, 1), float32];
free_var %y: Tensor[(10, 1), float32];
add(%x, %y) /* ty=Tensor[(10, 1), float32] */
} else {
free_var %x1: Tensor[(10, 1), float32];
free_var %y1: Tensor[(10, 1), float32];
multiply(%x1, %y1) /* ty=Tensor[(10, 1), float32] */
}
}
可能的错误原因
1) the if/else handling in this pass might not be correct.
2) apache/incubator-tvm/blob/main/tests/python/relay/test_pass_annotate_target.py
f = relay.Function([x], out)
mod = tvm.IRModule.from_expr(f)
return mod
mod = transform.AnnotateTarget("A")(before())
mod = transform.AnnotateTarget("B")(mod)
expected = transform.AnnotateTarget(["A", "B"])(before())
assert tvm.ir.structural_equal(expected, mod)
def test_if_else():
target = "test_if_else"
@tvm.ir.register_op_attr("equal", "target." + target)
def relu(attrs, args): # pylint: disable=unused-variable
return True
@tvm.ir.register_op_attr("tanh", "target." + target)
def tanh(attrs, args): # pylint: disable=unused-variable
return True
3) Isn’t it simply a problem of free variables? I suggest replacing
f = relay.Function([], result)
with
f = relay.Function(relay.analysis.free_vars(result), result)
4) 现在调通了。
想确认
1) relay中 PartitionGraph() 函数的功能,
2) ParitioGraph() 是否可用于特定用例。
这是对 PartitionGraph() 函数如何工作的理解:
这是主要问题:
- 注释是按运算符类型完成的,例如“add”,而不是运算符实例。例如,如果true 和 false 分支中有两个“add”运算符,并且想将 true 和 false 分支分开,PartitionGraph() 可以帮助吗?可以覆盖 ExprMutator 类中的 visit_if() 函数,实现刚刚描述的内容,正在为更复杂的问题寻找更高级的解决方案。
ParitioGraph() 似乎有限,基于附加到每个运算符种类的注释进行分区。
理想情况下,想要一个解决方案,执行以下操作:
- 将基于用户提供的表达式注释的relay IRModule 划分为单独的 Relay IR 函数(或 IRModule)
在 mod = tvm.IRModule({“main”: f}) 之后
print(mod)
def @main(%c, %x: Tensor[(10, 1), float32], %y: Tensor[(10, 1), float32], %x1: Tensor[(10, 1), float32], %y1: Tensor[(10, 1), float32]) {
if (%c) {
add(%x, %y)
} else {
multiply(%x1, %y1)
}
}
注释后: mod = relay.transform.AnnotateTarget([“special”])(mod)
print(mod)
def @main(%c: bool, %x: Tensor[(10, 1), float32], %y: Tensor[(10, 1), float32], %x1: Tensor[(10, 1), float32], %y1: Tensor[(10, 1), float32]) -> Tensor[(10, 1), float32] {
%0 = annotation.compiler_begin(%c, meta[relay.attrs.CompilerAttrs][0]) /* ty=bool */;
%9 = if (%0) {
%1 = annotation.compiler_begin(%x, meta[relay.attrs.CompilerAttrs][1]) /* ty=Tensor[(10, 1), float32] */;
%2 = annotation.compiler_begin(%y, meta[relay.attrs.CompilerAttrs][2]) /* ty=Tensor[(10, 1), float32] */;
%3 = add(%1, %2) /* ty=Tensor[(10, 1), float32] */;
%4 = annotation.compiler_end(%3, meta[relay.attrs.CompilerAttrs][3]) /* ty=Tensor[(10, 1), float32] */;
annotation.compiler_begin(%4, meta[relay.attrs.CompilerAttrs][4]) /* ty=Tensor[(10, 1), float32] */
} else {
%5 = annotation.compiler_begin(%x1, meta[relay.attrs.CompilerAttrs][5]) /* ty=Tensor[(10, 1), float32] */;
%6 = annotation.compiler_begin(%y1, meta[relay.attrs.CompilerAttrs][6]) /* ty=Tensor[(10, 1), float32] */;
%7 = multiply(%5, %6) /* ty=Tensor[(10, 1), float32] */;
%8 = annotation.compiler_end(%7, meta[relay.attrs.CompilerAttrs][7]) /* ty=Tensor[(10, 1), float32] */;
annotation.compiler_begin(%8, meta[relay.attrs.CompilerAttrs][8]) /* ty=Tensor[(10, 1), float32] */
};
annotation.compiler_end(%9, meta[relay.attrs.CompilerAttrs][9]) /* ty=Tensor[(10, 1), float32] */
}
在 mod = relay.transform.PartitionGraph()(mod) 之后
def @special_0(%special_0_i0: Tensor[(10, 1), float32], %special_0_i1: Tensor[(10, 1), float32], global_symbol="special_0", Primitive=1, Compiler="special", Inline=1) -> Tensor[(10, 1), float32] {
add(%special_0_i0, %special_0_i1) /* ty=Tensor[(10, 1), float32] */
}
def @main(%c: bool, %x: Tensor[(10, 1), float32], %y: Tensor[(10, 1), float32], %x1: Tensor[(10, 1), float32], %y1: Tensor[(10, 1), float32]) -> Tensor[(10, 1), float32] {
if (%c) {
@special_0(%x, %y) /* ty=Tensor[(10, 1), float32] */
} else {
multiply(%x1, %y1) /* ty=Tensor[(10, 1), float32] */
}
}
5) 这正是 PartitionGraph 所做的。
这是因为只调用AnnotateTarget-> PartitionGraph。还有另一个过程叫做MergeCompilerRegion删除不必要的注释,所以应该通过AnnotateTarget-> MergeCompilerRegion-> PartitionGraph。
示例的预期结果应该是:
def @special_0(%special_0_i0: Tensor[(10, 1), float32], %special_0_i1: Tensor[(10, 1), float32], global_symbol="special_0", Primitive=1, Compiler="special", Inline=1) -> Tensor[(10, 1), float32] {
add(%special_0_i0, %special_0_i1) /* ty=Tensor[(10, 1), float32] */
}
def @special_1(%special_0_i0: Tensor[(10, 1), float32], %special_0_i1: Tensor[(10, 1), float32], global_symbol="special_0", Primitive=1, Compiler="special", Inline=1) -> Tensor[(10, 1), float32] {
multiply(%special_0_i0, %special_0_i1) /* ty=Tensor[(10, 1), float32] */
}
def @main(%c: bool, %x: Tensor[(10, 1), float32], %y: Tensor[(10, 1), float32], %x1: Tensor[(10, 1), float32], %y1: Tensor[(10, 1), float32]) -> Tensor[(10, 1), float32] {
if (%c) {
@special_0(%x, %y) /* ty=Tensor[(10, 1), float32] */
} else {
@special_1(%x1, %y1) /* ty=Tensor[(10, 1), float32] */
}
}
如果不是,可能有一些问题/错误需要修复。
6) 曾尝试使用 MergeCompilerRegion,但有以下代码的错误。
下面的代码有效(注释掉了 MergeCompilerRegion),产生带有 UNMERGED @special _ 定义的输出。理想情况下,希望为 true 分支中的表达式设置一个分区,为 false 分支获取另一个分区。
def _register_external_op_helper(op_name, supported=True):
@tvm.ir.register_op_attr(op_name, "target.special")
def _func_wrapper(attrs, args):
return supported
return _func_wrapper
_register_external_op_helper("multiply")
_register_external_op_helper("add")
_register_external_op_helper("subtract")
if graph_type == 1:
# this is test case for graph type 1
print("Graph type 1")
# graph 1: true branch
x1 = relay.var('x1', shape=(10, 1))
y1 = relay.var('y1', shape=(10, 1))
f1 = relay.op.multiply(x1, y1)
x3 = relay.var('x3', shape=(10, 1))
y3 = relay.var('y3', shape=(10, 1))
f3 = relay.op.multiply(x3, y3)
true_branch = relay.op.add(f1, f3)
# graph 2: false branch
x2 = relay.var('x2', shape=(10, 1))
y2 = relay.var('y2', shape=(10, 1))
f2 = relay.op.add(x2, y2)
x4 = relay.var('x4', shape=(10, 1))
y4 = relay.var('y4', shape=(10, 1))
f4 = relay.op.add(x4, y4)
false_branch = relay.op.add(f2, f4)
cond = relay.var('c')
result = relay.If(cond, true_branch=true_branch, false_branch=false_branch)
# f = relay.Function([], result)
f = relay.Function(relay.analysis.free_vars(result), result)
mod = tvm.IRModule({"main": f})
mod = relay.transform.AnnotateTarget(["special"])(mod) # Output: Figure 2
#mod = relay.transform.MergeCompilerRegions()(mod)
mod = relay.transform.PartitionGraph()(mod) # Output: Figure 4
这是注销注释 MergeCompilerRegions 函数时得到的错误
Graph type 1
Traceback (most recent call last):
File "C:/repos/tvm23/tvm/graph_opt/subgraph/PartitionGraphTry.py", line 62, in <module>
mod = relay.transform.MergeCompilerRegions()(mod)
File "C: epos vm23 vmpython vmir ransform.py", line 127, in __call__
return _ffi_transform_api.RunPass(self, mod)
File "C: epos vm23 vmpython vm\_ffi\_ctypespacked_func.py", line 237, in __call__
raise get_last_ffi_error()
tvm._ffi.base.TVMError: TVMError: Cannot find the corresponding region for end annotation:
#[version = "0.0.5"]
free_var %c: bool;
%0 = annotation.compiler_begin(%c, meta[relay.attrs.CompilerAttrs][0]) /* ty=bool */;
%25 = if (%0) {
free_var %x1: Tensor[(10, 1), float32];
%1 = annotation.compiler_begin(%x1, meta[relay.attrs.CompilerAttrs][1]) /* ty=Tensor[(10, 1), float32] */;
free_var %y1: Tensor[(10, 1), float32];
%2 = annotation.compiler_begin(%y1, meta[relay.attrs.CompilerAttrs][2]) /* ty=Tensor[(10, 1), float32] */;
%3 = multiply(%1, %2) /* ty=Tensor[(10, 1), float32] */;
%4 = annotation.compiler_end(%3, meta[relay.attrs.CompilerAttrs][3]) /* ty=Tensor[(10, 1), float32] */;
%5 = annotation.compiler_begin(%4, meta[relay.attrs.CompilerAttrs][4]) /* ty=Tensor[(10, 1), float32] */;
free_var %x3: Tensor[(10, 1), float32];
%6 = annotation.compiler_begin(%x3, meta[relay.attrs.CompilerAttrs][5]) /* ty=Tensor[(10, 1), float32] */;
free_var %y3: Tensor[(10, 1), float32];
%7 = annotation.compiler_begin(%y3, meta[relay.attrs.CompilerAttrs][6]) /* ty=Tensor[(10, 1), float32] */;
%8 = multiply(%6, %7) /* ty=Tensor[(10, 1), float32] */;
%9 = annotation.compiler_end(%8, meta[relay.attrs.CompilerAttrs][7]) /* ty=Tensor[(10, 1), float32] */;
%10 = annotation.compiler_begin(%9, meta[relay.attrs.CompilerAttrs][8]) /* ty=Tensor[(10, 1), float32] */;
%11 = add(%5, %10) /* ty=Tensor[(10, 1), float32] */;
%12 = annotation.compiler_end(%11, meta[relay.attrs.CompilerAttrs][9]) /* ty=Tensor[(10, 1), float32] */;
annotation.compiler_begin(%12, meta[relay.attrs.CompilerAttrs][10]) /* ty=Tensor[(10, 1), float32] */
} else {
free_var %x2: Tensor[(10, 1), float32];
%13 = annotation.compiler_begin(%x2, meta[relay.attrs.CompilerAttrs][11]) /* ty=Tensor[(10, 1), float32] */;
free_var %y2: Tensor[(10, 1), float32];
%14 = annotation.compiler_begin(%y2, meta[relay.attrs.CompilerAttrs][12]) /* ty=Tensor[(10, 1), float32] */;
%15 = add(%13, %14) /* ty=Tensor[(10, 1), float32] */;
%16 = annotation.compiler_end(%15, meta[relay.attrs.CompilerAttrs][13]) /* ty=Tensor[(10, 1), float32] */;
%17 = annotation.compiler_begin(%16, meta[relay.attrs.CompilerAttrs][14]) /* ty=Tensor[(10, 1), float32] */;
free_var %x4: Tensor[(10, 1), float32];
%18 = annotation.compiler_begin(%x4, meta[relay.attrs.CompilerAttrs][15]) /* ty=Tensor[(10, 1), float32] */;
free_var %y4: Tensor[(10, 1), float32];
%19 = annotation.compiler_begin(%y4, meta[relay.attrs.CompilerAttrs][16]) /* ty=Tensor[(10, 1), float32] */;
%20 = add(%18, %19) /* ty=Tensor[(10, 1), float32] */;
%21 = annotation.compiler_end(%20, meta[relay.attrs.CompilerAttrs][17]) /* ty=Tensor[(10, 1), float32] */;
%22 = annotation.compiler_begin(%21, meta[relay.attrs.CompilerAttrs][18]) /* ty=Tensor[(10, 1), float32] */;
%23 = add(%17, %22) /* ty=Tensor[(10, 1), float32] */;
%24 = annotation.compiler_end(%23, meta[relay.attrs.CompilerAttrs][19]) /* ty=Tensor[(10, 1), float32] */;
annotation.compiler_begin(%24, meta[relay.attrs.CompilerAttrs][20]) /* ty=Tensor[(10, 1), float32] */
};
annotation.compiler_end(%25, meta[relay.attrs.CompilerAttrs][21]) /* ty=Tensor[(10, 1), float32] */
/* For debugging purposes the metadata section has been omitted.
* If you would like to see the full metadata section you can set the
* option to `True` when invoking `astext`.
*/
Process finished with exit code 1
7) 删除了if 语句,现在它可以工作了。
这是否意味着有些 MergeCompilerRegions 还不完全支持 if。
这是有效的代码。
# this is test case for graph type 1
print("Graph type 1")
# graph 1: true branch
x1 = relay.var('x1', shape=(10, 1))
y1 = relay.var('y1', shape=(10, 1))
f1 = relay.op.multiply(x1, y1)
x3 = relay.var('x3', shape=(10, 1))
y3 = relay.var('y3', shape=(10, 1))
f3 = relay.op.multiply(x3, y3)
true_branch = relay.op.add(f1, f3)
# graph 2: false branch
x2 = relay.var('x2', shape=(10, 1))
y2 = relay.var('y2', shape=(10, 1))
f2 = relay.op.add(x2, y2)
x4 = relay.var('x4', shape=(10, 1))
y4 = relay.var('y4', shape=(10, 1))
f4 = relay.op.add(x4, y4)
false_branch = relay.op.add(f2, f4)
cond = relay.var('c')
#result = relay.If(cond, true_branch=true_branch, false_branch=false_branch)
result = true_branch
#f = relay.Function([], result)
f = relay.Function(relay.analysis.free_vars(result), result)
mod = tvm.IRModule({"main": f})
mod = relay.transform.AnnotateTarget(["special"])(mod) # Output: Figure 2
mod = relay.transform.MergeCompilerRegions()(mod)
mod = relay.transform.PartitionGraph()(mod) # Output: Figure 4
这是不起作用的代码。
# this is test case for graph type 1
print("Graph type 1")
# graph 1: true branch
x1 = relay.var('x1', shape=(10, 1))
y1 = relay.var('y1', shape=(10, 1))
f1 = relay.op.multiply(x1, y1)
x3 = relay.var('x3', shape=(10, 1))
y3 = relay.var('y3', shape=(10, 1))
f3 = relay.op.multiply(x3, y3)
true_branch = relay.op.add(f1, f3)
# graph 2: false branch
x2 = relay.var('x2', shape=(10, 1))
y2 = relay.var('y2', shape=(10, 1))
f2 = relay.op.add(x2, y2)
x4 = relay.var('x4', shape=(10, 1))
y4 = relay.var('y4', shape=(10, 1))
f4 = relay.op.add(x4, y4)
false_branch = relay.op.add(f2, f4)
cond = relay.var('c')
result = relay.If(cond, true_branch=true_branch, false_branch=false_branch)
#result = true_branch
#f = relay.Function([], result)
f = relay.Function(relay.analysis.free_vars(result), result)
mod = tvm.IRModule({"main": f})
mod = relay.transform.AnnotateTarget(["special"])(mod) # Output: Figure 2
mod = relay.transform.MergeCompilerRegions()(mod)
mod = relay.transform.PartitionGraph()(mod) # Output: Figure 4
8) 正在 if 节点上工作,这应该已经修复了。
是否尝试过使用最新提交的主分支?
这是使用的脚本:
import tvm
from tvm import relay
def _register_external_op_helper(op_name, supported=True):
@tvm.ir.register_op_attr(op_name, "target.special")
def _func_wrapper(expr):
return supported
return _func_wrapper
_register_external_op_helper("add")
_register_external_op_helper("subtract")
# graph 1: true branch
x1 = relay.var('x1', shape=(10, 1))
y1 = relay.var('y1', shape=(10, 1))
f1 = relay.op.multiply(x1, y1)
x3 = relay.var('x3', shape=(10, 1))
y3 = relay.var('y3', shape=(10, 1))
f3 = relay.op.multiply(x3, y3)
true_branch = relay.op.add(f1, f3)
# graph 2: false branch
x2 = relay.var('x2', shape=(10, 1))
y2 = relay.var('y2', shape=(10, 1))
f2 = relay.op.add(x2, y2)
x4 = relay.var('x4', shape=(10, 1))
y4 = relay.var('y4', shape=(10, 1))
f4 = relay.op.add(x4, y4)
false_branch = relay.op.add(f2, f4)
cond = relay.var('c')
result = relay.If(cond, true_branch=true_branch, false_branch=false_branch)
f = relay.Function(relay.analysis.free_vars(result), result)
mod = tvm.IRModule({"main": f})
mod = relay.transform.AnnotateTarget(["special"])(mod)
mod = relay.transform.MergeCompilerRegions()(mod)
mod = relay.transform.PartitionGraph()(mod)
print(mod)
这是输出,看起来不错。
def @main(%c: bool, %x1: Tensor[(10, 1), float32], %y1: Tensor[(10, 1), float32], %x3: Tensor[(10, 1), float32], %y3: Tensor[(10, 1), float32], %x2: Tensor[(10, 1), float32], %y2: Tensor[(10, 1), float32], %x4: Tensor[(10, 1), float32], %y4: Tensor[(10, 1), float32]) -> Tensor[(10, 1), float32] {
if (%c) {
%0 = multiply(%x1, %y1) /* ty=Tensor[(10, 1), float32] */;
%1 = multiply(%x3, %y3) /* ty=Tensor[(10, 1), float32] */;
@special_0(%0, %1) /* ty=Tensor[(10, 1), float32] */
} else {
@special_2(%x2, %y2, %x4, %y4) /* ty=Tensor[(10, 1), float32] */
}
}
def @special_0(%special_0_i0: Tensor[(10, 1), float32], %special_0_i1: Tensor[(10, 1), float32], global_symbol="special_0", Primitive=1, Compiler="special", Inline=1) -> Tensor[(10, 1), float32] {
add(%special_0_i0, %special_0_i1) /* ty=Tensor[(10, 1), float32] */
}
def @special_2(%special_2_i0: Tensor[(10, 1), float32], %special_2_i1: Tensor[(10, 1), float32], %special_2_i2: Tensor[(10, 1), float32], %special_2_i3: Tensor[(10, 1), float32], global_symbol="special_2", Primitive=1, Compiler="special", Inline=1) -> Tensor[(10, 1), float32] {
%2 = add(%special_2_i0, %special_2_i1) /* ty=Tensor[(10, 1), float32] */;
%3 = add(%special_2_i2, %special_2_i3) /* ty=Tensor[(10, 1), float32] */;
add(%2, %3) /* ty=Tensor[(10, 1), float32] */
}
参考链接:
https://discuss.tvm.apache.org/t/understanding-tvm-relays-partitiongraph-mod-function/8290/10