Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/Microsoft/MMdnn
Browse files Browse the repository at this point in the history
  • Loading branch information
wangqianwen0418 committed Nov 29, 2017
2 parents d4786dc + 2dca0a4 commit 45afaa0
Show file tree
Hide file tree
Showing 18 changed files with 900 additions and 585 deletions.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,28 @@ The intermediate representation will store the network structures as a protobuf
- [MXNet](http://mxnet.incubator.apache.org/)
- [Tensorflow](https://www.tensorflow.org/) (Experimental)
- [Microsoft Cognitive Toolkit (CNTK)](http://www.microsoft.com/en-us/cognitive-toolkit) (Destination only)
- [PyTorch](http://pytorch.org/) (Destination only)

#### Tested models

The model conversion between current supported frameworks is tested on some **ImageNet** models.

Models | Caffe | Keras | Tensorflow | CNTK | MXNet | PyTorch |
:--------------------------------------------------:|:-----:|:-----:|:----------:|:----:|:-----:|:-------:|
[Inception V1](http://arxiv.org/abs/1409.4842v1) | √ | √ | √ | √ | √
[Inception V3](http://arxiv.org/abs/1512.00567) | × | √ | √ | √ | √
[Inception V1](http://arxiv.org/abs/1409.4842v1) | √ | √ | √ | √ | √ | x (No LRN)
[Inception V3](http://arxiv.org/abs/1512.00567) | × | √ | √ | √ | √ | √
[ResNet V1 50](https://arxiv.org/abs/1512.03385) | × | √ | √ | o | √ | √
[ResNet V2 152](https://arxiv.org/abs/1603.05027) | × | √ | √ | √ | √ |
[ResNet V2 152](https://arxiv.org/abs/1603.05027) | × | √ | √ | √ | √ |
[VGG 19](http://arxiv.org/abs/1409.1556.pdf) | √ | √ | √ | √ | √ | √
[MobileNet_v1](https://arxiv.org/pdf/1704.04861.pdf)| × | √ | √ | × (No Relu6) | ×
[Xception](https://arxiv.org/pdf/1610.02357.pdf) | × | √ | √ | × | × |
[SqueezeNet](https://arxiv.org/pdf/1602.07360) | | √ | √ | √ | √ |
[MobileNet_v1](https://arxiv.org/pdf/1704.04861.pdf)| × | √ | √ | × (No Relu6) | × | ×
[Xception](https://arxiv.org/pdf/1610.02357.pdf) | × | √ | √ | × | × | ×
[SqueezeNet](https://arxiv.org/pdf/1602.07360) | | √ | √ | √ | √ | ×

#### On-going frameworks

- [Caffe2](https://caffe2.ai/)
- [CoreML](https://developer.apple.com/documentation/coreml)
- [PyTorch](http://pytorch.org/)


#### Usage

Expand Down
5 changes: 5 additions & 0 deletions mmdnn/conversion/caffe/common_graph.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#----------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
#----------------------------------------------------------------------------------------------

from six import string_types as _string_types
from mmdnn.conversion.caffe.errors import ConversionError
from mmdnn.conversion.common.IR.graph_pb2 import GraphDef, NodeDef, TensorShape
Expand Down
76 changes: 39 additions & 37 deletions mmdnn/conversion/caffe/mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ def get_handler_name(node_kind):


class NodeMapper(object):

@classmethod
def _convert_output_shape(cls, kwargs, node):
shape = TensorShape()
dim = shape.dim.add()
dim.size = -1

if len(node.output_shape) > 2:
for i in node.output_shape[2:]:
dim = shape.dim.add()
Expand All @@ -31,22 +31,22 @@ def _convert_output_shape(cls, kwargs, node):
dim = shape.dim.add()
dim.size = node.output_shape[1]
kwargs['_output_shapes'] = [shape]

@classmethod
def get_kernel_params(cls, node):
kwargs = {}
kwargs = {}
if node.kernel_parameters.p_h > 0 or node.kernel_parameters.p_w > 0:
padding = [0, 0, node.kernel_parameters.p_h, node.kernel_parameters.p_h, node.kernel_parameters.p_w, node.kernel_parameters.p_w, 0, 0]
padding = [0, node.kernel_parameters.p_h, node.kernel_parameters.p_w, 0, 0, node.kernel_parameters.p_h, node.kernel_parameters.p_w, 0]
elif node.kernel_parameters.s_h > 1 or node.kernel_parameters.s_w > 1:
padding = [0, 0, (node.kernel_parameters.s_h - 1) // 2, node.kernel_parameters.s_h // 2, (node.kernel_parameters.s_w - 1) // 2, node.kernel_parameters.s_w // 2, 0, 0]
padding = [0, (node.kernel_parameters.s_h - 1) // 2, (node.kernel_parameters.s_w - 1) // 2, 0, 0, node.kernel_parameters.s_h // 2, node.kernel_parameters.s_w // 2, 0]
else:
padding = None
kwargs['padding'] = 'VALID'

kwargs['auto_pad'] = 'VALID'
kwargs['strides'] = [1, node.kernel_parameters.s_h, node.kernel_parameters.s_w, 1]
cls._convert_output_shape(kwargs, node)
return kwargs, {'paddings' : padding, 'mode' : 'CONSTANT', 'constant_values' : 0.0}

return kwargs, {'pads' : padding, 'mode' : 'constant', 'constant_values' : 0.0}


@classmethod
Expand All @@ -60,7 +60,7 @@ def map_data(cls, node):
dim.size = i
dim = shape.dim.add()
dim.size = node.output_shape.channels

kwargs = {'shape': shape} # Ignore the dimension of batch size
cls._convert_output_shape(kwargs, node)
return Node.create('DataInput', **kwargs)
Expand All @@ -74,29 +74,30 @@ def map_input(cls, node):
def map_convolution(cls, node):
kwargs, padding = cls.get_kernel_params(node)
parent, _ = node.get_only_parent()
kwargs['filter'] = [node.kernel_parameters.k_h, node.kernel_parameters.k_w, parent.output_shape.channels, node.parameters.num_output]
kwargs['use_bias'] = node.parameters.bias_term
kwargs['kernel_shape'] = [node.kernel_parameters.k_h, node.kernel_parameters.k_w, parent.output_shape.channels, node.parameters.num_output]
kwargs['use_bias'] = node.parameters.bias_term
group = node.parameters.group
if group != 1:
kwargs['group'] = group
if padding['paddings'] != None:
return [Node.create('Pad', **padding), Node.create('Convolution', **kwargs)]

if padding['pads'] != None:
return [Node.create('Pad', **padding), Node.create('Conv', **kwargs)]
else:
return Node.create('Convolution', **kwargs)
kwargs['pads'] = [0] * 8
return Node.create('Conv', **kwargs)


@classmethod
def map_deconvolution(cls, node):
raise NotImplementedError()
kwargs = cls.get_kernel_params(node)
parent, _ = node.get_only_parent()
kwargs['filter'] = [node.kernel_parameters.k_h, node.kernel_parameters.k_w, parent.output_shape.channels, node.parameters.num_output]
kwargs['kernel_shape'] = [node.kernel_parameters.k_h, node.kernel_parameters.k_w, parent.output_shape.channels, node.parameters.num_output]
group = node.parameters.group
if group != 1:
kwargs['group'] = group
kwargs['group'] = group
return Node.create('deconv', **kwargs)

@classmethod
def map_crop(cls, node):
offset = node.parameters.offset
Expand All @@ -105,13 +106,13 @@ def map_crop(cls, node):
return Node.create('crop', **kwargs)
else:
return Node.create('crop')

@classmethod
def map_relu(cls, node):
kwargs = {}
cls._convert_output_shape(kwargs, node)
return Node.create('Relu', **kwargs)

@classmethod
def map_pooling(cls, node):
kwargs, padding = cls.get_kernel_params(node)
Expand All @@ -122,21 +123,22 @@ def map_pooling(cls, node):
else:
# Stochastic pooling, for instance.
raise ConversionError('Unsupported pooling type.')
kwargs['window_shape'] = [1, node.kernel_parameters.k_h, node.kernel_parameters.k_w, 1]
kwargs['kernel_shape'] = [1, node.kernel_parameters.k_h, node.kernel_parameters.k_w, 1]
cls._convert_output_shape(kwargs, node)
if padding['paddings'] != None:

if padding['pads'] != None:
return [Node.create('Pad', **padding), Node.create('Pool', **kwargs)]
else:
kwargs['pads'] = [0] * 8
return Node.create('Pool', **kwargs)


@classmethod
def _add_flatten_layer(cls, node):
shape = TensorShape()
dim = shape.dim.add()
dim.size = -1
dim.size = -1

dim = shape.dim.add()
dim.size = 1
for i in node.output_shape[1:]:
Expand All @@ -149,40 +151,40 @@ def map_inner_product(cls, node):
#TODO: Axis
assert node.parameters.axis == 1
#TODO: Unbiased
kwargs = {'use_bias' : node.parameters.bias_term, 'units' : node.parameters.num_output}
kwargs = {'use_bias' : node.parameters.bias_term, 'units' : node.parameters.num_output}

# check if need the Flatten layer
parent, _ = node.get_only_parent()
ret = []
if parent.output_shape.height > 1 or parent.output_shape.width > 1:
ret.append(cls._add_flatten_layer(parent))
ret.append(Node.create('FullyConnected', **kwargs))
return ret

@classmethod
def map_softmax(cls, node):
return Node.create('Softmax')

@classmethod
def map_lrn(cls, node):
params = node.parameters
assert params.local_size % 2 == 1
kwargs = {'size': int((params.local_size + 1) / 2), 'alpha': params.alpha, 'beta': params.beta, 'k' : params.k}
cls._convert_output_shape(kwargs, node)
return Node.create('LRN', **kwargs)

@classmethod
def map_concat(cls, node):
kwargs = {'axis': (2, 3, 1, 0)[node.parameters.axis]}
cls._convert_output_shape(kwargs, node)
return Node.create('Concat', **kwargs)

@classmethod
def map_dropout(cls, node):
kwargs = {'keep_prob': node.parameters.dropout_ratio}
cls._convert_output_shape(kwargs, node)
return Node.create('Dropout', **kwargs)

@classmethod
def map_batch_norm(cls, node):
scale_offset = len(node.data) == 4
Expand All @@ -191,12 +193,12 @@ def map_batch_norm(cls, node):
kwargs['epsilon'] = epsilon
cls._convert_output_shape(kwargs, node)
return Node.create('batch_normalization', **kwargs)

@classmethod
def map_eltwise(cls, node):
operations = {0: 'mul', 1: 'sum', 2: 'max'}
op_code = node.parameters.operation
try:
try:
return Node.create(operations[op_code])
except KeyError:
raise ConversionError('Unknown elementwise operation: {}'.format(op_code))
Expand Down
16 changes: 5 additions & 11 deletions mmdnn/conversion/caffe/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#----------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
#----------------------------------------------------------------------------------------------

import re


def get_lower_case(text):
'''
Convert PascalCase name to words concatenated by '_'.
Expand All @@ -19,13 +23,3 @@ def get_upper_case(text):
def get_real_name(text):
text = text.strip().split(':')
return ''.join(text[:-1])

def listToStr(data):
ret = ""
first = True
for e in data:
if first == False:
ret += ", "
ret += str(e)
first = False
return ret
Loading

0 comments on commit 45afaa0

Please sign in to comment.