未验证 提交 e8b8e410 编写于 作者: J Jason 提交者: GitHub

Merge pull request #380 from SunAhong1993/me

add pytorch convertor
__version__ = "0.7.4"
from .core.program import PaddleProgram
from .core.program import PaddleGraph
program = PaddleProgram()
program = PaddleGraph()
name_counter = dict()
......
......@@ -18,15 +18,15 @@ import paddle.fluid as fluid
from paddle.fluid.proto import framework_pb2
from collections import OrderedDict
import numpy
import time
import collections
import sys
import os
import six
import pickle
class PaddleLayer(object):
def __init__(self, kernel, inputs, outputs, **kwargs):
def __init__(self, id, kernel, inputs, outputs, **kwargs):
assert isinstance(
inputs,
dict), "parameter 'inputs' for PaddleLayer should be type of dict"
......@@ -51,22 +51,29 @@ class PaddleLayer(object):
self.inputs = inputs
self.outputs = outputs
self.attrs = kwargs
self.id = str(time.time())
self.id = id
self.blocks = list()
def add_block(self, block):
block.father_layer = self
self.blocks.append(block)
class PaddleProgram(object):
def __init__(self):
class PaddleGraph(object):
def __init__(self, parent_layer=None, graph_type="static"):
self.layers = OrderedDict()
self.edges_out = dict()
self.edges_in = dict()
self.inputs = list()
self.outputs = list()
self.parameters = dict()
self.father_layer = None
self.parent_layer = parent_layer
self.graph_type = graph_type
def set_name(self, name):
self.name = name
def set_parameters(self, parameters):
self.parameters = parameters
def clear(self):
self.layers = OrderedDict()
......@@ -76,25 +83,44 @@ class PaddleProgram(object):
self.outputs = list()
self.parameters = dict()
def clear_edges(self):
self.edges_out = dict()
self.edges_in = dict()
def add_layer(self, kernel, inputs, outputs, **kwargs):
layer = PaddleLayer(kernel, inputs, outputs, **kwargs)
layer_id = str(len(self.layers))
if self.father_layer is not None:
layer_id = "{}.{}.{}".format(layer_id, len(self.father_layer.blocks()), self.father_layer.id)
if self.parent_layer is not None:
layer_id = "{}.{}.{}".format(self.parent_layer.id,
len(self.parent_layer.blocks),
layer_id)
layer = PaddleLayer(layer_id, kernel, inputs, outputs, **kwargs)
self.layers[layer_id] = layer
return layer_id
def build(self):
def build(self, inputs=None, outputs=None):
self.clear_edges()
outputs_from_nodes = dict()
for layer_id, layer in self.layers.items():
# if "x5109" in layer.outputs or "x5110" in layer.outputs:
# print(layer.kernel)
# print(layer.inputs)
# print(layer.outputs)
# print(layer.attrs)
for input_key, input_var in layer.inputs.items():
vs = input_var
if not isinstance(vs, list):
vs = [vs]
for v in vs:
assert v in outputs_from_nodes, "Couldn't find {} in previous layers, the layers should be make by topological sort".format(
assert v in outputs_from_nodes or (
inputs is not None and v in list(inputs.values())
) or (
outputs is not None and v in outputs
), "Couldn't find {} in previous layers, the layers should be make by topological sort".format(
v)
in_layer_id = outputs_from_nodes[v]
if v in outputs_from_nodes:
in_layer_id = outputs_from_nodes[v]
else:
in_layer_id = -1
if in_layer_id not in self.edges_out:
self.edges_out[in_layer_id] = list()
self.edges_out[in_layer_id].append(layer_id)
......@@ -105,6 +131,27 @@ class PaddleProgram(object):
for output in layer.outputs:
outputs_from_nodes[output] = layer_id
if len(layer.blocks) > 0:
for block in layer.blocks:
block.build(layer.inputs, layer.outputs)
if self.graph_type == "dygraph":
self.get_dygraph_inputs()
self.get_dygraph_outputs()
def get_global_layers(self):
# 该全局layers的信息是按照拓扑排序组成的
def update(layers):
global_layers = dict()
for layer_id, layer in layers.items():
global_layers[layer_id] = layer
for block in layer.blocks:
block_global_layers = update(block.layers)
global_layers.update(block_global_layers)
return global_layers
return update(self.layers)
def gen_code(self, code_dir):
def write_code(f, code_list, indent=0):
indent_blank = " " * indent
......@@ -162,37 +209,41 @@ class PaddleProgram(object):
f.close()
def gen_model(self, save_dir):
code_dir = os.path.join(save_dir, 'model_with_code')
infer_dir = os.path.join(save_dir, 'inference_model')
self.gen_code(code_dir)
sys.path.append(code_dir)
import x2paddle_model
scope = fluid.Scope()
startup_program = fluid.Program()
main_program = fluid.Program()
with fluid.scope_guard(scope):
with fluid.program_guard(main_program, startup_program):
inputs, outputs = x2paddle_model.x2paddle_net()
exe = fluid.Executor(fluid.CPUPlace())
exe.run(startup_program)
param_dir = os.path.join(code_dir, 'weights')
for k, v in self.parameters.items():
if scope.find_var(k):
self.dump_parameter(k, v, param_dir)
def if_exist(var):
b = os.path.exists(
os.path.join(os.path.join(param_dir, var.name)))
return b
fluid.io.load_vars(
exe, param_dir, main_program, predicate=if_exist)
fluid.io.save_inference_model(
dirname=infer_dir,
feeded_var_names=[i.name for i in inputs],
target_vars=outputs,
executor=exe)
if self.graph_type == "static":
code_dir = os.path.join(save_dir, 'model_with_code')
infer_dir = os.path.join(save_dir, 'inference_model')
self.gen_code(code_dir)
sys.path.append(code_dir)
import x2paddle_model
scope = fluid.Scope()
startup_program = fluid.Program()
main_program = fluid.Program()
with fluid.scope_guard(scope):
with fluid.program_guard(main_program, startup_program):
inputs, outputs = x2paddle_model.x2paddle_net()
exe = fluid.Executor(fluid.CPUPlace())
exe.run(startup_program)
param_dir = os.path.join(code_dir, 'weights')
for k, v in self.parameters.items():
if scope.find_var(k):
self.dump_parameter(k, v, param_dir)
def if_exist(var):
b = os.path.exists(
os.path.join(os.path.join(param_dir, var.name)))
return b
fluid.io.load_vars(
exe, param_dir, main_program, predicate=if_exist)
fluid.io.save_inference_model(
dirname=infer_dir,
feeded_var_names=[i.name for i in inputs],
target_vars=outputs,
executor=exe)
else:
self.gen_dygraph_code(save_dir)
self.dump_dygraph_parameter(save_dir)
def dump_parameter(self, param_name, param, save_dir):
if not os.path.exists(save_dir):
......@@ -227,3 +278,167 @@ class PaddleProgram(object):
fp.write(tensor_desc.SerializeToString())
param.tofile(fp)
fp.close()
def get_dygraph_inputs(self):
def update(layers):
for layer_id, layer in layers.items():
if self.edges_in.get(layer_id, 0) == 0 and self.edges_out.get(
layer_id, 0) == 0:
continue
if layer.kernel == "fluid.dygraph.base.to_variable":
value = layer.attrs["value"]
if not value.startswith("params["):
self.inputs.append(value)
if len(layer.blocks) > 0:
for block in layer.blocks:
block.get_dygraph_inputs()
self.inputs.extend(block.inputs)
update(self.layers)
self.inputs = list(set(self.inputs))
def get_dygraph_outputs(self):
for layer_id, layer in self.layers.items():
if self.edges_in.get(layer_id, 0) == 0 and self.edges_out.get(
layer_id, 0) == 0:
continue
if self.edges_out.get(layer_id, 0) == 0:
for output_name in layer.outputs:
if not output_name.startswith("x"):
continue
self.outputs.append(output_name)
self.outputs = list(set(self.outputs))
def gen_dygraph_code(self, code_dir=None, indent=2):
def gen_codes(code_list, indent=0):
indent_blank = " " * indent
codes = []
for code_line in code_list:
if code_line.strip() == "":
codes.append('\n')
else:
codes.append(indent_blank + code_line + '\n')
return codes
def gen_head():
self.head = gen_codes(
[
"from paddle.fluid.initializer import Constant",
"from paddle.fluid.param_attr import ParamAttr",
"import paddle.fluid as fluid",
"",
"class {}(fluid.dygraph.Layer):".format(self.name),
],
indent=0)
input_data_name = ', '.join(self.inputs)
self.init_func.extend(
gen_codes(
["def __init__(self, params):"], indent=1))
self.init_func.extend(
gen_codes(
["super({}, self).__init__()".format(self.name)], indent=2))
self.forward_func.extend(
gen_codes(
["def forward(self, {}):".format(input_data_name)],
indent=1))
def write_code(code_dir):
f = open(os.path.join(code_dir, 'code.py'), 'w')
for code_line in self.head:
f.write(code_line)
init_writen_codes = []
for code_line in self.init_func:
if code_line in init_writen_codes:
continue
f.write(code_line)
init_writen_codes.append(code_line)
f.write("\n")
return_code = "return {}".format(", ".join(self.outputs))
self.forward_func.extend(gen_codes([return_code], indent=2))
for code_line in self.forward_func:
f.write(code_line)
f.close()
self.init_func = []
self.forward_func = []
if indent == 2 and code_dir is not None:
gen_head()
for layer_id, layer in self.layers.items():
if len(self.layers) > 1:
if self.edges_in.get(layer_id, 0) == 0 and self.edges_out.get(
layer_id, 0) == 0 and layer.kernel != "prim.assert" \
and layer.kernel != "prim.exception" \
and layer.kernel != "prim.warnings":
continue
if "dygraph" in layer.kernel:
line = "{}".format(
layer.outputs[0]
) if layer.kernel == "fluid.dygraph.base.to_variable" and not layer.attrs[
"value"].startswith("params[") else "self.{}".format(
layer.outputs[0])
line += " = {}(".format(layer.kernel)
for k, v in layer.attrs.items():
line += "{}={}, ".format(k, v)
line = line.strip(", ")
line += ")"
if layer.kernel == "fluid.dygraph.base.to_variable" and not layer.attrs[
"value"].startswith("params["):
self.forward_func.extend(gen_codes([line], indent=indent))
continue
else:
self.init_func.extend(gen_codes([line], indent=2))
if len(layer.outputs) == 1:
line = layer.outputs[0]
elif len(layer.outputs) == 2:
line = layer.outputs[1]
else:
line = ','.join(layer.outputs[1:])
if layer.kernel == "fluid.dygraph.base.to_variable" and layer.attrs[
"value"].startswith("params["):
line += " = self.{}".format(layer.outputs[0])
else:
line += " = self.{}(".format(layer.outputs[0])
for k, v in layer.inputs.items():
line += "{}, ".format(v)
line = line.strip(", ")
line += ")"
self.forward_func.extend(gen_codes([line], indent=indent))
elif "prim" in layer.kernel:
func_name = layer.kernel.replace(".", "_")
from x2paddle.op_mapper.pytorch2paddle import prim2code
if hasattr(prim2code, func_name):
func = getattr(prim2code, func_name)
func(
layer,
indent=indent,
init_func=self.init_func,
forward_func=self.forward_func)
else:
raise Exception(
"The kind {} in paddle model is not supported yet.".
format(layer.kernel))
else:
if len(layer.outputs) == 1:
line = layer.outputs[0]
else:
line = ','.join(layer.outputs)
line += " = {}(".format(layer.kernel)
for k, v in layer.inputs.items():
line += "{}={}, ".format(k, v)
for k, v in layer.attrs.items():
line += "{}={}, ".format(k, v)
line = line.strip(", ")
line += ")"
self.forward_func.extend(gen_codes([line], indent=indent))
if indent == 2:
write_code(code_dir)
else:
return self.init_func, self.forward_func
def dump_dygraph_parameter(self, code_dir):
params_output = open(os.path.join(code_dir, 'model.pdparams'), 'wb')
pickle.dump(self.parameters, params_output)
params_output.close()
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import torch
class PyTorchDecoder(object):
def __init__(self, script_path):
self.script = torch.jit.load(script_path)
self.graph = self._optimize_graph(self.script.inlined_graph)
def _optimize_graph(self, graph):
torch._C._jit_pass_constant_propagation(graph)
torch._C._jit_pass_dce(graph)
torch._C._jit_pass_lint(graph)
torch._C._jit_pass_peephole(graph)
torch._C._jit_pass_lint(graph)
torch._C._jit_pass_dce(graph)
torch._C._jit_pass_lint(graph)
graph = torch._C._jit_pass_canonicalize(graph)
torch._C._jit_pass_lint(graph)
return graph
此差异已折叠。
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import torch
from x2paddle.core.util import *
def prim_Constant(mapper, graph, node):
""" 构造constant的PaddleLayer,该节点实现常量赋值。
TorchScript示例:
%2 : int = prim::Constant[value=-1]()
参数含义:
%2 (常量类型由赋值类型定义,该示例中为int型): 常量赋值结果输出。
"""
output_name = mapper._get_outputs_name(node)[0]
output = list(node.outputs())[0]
value = output.toIValue()
mapper.attrs[output_name] = value
if isinstance(value, str):
value = string(value)
graph.add_layer(
"prim.constant", inputs={}, outputs=[output_name], value=value)
return [], [output_name]
def prim_GetAttr(mapper, graph, node):
""" 获取attribute信息。
TorchScript示例:
%27 : Tensor? = prim::GetAttr[name="bias"](%7)
参数含义:
%7 (Tensor): 输入Tensor。
%27 (Tensor): 输入Tensor。
"""
current_node = node
field_name_list = [node.s('name')]
while True:
input_node = list(node.inputs())[0].node()
try:
field_name_list.insert(0, input_node.s('name'))
node = input_node
except Exception:
break
attr_name = ".".join(field_name_list)
output_name = mapper._get_outputs_name(current_node, attr_name)[0]
part_script = mapper.script
for field_name in field_name_list:
if hasattr(part_script, field_name):
param = getattr(part_script, field_name)
if isinstance(param, torch.Tensor):
param = param.detach().numpy()
mapper.pytorch_params[output_name] = param
part_script = param
return [], [output_name]
def prim_ListConstruct(mapper, graph, node):
""" 构造list的PaddleLayer。
TorchScript示例:
%86 : int[] = prim::ListConstruct(%84, %85)
参数含义:
%86 (list): list节点输出。
%84 (int/其他): list第一个元素信息。
%85 (int/其他): list第二个元素信息。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理每个输入
for i, input_name in enumerate(inputs_name):
layer_inputs["input{}".format(i)] = input_name
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.list", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def prim_RaiseException(mapper, graph, node):
""" 构造抛出异常的PaddleLayer。
TorchScript示例:
= prim::RaiseException(%76)
参数含义:
%76 (str): 异常信息。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%76
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["input"] = inputs_name[0]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer(
"prim.exception", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def prim_Loop(mapper, graph, node):
""" 构造loop循环的PaddleLayer。
TorchScript示例:
%x : Tensor = prim::Loop(%4, %3, %x.3)
block0(%i : int, %x.12 : Tensor):
%72 : int[] = prim::Constant[value=[6, 6]]()
...
%x.5 : Tensor = aten::adaptive_avg_pool2d(%x.12, %_output_size.1)
-> (%3, %x.5)
参数含义:
%4 (int): 循环次数。
%3 (bool): 是否进入退出。
%x.3 (Tensor): 循环中修改的Tensor。
%x (Tensor): loop循环的输出,与%x.5对应。
"""
node_outputs = mapper._get_outputs_name(node)
loop_inputs = {}
block = list(node.blocks())[0]
loop_outputs = node_outputs.copy()
for i, block_input_ivalue in enumerate(block.inputs()):
if i == 0:
block_input_node_name = '_x' + str(mapper.output_index)
else:
block_input_node_name = 'x' + str(mapper.output_index)
unique_id = block_input_ivalue.unique()
if unique_id not in mapper.outputs_info:
mapper.outputs_info[unique_id] = block_input_node_name
mapper.output_index += 1
if i == 0:
loop_input_node = list(node.inputs())[0].node()
script_loop_input_unique_id = list(node.inputs())[0].unique()
loop_input_node_name = mapper.outputs_info[
script_loop_input_unique_id]
mapper._check_input(graph, loop_input_node, loop_input_node_name,
node_outputs)
loop_inputs['input'] = loop_input_node_name
loop_outputs.append(block_input_node_name)
node_outputs.append(block_input_node_name)
else:
loop_input_node = list(node.inputs())[i + 1].node()
script_loop_input_unique_id = list(node.inputs())[i + 1].unique()
loop_input_node_name = mapper.outputs_info[
script_loop_input_unique_id]
mapper._check_input(graph, loop_input_node, loop_input_node_name,
node_outputs)
graph.add_layer(
"prim.equal",
inputs={'input': loop_input_node_name},
outputs=[block_input_node_name])
node_outputs.append(block_input_node_name)
graph.add_layer("prim.loop", inputs=loop_inputs, outputs=loop_outputs)
current_layer = list(graph.layers.values())[-1]
block_graph, graph_inputs = mapper.traverse(block, current_layer)
for i, input_name in enumerate(graph_inputs):
if input_name == loop_outputs[1]:
continue
current_layer.inputs['input-{}'.format(i)] = input_name
current_layer.add_block(block_graph)
return list(current_layer.inputs.values()), node_outputs
def prim_If(mapper, graph, node):
""" 构造if控制流的PaddleLayer。
TorchScript示例:
%input.5 : Tensor = prim::If(%107)
block0():
%109 : Tensor = aten::t(%102)
%ret.2 : Tensor = aten::addmm(%103, %101, %109, %104, %104)
-> (%ret.2)
block1():
%111 : Tensor = aten::t(%102)
...
-> (%output.4)
参数含义:
%107 (bool): if判断条件。
%input.5 (Tensor): if控制流的输出,与%output.4对应。
"""
output_name = mapper._get_outputs_name(node)[0]
node_outputs = [output_name]
input_node = list(node.inputs())[0].node()
script_input_unique_id = list(node.inputs())[0].unique()
input_node_name = mapper.outputs_info[script_input_unique_id]
mapper._check_input(graph, input_node, input_node_name, node_outputs)
graph.add_layer("prim.if", {'input': input_node_name}, [output_name])
current_layer = list(graph.layers.values())[-1]
block0 = list(node.blocks())[0]
block0_graph, graph_inputs0 = mapper.traverse(block0, current_layer)
len0 = 0
for i, input_name in enumerate(graph_inputs0):
current_layer.inputs['input-{}'.format(i)] = input_name
len0 = i
current_layer.add_block(block0_graph)
block1 = list(node.blocks())[1]
block1_graph, graph_inputs1 = mapper.traverse(block1, current_layer)
for i, input_name in enumerate(graph_inputs1):
current_layer.inputs['input-{}'.format(len0 + 1 + i)] = input_name
current_layer.add_block(block1_graph)
return list(current_layer.inputs.values()), node_outputs
def prim_min(mapper, graph, node):
""" 构造min的PaddleLayer。
TorchScript示例:
%87 : int = prim::min(%86)
参数含义:
%86 (list): 输入。
%87 (int): 输出。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%86
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["input"] = inputs_name[0]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.min", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def prim_requires_grad(mapper, graph, node):
""" 构造是否计算梯度的PaddleLayer。
TorchScript示例:
%356 : bool = prim::requires_grad(%tensor.31)
参数含义:
%356 (bool): 输出,当前Tensor是否计算梯度。
%tensor.31 (Tensor): 输入的Tensor。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%86
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["input"] = inputs_name[0]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer(
"prim.requires_grad", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def prim_SetAttr(mapper, graph, node):
""" 设置attribute信息。
TorchScript示例:
= prim::SetAttr[name="num_batches_tracked"](%260, %277)
参数含义:
%260 (-): 属性名前缀。
%277 (-): 需要设置的值。
"""
output_name = mapper._get_outputs_name(node)[0]
field_name_list = []
tmp_node = node
while True:
input_node = list(tmp_node.inputs())[0].node()
try:
field_name_list.insert(0, input_node.s('name'))
tmp_node = input_node
except Exception:
break
field_name_list.append(node.s('name'))
inputs_name, inputs_node = mapper._get_inputs_name(node)
param = {
"Tensor": "self." + ".".join(field_name_list).replace(".", "_"),
"parent_layer_id": graph.parent_layer.id
}
mapper.pytorch_params[".".join(field_name_list)] = param
graph.add_layer(
"prim.set_attr",
inputs={"input": inputs_name[1]},
outputs=["self." + ".".join(field_name_list).replace(".", "_")])
return [], [output_name]
def prim_shape(mapper, graph, node):
""" 构造获取shape的PaddleLayer。
TorchScript示例:
%4701 : int[] = prim::shape(%result.1)
参数含义:
%4701 (list): 输出,shape信息。
%result.1 (Tensor): 需要获取shape的值。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%input.8
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["input"] = inputs_name[0]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.shape", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def prim_TupleConstruct(mapper, graph, node):
""" 构造tuple的PaddleLayer。
TorchScript示例:
%4492 : (Tensor, Tensor?) = prim::TupleConstruct(%x.46, %aux)
参数含义:
%4492 (tuple): 输出,tuple。
%x.46 (Tensor/其他): tuple第一个元素信息。
%aux (Tensor/其他): tuple第二个元素信息。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理每个输入
for i, input_name in enumerate(inputs_name):
layer_inputs["input{}".format(i)] = input_name
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.tuple", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def prim_TupleUnpack(mapper, graph, node):
""" 构造获取tuple元素的PaddleLayer。
TorchScript示例:
%x.223 : Tensor, %aux.3 : Tensor? = prim::TupleUnpack(%4492)
参数含义:
%x.223 (Tensor/其他): 输出,tuple第一个元素信息。
%aux.3 (Tensor/其他): 输出,tuple第二个元素信息。
%4492 (tuple): 需要获取元素的tuple。
"""
outputs_name = mapper._get_outputs_name(node)
layer_outputs = outputs_name
layer_inputs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = outputs_name
layer_inputs["input"] = inputs_name[0]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer(
"prim.tuple_unpack", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def prim_Uninitialized(mapper, graph, node):
""" 构造表示编译器永远不会使用的值的PaddleLayer,该节点转换为None。
TorchScript示例:
%345 : bool = prim::Uninitialized()
参数含义:
%345 (bool): 输出,为赋值的bool。
"""
output_name = mapper._get_outputs_name(node)[0]
output = list(node.outputs())[0]
mapper.attrs[output_name] = None
graph.add_layer(
"prim.constant", inputs={}, outputs=[output_name], value=None)
return [], [output_name]
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
def gen_codes(code_list, indent=0):
indent_blank = " " * indent
codes = []
for code_line in code_list:
if code_line.strip() == "":
codes.append('\n')
else:
codes.append(indent_blank + code_line + '\n')
return codes
def get_value(layer, key):
""" 进行optimizer后可能把inputs的value直接用数值代替(ConstantFuser),
会把input换成attr,所以需要此处的操作。
"""
if key in layer.inputs:
return layer.inputs[key]
else:
return str(layer.attrs[key])
def prim_add(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {} + {}".format(layer.outputs[0],
get_value(layer, "x"), get_value(layer, "y"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_add_(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {} + {} * {}".format(layer.outputs[0],
get_value(layer, "x"),
layer.attrs["alpha"],
get_value(layer, "y"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_and(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {} and {}".format(layer.outputs[0],
get_value(layer, "x"), get_value(layer, "y"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_append(layer, indent=1, init_func=[], forward_func=[]):
line = "{}.append({})".format(
get_value(layer, "list"), get_value(layer, "element"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_assert(layer, indent=1, init_func=[], forward_func=[]):
if layer.attrs["type"] == "eq":
if isinstance(layer.attrs["value"], list):
s = ""
for v in layer.attrs["value"]:
s += "{} == {} or ".format(layer.attrs["key"], v)
if len(s) > 0:
s = s[:-4]
line = "assert {}, \'The {} must be {}!\'".format(
s, layer.attrs["key"], layer.attrs["value"])
else:
line = "assert {} == {}, \'The {} must be {}!\'".format(
layer.attrs["key"], layer.attrs["value"], layer.attrs["key"],
layer.attrs["value"])
else:
raise Exception("Not implement yet!")
forward_func.extend(gen_codes([line], indent=indent))
def prim_constant(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {}".format(layer.outputs[0], layer.attrs["value"])
forward_func.extend(gen_codes([line], indent=indent))
def prim_eq(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {} == {}".format(layer.outputs[0],
get_value(layer, "x"), get_value(layer, "y"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_equal(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {}".format(layer.outputs[0], get_value(layer, "input"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_exception(layer, indent=1, init_func=[], forward_func=[]):
line = "raise RaiseException({})".format(get_value(layer, "input"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_if(layer, indent=1, init_func=[], forward_func=[]):
line = "if {} :".format(get_value(layer, "input"))
forward_func.extend(gen_codes([line], indent=indent))
block = layer.blocks[0]
b_init_lines, b_forward_lines = block.gen_dygraph_code(indent=indent + 1)
init_func.extend(b_init_lines)
forward_func.extend(b_forward_lines)
block = layer.blocks[1]
if len(block.layers) > 0:
line = "else:"
forward_func.extend(gen_codes([line], indent=indent))
b_init_lines, b_forward_lines = block.gen_dygraph_code(
indent=indent + 1)
init_func.extend(b_init_lines)
forward_func.extend(b_forward_lines)
def prim_getitem(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {}[{}]".format(layer.outputs[0],
get_value(layer, "list"),
get_value(layer, "index"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_gt(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {} > {}".format(layer.outputs[0],
get_value(layer, "x"), get_value(layer, "y"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_le(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {} <= {}".format(layer.outputs[0],
get_value(layer, "x"), get_value(layer, "y"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_len(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = len({})".format(layer.outputs[0], get_value(layer, "input"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_lt(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {} < {}".format(layer.outputs[0],
get_value(layer, "x"), get_value(layer, "y"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_list(layer, indent=1, init_func=[], forward_func=[]):
input_len = len(layer.inputs) + len(layer.attrs)
inputs_list = list()
for i in range(input_len):
inputs_list.append(get_value(layer, "input{}".format(i)))
inputs_str = ', '.join(inputs_list)
line = "{} = [{}]".format(layer.outputs[0], inputs_str)
forward_func.extend(gen_codes([line], indent=indent))
def prim_loop(layer, indent=1, init_func=[], forward_func=[]):
loop_range = get_value(layer, "input")
line = "for {} in range({}):".format(layer.outputs[1], loop_range)
forward_func.extend(gen_codes([line], indent=indent))
block = layer.blocks[0]
b_init_lines, b_forward_lines = block.gen_dygraph_code(indent=indent + 1)
init_func.extend(b_init_lines)
forward_func.extend(b_forward_lines)
def prim_min(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = min({})".format(layer.outputs[0], get_value(layer, "input"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_mul(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {} * {}".format(layer.outputs[0],
get_value(layer, "x"), get_value(layer, "y"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_ne(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {} != {}".format(layer.outputs[0],
get_value(layer, "x"), get_value(layer, "y"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_neg(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = -{}".format(layer.outputs[0], get_value(layer, "input"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_not(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = not {}".format(layer.outputs[0], get_value(layer, "input"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_requires_grad(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = not {}.stop_gradient".format(layer.outputs[0],
get_value(layer, "input"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_select(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {}[".format(layer.outputs[0], get_value(layer, "input"))
for dim in range(layer.attrs["dim"]):
line += ":, "
line += (get_value(layer, "index") + "]")
forward_func.extend(gen_codes([line], indent=indent))
def prim_set_attr(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {}".format(layer.outputs[0], get_value(layer, "input"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_shape(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {}.shape".format(layer.outputs[0], get_value(layer, "input"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_slice(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {}[{}: {}: {}]".format(layer.outputs[0],
get_value(layer, "input"),
get_value(layer, "start"),
get_value(layer, "end"),
get_value(layer, "step"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_sub(layer, indent=1, init_func=[], forward_func=[]):
line = "{} = {} - {}".format(layer.outputs[0],
get_value(layer, "x"), get_value(layer, "y"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_tuple(layer, indent=1, init_func=[], forward_func=[]):
input_len = len(layer.inputs) + len(layer.attrs)
inputs_list = list()
for i in range(input_len):
inputs_list.append(get_value(layer, "input{}".format(i)))
inputs_str = ', '.join(inputs_list)
line = "{} = ({})".format(layer.outputs[0], inputs_str)
forward_func.extend(gen_codes([line], indent=indent))
def prim_tuple_unpack(layer, indent=1, init_func=[], forward_func=[]):
outputs_str = ', '.join(layer.outputs)
line = "{} = {}".format(outputs_str, get_value(layer, "input"))
forward_func.extend(gen_codes([line], indent=indent))
def prim_warnings(layer, indent=1, init_func=[], forward_func=[]):
lines = ["import warnings"]
line = "warnings.warn({}, stacklevel={})".format(
get_value(layer, "input"), layer.attrs["stacklevel"])
lines.append(line)
forward_func.extend(gen_codes(lines, indent=indent))
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import torch
import numpy as np
from x2paddle.core.op_mapper import OpMapper
from x2paddle.core.util import *
from x2paddle.core.program import PaddleGraph
from x2paddle.op_mapper.pytorch2paddle import prim
from x2paddle.op_mapper.pytorch2paddle import aten
class PyTorchOpMapper(OpMapper):
def __init__(self, decoder):
super(PyTorchOpMapper, self).__init__()
self.script = decoder.script
self.paddle_params = dict()
self.outputs_info = {} # key为output unique id,value为当前节点的输出名字
self.pytorch_params = {} # key为节点名,value为参数
self.attrs = {} # key为节点名,value为属性值
self.output_index = 0
self.dygraph_name_id = {} # 动态图__init__输出名字中的id,key为kernel类型,value为id
# 转换
self.check_op(decoder.graph)
self.graph, _ = self.traverse(decoder.graph)
def check_op(self, script_graph):
def _update_op_list(graph):
for node in graph.nodes():
op_list.append(node.kind())
for block in node.blocks():
_update_op_list(block)
op_list = list()
_update_op_list(script_graph)
op_list = list(set(op_list))
unsupported_op_list = []
for op in op_list:
func_name = op.replace('::', '_')
if not (hasattr(prim, func_name) or hasattr(aten, func_name)):
unsupported_op_list.append(op)
if len(unsupported_op_list) > 0:
raise Exception("The kind {} in model is not supported yet.".format(
unsupported_op_list))
def traverse(self, script_graph, parent_layer=None):
# 用于获取graph的输入
def _update_graph_inputs(kind, inputs, outputs):
# extend只能放更新graph_inputs之前的情况:
# 1. loop的输出i也是输入;i是输入的原因是:子图中为父图得到的。
# 2. 在_check_input中需要使用to_variable。
# extend只能放更新graph_inputs之后的情况:
# 使用了append。
if kind != "aten::append":
current_node_outputs.extend(outputs)
for name in inputs:
if name not in current_node_outputs:
graph_inputs.append(name)
if kind == "aten::append":
current_node_outputs.extend(outputs)
# 初始化
graph = PaddleGraph(parent_layer, graph_type="dygraph")
current_node_outputs = []
graph_inputs = []
# 转换输入节点
if isinstance(script_graph, torch._C.Graph):
for i, ivalue in enumerate(script_graph.inputs()):
node = ivalue.node()
if str(ivalue.type()) != "Tensor":
graph.set_name(str(ivalue.type()).split(".")[-1])
inputs, outputs = self.data(graph, node, ivalue.unique())
# 转换中间节点
for node in script_graph.nodes():
kind = node.kind()
func_name = kind.replace('::', '_')
if hasattr(prim, func_name):
func = getattr(prim, func_name)
inputs, outputs = func(self, graph, node)
_update_graph_inputs(kind, inputs, outputs)
elif hasattr(aten, func_name):
func = getattr(aten, func_name)
inputs, outputs = func(self, graph, node)
_update_graph_inputs(kind, inputs, outputs)
# 转换输出节点
if hasattr(script_graph, 'returnNode'):
for i, ivalue in enumerate(script_graph.returnNode().inputs()):
if parent_layer.kernel == "prim.loop" and i == 0:
continue
node = ivalue.node()
script_unique_id = ivalue.unique()
inputs, outputs = self.equal(
graph,
node,
uid=script_unique_id,
parent_layer=parent_layer,
index=i)
_update_graph_inputs("equal", inputs, outputs)
# 设置graph的参数
if isinstance(script_graph, torch._C.Graph):
graph.set_parameters(self.paddle_params)
return graph, graph_inputs
def _get_outputs_name(self, node, attr_name=None):
outputs_name = []
for output_ivalue in node.outputs():
script_unique_id = output_ivalue.unique()
if attr_name is None:
output_name = 'x' + str(self.output_index)
if script_unique_id in self.outputs_info:
output_name = self.outputs_info[script_unique_id]
else:
output_name = attr_name.replace(".", "_")
self.outputs_info[script_unique_id] = output_name
self.output_index += 1
outputs_name.append(output_name)
# if或loop节点没有输出的情况
if len(list(node.outputs())) == 0:
output_name = '_x' + str(self.output_index)
self.output_index += 1
outputs_name.append(output_name)
return outputs_name
def _check_input(self,
graph,
node,
output_name,
node_outputs,
add_dim=False):
if node.kind() == "prim::GetAttr":
param = self.pytorch_params[output_name]
if isinstance(param, np.ndarray):
if add_dim:
param = param[np.newaxis, :]
self.paddle_params[output_name] = param
graph.add_layer(
"fluid.dygraph.base.to_variable",
inputs={},
outputs=[output_name],
value="params[{}]".format(string(output_name)))
else:
if isinstance(param, dict) and "Tensor" in param and \
"parent_layer_id" in param:
if graph.parent_layer is not None:
# 当某个param被2个控制流(if-else)赋值时,else不可以引用if中的赋值结果
id1 = param["parent_layer_id"]
id2 = graph.parent_layer.id
id1_part = id1.split(".")
id2_part = id2.split(".")
if len(id1_part) >= len(id2_part):
for i in range(len(id1_part)):
if id1_part[i] == id2_part[i]:
continue
else:
if id1_part[i] == "0" and id2_part[
i] == "1":
if add_dim:
param = param[np.newaxis, :]
self.paddle_params[output_name] = param
graph.add_layer(
"fluid.dygraph.base.to_variable",
inputs={},
outputs=[output_name],
value="params[{}]".format(
string(output_name)))
node_outputs.append(output_name)
return
# 若if-else外,则可直接引用if-else中的赋值结果
graph.add_layer(
"prim.constant",
inputs={},
outputs=[output_name],
value=param["Tensor"])
else:
graph.add_layer(
"prim.constant",
inputs={},
outputs=[output_name],
value=string(param)
if isinstance(param, str) else param)
node_outputs.append(output_name)
def _get_inputs_name(self, node):
inputs_name = []
inputs_node = []
for script_input_ivalue in node.inputs():
script_input_node = script_input_ivalue.node()
script_input_unique_id = script_input_ivalue.unique()
input_name = self.outputs_info[script_input_unique_id]
inputs_node.append(script_input_node)
inputs_name.append(input_name)
return inputs_name, inputs_node
def data(self, graph, node, uid):
for output_ivalue in node.outputs():
script_unique_id = output_ivalue.unique()
if script_unique_id in self.outputs_info or script_unique_id != uid:
continue
node_name = 'x' + str(self.output_index)
self.outputs_info[script_unique_id] = node_name
self.output_index += 1
output_name = self.outputs_info[uid]
graph.add_layer(
"fluid.dygraph.base.to_variable",
inputs={},
outputs=[node_name],
value=output_name)
return [], [output_name]
def equal(self, graph, node, uid=None, parent_layer=None, index=None):
if parent_layer is not None and index is not None:
# block的输出
input_node_name = self.outputs_info[uid]
control_output_id = index
if parent_layer.kernel == "prim.loop":
control_output_id = index - 1
output_node_name = parent_layer.outputs[control_output_id]
current_outputs = [output_node_name]
self._check_input(graph, node, input_node_name, current_outputs)
graph.add_layer(
"prim.equal",
inputs={'input': input_node_name},
outputs=[output_node_name])
return [input_node_name], current_outputs
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from .fc_fuser import FcFuser
from .fc_fuse_pass import FcFusePass
from .nn_adaptive_pool2d_fuser import NnAdaptivePool2dFuser
from .nn_adaptive_pool2d_fuse_pass import NnAdaptivePool2dFusePass
from .functional_adaptive_pool2d_fuser import FunctionalAdaptivePool2dFuser
from .functional_adaptive_pool2d_fuse_pass import FunctionalAdaptivePool2dFusePass
from .constant_fuser import ConstantFuser
from .constant_fuse_pass import ConstantFusePass
from .batchnorm2d_fuser import BatchNorm2dFuser
from .batchnorm2d_fuse_pass import BatchNorm2dFusePass
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from x2paddle.optimizer.pass_ import Pass
from x2paddle.optimizer.fusion import FcFuser
from x2paddle.optimizer.pass_manager import pass_register
@pass_register
class FcFusePass(Pass):
name = "fc_fuse_pass"
def __init__(self):
Pass.__init__(self)
def apply(self, graph):
fuser = FcFuser()
fuser.operate(graph, match_kind="topo")
# 用于注册
fc_fuse_pass = FcFusePass()
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import numpy as np
from x2paddle.optimizer.pattern_matcher import FuseBase
from x2paddle.core.program import PaddleGraph, PaddleLayer
from x2paddle.core.util import *
class FcFuser(FuseBase):
def __init__(self):
self.linear_index = 0
super(FcFuser, self).__init__(graph_type="dygraph")
def build_pattern(self):
""" 描述需要替换的fc图结构。
fc层模式python实现代码示例:
x149 = 2
x151 = x146.shape
x151 = len(x151)
x152 = x151 == x149
if x152 :
x147 = self.x147
x154 = fluid.layers.transpose(x=x147, perm=[1, 0])
x148 = self.x148
x155 = fluid.layers.addmm(input=x148, x=x146, y=x154, beta=1, alpha=1)
x153 = x155
else:
x147 = self.x147
x157 = fluid.layers.transpose(x=x147, perm=[1, 0])
x158 = fluid.layers.matmul(x=x146, y=x157)
x159 = True
if x159 :
x148 = self.x148
x161 = x158 + 1 * x148
x160 = x161
else:
x160 = x158
x153 = x160
"""
def gen_name(id):
return "x" + str(id)
self.pattern.add_layer(
"prim.constant", inputs={}, outputs=[gen_name(0)], value=2)
self.pattern.add_layer(
"prim.constant", inputs={}, outputs=[gen_name(1)], value=1)
self.pattern.add_layer(
"prim.shape", inputs={'input': "fc-input-0"},
outputs=[gen_name(2)])
self.pattern.add_layer(
"prim.len", inputs={'input': gen_name(2)}, outputs=[gen_name(2)])
self.pattern.add_layer(
"prim.eq",
inputs={"eq0": gen_name(2),
"eq1": gen_name(0)},
outputs=[gen_name(3)])
self.pattern.add_layer("prim.if", {'input': gen_name(3)}, [gen_name(4)])
self.pattern.outputs.append(gen_name(4))
if_layer1 = self.pattern.layers[list(self.pattern.layers.keys())[-1]]
pattern_block0 = PaddleGraph(if_layer1, graph_type="dygraph")
pattern_block0.add_layer(
"fluid.dygraph.base.to_variable",
inputs={},
outputs=[gen_name(5)],
value="params[{}]".format(string(gen_name(5))))
pattern_block0.add_layer(
"fluid.layers.transpose",
inputs={"x": gen_name(5)},
outputs=[gen_name(6)],
perm=[1, 0])
pattern_block0.add_layer(
"fluid.dygraph.base.to_variable",
inputs={},
outputs=[gen_name(7)],
value="params[{}]".format(string(gen_name(7))))
pattern_block0.add_layer(
"fluid.layers.addmm",
inputs={"input": gen_name(7),
"x": "fc-input-0",
"y": gen_name(6)},
outputs=[gen_name(8)],
beta=1,
alpha=1)
if_layer1.inputs["input-0"] = "fc-input-0"
self.pattern.inputs.append("fc-input-0")
pattern_block0.add_layer(
"prim.equal", inputs={'input': gen_name(8)}, outputs=[gen_name(4)])
if_layer1.add_block(pattern_block0)
pattern_block1 = PaddleGraph(if_layer1, graph_type="dygraph")
pattern_block1.add_layer(
"fluid.dygraph.base.to_variable",
inputs={},
outputs=[gen_name(5)],
value="params[{}]".format(string(gen_name(5))))
pattern_block1.add_layer(
"fluid.layers.transpose",
inputs={"x": gen_name(5)},
outputs=[gen_name(6)],
perm=[1, 0])
pattern_block1.add_layer(
"fluid.layers.matmul",
inputs={"x": "fc-input-0",
"y": gen_name(6)},
outputs=[gen_name(9)])
if_layer1.inputs["input-1"] = "fc-input-0"
pattern_block1.add_layer(
"prim.constant", inputs={}, outputs=[gen_name(10)], value=True)
pattern_block1.add_layer("prim.if", {'input': gen_name(10)},
[gen_name(11)])
if_layer2 = pattern_block1.layers[list(pattern_block1.layers.keys())[
-1]]
pattern_block1_block0 = PaddleGraph(if_layer2, graph_type="dygraph")
pattern_block1_block0.add_layer(
"fluid.dygraph.base.to_variable",
inputs={},
outputs=[gen_name(12)],
value="params[{}]".format(string(gen_name(12))))
pattern_block1_block0.add_layer(
"prim.add_",
inputs={"x": gen_name(9),
"y": gen_name(12)},
outputs=[gen_name(13)],
alpha=1)
if_layer2.inputs["input-0"] = gen_name(9)
pattern_block1_block0.add_layer(
"prim.equal",
inputs={'input': gen_name(13)},
outputs=[gen_name(11)])
if_layer2.add_block(pattern_block1_block0)
pattern_block1_block1 = PaddleGraph(if_layer2, graph_type="dygraph")
pattern_block1_block1.add_layer(
"prim.equal", inputs={'input': gen_name(9)},
outputs=[gen_name(11)])
if_layer2.inputs["input-1"] = gen_name(9)
pattern_block1.add_layer(
"prim.equal", inputs={'input': gen_name(11)},
outputs=[gen_name(4)])
if_layer2.add_block(pattern_block1_block1)
if_layer1.add_block(pattern_block1)
self.pattern.build(inputs={"input-0": "fc-input-0"})
def insert_new_layer(self, graph, parameters, matches):
new_layer = self.gen_new_layer(parameters, matches)
new_layer_id = list(matches.keys())[0]
graph.layers[new_layer_id] = new_layer
matches.pop(new_layer_id)
def gen_new_layer(self, parameters, matches):
layers_id = list(matches.keys())
layer = matches[layers_id[2]]
input_name = layer.inputs["input"]
layer = matches[layers_id[5]]
output_name = layer.outputs[0]
layer = matches[layers_id[6]]
weight_name = layer.attrs["value"][8:-2]
layer = matches[layers_id[8]]
bias_name = layer.attrs["value"][8:-2]
attrs = dict()
attrs["input_dim"] = parameters[weight_name].shape[1]
attrs["output_dim"] = parameters[weight_name].shape[0]
linear_name = "linear{}".format(self.linear_index)
self.linear_index += 1
parameters["{}.weight".format(linear_name)] = parameters[
weight_name].transpose((1, 0))
parameters["{}.bias".format(linear_name)] = np.squeeze(parameters[
bias_name])
new_layer = PaddleLayer(
layers_id[0],
"fluid.dygraph.Linear",
inputs={"input": input_name},
outputs=[linear_name, output_name],
**attrs)
return new_layer
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from x2paddle.optimizer.fusion import *
from x2paddle.optimizer.pass_manager import PassManager
class GraphOptimizer(object):
def __init__(self):
self.passes = [
"fc_fuse_pass",
# "nn_adaptive_pool2d_fuse_pass",
# "functional_adaptive_pool2d_fuse_pass",
# "batchnorm2d_fuse_pass",
"constant_fuse_pass"
]
def optimize(self, graph):
for pass_name in self.passes:
pass_ = PassManager.lookup(pass_name)()
pass_.apply(graph)
print("{} done!".format(pass_name))
return graph
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class Pass(object):
name = "pass"
def __init__(self):
pass
def apply(self, graph):
raise NotImplementedError("The apply function must be implemented!")
@classmethod
def get_name(cls):
return cls.name
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class PassManager(object):
""" pass管理器。
"""
# pass_map存储name与其对应的pass
pass_map = dict()
def __init__(self):
pass
@staticmethod
def add_new_pass(name, pass_):
if name not in PassManager.pass_map:
PassManager.pass_map[name] = pass_
@staticmethod
def clear():
PassManager.passes = list()
@staticmethod
def lookup(name):
return PassManager.pass_map[name]
def pass_register(cls):
name = cls.get_name()
PassManager.add_new_pass(name, cls)
return cls
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from x2paddle.core.program import PaddleGraph
class PatternMatcher(object):
def __init__(self, pattern):
self.pattern = pattern
# matches的每个match是按照拓扑排序组成layer的dict
self.matches = list()
def operate(self, graph, match_kind="topo"):
if match_kind == "topo":
self.detect_patterns_by_topo(graph)
elif match_kind == "edge":
self.detect_patterns_by_edge(graph)
self.remove_overlapped_match()
return self.matches
def detect_patterns_by_topo(self, graph):
""" 找到与模式匹配的子图,
并将子图的id以拓扑排序存放到subgraph_id2layers。
"""
def get_subgraph(pattern, graph, start_index):
pattern_index = 0
pattern_id2layers = pattern.get_global_layers()
pattern_ids = list(pattern_id2layers.keys())
subgraph_id2layers = dict()
graph_layers = dict(list(graph.layers.items())[start_index:])
for layer_id, layer in graph_layers.items():
pattern_layer = pattern.layers[list(pattern.layers.keys())[
pattern_index]]
if layer.kernel == pattern_layer.kernel:
subgraph_id2layers[layer_id] = layer
pattern_layer_id = pattern_layer.id
# 判断输入连接是否一致
if layer_id in graph.edges_in:
if pattern_layer_id not in pattern.edges_in:
return False
else:
if len(graph.edges_in[layer_id]) != len(
pattern.edges_in[pattern_layer_id]):
return False
layer_in = graph.edges_in[layer_id]
pattern_layer_in = pattern.edges_in[pattern_layer_id]
for i in range(len(layer_in)):
layer_id_in = layer_in[i]
pattern_layer_id_in = pattern_layer_in[i]
if pattern_layer_id_in != -1:
subgraph_ids = list(subgraph_id2layers.keys())
if pattern_ids.index(pattern_layer_id_in) == \
subgraph_ids.index(layer_id_in):
# 判断pattern输入在pattern_ids的索引
# 和graph输入在subgraph_ids的索引一致
continue
return False
# 判断subgraph中的节点是否被外部图使用到(如若被使用到则无效)
if layer_id in graph.edges_out:
if pattern_layer_id not in pattern.edges_out:
if not set(pattern_layer.outputs).issubset(
pattern.outputs):
# 若pattern当前layer的输出是pattern的输出,则是正确的
return False
else:
if len(graph.edges_out[layer_id]) != len(
pattern.edges_out[pattern_layer_id]):
# 如果在每个节点edges_in相同的情况下,edges_out数目相同则说明无节点在subgraph外被用到
if not set(pattern_layer.outputs).issubset(
pattern.outputs):
# 若pattern当前layer的输出是pattern的输出,则是正确的
return False
# 当为控制流时的处理
if layer.kernel == "prim.if" or layer.kernel == "prim.loop":
if len(pattern_layer.blocks) != len(layer.blocks):
return False
for i, b in enumerate(pattern_layer.blocks):
match_info = get_subgraph(pattern_layer.blocks[i],
layer.blocks[i], 0)
if match_info is not False:
subgraph_id2layers.update(match_info)
else:
return False
pattern_index += 1
if pattern_index == len(pattern.layers):
return subgraph_id2layers
else:
return False
return subgraph_id2layers
for i, (layer_id, layer) in enumerate(graph.layers.items()):
match_info = get_subgraph(self.pattern, graph, i)
if match_info:
self.matches.append(match_info)
for j, block in enumerate(layer.blocks):
if len(block.layers) > 0:
self.detect_patterns_by_topo(layer.blocks[j])
def detect_patterns_by_edge(self, graph):
"""当遇见顺序没有强制规定的pattern时使用该方式
"""
pass
def remove_overlapped_match(self):
""" 如果2个子图有重叠,只取前一个子图。
"""
match_ids = []
for i, match in enumerate(self.matches):
is_overlapped = False
for id in match.keys():
if id in match_ids:
self.matches.pop(i)
is_overlapped = True
break
if not is_overlapped:
match_ids.extend(list(match.keys()))
def get_subgraph(prefix_layer_id, suffix_layer_id, graph):
""" 根据prefix_layer_id和suffix_layer_id获取需要子图。
Args:
prefix_layer_id (str): 起初为一个空字符串,之后为suffix_layer_id分割出来的前缀。
suffix_layer_id (str): 起初为以一个layer的id,之后将分割部分给prefix_layer_id;例如”57.0.1“;
graph (x2paddle.core.program.PaddleGraph): 需要进行pass的子图。
"""
id_part = suffix_layer_id.split(".")
if len(id_part) == 1:
return graph
if prefix_layer_id == "":
layer_id = id_part[0]
prefix_layer_id += ".".join(id_part[:2])
else:
layer_id = prefix_layer_id + "." + id_part[0]
prefix_layer_id += ("." + ".".join(id_part[:2]))
subgraph = graph.layers[layer_id].blocks[int(id_part[1])]
suffix_layer_id = ".".join(id_part[2:])
return get_subgraph(prefix_layer_id, suffix_layer_id, subgraph)
class FuseBase(object):
def __init__(self, graph_type):
self.pattern = PaddleGraph(graph_type=graph_type)
def operate(self, graph, match_kind="topo"):
parameters = graph.parameters
self.build_pattern()
self.perform_pattern_matcher(graph, match_kind)
for match in self.matches:
first_layer_id = list(match.keys())[0]
subgraph = get_subgraph("", first_layer_id, graph)
self.insert_new_layer(subgraph, parameters, match)
self.delete_inter_layer(graph)
graph.build()
def perform_pattern_matcher(self, graph, match_kind="topo"):
""" 执行模式匹配,找到匹配的子图。
"""
pattern_matcher = PatternMatcher(self.pattern)
self.matches = pattern_matcher.operate(graph, match_kind)
def delete_inter_layer(self, graph):
""" 删除不需要的中间layer及其对应参数。
"""
for match in self.matches:
first_layer_id = list(match.keys())[0]
subgraph = get_subgraph("", first_layer_id, graph)
for layer_id, layer in match.items():
if layer.kernel == "fluid.dygraph.base.to_variable" and \
layer.attrs["value"].startswith("params["):
param_name = layer.attrs["value"][8:-2]
if param_name in graph.parameters:
graph.parameters.pop(param_name)
if layer_id in subgraph.layers:
# layer_id可能是属于子图的,此时删除父layer,即删除整个子图
subgraph.layers.pop(layer_id)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册