Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support conversion of recognize_digits models #19

Merged
merged 10 commits into from
Apr 16, 2018
11 changes: 6 additions & 5 deletions convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,13 @@ def convert(args):
if op.type in ops.node_maker:
# TODO(kuke): deal with the corner case that vars in
# different blocks have the same name
node_proto = ops.node_maker[op.type](
inputs=op.input_arg_names,
attrs=op.attr_names,
outputs=op.output_arg_names)
node_proto = ops.node_maker[op.type](operator=op,
scope=inference_scope)

onnx_nodes.append(node_proto)
if isinstance(node_proto, tuple):
onnx_nodes.extend(list(node_proto))
else:
onnx_nodes.append(node_proto)
else:
if op.type not in ['feed', 'fetch']:
raise NotImplementedError("OP[%s] is not supported in "
Expand Down
13 changes: 13 additions & 0 deletions fluid/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright (c) 2018 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.
23 changes: 23 additions & 0 deletions fluid/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright (c) 2018 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.

Copy link
Author

Choose a reason for hiding this comment

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

I add a new directory fluid here to put some modules related to Fluid but independent of ONNX.


def get_op_io_info(op):
inputs = dict([(name, op.input(name)) for name in op.input_names])
attrs = dict(
[(name, op.attr(name))
for name in op.attr_names]) if op.attr_names is not None else None
outputs = dict([(name, op.output(name)) for name in op.output_names])

return inputs, attrs, outputs
153 changes: 126 additions & 27 deletions fluid_onnx/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import sys
from onnx.helper import make_node
from paddle.fluid.executor import fetch_var
from fluid.utils import get_op_io_info
"""
Priority of ops (uniques) to figure out support for.

Expand Down Expand Up @@ -55,8 +58,14 @@ def abs_op():
pass


def add_op(inputs, attrs, outputs):
return make_node('Add', inputs=inputs, outputs=outputs, broadcast=1)
def add_op(operator, scope):
inputs, attrs, outputs = get_op_io_info(operator)
return make_node(
'Add',
inputs=inputs['X'] + inputs['Y'],
outputs=outputs['Out'],
axis=attrs['axis'],
broadcast=1)


def and_op():
Expand All @@ -81,8 +90,17 @@ def averagepool_op():
pass


def batchnorm_op():
pass
def batchnorm_op(operator, scope):
inputs, attrs, outputs = get_op_io_info(operator)
bn_op = make_node(
'BatchNormalization',
inputs=inputs['X'] + inputs['Scale'] + inputs['Bias'] + inputs['Mean'] +
inputs['Variance'],
outputs=outputs['Y'],
is_test=attrs['is_test'],
epsilon=attrs['epsilon'],
momentum=attrs['momentum'])
return bn_op


def cast_op():
Expand All @@ -105,11 +123,21 @@ def constant_op():
pass


def conv_op():
"""
Need to support broadcast.
"""
pass
def conv2d_op(operator, scope):
inputs, attrs, outputs = get_op_io_info(operator)
kernel_shape = fetch_var(
operator.input('Filter')[0].decode('string_escape'), scope).shape

conv2d = make_node(
'Conv',
inputs=inputs['Input'] + inputs['Filter'],
outputs=outputs['Output'],
dilations=attrs['dilations'],
kernel_shape=kernel_shape[-2:],
strides=attrs['strides'],
group=attrs['groups'],
pads=attrs['paddings'] + attrs['paddings'])
return conv2d


def convtranspose_op():
Expand Down Expand Up @@ -224,8 +252,63 @@ def lppool_op():
pass


def matmul_op(inputs, attrs, outputs):
return make_node('MatMul', inputs=inputs, outputs=outputs)
def mul_op(operator, scope):
Copy link
Author

Choose a reason for hiding this comment

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

As we can see, sometimes we can't implement the operator in Fluid by only operator in ONNX. So it would be better to make the function name (mul_op here) consistent with the op's name in Fluid ('mul').

inputs, attrs, outputs = get_op_io_info(operator)

# Flatten input(X) and input(Y) into 2-D matries
x_flat_out = [inputs['X'][0] + '@flatten_0']
Copy link
Author

@kuke kuke Apr 13, 2018

Choose a reason for hiding this comment

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

Please pay attention to the intermediate tensors between nodes. They should be named uniquely in the ONNX graph.

y_flat_out = [inputs['Y'][0] + '@flatten_0']
flatten_x_node = make_node(
'Flatten',
inputs=inputs['X'],
outputs=x_flat_out,
axis=attrs['x_num_col_dims'])
flatten_y_node = make_node(
'Flatten',
inputs=inputs['Y'],
outputs=y_flat_out,
axis=attrs['y_num_col_dims'])

# Mat mul
matmul_out = [outputs['Out'][0] + '@matmul_0']
matmul_node = make_node(
'MatMul', inputs=x_flat_out + y_flat_out, outputs=matmul_out)

# Get the shape of input(X) and input(Y)
x_shape_out = [inputs['X'][0] + '@shape_0']
y_shape_out = [inputs['Y'][0] + '@shape_0']
x_shape_node = make_node('Shape', inputs=inputs['X'], outputs=x_shape_out)
y_shape_node = make_node('Shape', inputs=inputs['Y'], outputs=y_shape_out)

# Get the real shape of output(Out)
x_shape_slice_out = [inputs['X'][0] + '@shape_slice_0']
y_shape_slice_out = [inputs['Y'][0] + '@shape_slice_0']
output_shape = [outputs['Out'][0] + '@shape_concat_0']
x_shape_slice_node = make_node(
'Slice',
inputs=x_shape_out,
outputs=x_shape_slice_out,
starts=[0],
ends=[attrs['x_num_col_dims']])
y_shape_slice_node = make_node(
'Slice',
inputs=y_shape_out,
outputs=y_shape_slice_out,
starts=[attrs['y_num_col_dims']],
ends=[sys.maxint])
output_shape_node = make_node(
'Concat',
inputs=x_shape_slice_out + y_shape_slice_out,
outputs=output_shape,
axis=0)

# Reshpe output
output_node = make_node(
'Reshape', inputs=matmul_out + output_shape, outputs=outputs['Out'])

return (flatten_x_node, flatten_y_node, matmul_node, x_shape_node,
y_shape_node, x_shape_slice_node, y_shape_slice_node,
output_shape_node, output_node)


def max_op():
Expand All @@ -251,10 +334,6 @@ def min_op():
pass


def mul_op():
pass


def neg_op():
pass

Expand All @@ -281,6 +360,26 @@ def pad_op():
pass


def pool2d_op(operator, scope):
inputs, attrs, outputs = get_op_io_info(operator)
if attrs['global_pooling'] is False:
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

op_type = {'max': 'MaxPool', 'ave': 'AveragePool'}
pool2d = make_node(
op_type[attrs['pooling_type']],
inputs=inputs['X'],
outputs=outputs['Out'],
kernel_shape=attrs['ksize'],
strides=attrs['strides'],
pads=attrs['paddings'] + attrs['paddings'], )
else:
op_type = {'max': 'GlobalMaxPool', 'ave': 'GlobalAveragePool'}
pool2d = make_node(
op_type[attrs['pooling_type']],
inputs=inputs['X'],
outputs=outputs['Out'])
return pool2d


def pow_op():
pass

Expand Down Expand Up @@ -349,8 +448,9 @@ def reducesumsquare_op():
pass


def relu_op():
pass
def relu_op(operator, scope):
inputs, _, outputs = get_op_io_info(operator)
return make_node('Relu', inputs=inputs['X'], outputs=outputs['Out'])


def reshape_op():
Expand All @@ -377,8 +477,9 @@ def slice_op():
pass


def softmax_op():
pass
def softmax_op(operator, scope):
inputs, attrs, outputs = get_op_io_info(operator)
return make_node('Softmax', inputs=inputs['X'], outputs=outputs['Out'])


def softplus_op():
Expand Down Expand Up @@ -444,9 +545,6 @@ def xor_op():
# Reference for paddle operator availability taken from:
# https://github.com/PaddlePaddle/Paddle/issues/8028

# ONNX Ops that use multiple Paddle ops are keyed by '<op1>,<op2>' fed into the
# modifier.

node_maker = {
# Paddle op name : (ONNX op name, modifier)
'abs': ('Abs', abs_op),
Expand All @@ -456,13 +554,13 @@ def xor_op():
# 'ArgMax', NEEDS ATTENTION.
# 'ArgMin', NEEDS ATTENTION.
'': ('AveragePool', averagepool_op),
'batch_norm': ('BatchNormalization', batchnorm_op),
'batch_norm': batchnorm_op,
'cast': ('Cast', cast_op),
# 'Ceil', NEEDS ATTENTION.
'cast': ('Clip', clip_op),
'concat': ('Concat', concat_op),
',': ('Constant', constant_op),
'conv': ('Conv', conv_op),
'conv2d': conv2d_op,

# Need to continue the mapping below.
'': 'ConvTranspose',
Expand Down Expand Up @@ -498,12 +596,13 @@ def xor_op():
'': 'MaxRoiPool',
'mean': ('Mean', mean_op),
'': 'Min',
'mul': matmul_op,
'mul': mul_op,
',': 'Neg',
'': 'Not',
'': 'Or',
'': 'PRelu',
'': 'Pad',
'pool2d': pool2d_op,
'': 'Pow',
',': 'RNN',
'': 'RandomNormal',
Expand All @@ -521,14 +620,14 @@ def xor_op():
# 'ReduceProd', NEEDS ATTENTION.
'': 'ReduceSum',
',': 'ReduceSumSquare',
'': 'Relu',
'relu': relu_op,
'': 'Reshape',
# 'Selu', NEEDS ATTENTION.
'': 'Shape',
'': 'Sigmoid',
'': 'Size',
# 'Slice', NEEDS ATTENTION.
'': 'Softmax',
'softmax': softmax_op,
'': 'Softplus',
'': 'Softsign',
'': 'SpaceToDepth',
Expand Down