提交 a5b69c1c 编写于 作者: S SunAhong1993

add the custom layer

上级 7ba875b4
...@@ -23,11 +23,11 @@ def arg_parser(): ...@@ -23,11 +23,11 @@ def arg_parser():
type=_text_type, type=_text_type,
default=None, default=None,
help="model file path") help="model file path")
parser.add_argument("--proto", parser.add_argument("--prototxt",
"-p", "-p",
type=_text_type, type=_text_type,
default=None, default=None,
help="proto file of caffe model") help="prototxt file of caffe model")
parser.add_argument("--weight", parser.add_argument("--weight",
"-w", "-w",
type=_text_type, type=_text_type,
...@@ -43,6 +43,11 @@ def arg_parser(): ...@@ -43,6 +43,11 @@ def arg_parser():
type=_text_type, type=_text_type,
default=None, default=None,
help="define which deeplearning framework") help="define which deeplearning framework")
parser.add_argument("--caffe_proto",
"-c",
type=_text_type,
default=None,
help="caffe proto file of caffe model")
return parser return parser
...@@ -57,12 +62,12 @@ def tf2paddle(model_path, save_dir): ...@@ -57,12 +62,12 @@ def tf2paddle(model_path, save_dir):
mapper.save_python_model(save_dir) mapper.save_python_model(save_dir)
def caffe2paddle(proto, weight, save_dir): def caffe2paddle(proto, weight, save_dir, caffe_proto):
from x2paddle.decoder.caffe_decoder import CaffeDecoder from x2paddle.decoder.caffe_decoder import CaffeDecoder
from x2paddle.op_mapper.caffe_op_mapper import CaffeOpMapper from x2paddle.op_mapper.caffe_op_mapper import CaffeOpMapper
print("Now translating model from caffe to paddle.") print("Now translating model from caffe to paddle.")
model = CaffeDecoder(proto, weight) model = CaffeDecoder(proto, weight, caffe_proto)
mapper = CaffeOpMapper(model) mapper = CaffeOpMapper(model)
mapper.run() mapper.run()
mapper.save_python_model(save_dir) mapper.save_python_model(save_dir)
...@@ -80,8 +85,9 @@ def main(): ...@@ -80,8 +85,9 @@ def main():
tf2paddle(args.model, args.save_dir) tf2paddle(args.model, args.save_dir)
elif args.framework == "caffe": elif args.framework == "caffe":
assert args.proto is not None, "--proto and --weight should be defined while translating caffe model" assert args.prototxt is not None and args.weight is not None, "--prototxt and --weight should be defined while translating caffe model"
caffe2paddle(args.proto, args.weight, args.save_dir) caffe2paddle(args.prototxt, args.weight, args.save_dir,
args.caffe_proto)
else: else:
raise Exception("--framework only support tensorflow/caffe now") raise Exception("--framework only support tensorflow/caffe now")
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
# limitations under the License. # limitations under the License.
from x2paddle.core.graph import GraphNode from x2paddle.core.graph import GraphNode
import collections
class Layer(object): class Layer(object):
...@@ -22,6 +21,7 @@ class Layer(object): ...@@ -22,6 +21,7 @@ class Layer(object):
self.param_attr = dict() self.param_attr = dict()
self.inputs = dict() self.inputs = dict()
self.output = None self.output = None
self.is_new = False
def get_code(self): def get_code(self):
layer_code = "" layer_code = ""
...@@ -36,34 +36,25 @@ class Layer(object): ...@@ -36,34 +36,25 @@ class Layer(object):
if isinstance(self.inputs, list): if isinstance(self.inputs, list):
in_list = "[" in_list = "["
for input in self.inputs: for input in self.inputs:
if isinstance(input, GraphNode): assert isinstance(
if hasattr(input, "index"): input, GraphNode), "Type of input should be GraphNode"
in_list += (input.layer_name + if hasattr(input, "index"):
"[{}]".format(input.index) + ", ") in_list += (input.layer_name + "[{}]".format(input.index) +
else: ", ")
in_list += (input.layer_name + ", ")
elif isinstance(input, str):
in_list += (input + ", ")
else: else:
raise Exception( in_list += (input.layer_name + ", ")
"Element of inputs should GraphNode or String")
in_list = in_list.strip(", ") + "], " in_list = in_list.strip(", ") + "], "
layer_code += in_list layer_code += in_list
elif isinstance(self.inputs, dict): elif isinstance(self.inputs, dict):
inputs = collections.OrderedDict(self.inputs) for key, input in self.inputs.items():
for key, input in inputs.items(): assert isinstance(
if isinstance(input, GraphNode): input, GraphNode), "Type of input should be GraphNode"
if hasattr(input, "index"): if hasattr(input, "index"):
layer_code = layer_code + key + "={}, ".format( layer_code = layer_code + key + "={}, ".format(
input.layer_name + "[{}]".format(input.index)) input.layer_name + "[{}]".format(input.index))
else:
layer_code = layer_code + key + "={}, ".format(
input.layer_name)
elif isinstance(input, str):
layer_code = layer_code + key + "={}, ".format(input)
else: else:
raise Exception( layer_code = layer_code + key + "={}, ".format(
"Element of inputs should GraphNode or String") input.layer_name)
elif isinstance(self.inputs, GraphNode): elif isinstance(self.inputs, GraphNode):
if hasattr(self.inputs, "index"): if hasattr(self.inputs, "index"):
layer_code += (self.inputs.layer_name + layer_code += (self.inputs.layer_name +
...@@ -75,8 +66,38 @@ class Layer(object): ...@@ -75,8 +66,38 @@ class Layer(object):
else: else:
raise Exception("Unknown type of inputs.") raise Exception("Unknown type of inputs.")
param_attr = collections.OrderedDict(self.param_attr) for key, value in self.param_attr.items():
for key, value in param_attr.items(): layer_code = layer_code + key + "={}, ".format(value)
layer_code = layer_code.strip(", ")
return layer_code + ")"
def get_custom_code(self):
layer_code = ""
if self.output is not None:
if isinstance(self.output, str):
layer_code = self.output + " = "
else:
layer_code = self.output.layer_name + " = "
layer_code = layer_code + self.op + "("
if isinstance(self.inputs, list):
in_list = "["
for input in self.inputs:
assert isinstance(
input, GraphNode), "Type of input should be GraphNode"
if hasattr(input, "index"):
in_list += (input.layer_name + "[{}]".format(input.index) +
", ")
else:
in_list += (input.layer_name + ", ")
in_list = in_list.strip(", ") + "], "
layer_code += in_list
else:
raise Exception("Unknown type of inputs.")
for key, value in self.param_attr.items():
layer_code = layer_code + key + "={}, ".format(value) layer_code = layer_code + key + "={}, ".format(value)
layer_code = layer_code.strip(", ") layer_code = layer_code.strip(", ")
...@@ -87,9 +108,15 @@ class FluidCode(object): ...@@ -87,9 +108,15 @@ class FluidCode(object):
def __init__(self): def __init__(self):
self.layers = list() self.layers = list()
def add_layer(self, op, inputs, output, param_attr=None): def add_layer(self,
op,
inputs,
output,
param_attr=None,
is_custom_layer=False):
layer = Layer() layer = Layer()
layer.op = op layer.op = op
layer.is_custom_layer = is_custom_layer
if inputs is not None: if inputs is not None:
layer.inputs = inputs layer.inputs = inputs
layer.output = output layer.output = output
...@@ -108,7 +135,10 @@ class FluidCode(object): ...@@ -108,7 +135,10 @@ class FluidCode(object):
codes = list() codes = list()
for layer in self.layers: for layer in self.layers:
if isinstance(layer, Layer): if isinstance(layer, Layer):
codes.append(layer.get_code()) if layer.is_custom_layer:
codes.append(layer.get_custom_code())
else:
codes.append(layer.get_code())
elif isinstance(layer, str): elif isinstance(layer, str):
codes.append(layer) codes.append(layer)
return codes return codes
...@@ -18,19 +18,20 @@ from google.protobuf import text_format ...@@ -18,19 +18,20 @@ from google.protobuf import text_format
import numpy as np import numpy as np
from x2paddle.core.graph import GraphNode, Graph from x2paddle.core.graph import GraphNode, Graph
from x2paddle.core.fluid_code import FluidCode from x2paddle.core.fluid_code import FluidCode
from x2paddle.decoder import caffe_shape from x2paddle.op_mapper import caffe_shape
class CaffeResolver(object): class CaffeResolver(object):
def __init__(self, use_default=True): def __init__(self, caffe_proto_folder=None):
self.use_default = use_default self.proto_path = caffe_proto_folder
if self.proto_path == None:
self.use_default = True
else:
self.use_default = False
self.import_caffe() self.import_caffe()
def import_caffepb(self): def import_caffepb(self):
p = os.path.realpath(__file__) sys.path.append(self.proto_path)
p = os.path.dirname(p)
p = os.path.join(p, './proto')
sys.path.insert(0, p)
import caffe_pb2 import caffe_pb2
return caffe_pb2 return caffe_pb2
...@@ -60,11 +61,13 @@ class CaffeResolver(object): ...@@ -60,11 +61,13 @@ class CaffeResolver(object):
class CaffeGraphNode(GraphNode): class CaffeGraphNode(GraphNode):
def __init__(self, layer, layer_name=None): def __init__(self, layer, layer_name=None):
if layer_name is None: if layer_name is None:
super(CaffeGraphNode, self).__init__(layer, super(CaffeGraphNode,
layer.name.replace('/', '_')) self).__init__(layer,
layer.name.replace('/', '_').replace('-', '_'))
else: else:
super(CaffeGraphNode, self).__init__(layer, super(CaffeGraphNode,
layer_name.replace('/', '_')) self).__init__(layer,
layer_name.replace('/', '_').replace('-', '_'))
self.layer_type = layer.type self.layer_type = layer.type
self.fluid_code = FluidCode() self.fluid_code = FluidCode()
self.data = None self.data = None
...@@ -72,10 +75,13 @@ class CaffeGraphNode(GraphNode): ...@@ -72,10 +75,13 @@ class CaffeGraphNode(GraphNode):
def set_params(self, params): def set_params(self, params):
self.data = params self.data = params
def set_output_shape(self, input_shape): def set_output_shape(self, input_shape, is_input=True):
func_name = 'shape_' + self.layer_type.lower() func_name = 'shape_' + self.layer_type.lower()
self.output_shape = getattr(caffe_shape, func_name)(self.layer, if is_input:
input_shape) self.output_shape = getattr(caffe_shape, func_name)(self.layer,
input_shape)
else:
self.output_shape = input_shape
def set_input_shape(self, input_shape): def set_input_shape(self, input_shape):
self.input_shape = input_shape self.input_shape = input_shape
...@@ -135,7 +141,7 @@ class CaffeGraph(Graph): ...@@ -135,7 +141,7 @@ class CaffeGraph(Graph):
]))).to_proto().layer[0]) ]))).to_proto().layer[0])
except: except:
raise ImportError( raise ImportError(
'You must install the caffe first when you use old style prototxt.' 'The .proto file does not work for the old style prototxt. You must install the caffe or modify the old style to new style in .protottx file.'
) )
data.name = self.model.input[i] data.name = self.model.input[i]
data.top[0] = self.model.input[i] data.top[0] = self.model.input[i]
...@@ -151,7 +157,7 @@ class CaffeGraph(Graph): ...@@ -151,7 +157,7 @@ class CaffeGraph(Graph):
]))).to_proto().layer[0]) ]))).to_proto().layer[0])
except: except:
raise ImportError( raise ImportError(
'You must install the caffe first when you use old style prototxt.' 'The .proto file does not work for the old style prototxt. You must install the caffe or modify the old style to new style in .protottx file.'
) )
data.name = self.model.input[i] data.name = self.model.input[i]
data.top[0] = self.model.input[i] data.top[0] = self.model.input[i]
...@@ -180,19 +186,6 @@ class CaffeGraph(Graph): ...@@ -180,19 +186,6 @@ class CaffeGraph(Graph):
else: else:
notice('Ignoring parameters for non-existent layer: %s' % \ notice('Ignoring parameters for non-existent layer: %s' % \
layer_name) layer_name)
for layer_name in self.node_map:
node = self.node_map[layer_name]
inputs = node.inputs
i = 0
input_shape = []
for nm in inputs:
last_node = self.get_node(nm)
tmp = node.layer.bottom[i]
i = i + 1
idx = list(last_node.layer.top).index(tmp)
input_shape.append(last_node.output_shape[idx])
node.set_output_shape(input_shape)
node.set_input_shape(input_shape)
super(CaffeGraph, self).build() super(CaffeGraph, self).build()
...@@ -210,11 +203,11 @@ class CaffeGraph(Graph): ...@@ -210,11 +203,11 @@ class CaffeGraph(Graph):
class CaffeDecoder(object): class CaffeDecoder(object):
def __init__(self, proto_path, model_path, use_caffe=True): def __init__(self, proto_path, model_path, caffe_proto_folder=None):
self.proto_path = proto_path self.proto_path = proto_path
self.model_path = model_path self.model_path = model_path
self.resolver = CaffeResolver(use_default=use_caffe) self.resolver = CaffeResolver(caffe_proto_folder=caffe_proto_folder)
self.net = self.resolver.NetParameter() self.net = self.resolver.NetParameter()
with open(proto_path, 'rb') as proto_file: with open(proto_path, 'rb') as proto_file:
proto_str = proto_file.read() proto_str = proto_file.read()
......
from .register import get_registered_layers
#custom layer import begins
# from . import roipooling
# from . import priorbox
# from . import permute
# from . import detection_out
# from . import normalize
# from . import select
from . import convolutiondepthwise
#custom layer import ends
custom_layers = get_registered_layers()
def set_args(f, params):
""" set args for function 'f' using the parameters in node.layer.param
Args:
f (function): a python function object
params (object): a object contains attributes needed by f's arguments
Returns:
arg_names (list): a list of argument names
kwargs (dict): a dict contains needed arguments
"""
argc = f.__code__.co_argcount
arg_list = f.__code__.co_varnames[0:argc]
kwargs = {}
for arg_name in arg_list:
if hasattr(params, arg_name) and params is not None:
kwargs[arg_name] = getattr(params, arg_name)
return arg_list, kwargs
def has_layer(layer_type):
""" test whether this layer exists in custom layer
"""
return layer_type in custom_layers
def get_params(layer, layer_type):
if layer_type.lower() == "deconvolution" or layer_type.lower(
) == "convolutiondepthwise":
param_name = '_'.join(('convolution', 'param'))
elif layer_type.lower() == "normalize":
param_name = '_'.join(('norm', 'param'))
else:
param_name = '_'.join((layer_type.lower(), 'param'))
return getattr(layer, param_name, None)
def compute_output_shape(node):
""" compute the output shape of custom layer
"""
layer_type = node.layer_type
assert layer_type in custom_layers, "layer[%s] not exist in custom layers" % (
layer_type)
shape_func = custom_layers[layer_type]['shape']
layer = node.layer
params = get_params(layer, layer_type)
arg_names, kwargs = set_args(shape_func, params)
input_shape = node.input_shape
return shape_func(input_shape, **kwargs)
def make_custom_layer(node):
""" get the code which implement the custom layer function
"""
layer_type = node.layer_type
assert layer_type in custom_layers, "layer[%s] not exist in custom layers" % (
layer_type)
layer_func = custom_layers[layer_type]['layer']
import inspect
return inspect.getsource(layer_func), layer_func
def deal_weights(node, data=None):
""" deal the weights of the custom layer
"""
layer_type = node.layer_type
weights_func = custom_layers[layer_type]['weights']
name = node.layer_name
return weights_func(name, data)
from .register import register
from x2paddle.core.util import *
import numbers
def convolutiondepthwise_shape(input_shape,
num_output=None,
pad=None,
kernel_size=None,
stride=None,
dilation=None,
pad_h=None,
pad_w=None,
kernel_h=None,
kernel_w=None,
stride_h=None,
stride_w=None):
[k_h, k_w] = [1, 1]
if isinstance(kernel_size, numbers.Number):
[k_h, k_w] = [kernel_size] * 2
elif isinstance(kernel_size, list):
k_h = kernel_h if kernel_h else kernel_size[0]
k_w = kernel_w if kernel_w else kernel_size[len(kernel_size) - 1]
[s_h, s_w] = [1, 1]
if isinstance(stride, numbers.Number):
[s_h, s_w] = [stride] * 2
elif isinstance(stride, list):
s_h = stride_h if stride_h else stride[0]
s_w = stride_w if stride_w else stride[len(stride) - 1]
[p_h, p_w] = [0, 0]
if isinstance(pad, numbers.Number):
[p_h, p_w] = [pad] * 2
elif isinstance(pad, list):
p_h = pad_h if pad_h else pad[0]
p_w = pad_w if pad_w else pad[len(pad) - 1]
dila_len = len(dilation)
dila_h = 1
dila_w = 1
if dila_len == 2:
dila_h = dilation[0]
dila_w = dilation[1]
elif dila_len == 1:
dila_h = dila_w = dilation[0]
else:
assert dila_len == 0, "invalid length[%s] of dilation in convolution" % (
dila_len)
i_w = input_shape[0][2]
i_h = input_shape[0][3]
o_h = (i_h + 2 * p_h - (dila_h * (k_h - 1) + 1)) / float(s_h) + 1
o_w = (i_w + 2 * p_w - (dila_w * (k_w - 1) + 1)) / float(s_w) + 1
import math
o_h = int(math.floor(o_h))
o_w = int(math.floor(o_w))
c = num_output if num_output is not None else input_shape[0][1]
return [[input_shape[0][0], c, o_h, o_w]]
def convolutiondepthwise_layer(inputs,
num_output=None,
pad=None,
kernel_size=None,
stride=None,
dilation=None,
pad_h=None,
pad_w=None,
kernel_h=None,
kernel_w=None,
stride_h=None,
stride_w=None,
input_shape=[],
name=None):
[k_h, k_w] = [1, 1]
if isinstance(kernel_size, numbers.Number):
[k_h, k_w] = [kernel_size] * 2
elif isinstance(kernel_size, list):
k_h = kernel_h if kernel_h else kernel_size[0]
k_w = kernel_w if kernel_w else kernel_size[len(kernel_size) - 1]
[s_h, s_w] = [1, 1]
if isinstance(stride, numbers.Number):
[s_h, s_w] = [stride] * 2
elif isinstance(stride, list):
s_h = stride_h if stride_h else stride[0]
s_w = stride_w if stride_w else stride[len(stride) - 1]
[p_h, p_w] = [0, 0]
if isinstance(pad, numbers.Number):
[p_h, p_w] = [pad] * 2
elif isinstance(pad, list):
p_h = pad_h if pad_h else pad[0]
p_w = pad_w if pad_w else pad[len(pad) - 1]
input = inputs[0]
dila_len = len(dilation)
dila_h = 1
dila_w = 1
if dila_len == 2:
dila_h = dilation[0]
dila_w = dilation[1]
elif dila_len == 1:
dila_h = dila_w = dilation[0]
else:
assert dila_len == 0, "invalid length[%s] of dilation in convolution" % (
dila_len)
c_in = input_shape[0][1]
c_out = num_output if num_output is not None else input_shape[0][1]
group = int(c_in / (c_in / c_out)) if c_in > c_out else int(c_in /
(c_out / c_in))
out = fluid.layers.conv2d(input,
dilation=[dila_h, dila_w],
filter_size=[k_h, k_w],
stride=[s_h, s_w],
padding=[p_h, p_w],
groups=group,
num_filters=c_out,
param_attr=name + '_weights',
bias_attr=name + '_bias',
name=name)
return out
def convolutiondepthwise_weights(name, data=None):
weights_name = []
weights_name.append(name + '_weights')
weights_name.append(name + '_bias')
return weights_name
register(kind='ConvolutionDepthwise',
shape=convolutiondepthwise_shape,
layer=convolutiondepthwise_layer,
weights=convolutiondepthwise_weights)
""" this module provides 'register' for registering customized layers
"""
g_custom_layers = {}
def register(kind, shape, layer, weights):
""" register a custom layer or a list of custom layers
Args:
@kind (str or list): type name of the layer
@shape (function): a function to generate the shape of layer's output
@layer (function): a function to generate the paddle code of layer
@weights (function): a function to deal with weights data
Returns:
None
"""
assert type(shape).__name__ == 'function', 'shape should be a function'
assert type(layer).__name__ == 'function', 'layer should be a function'
if type(kind) is str:
kind = [kind]
else:
assert type(
kind
) is list, 'invalid param "kind" for register, not a list or str'
for k in kind:
assert type(
k) is str, 'invalid param "kind" for register, not a list of str'
assert k not in g_custom_layers, 'this type[%s] has already been registered' % (
k)
print('register layer[%s]' % (k))
g_custom_layers[k] = {
'shape': shape,
'layer': layer,
'weights': weights
}
def get_registered_layers():
return g_custom_layers
...@@ -17,6 +17,7 @@ import numpy as np ...@@ -17,6 +17,7 @@ import numpy as np
from x2paddle.decoder.caffe_decoder import CaffeGraph from x2paddle.decoder.caffe_decoder import CaffeGraph
from x2paddle.core.op_mapper import OpMapper from x2paddle.core.op_mapper import OpMapper
from x2paddle.core.util import * from x2paddle.core.util import *
from x2paddle.op_mapper.caffe_custom_layer import *
class CaffeOpMapper(OpMapper): class CaffeOpMapper(OpMapper):
...@@ -25,34 +26,72 @@ class CaffeOpMapper(OpMapper): ...@@ -25,34 +26,72 @@ class CaffeOpMapper(OpMapper):
self.graph = decoder.caffe_graph self.graph = decoder.caffe_graph
self.weights = dict() self.weights = dict()
resolver = decoder.resolver resolver = decoder.resolver
self.mylayers = {}
if resolver.has_pycaffe(): if resolver.has_pycaffe():
self.did_use_pb = False self.did_use_pb = False
else: else:
self.did_use_pb = True self.did_use_pb = True
def op_checker(self):
unsupported_ops = set()
for node_name in self.graph.topo_sort:
node = self.graph.get_node(node_name)
op = node.layer_type
if not hasattr(self, op) and op not in custom_layers:
unsupported_ops.add(op)
if len(unsupported_ops) == 0:
return True
else:
print("There are {} ops not supported yet, list as below".format(
len(unsupported_ops)))
for op in unsupported_ops:
print(op)
return False
def run(self): def run(self):
print("Total nodes: {}".format(len(self.graph.topo_sort))) print("Total nodes: {}".format(len(self.graph.topo_sort)))
# check if ops in model are all supported # check if ops in model are all supported
if not self.op_checker(): if not self.op_checker():
raise Exception("Model are not supported yet.") raise Exception("Model are not supported yet.")
for node_name in self.graph.topo_sort: for node_name in self.graph.topo_sort:
node = self.graph.get_node(node_name) node = self.graph.get_node(node_name)
op = node.layer_type op = node.layer_type
if hasattr(self, op): if hasattr(self, op):
self.set_shape(node)
func = getattr(self, op) func = getattr(self, op)
func(node) func(node)
elif op in custom_layers:
self.set_shape(node, is_fluid_op=False)
self.deal_custom_layer(node)
else:
raise Exception("Model are not supported yet.")
for key in self.mylayers:
self.net_code.append(self.mylayers[key])
for i in range(len(self.graph.topo_sort)): for i in range(len(self.graph.topo_sort)):
node_name = self.graph.topo_sort[i] node_name = self.graph.topo_sort[i]
node = self.graph.get_node(node_name) node = self.graph.get_node(node_name)
self.net_code += node.fluid_code.gen_codes() self.net_code += node.fluid_code.gen_codes()
def adjust_parameters(self, node, data): def set_shape(self, node, is_fluid_op=True):
inputs = node.inputs
input_shape = []
for i, nm in enumerate(inputs):
last_node = self.graph.get_node(nm)
tmp = node.layer.bottom[i]
idx = list(last_node.layer.top).index(tmp)
input_shape.append(last_node.output_shape[idx])
node.set_input_shape(input_shape)
if is_fluid_op:
node.set_output_shape(input_shape)
else:
node.set_output_shape(compute_output_shape(node),
is_input=is_fluid_op)
def adjust_parameters(self, node):
data = node.data
if not self.did_use_pb: if not self.did_use_pb:
return data return data
# When using the protobuf-backend, each parameter initially has four dimensions. # When using the protobuf-backend, each parameter initially has four dimensions.
# In certain cases (like FC layers), we want to eliminate the singleton dimensions. # In certain cases (like FC layers), we want to eliminate the singleton dimensions.
# This implementation takes care of the common cases. However, it does leave the # This implementation takes care of the common cases. However, it does leave the
...@@ -61,7 +100,7 @@ class CaffeOpMapper(OpMapper): ...@@ -61,7 +100,7 @@ class CaffeOpMapper(OpMapper):
data = list(data) data = list(data)
squeeze_indices = [1] # Squeeze biases. squeeze_indices = [1] # Squeeze biases.
if node.kind == NodeKind.InnerProduct: if node.layer_type == 'InnerProduct':
squeeze_indices.append(0) # Squeeze FC. squeeze_indices.append(0) # Squeeze FC.
for idx in squeeze_indices: for idx in squeeze_indices:
...@@ -85,55 +124,44 @@ class CaffeOpMapper(OpMapper): ...@@ -85,55 +124,44 @@ class CaffeOpMapper(OpMapper):
data[idx] = np.squeeze(d, axis=sq_axis) data[idx] = np.squeeze(d, axis=sq_axis)
shape_new = data[idx].shape shape_new = data[idx].shape
print('shape-old' + str(shape_old))
print('shape-new' + str(shape_new))
if len(shape_old) != shape_new: if len(shape_old) != shape_new:
debug('squeeze idx:%d, with kind:%s,name:%s' % \ print('squeeze idx:%d, with kind:%s,name:%s' % \
(idx, node.kind, node.name)) (idx, node.layer_type, node.layer.name))
return data return data
@staticmethod
def get_kernel_value(scalar, repeated, idx, default=None):
if scalar:
return scalar
if repeated:
if isinstance(repeated, numbers.Number):
return repeated
if len(repeated) == 1:
# Same value applies to all spatial dimensions
return int(repeated[0])
assert idx < len(repeated)
# Extract the value for the given spatial dimension
return repeated[idx]
if default is None:
raise ValueError('Unable to determine kernel parameter!')
return default
def get_kernel_parameters(self, kind, params): def get_kernel_parameters(self, kind, params):
assert kind in ['Convolution', 'Pooling', 'Deconvolution'] assert kind in [
'Convolution', 'Pooling', 'Deconvolution', 'ConvolutionDepthwise'
k_h = self.get_kernel_value(params.kernel_h, ]
params.kernel_size, [k_h, k_w] = [1, 1]
0, print(params.kernel_size)
default=1) if isinstance(params.kernel_size, numbers.Number):
k_w = self.get_kernel_value(params.kernel_w, [k_h, k_w] = [params.kernel_size] * 2
params.kernel_size, else:
1, k_h = params.kernel_h if params.kernel_h else params.kernel_size[0]
default=1) k_w = params.kernel_w if params.kernel_w else params.kernel_size[
s_h = self.get_kernel_value(params.stride_h, len(params.kernel_size) - 1]
params.stride, [s_h, s_w] = [1, 1]
0, if isinstance(params.stride, numbers.Number):
default=1) [s_h, s_w] = [params.stride] * 2
s_w = self.get_kernel_value(params.stride_w, else:
params.stride, s_h = params.stride_h if params.stride_h else params.stride[0]
1, s_w = params.stride_w if params.stride_w else params.stride[
default=1) len(params.stride) - 1]
p_h = self.get_kernel_value(params.pad_h, params.pad, 0, default=0) [p_h, p_w] = [0, 0]
p_w = self.get_kernel_value(params.pad_w, params.pad, 1, default=0) if isinstance(params.pad, numbers.Number):
[p_h, p_w] = [params.pad] * 2
else:
p_h = params.pad_h if params.pad_h else params.pad[0]
p_w = params.pad_w if params.pad_w else params.pad[len(params.pad) -
1]
dila_h = dila_w = 1 dila_h = dila_w = 1
group = 1 group = 1
c_o = 1 c_o = 1
if kind in ['Convolution', 'Deconvolution']: if kind in ['Convolution', 'Deconvolution', 'ConvolutionDepthwise']:
c_o = params.num_output c_o = params.num_output
group = params.group
dila_len = len(params.dilation) dila_len = len(params.dilation)
if dila_len == 2: if dila_len == 2:
dila_h = params.dilation[0] dila_h = params.dilation[0]
...@@ -143,12 +171,12 @@ class CaffeOpMapper(OpMapper): ...@@ -143,12 +171,12 @@ class CaffeOpMapper(OpMapper):
else: else:
assert dila_len == 0, "invalid length[%s] of dilation in convolution" % ( assert dila_len == 0, "invalid length[%s] of dilation in convolution" % (
dila_len) dila_len)
if kind in ['Convolution', 'Deconvolution']:
group = params.group
kernel = [k_h, k_w] kernel = [k_h, k_w]
stride = [s_h, s_w] stride = [s_h, s_w]
pad = [p_h, p_w] pad = [p_h, p_w]
dilation = [dila_h, dila_w] dilation = [dila_h, dila_w]
return c_o, kernel, stride, pad, dilation, group return c_o, kernel, stride, pad, dilation, group
def get_input_name(self, node): def get_input_name(self, node):
...@@ -180,7 +208,7 @@ class CaffeOpMapper(OpMapper): ...@@ -180,7 +208,7 @@ class CaffeOpMapper(OpMapper):
data = node.data data = node.data
assert data is not None, 'The parameter of {} (type is {}) is not set. You need to use python package of caffe to set the default value.'.format( assert data is not None, 'The parameter of {} (type is {}) is not set. You need to use python package of caffe to set the default value.'.format(
node.layer_name, node.layer_type) node.layer_name, node.layer_type)
data = self.adjust_parameters(node, data) data = self.adjust_parameters(node)
self.weights[node.layer_name + '_weights'] = data[0] self.weights[node.layer_name + '_weights'] = data[0]
if len(data) == 2: if len(data) == 2:
self.weights[node.layer_name + '_bias'] = data[1] self.weights[node.layer_name + '_bias'] = data[1]
...@@ -224,7 +252,7 @@ class CaffeOpMapper(OpMapper): ...@@ -224,7 +252,7 @@ class CaffeOpMapper(OpMapper):
data = node.data data = node.data
assert data is not None, 'The parameter of {} (type is {}) is not set. You need to use python package of caffe to set the default value.'.format( assert data is not None, 'The parameter of {} (type is {}) is not set. You need to use python package of caffe to set the default value.'.format(
node.layer_name, node.layer_type) node.layer_name, node.layer_type)
data = self.adjust_parameters(node, data) data = self.adjust_parameters(node)
self.weights[node.layer_name + '_weights'] = data[0] self.weights[node.layer_name + '_weights'] = data[0]
if len(data) == 2: if len(data) == 2:
self.weights[node.layer_name + '_bias'] = data[1] self.weights[node.layer_name + '_bias'] = data[1]
...@@ -343,7 +371,7 @@ class CaffeOpMapper(OpMapper): ...@@ -343,7 +371,7 @@ class CaffeOpMapper(OpMapper):
data = node.data data = node.data
assert data is not None, 'The parameter of {} (type is {}) is not set. You need to use python package of caffe to set the default value.'.format( assert data is not None, 'The parameter of {} (type is {}) is not set. You need to use python package of caffe to set the default value.'.format(
node.layer_name, node.layer_type) node.layer_name, node.layer_type)
data = self.adjust_parameters(node, data) data = self.adjust_parameters(node)
# Reshape the parameters to Paddle's ordering # Reshape the parameters to Paddle's ordering
transpose_order = (1, 0) transpose_order = (1, 0)
w = data[0] w = data[0]
...@@ -396,40 +424,11 @@ class CaffeOpMapper(OpMapper): ...@@ -396,40 +424,11 @@ class CaffeOpMapper(OpMapper):
shape = node.input_shape[0] shape = node.input_shape[0]
dims = len(shape) dims = len(shape)
axis = axis + dims if axis < 0 else axis axis = axis + dims if axis < 0 else axis
need_transpose = False attr = {'axis': axis, 'name': string(node.layer_name + '_softmax')}
if axis + 1 != dims:
need_transpose = True
if need_transpose:
in_order = list(range(dims))
in_order.remove(axis)
in_order.append(axis)
attr = {
'perm': in_order,
'name': string(node.layer_name + '_transpose_in')
}
node.fluid_code.add_layer("transpose",
inputs=input,
output=node,
param_attr=attr)
attr = {'name': string(node.layer_name + '_softmax')}
node.fluid_code.add_layer("softmax", node.fluid_code.add_layer("softmax",
inputs=node if need_transpose else input, inputs=input,
output=node, output=node,
param_attr=attr) param_attr=attr)
if need_transpose:
out_order = [
0,
] * dims
for id, v in enumerate(in_order):
out_order[v] = id
attr = {
'perm': out_order,
'name': string(node.layer_name + '_transpose_out')
}
node.fluid_code.add_layer("transpose",
inputs=node,
output=node,
param_attr=attr)
def Slice(self, node): def Slice(self, node):
assert len( assert len(
...@@ -451,13 +450,11 @@ class CaffeOpMapper(OpMapper): ...@@ -451,13 +450,11 @@ class CaffeOpMapper(OpMapper):
attr = { attr = {
'axes': [axis], 'axes': [axis],
'starts': [points[i]], 'starts': [points[i]],
'ends': [points[i + 1]], 'ends': [points[i + 1]]
'name': string(node.layer_name + '_' + str(i))
} }
node.fluid_code.add_layer("slice", node.fluid_code.add_layer("slice",
inputs=input, inputs=input,
output=string(node.layer_name + '_' + output=node.layer_name + '_' + str(i),
str(i)),
param_attr=attr) param_attr=attr)
node.fluid_code.add_note('{}.append({})'.format( node.fluid_code.add_note('{}.append({})'.format(
node.layer_name, node.layer_name + '_' + str(i))) node.layer_name, node.layer_name + '_' + str(i)))
...@@ -503,7 +500,7 @@ class CaffeOpMapper(OpMapper): ...@@ -503,7 +500,7 @@ class CaffeOpMapper(OpMapper):
node.layer_name, node.layer_type) node.layer_name, node.layer_type)
self.weights[node.layer_name + '_weights'] = data[0] self.weights[node.layer_name + '_weights'] = data[0]
attr = { attr = {
'mode': mode, 'mode': string(mode),
'param_attr': string(node.layer_name + '_weights'), 'param_attr': string(node.layer_name + '_weights'),
'name': string(node.layer_name) 'name': string(node.layer_name)
} }
...@@ -731,7 +728,7 @@ class CaffeOpMapper(OpMapper): ...@@ -731,7 +728,7 @@ class CaffeOpMapper(OpMapper):
def Scale(self, node): def Scale(self, node):
assert len( assert len(
node.outputs) == 1, 'The count of Scale node\'s output is not 1.' node.inputs) == 1, 'The count of Scale node\'s input is not 1.'
if len(node.inputs) == 1 and self.graph.get_node( if len(node.inputs) == 1 and self.graph.get_node(
node.inputs[0]).layer_type == 'BatchNorm': node.inputs[0]).layer_type == 'BatchNorm':
return return
...@@ -1058,7 +1055,9 @@ class CaffeOpMapper(OpMapper): ...@@ -1058,7 +1055,9 @@ class CaffeOpMapper(OpMapper):
param_attr=attr) param_attr=attr)
input_name = self.get_input_name(input) input_name = self.get_input_name(input)
data = node.data data = node.data
data = self.adjust_parameters(node, data) assert data is not None, 'The parameter of {} (type is {}) is not set. You need to use python package of caffe to set the default value.'.format(
node.layer_name, node.layer_type)
data = self.adjust_parameters(node)
self.weights[node.layer_name + '_scale'] = data[0] self.weights[node.layer_name + '_scale'] = data[0]
node.fluid_code.add_note( node.fluid_code.add_note(
'{}_scale_attr = ParamAttr(name=\'{}\')'.format( '{}_scale_attr = ParamAttr(name=\'{}\')'.format(
...@@ -1297,7 +1296,7 @@ class CaffeOpMapper(OpMapper): ...@@ -1297,7 +1296,7 @@ class CaffeOpMapper(OpMapper):
def Select(self, node): def Select(self, node):
assert len( assert len(
node.inputs) == 1, 'The count of Select node\'s input is not 2.' node.inputs) == 1, 'The count of Select node\'s input is not 1.'
input = self.graph.get_bottom_node(node, idx=0, copy=True) input = self.graph.get_bottom_node(node, idx=0, copy=True)
if self.is_Scale(input): if self.is_Scale(input):
tmp = self.graph.get_bottom_node(input, idx=0, copy=True) tmp = self.graph.get_bottom_node(input, idx=0, copy=True)
...@@ -1327,3 +1326,49 @@ class CaffeOpMapper(OpMapper): ...@@ -1327,3 +1326,49 @@ class CaffeOpMapper(OpMapper):
node.layer_name, node.layer_name + '_' + str(i))) node.layer_name, node.layer_name + '_' + str(i)))
if i == len(slice_point) - 2: if i == len(slice_point) - 2:
break break
def ShuffleChannel(self, node):
assert len(node.inputs
) == 1, 'The count of ShuffleChannel node\'s input is not 1.'
params = node.layer.shuffle_channel_param
group = params.group
input = self.graph.get_bottom_node(node, idx=0, copy=True)
if self.is_Scale(input):
tmp = self.graph.get_bottom_node(input, idx=0, copy=True)
if self.is_BN(tmp):
input = tmp
attr = {'group': group, 'name': string(node.layer_name)}
node.fluid_code.add_layer("shuffle_channel",
inputs=input,
output=node,
param_attr=attr)
def deal_custom_layer(self, node):
op = node.layer_type
custom_code, func = make_custom_layer(node)
params = get_params(node.layer, node.layer_type)
arg_names, kwargs = set_args(func, params)
kwargs['name'] = string(node.layer_name)
kwargs['input_shape'] = node.input_shape
data = node.data
assert data is not None, 'The parameter of {} (type is {}) is not set. You need to use python package of caffe to set the default value.'.format(
node.layer_name, node.layer_type)
data = self.adjust_parameters(node)
weights_name = deal_weights(node)
for i in range(len(data)):
self.weights[weights_name[i]] = data[i]
inputs_node = []
for i in range(len(node.inputs)):
input = self.graph.get_bottom_node(node, idx=i, copy=True)
if self.is_Scale(input):
tmp = self.graph.get_bottom_node(input, idx=0, copy=True)
if self.is_BN(tmp):
input = tmp
inputs_node.append(input)
node.fluid_code.add_layer(func.__code__.co_name,
inputs=inputs_node,
output=node,
param_attr=kwargs,
is_custom_layer=True)
if op not in self.mylayers:
self.mylayers[op] = custom_code
...@@ -453,3 +453,12 @@ def shape_select(layer, input_shape): ...@@ -453,3 +453,12 @@ def shape_select(layer, input_shape):
output_shape = input_shape output_shape = input_shape
output_shape[axis] = end - start output_shape[axis] = end - start
return [output_shape] return [output_shape]
def shape_shufflechannel(layer, input_shape):
return input_shape
# def shape_convolutiondepthwise(layer, input_shape):
# params = layer.convolution_param
# return get_strided_kernel_output_shape(params, input_shape[0], math.floor)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册