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

Merge pull request #380 from SunAhong1993/me

add pytorch convertor
__version__ = "0.7.4" __version__ = "0.7.4"
from .core.program import PaddleProgram from .core.program import PaddleGraph
program = PaddleProgram() program = PaddleGraph()
name_counter = dict() name_counter = dict()
......
...@@ -18,15 +18,15 @@ import paddle.fluid as fluid ...@@ -18,15 +18,15 @@ import paddle.fluid as fluid
from paddle.fluid.proto import framework_pb2 from paddle.fluid.proto import framework_pb2
from collections import OrderedDict from collections import OrderedDict
import numpy import numpy
import time
import collections import collections
import sys import sys
import os import os
import six import six
import pickle
class PaddleLayer(object): class PaddleLayer(object):
def __init__(self, kernel, inputs, outputs, **kwargs): def __init__(self, id, kernel, inputs, outputs, **kwargs):
assert isinstance( assert isinstance(
inputs, inputs,
dict), "parameter 'inputs' for PaddleLayer should be type of dict" dict), "parameter 'inputs' for PaddleLayer should be type of dict"
...@@ -51,22 +51,29 @@ class PaddleLayer(object): ...@@ -51,22 +51,29 @@ class PaddleLayer(object):
self.inputs = inputs self.inputs = inputs
self.outputs = outputs self.outputs = outputs
self.attrs = kwargs self.attrs = kwargs
self.id = str(time.time()) self.id = id
self.blocks = list()
def add_block(self, block): def add_block(self, block):
block.father_layer = self
self.blocks.append(block) self.blocks.append(block)
class PaddleProgram(object): class PaddleGraph(object):
def __init__(self): def __init__(self, parent_layer=None, graph_type="static"):
self.layers = OrderedDict() self.layers = OrderedDict()
self.edges_out = dict() self.edges_out = dict()
self.edges_in = dict() self.edges_in = dict()
self.inputs = list() self.inputs = list()
self.outputs = list() self.outputs = list()
self.parameters = dict() 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): def clear(self):
self.layers = OrderedDict() self.layers = OrderedDict()
...@@ -76,25 +83,44 @@ class PaddleProgram(object): ...@@ -76,25 +83,44 @@ class PaddleProgram(object):
self.outputs = list() self.outputs = list()
self.parameters = dict() self.parameters = dict()
def clear_edges(self):
self.edges_out = dict()
self.edges_in = dict()
def add_layer(self, kernel, inputs, outputs, **kwargs): def add_layer(self, kernel, inputs, outputs, **kwargs):
layer = PaddleLayer(kernel, inputs, outputs, **kwargs)
layer_id = str(len(self.layers)) layer_id = str(len(self.layers))
if self.father_layer is not None: if self.parent_layer is not None:
layer_id = "{}.{}.{}".format(layer_id, len(self.father_layer.blocks()), self.father_layer.id) 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 self.layers[layer_id] = layer
return layer_id return layer_id
def build(self): def build(self, inputs=None, outputs=None):
self.clear_edges()
outputs_from_nodes = dict() outputs_from_nodes = dict()
for layer_id, layer in self.layers.items(): 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(): for input_key, input_var in layer.inputs.items():
vs = input_var vs = input_var
if not isinstance(vs, list): if not isinstance(vs, list):
vs = [vs] vs = [vs]
for v in 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) 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: if in_layer_id not in self.edges_out:
self.edges_out[in_layer_id] = list() self.edges_out[in_layer_id] = list()
self.edges_out[in_layer_id].append(layer_id) self.edges_out[in_layer_id].append(layer_id)
...@@ -105,6 +131,27 @@ class PaddleProgram(object): ...@@ -105,6 +131,27 @@ class PaddleProgram(object):
for output in layer.outputs: for output in layer.outputs:
outputs_from_nodes[output] = layer_id 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 gen_code(self, code_dir):
def write_code(f, code_list, indent=0): def write_code(f, code_list, indent=0):
indent_blank = " " * indent indent_blank = " " * indent
...@@ -162,37 +209,41 @@ class PaddleProgram(object): ...@@ -162,37 +209,41 @@ class PaddleProgram(object):
f.close() f.close()
def gen_model(self, save_dir): def gen_model(self, save_dir):
code_dir = os.path.join(save_dir, 'model_with_code') if self.graph_type == "static":
infer_dir = os.path.join(save_dir, 'inference_model') code_dir = os.path.join(save_dir, 'model_with_code')
self.gen_code(code_dir) infer_dir = os.path.join(save_dir, 'inference_model')
sys.path.append(code_dir) self.gen_code(code_dir)
import x2paddle_model sys.path.append(code_dir)
scope = fluid.Scope() import x2paddle_model
startup_program = fluid.Program() scope = fluid.Scope()
main_program = fluid.Program() startup_program = fluid.Program()
with fluid.scope_guard(scope): main_program = fluid.Program()
with fluid.program_guard(main_program, startup_program): with fluid.scope_guard(scope):
inputs, outputs = x2paddle_model.x2paddle_net() with fluid.program_guard(main_program, startup_program):
exe = fluid.Executor(fluid.CPUPlace()) inputs, outputs = x2paddle_model.x2paddle_net()
exe.run(startup_program) exe = fluid.Executor(fluid.CPUPlace())
exe.run(startup_program)
param_dir = os.path.join(code_dir, 'weights')
for k, v in self.parameters.items(): param_dir = os.path.join(code_dir, 'weights')
if scope.find_var(k): for k, v in self.parameters.items():
self.dump_parameter(k, v, param_dir) if scope.find_var(k):
self.dump_parameter(k, v, param_dir)
def if_exist(var):
b = os.path.exists( def if_exist(var):
os.path.join(os.path.join(param_dir, var.name))) b = os.path.exists(
return b 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.load_vars(
fluid.io.save_inference_model( exe, param_dir, main_program, predicate=if_exist)
dirname=infer_dir, fluid.io.save_inference_model(
feeded_var_names=[i.name for i in inputs], dirname=infer_dir,
target_vars=outputs, feeded_var_names=[i.name for i in inputs],
executor=exe) 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): def dump_parameter(self, param_name, param, save_dir):
if not os.path.exists(save_dir): if not os.path.exists(save_dir):
...@@ -227,3 +278,167 @@ class PaddleProgram(object): ...@@ -227,3 +278,167 @@ class PaddleProgram(object):
fp.write(tensor_desc.SerializeToString()) fp.write(tensor_desc.SerializeToString())
param.tofile(fp) param.tofile(fp)
fp.close() 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.
from x2paddle.core.util import *
def aten_adaptive_avg_pool2d(mapper, graph, node):
""" 构造average adaptive pool2d的PaddleLayer。
TorchScript示例:
%x.5 : Tensor = aten::adaptive_avg_pool2d(%x.3, %_output_size.1)
参数含义:
%x.5 (Tensor): 池化后结果Tensor。
%x.3 (Tensor): 输入Tensor。
%_output_size.1 (list): 自适应池化后的Tensor的宽、高大小。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
layer_attrs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%x.3
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())
# 处理输入1,即%_output_size.1
if inputs_name[1] in mapper.attrs:
layer_attrs["pool_size"] = mapper.attrs[inputs_name[1]]
else:
mapper._check_input(graph, inputs_node[1], inputs_name[1],
current_outputs)
layer_attrs["pool_size"] = inputs_name[1]
current_inputs.append(inputs_name[1])
layer_attrs["pool_type"] = string("avg")
graph.add_layer(
"fluid.layers.adaptive_pool2d",
inputs=layer_inputs,
outputs=layer_outputs,
**layer_attrs)
return current_inputs, current_outputs
def aten_addmm(mapper, graph, node):
""" 构造addmm的PaddleLayer,该节点实现out = alpha ∗ x ∗ y + beta ∗ input。
TorchScript示例:
%ret.2 : Tensor = aten::addmm(%150, %input.3, %156, %151, %152)
参数含义:
%ret.2 (Tensor): addmm结果Tensor。
%150 (Tensor): 输入Tensor input。
%input.3 (Tensor): 输入Tensor x。
%156 (Tensor): 输入Tensor y。
%151 (int/float): 输入alpha。
%152 (int/float): 输入beta。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
layer_attrs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%150
mapper._check_input(
graph, inputs_node[0], inputs_name[0], current_outputs, add_dim=True)
layer_inputs["input"] = inputs_name[0]
# 处理输入1,即%input.3
mapper._check_input(graph, inputs_node[1], inputs_name[1], current_outputs)
layer_inputs["x"] = inputs_name[1]
# 处理输入2,即%156
mapper._check_input(graph, inputs_node[2], inputs_name[2], current_outputs)
layer_inputs["y"] = inputs_name[2]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
# 处理输入3,即%152
if inputs_name[3] in mapper.attrs:
layer_attrs["beta"] = mapper.attrs[inputs_name[3]]
else:
mapper._check_input(graph, inputs_node[3], inputs_name[3],
current_outputs)
layer_attrs["beta"] = inputs_name[3]
current_inputs.append(inputs_name[3])
# 处理输入4,即%151
if inputs_name[4] in mapper.attrs:
layer_attrs["alpha"] = mapper.attrs[inputs_name[4]]
else:
mapper._check_input(graph, inputs_node[4], inputs_name[4],
current_outputs)
layer_attrs["alpha"] = inputs_name[4]
current_inputs.append(inputs_name[4])
graph.add_layer(
"fluid.layers.addmm",
inputs=layer_inputs,
outputs=layer_outputs,
**layer_attrs)
return current_inputs, current_outputs
def aten_add(mapper, graph, node):
""" 构造数值相加的PaddleLayer,该节点实现out = x + y。
TorchScript示例:
%296 : int = aten::add(%i.12, %288)
参数含义:
%296 (-): 相加结果。
%i.12 (-): 输入数值 x。
%288 (-): 输入数值 y。
"""
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,即%i.12
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 处理输入1,即%288
mapper._check_input(
graph, inputs_node[1], inputs_name[1], current_outputs, add_dim=True)
layer_inputs["y"] = inputs_name[1]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.add", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_add_(mapper, graph, node):
""" 构造数值相加的PaddleLayer,该节点实现out = x + alpha * y。
TorchScript示例:
%137 : Tensor = aten::add(%136, %130, %130)
参数含义:
%output.5 (Tensor): add结果Tensor。
%output.2 (Tensor): 输入Tensor x。
%150 (Tensor): 输入Tensor y。
%151 (int/float): 输入alpha。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
layer_attrs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%output.2
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 处理输入1,即%150
mapper._check_input(
graph, inputs_node[1], inputs_name[1], current_outputs, add_dim=True)
layer_inputs["y"] = inputs_name[1]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
# 处理输入2,即%151
if inputs_name[2] in mapper.attrs:
layer_attrs["alpha"] = mapper.attrs[inputs_name[2]]
else:
mapper._check_input(graph, inputs_node[2], inputs_name[2],
current_outputs)
layer_attrs["alpha"] = inputs_name[2]
current_inputs.append(inputs_name[2])
graph.add_layer(
"prim.add_", inputs=layer_inputs, outputs=layer_outputs, **layer_attrs)
return current_inputs, current_outputs
def aten___and__(mapper, graph, node):
""" 构造与计算的PaddleLayer。
TorchScript示例:
%361 : bool = aten::__and__(%360, %358)
参数含义:
%361 (bool): 输出,与计算结果。
%360 (-): 输入 x。
%358 (-): 输入 y。
"""
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,即%i.12
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 处理输入1,即%288
mapper._check_input(
graph, inputs_node[1], inputs_name[1], current_outputs, add_dim=True)
layer_inputs["y"] = inputs_name[1]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.and", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_append(mapper, graph, node):
""" 构造对list进行append的PaddleLayer。
TorchScript示例:
%90 : int[] = aten::append(%_output_size.1, %v.1)
参数含义:
%90 (list): 输出,append后的list。
%_output_size.1 (list): 需要进行append的list。
%v.1 (-): append的元素。
"""
layer_inputs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
layer_outputs = [inputs_name[0]]
# 获取当前节点输出的list
current_outputs = [inputs_name[0]]
# 处理输入0,即_output_size.1
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["list"] = inputs_name[0]
# 处理输入1,即v.1
mapper._check_input(graph, inputs_node[1], inputs_name[1], current_outputs)
layer_inputs["element"] = inputs_name[1]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.append", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_avg_pool2d(mapper, graph, node):
""" 构造最大池化的PaddleLayer。
TorchScript示例:
%branch_pool.2 : Tensor = aten::avg_pool2d(%x.43, %538, %539, %540, %273, %272, %271)
参数含义:
%branch_pool.2 (Tensor): 输出,池化后的结果。
%x.43 (Tensor): 需要池化的Tensor。
%538 (list): 池化kernel的大小。
%539 (list): 步长大小。
%540 (list): 填充大小。
%273 (bool): 是否用ceil函数计算输出高度和宽度。
%272 (bool): 是否在平均池化模式不忽略填充值,False为忽略。
%271 (int): 如果指定,它将用作除数,否则将使用池化区域的大小。
"""
if "pool" in mapper.dygraph_name_id:
mapper.dygraph_name_id["pool"] += 1
else:
mapper.dygraph_name_id["pool"] = 0
pool_name = "pool" + str(mapper.dygraph_name_id["pool"])
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [pool_name, output_name]
layer_inputs = {}
layer_attrs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%x.34
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())
# 处理输入1,即%538
layer_attrs["pool_size"] = mapper.attrs[inputs_name[1]]
# 处理输入2,即%539
layer_attrs["pool_stride"] = mapper.attrs[inputs_name[2]]
# 处理输入3,即%540
layer_attrs["pool_padding"] = mapper.attrs[inputs_name[3]]
# 处理输入4,即%273
layer_attrs["ceil_mode"] = mapper.attrs[inputs_name[4]]
# 处理输入5,即%272
layer_attrs["exclusive"] = not mapper.attrs[inputs_name[5]]
# 处理输入6,即%271
graph.add_layer(
"prim.assert",
inputs={},
outputs=[inputs_name[6]],
type="eq",
key=mapper.attrs[inputs_name[6]],
value=None)
layer_attrs["pool_type"] = string("avg")
graph.add_layer(
"fluid.dygraph.Pool2D",
inputs=layer_inputs,
outputs=layer_outputs,
**layer_attrs)
return current_inputs, current_outputs
def aten_batch_norm(mapper, graph, node):
""" 构造BatchNorm的PaddleLayer。
TorchScript示例:
%input.81 : Tensor = aten::batch_norm(%input.80, %778, %779, %776, %777, %780,
%exponential_average_factor.23, %766, %781)
参数含义:
%input.81 (Tensor): 输出,批处理后的结果。
%input.80 (Tensor): 需要进行批处理的特征层。
%778 (Tensor): weights。
%779 (Tensor): bias。
%776 (Tensor): 全局均值。
%777 (Tensor): 全局方差。
%780 (bool): 是否训练。
%exponential_average_factor.23 (float): 用于计算均值和方差的比例。
%766 (float): 为了数值稳定加在分母上的值。
%781 (bool): 是否启用cudnn。
"""
if "batchnorm" in mapper.dygraph_name_id:
mapper.dygraph_name_id["batchnorm"] += 1
else:
mapper.dygraph_name_id["batchnorm"] = 0
batchnorm_name = "batchnorm" + str(mapper.dygraph_name_id["batchnorm"])
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [batchnorm_name, output_name]
layer_inputs = {}
layer_attrs = {}
layer_attrs["is_test"] = True
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%input.80
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())
# 处理输入1,即%778
weights = mapper.pytorch_params[inputs_name[1]]
mapper.paddle_params[batchnorm_name + ".weight"] = weights
layer_attrs['num_channels'] = weights.shape[0]
# 处理输入2,即%779
if inputs_name[2] in mapper.pytorch_params:
bias = mapper.pytorch_params[inputs_name[2]]
if bias is not None:
mapper.paddle_params[batchnorm_name + ".bias"] = bias
else:
mapper.paddle_params[batchnorm_name + ".bias"] = False
# 处理输入3,即%776
mean = mapper.pytorch_params[inputs_name[3]]
mapper.paddle_params[batchnorm_name + "._mean"] = mean
# 处理输入4,即%777
var = mapper.pytorch_params[inputs_name[4]]
mapper.paddle_params[batchnorm_name + "._variance"] = var
# 处理输入6,即%exponential_average_factor.23
layer_attrs["momentum"] = mapper.attrs[inputs_name[6]]
# 处理输入7,即%766
layer_attrs["epsilon"] = mapper.attrs[inputs_name[7]]
graph.add_layer(
"fluid.dygraph.BatchNorm",
inputs=layer_inputs,
outputs=layer_outputs,
**layer_attrs)
return current_inputs, current_outputs
def aten_cat(mapper, graph, node):
""" 构造连接Tensor的PaddleLayer。
TorchScript示例:
%x.222 : Tensor = aten::cat(%32, %7)
参数含义:
%x.222 (Tensor): 输出,连接后的结果。
%i.12 (list): 需要连接的Tensor组成的list。
%7 (int): 连接的轴。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
layer_attrs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%13
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())
# 处理输入1,即%12
if inputs_name[1] in mapper.attrs:
layer_attrs["axis"] = mapper.attrs[inputs_name[1]]
else:
mapper._check_input(graph, inputs_node[1], inputs_name[1],
current_outputs)
layer_attrs["axis"] = inputs_name[1]
current_inputs.append(inputs_name[1])
graph.add_layer(
"fluid.layers.concat",
inputs=layer_inputs,
outputs=layer_outputs,
**layer_attrs)
return current_inputs, current_outputs
def aten_conv2d(mapper, graph, node):
""" 构造conv2d的PaddleLayer。
TorchScript示例:
%input.10 : Tensor = aten::conv2d(%input.8, %25, %27, %28, %29, %30, %26)
参数含义:
%input.10 (Tensor): 输出,卷积后的结果。
%input.8 (Tensor): 需要进行卷积的特征层。
%25 (Tensor): weights。
%27 (Tensor): bias。
%28 (int): 步长大小。
%29 (int): 填充大小。
%30 (int): 膨胀系数大小。
%26 (int): 卷积的组数。
"""
if "conv" in mapper.dygraph_name_id:
mapper.dygraph_name_id["conv"] += 1
else:
mapper.dygraph_name_id["conv"] = 0
conv2d_name = "conv" + str(mapper.dygraph_name_id["conv"])
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [conv2d_name, output_name]
layer_inputs = {}
layer_attrs = {}
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())
# 处理输入1,即%25
weights = mapper.pytorch_params[inputs_name[1]]
mapper.paddle_params[conv2d_name + ".weight"] = weights
layer_attrs["num_filters"] = weights.shape[0]
layer_attrs["filter_size"] = weights.shape[2:]
# 处理输入2,即%27
if inputs_name[2] in mapper.pytorch_params:
bias = mapper.pytorch_params[inputs_name[2]]
if bias is not None:
mapper.paddle_params[conv2d_name + ".bias"] = bias
else:
layer_attrs["bias_attr"] = False
else:
layer_attrs["bias_attr"] = False
# 处理输入3,即%28
layer_attrs["stride"] = mapper.attrs[inputs_name[3]]
# 处理输入4,即%29
layer_attrs["padding"] = mapper.attrs[inputs_name[4]]
# 处理输入5,即%30
layer_attrs["dilation"] = mapper.attrs[inputs_name[5]]
# 处理输入6,即%26
layer_attrs["groups"] = mapper.attrs[inputs_name[6]]
layer_attrs['num_channels'] = weights.shape[1] * mapper.attrs[inputs_name[
6]]
graph.add_layer(
"fluid.dygraph.Conv2D",
inputs=layer_inputs,
outputs=layer_outputs,
**layer_attrs)
return current_inputs, current_outputs
def aten_dim(mapper, graph, node):
""" 构造获取维度的PaddleLayer。
TorchScript示例:
%106 : int = aten::dim(%101)
参数含义:
%106 (int): 输出,Tensor的维度。
%101 (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,即%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)
graph.add_layer(
"prim.len", inputs={"input": output_name}, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_dropout(mapper, graph, node):
""" 构造Dropout的PaddleLayer。
TorchScript示例:
%119 : Tensor = aten::dropout(%result.3, %117, %118)
参数含义:
%119 (Tensor): Dropout后的Tensor。
%result.3 (Tensor): 输入Tensor。
%118 (bool): 是否是训练阶段。
"""
if "dropout" in mapper.dygraph_name_id:
mapper.dygraph_name_id["dropout"] += 1
else:
mapper.dygraph_name_id["dropout"] = 0
dropout_name = "dropout" + str(mapper.dygraph_name_id["dropout"])
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [dropout_name, output_name]
layer_inputs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%119
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(
"fluid.dygraph.Dropout",
inputs=layer_inputs,
outputs=layer_outputs,
p=0.0)
return current_inputs, current_outputs
def aten_dropout_(mapper, graph, node):
""" 构造Dropout的PaddleLayer。
TorchScript示例:
%119 : Tensor = aten::dropout_(%result.3, %117, %118)
参数含义:
%119 (Tensor): Dropout后的Tensor。
%result.3 (Tensor): 输入Tensor。
%118 (bool): 是否是训练阶段。
"""
if "dropout" in mapper.dygraph_name_id:
mapper.dygraph_name_id["dropout"] += 1
else:
mapper.dygraph_name_id["dropout"] = 0
dropout_name = "dropout" + str(mapper.dygraph_name_id["dropout"])
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [dropout_name, output_name]
layer_inputs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%119
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(
"fluid.dygraph.Dropout",
inputs=layer_inputs,
outputs=layer_outputs,
p=0.0)
return current_inputs, current_outputs
def aten_eq(mapper, graph, node):
""" 构造判断数值是否相等的PaddleLayer。
TorchScript示例:
%125 : bool = aten::eq(%124, %123)
参数含义:
%125 (bool): 对比后结果。
%124 (-): 需对比的输入1。
%123 (-): 需对比的输入2。
"""
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,即%124
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 处理输入1,即%123
mapper._check_input(graph, inputs_node[1], inputs_name[1], current_outputs)
layer_inputs["y"] = inputs_name[1]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.eq", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_flatten(mapper, graph, node):
""" 构造flatten的PaddleLayer。
TorchScript示例:
%x.8 : Tensor = aten::flatten(%x, %4, %2)
参数含义:
%x.8 (Tensor): flatten后结果。
%x (Tensor): 输入Tensor。
%4 (int): flatten的开始维度。
%2 (int): flatten的结束维度。
注意:目前flatten只支持第一维的flatten
"""
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]
# 处理输入1,即%4
graph.add_layer(
"prim.assert",
inputs={},
outputs=[inputs_name[1]],
type='eq',
key=mapper.attrs[inputs_name[1]],
value=1)
# 处理输入2,即%2
graph.add_layer(
"prim.assert",
inputs={},
outputs=[inputs_name[2]],
type='eq',
key=mapper.attrs[inputs_name[2]],
value=-1)
# 处理输入0,即%x
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer(
"fluid.layers.flatten",
inputs=layer_inputs,
outputs=layer_outputs,
axis=1)
return current_inputs, current_outputs
def aten___getitem__(mapper, graph, node):
""" 构造获取list中元素的PaddleLayer。
TorchScript示例:
%v.1 : int = aten::__getitem__(%72, %88)
参数含义:
%v.1 (-): 输出,list中的元素。
%72 (list): 需要获取元素的list。
%88 (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,即%72
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["list"] = inputs_name[0]
# 处理输入1,即%88
mapper._check_input(graph, inputs_node[1], inputs_name[1], current_outputs)
layer_inputs["index"] = inputs_name[1]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.getitem", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_gt(mapper, graph, node):
""" 构造对比大小的PaddleLayer。
TorchScript示例:
%83 : bool = aten::gt(%82, %78)
参数含义:
%83 (bool): 输出,第一个元素是否大于第二个元素。
%82 (-): 需对比的输入1。
%78 (-): 需对比的输入2。
"""
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,即%82
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 处理输入1,即%78
mapper._check_input(graph, inputs_node[1], inputs_name[1], current_outputs)
layer_inputs["y"] = inputs_name[1]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.gt", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_hardtanh_(mapper, graph, node):
""" 构造hardtanh激活的PaddleLayer。
TorchScript示例:
%result.9 : Tensor = aten::hardtanh_(%input.20, %67, %66)
参数含义:
%result.9 (Tensor): 输出,hardtanh激活后的Tensor。
%input.20 (Tensor): 需要hardtanh激活的Tensor。
%67 (float): hardtanh激活的最小阈值。
%66 (float): hardtanh激活的最大阈值。
"""
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]
# 处理输入1,即%67
graph.add_layer(
"prim.assert",
inputs={},
outputs=[inputs_name[1]],
type='eq',
key=mapper.attrs[inputs_name[1]],
value=0.0)
# 处理输入2,即%66
graph.add_layer(
"prim.assert",
inputs={},
outputs=[inputs_name[2]],
type='eq',
key=mapper.attrs[inputs_name[2]],
value=6.0)
# 处理输入0,即%input.20
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer(
'fluid.layers.relu6',
inputs=layer_inputs,
outputs=layer_outputs,
threshold=6.0)
return current_inputs, current_outputs
def aten_le(mapper, graph, node):
""" 构造对比大小的PaddleLayer。
TorchScript示例:
%80 : bool = aten::le(%78, %79)
参数含义:
%80 (bool): 输出,第一个元素是否小于等于第二个元素。
%78 (-): 需对比的输入1。
%79 (-): 需对比的输入2。
"""
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,即%78
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 处理输入1,即%79
mapper._check_input(graph, inputs_node[1], inputs_name[1], current_outputs)
layer_inputs["y"] = inputs_name[1]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.le", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_len(mapper, graph, node):
""" 构造获取list长度的PaddleLayer。
TorchScript示例:
%85 : int = aten::len(%83)
参数含义:
%85 (int): 输出,list的长度。
%72 (list): 需要获取长度的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]
# 处理输入0,即%72
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.len", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_lt(mapper, graph, node):
""" 构造对比大小的PaddleLayer。
TorchScript示例:
%80 : bool = aten::lt(%78, %79)
参数含义:
%80 (bool): 输出,第一个元素是否小于第二个元素。
%78 (-): 需对比的输入1。
%79 (-): 需对比的输入2。
"""
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,即%78
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 处理输入1,即%79
mapper._check_input(graph, inputs_node[1], inputs_name[1], current_outputs)
layer_inputs["y"] = inputs_name[1]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.lt", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_max_pool2d(mapper, graph, node):
""" 构造最大池化的PaddleLayer。
TorchScript示例:
%input.8 : Tensor = aten::max_pool2d(%result.11, %20, %23, %21, %22, %19)
参数含义:
%input.8 (Tensor): 输出,池化后的结果。
%result.11 (Tensor): 需要池化的Tensor。
%20 (list): 池化kernel的大小。
%23 (list): 步长大小。
%21 (list): 填充大小。
%22 (list): 膨胀系数大小。
%19 (bool): 是否用ceil函数计算输出高度和宽度。
"""
if "pool" in mapper.dygraph_name_id:
mapper.dygraph_name_id["pool"] += 1
else:
mapper.dygraph_name_id["pool"] = 0
pool_name = "pool" + str(mapper.dygraph_name_id["pool"])
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [pool_name, output_name]
layer_inputs = {}
layer_attrs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%result.11
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())
# 处理输入1,即%20
layer_attrs["pool_size"] = mapper.attrs[inputs_name[1]]
# 处理输入2,即%23
layer_attrs["pool_stride"] = mapper.attrs[inputs_name[2]]
# 处理输入3,即%21
layer_attrs["pool_padding"] = mapper.attrs[inputs_name[3]]
# 处理输入4,即%22
graph.add_layer(
"prim.assert",
inputs={},
outputs=[inputs_name[4]],
type="eq",
key=mapper.attrs[inputs_name[4]],
value=[1, [1, 1]])
# 处理输入5,即%19
layer_attrs["ceil_mode"] = mapper.attrs[inputs_name[5]]
layer_attrs["pool_type"] = string("max")
graph.add_layer(
"fluid.dygraph.Pool2D",
inputs=layer_inputs,
outputs=layer_outputs,
**layer_attrs)
return current_inputs, current_outputs
def aten_matmul(mapper, graph, node):
""" 构造矩阵相乘的PaddleLayer。
TorchScript示例:
%output.2 : Tensor = aten::matmul(%101, %111)
参数含义:
%output.2 (Tensor): 输出,相乘后的结果。
%101 (Tensor): 矩阵1。
%102 (Tensor): 矩阵2。
"""
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,即%101
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 处理输入1,即%102
mapper._check_input(graph, inputs_node[1], inputs_name[1], current_outputs)
layer_inputs["y"] = inputs_name[1]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer(
"fluid.layers.matmul", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_mul(mapper, graph, node):
""" 构造数值相乘的PaddleLayer。
TorchScript示例:
%size_prods.39 : int = aten::mul(%size_prods.38, %114)
参数含义:
%size_prods.39 (Tensor): 输出,相乘后的结果。
%size_prods.38 (-): 数值1。
%114 (-): 数值2。
"""
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,即%size_prods.38
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 处理输入1,即%114
mapper._check_input(graph, inputs_node[1], inputs_name[1], current_outputs)
layer_inputs["y"] = inputs_name[1]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
current_outputs = layer_outputs
graph.add_layer("prim.mul", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_ne(mapper, graph, node):
""" 构造判断数值是否不相等的PaddleLayer。
TorchScript示例:
%134 : bool = aten::ne(%133, %132)
参数含义:
%134 (bool): 对比后结果。
%133 (-): 需对比的输入1。
%132 (-): 需对比的输入2。
"""
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,即%124
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 处理输入1,即%123
mapper._check_input(graph, inputs_node[1], inputs_name[1], current_outputs)
layer_inputs["y"] = inputs_name[1]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.ne", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_neg(mapper, graph, node):
""" 构造对数值取负的PaddleLayer。
TorchScript示例:
%909 : int = aten::neg(%908)
参数含义:
%909 (int): 取负后结果。
%908 (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,即%124
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.neg", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten___not__(mapper, graph, node):
""" 构造对bool型取负的PaddleLayer。
TorchScript示例:
%4498 : bool = aten::__not__(%aux_defined.2)
参数含义:
%4498 (bool): 取负后结果。
%aux_defined.2 (bool): 需取负的输入。
"""
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,即%124
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.not", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_relu(mapper, graph, node):
""" 构造ReLU激活的PaddleLayer。
TorchScript示例:
%result.3 : Tensor = aten::relu(%input.5)
参数含义:
%result.3 (Tensor): 输出,ReLU后的结果。
%result.5 (Tensor): 需要ReLU的Tensor。
注意: inplace这个参数在paddle中未实现
"""
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,即%result.5
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer(
"fluid.layers.relu", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_relu_(mapper, graph, node):
""" 构造ReLU激活的PaddleLayer。
TorchScript示例:
%result.3 : Tensor = aten::relu_(%input.5)
参数含义:
%result.3 (Tensor): 输出,ReLU后的结果。
%result.5 (Tensor): 需要ReLU的Tensor。
注意: inplace这个参数在paddle中未实现
"""
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,即%result.5
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer(
"fluid.layers.relu", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_relu6(mapper, graph, node):
""" 构造ReLU6激活的PaddleLayer。
TorchScript示例:
%result.3 : Tensor = aten::relu6(%input.5)
参数含义:
%result.3 (Tensor): 输出,ReLU6后的结果。
%result.5 (Tensor): 需要ReLU6的Tensor。
注意: inplace这个参数在paddle中未实现
"""
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,即%result.5
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 获取当前节点输入、输出的list
current_inputs = list(layer_inputs.values())
graph.add_layer(
"fluid.layers.relu6",
inputs=layer_inputs,
outputs=layer_outputs,
threshold=6.0)
return current_inputs, current_outputs
def aten_reshape(mapper, graph, node):
""" 构造调整大小的PaddleLayer。
TorchScript示例:
%x.6 : Tensor = aten::reshape(%4700, %4703)
参数含义:
%x.6 (Tensor): 输出,reshape后的Tensor。
%4700 (Tensor): 需要reshape的Tensor。
%4703 (list): 形状大小组成的list。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
layer_attrs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%4700
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 处理输入1,即%4703
mapper._check_input(graph, inputs_node[1], inputs_name[1], current_outputs)
layer_inputs["shape"] = inputs_name[1]
# 获取当前节点输入、输出的list
current_inputs = list(layer_inputs.values())
graph.add_layer(
"fluid.layers.reshape", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_select(mapper, graph, node):
""" 构造选取特定维度Variable的PaddleLayer。
TorchScript示例:
%19 : Tensor = aten::select(%18, %8, %7)
参数含义:
%19 (Tensor): 输出,选取的Tensor。
%18 (Tensor): 需要选取的Tensor。
%8 (int): select的维度。
%7 (int): select的第n个向量。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
layer_attrs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%18
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["input"] = inputs_name[0]
# 处理输入1,即%8
layer_attrs["dim"] = mapper.attrs[inputs_name[1]]
# 处理输入2,即%75
mapper._check_input(graph, inputs_node[2], inputs_name[2], current_outputs)
layer_inputs["index"] = inputs_name[2]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer(
"prim.select",
inputs=layer_inputs,
outputs=current_outputs,
**layer_attrs)
return current_inputs, current_outputs
def aten_size(mapper, graph, node):
""" 构造获取shape的PaddleLayer。
TorchScript示例:
%73 : int[] = aten::size(%x.12)
参数含义:
%73 (list): 输出,shape的list。
%x.12 (Tensor): 需要获取shape的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,即%x.12
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 aten_slice(mapper, graph, node):
""" 构造切分list或Variable的PaddleLayer。
TorchScript示例:
%83 : int[] = aten::slice(%73, %82, %75, %77)
参数含义:
%83 (list/Tensor): 输出,切分后的list。
%73 (list/Tensor): 需要切分的list。
%82 (int): 切分的开始索引。
%75 (int): 切分的结束索引。
%77 (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,即%73
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["input"] = inputs_name[0]
# 处理输入1,即%82
mapper._check_input(graph, inputs_node[1], inputs_name[1], current_outputs)
layer_inputs["start"] = inputs_name[1]
# 处理输入2,即%75
mapper._check_input(graph, inputs_node[2], inputs_name[2], current_outputs)
layer_inputs["end"] = inputs_name[2]
# 处理输入3,即%77
mapper._check_input(graph, inputs_node[3], inputs_name[3], current_outputs)
layer_inputs["step"] = inputs_name[3]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.slice", inputs=layer_inputs, outputs=current_outputs)
return current_inputs, current_outputs
def aten_sub(mapper, graph, node):
""" 构造数值相减的PaddleLayer。
TorchScript示例:
%840 : int = aten::sub(%839, %836)
参数含义:
%840 (-): 相减结果。
%839 (-): 输入数值 x。
%836 (-): 输入数值 y。
"""
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,即%839
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 处理输入1,即%836
mapper._check_input(
graph, inputs_node[1], inputs_name[1], current_outputs, add_dim=True)
layer_inputs["y"] = inputs_name[1]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer("prim.sub", inputs=layer_inputs, outputs=layer_outputs)
return current_inputs, current_outputs
def aten_t(mapper, graph, node):
""" 构造矩阵转置的PaddleLayer。
TorchScript示例:
%840 : int = aten::sub(%839, %836)
参数含义:
%109 (Tensor): 输出,转置后的矩阵。
%102 (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,即%x.12
mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs)
layer_inputs["x"] = inputs_name[0]
# 获取当前节点输入的list
current_inputs = list(layer_inputs.values())
graph.add_layer(
"fluid.layers.transpose",
inputs=layer_inputs,
outputs=layer_outputs,
perm=[1, 0])
return current_inputs, current_outputs
def aten_unsqueeze(mapper, graph, node):
""" 构造插入维度的PaddleLayer。
TorchScript示例:
%13 : Tensor = aten::unsqueeze(%12, %7)
参数含义:
%13 (Tensor): 输出,插入维度后的Tensor。
%12 (Tensor): 需要插入维度的Tensor。
%7 (int): 维度。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
layer_attrs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%13
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())
# 处理输入1,即%12
if inputs_name[1] in mapper.attrs:
layer_attrs["axes"] = mapper.attrs[inputs_name[1]]
else:
mapper._check_input(graph, inputs_node[1], inputs_name[1],
current_outputs)
layer_attrs["axes"] = inputs_name[1]
current_inputs.append(inputs_name[1])
graph.add_layer(
"fluid.layers.unsqueeze",
inputs=layer_inputs,
outputs=layer_outputs,
**layer_attrs)
return current_inputs, current_outputs
def aten_warn(mapper, graph, node):
""" 构造warning的PaddleLayer。
TorchScript示例:
= aten::warn(%3, %2)
参数含义:
%3 (str): warning的提示字符串。
%2 (int): warning的stacklevel。
"""
output_name = mapper._get_outputs_name(node)[0]
layer_outputs = [output_name]
layer_inputs = {}
layer_attrs = {}
inputs_name, inputs_node = mapper._get_inputs_name(node)
# 获取当前节点输出的list
current_outputs = [output_name]
# 处理输入0,即%3
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())
# 处理输入1,即%2
if inputs_name[1] in mapper.attrs:
layer_attrs["stacklevel"] = mapper.attrs[inputs_name[1]]
else:
mapper._check_input(graph, inputs_node[1], inputs_name[1],
current_outputs)
layer_attrs["stacklevel"] = inputs_name[1]
current_inputs.append(inputs_name[1])
graph.add_layer(
"prim.warnings",
inputs=layer_inputs,
outputs=layer_outputs,
**layer_attrs)
return current_inputs, 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.
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.
先完成此消息的编辑!
想要评论请 注册