зеркало из https://github.com/microsoft/MMdnn.git
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
Коммит
79b061ee2e
|
@ -1,6 +1,21 @@
|
|||
# CoreML README
|
||||
|
||||
Currently we only implemented the CoreML emitter (IR -> CoreML) part. Any contribution to CoreML model parser (CoreML -> IR) part or emitter part is welcome.
|
||||
|
||||
We tested the [Awesome-CoreML-Models](https://github.com/likedan/Awesome-CoreML-Models) and the parser works. Any contribution is welcome.
|
||||
|
||||
Models | Caffe | CoreML | CNTK | Keras | MXNet | PyTorch | TensorFlow
|
||||
:-----------------------:|:-----:|:------:|:----:|:-----:|:-----:|:-------:|:------:|
|
||||
Vgg16 | √ | √ | | √ | √ | √ | √
|
||||
Inception_v3 | √ | √ | | √ | √ | √ | √
|
||||
ResNet 50 | √ | √ | | √ | √ | √ | √
|
||||
MobileNet V1 | √ | √ | | √ | √ | √ | √
|
||||
Tiny-yolo | | √ | | √ | √ | √ | √
|
||||
|
||||
**√** - Correctness tested
|
||||
|
||||
**o** - Some difference after conversion
|
||||
|
||||
**space** - not tested
|
||||
|
||||
---
|
||||
|
||||
|
@ -90,7 +105,7 @@ The inference result is slightly different from the original keras model. Curren
|
|||
|
||||
## Develop version
|
||||
|
||||
macOS High Sierra 10.13.2 (17C205)
|
||||
macOS High Sierra 10.13.3 (17C205)
|
||||
|
||||
@ 2018/01/10
|
||||
|
||||
|
|
|
@ -490,47 +490,6 @@ class CoremlParser(Parser):
|
|||
|
||||
|
||||
|
||||
|
||||
def _convert_pooling(self, source_node, dim, pooling_type, is_global):
|
||||
source_node_layer = source_node.layer
|
||||
IR_node = self.IR_graph.node.add()
|
||||
|
||||
# name, op
|
||||
CoremlParser._copy_and_repo(resource_node, IR_node, "Pool")
|
||||
|
||||
# input edge
|
||||
self.convert_inedge(source_node, IR_node)
|
||||
|
||||
kwargs = {}
|
||||
|
||||
kwargs['pooling_type'] = pooling_type
|
||||
|
||||
if is_global:
|
||||
kwargs['global_pooling'] = True
|
||||
kwargs['strides'] = [1] * (dim + 2)
|
||||
else:
|
||||
|
||||
# padding
|
||||
self._convert_padding(source_node, IR_node)
|
||||
|
||||
# strides
|
||||
# [1, sd, sh, sw, 1]
|
||||
kwargs['strides'] = [1, 1] + list(source_node) + [1]
|
||||
|
||||
# window_shape
|
||||
# [1, pd, ph, pw, 1]
|
||||
kwagrs['kernel_shape'] = [1,1] + list(source_node_layer.kernelSize) + [1]
|
||||
|
||||
assign_IRnode_values(IR_node, kwargs)
|
||||
|
||||
if is_global:
|
||||
flatten_node = self.IR_graph.node.add()
|
||||
flatten_node.name = source_node_layer.name + "_flatten"
|
||||
flatten_node.op = 'Flatten'
|
||||
flatten_node.input.append(source_node_layer.name)
|
||||
CoremlParser._set_output_shape(source_node, flatten_node)
|
||||
source_node.real_name = flatten_node_layer.name
|
||||
|
||||
def _convert_merge(self, source_node, new_name = None):
|
||||
|
||||
IR_node = self.IR_graph.node.add()
|
||||
|
@ -865,6 +824,7 @@ class CoremlParser(Parser):
|
|||
# [1, sd, sh, sw, 1]
|
||||
kwargs['strides'] = [1] + list(coreml_node_pool.stride) + [1]
|
||||
|
||||
|
||||
# window_shape
|
||||
# [1, pd, ph, pw, 1]
|
||||
kwargs['kernel_shape'] = [1] + list(coreml_node_pool.kernelSize) + [1]
|
||||
|
|
|
@ -34,7 +34,7 @@ def _main():
|
|||
type=_text_type, help='Test Image Path')
|
||||
|
||||
parser.add_argument('-o', '--output_dir', default='./',
|
||||
type=_text_type, help='Caffe Checkpoint file name')
|
||||
type=_text_type, help='CNTK Checkpoint file name')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
|
|
@ -201,25 +201,50 @@ class KitModel(nn.Module):
|
|||
))
|
||||
|
||||
else:
|
||||
for e in IR_node.get_attr('dilations', []):
|
||||
assert e == 1
|
||||
|
||||
pool_size = IR_node.get_attr('kernel_shape')[1:-1]
|
||||
strides = IR_node.get_attr('strides')[1:-1]
|
||||
if IR_node.get_attr('pooling_type') == "MAX":
|
||||
# Change to padding defuse
|
||||
input_node = self._defuse_padding(IR_node,", value=float('-inf')")
|
||||
for e in IR_node.get_attr('dilations', []):
|
||||
assert e == 1
|
||||
|
||||
padding = IR_node.get_attr('pads')[1:dim]
|
||||
ceil_mode = self.is_ceil_mode(IR_node.get_attr('pads'))
|
||||
pool_size = IR_node.get_attr('kernel_shape')[1:-1]
|
||||
strides = IR_node.get_attr('strides')[1:-1]
|
||||
|
||||
# input_node = self._defuse_padding(IR_node, exstr)
|
||||
self.add_body(2, "{:<15} = F.{}({}, kernel_size={}, stride={}, padding={}, ceil_mode={})".format(
|
||||
IR_node.variable_name,
|
||||
pool_name,
|
||||
self.parent_variable_name(IR_node),
|
||||
tuple(pool_size),
|
||||
tuple(strides),
|
||||
tuple(padding),
|
||||
ceil_mode
|
||||
))
|
||||
self.add_body(2, "{:<15} = F.{}({}, kernel_size={}, stride={}, padding={}, ceil_mode={})".format(
|
||||
IR_node.variable_name,
|
||||
pool_name,
|
||||
input_node,
|
||||
tuple(pool_size),
|
||||
tuple(strides),
|
||||
0,
|
||||
False
|
||||
))
|
||||
|
||||
elif IR_node.get_attr('pooling_type') == "AVG":
|
||||
|
||||
for e in IR_node.get_attr('dilations', []):
|
||||
assert e == 1
|
||||
|
||||
pool_size = IR_node.get_attr('kernel_shape')[1:-1]
|
||||
strides = IR_node.get_attr('strides')[1:-1]
|
||||
|
||||
padding = IR_node.get_attr('pads')[1:dim]
|
||||
ceil_mode = self.is_ceil_mode(IR_node.get_attr('pads'))
|
||||
|
||||
# input_node = self._defuse_padding(IR_node, exstr)
|
||||
self.add_body(2, "{:<15} = F.{}({}, kernel_size={}, stride={}, padding={}, ceil_mode={})".format(
|
||||
IR_node.variable_name,
|
||||
pool_name,
|
||||
self.parent_variable_name(IR_node),
|
||||
tuple(pool_size),
|
||||
tuple(strides),
|
||||
tuple(padding),
|
||||
ceil_mode
|
||||
))
|
||||
|
||||
else:
|
||||
raise ValueError()
|
||||
|
||||
|
||||
def emit_UNKNOWN(self, IR_node):
|
||||
|
@ -306,6 +331,14 @@ class KitModel(nn.Module):
|
|||
self.IR_graph.get_parent(IR_node.name, [0]).real_variable_name))
|
||||
|
||||
|
||||
def emit_LeakyRelu(self, IR_node):
|
||||
self.add_body(2, "{:<15} = F.leaky_relu({}, negative_slope={})".format(
|
||||
IR_node.variable_name,
|
||||
self.IR_graph.get_parent(IR_node.name, [0]).real_variable_name,
|
||||
IR_node.get_attr('alpha')))
|
||||
|
||||
|
||||
|
||||
def emit_Relu6(self, IR_node):
|
||||
self.add_body(2, "{:<15} = F.relu6({})".format(
|
||||
IR_node.variable_name,
|
||||
|
@ -432,6 +465,23 @@ class KitModel(nn.Module):
|
|||
self.parent_variable_name(IR_node)
|
||||
))
|
||||
|
||||
def emit_Scale(self, IR_node):
|
||||
self.used_layers.add(IR_node.type)
|
||||
dim = len(IR_node.layer.attr['_output_shapes'].list.shape[0].dim) - 2
|
||||
|
||||
self.add_init(2, "self.{} = self.__scale({}, '{}', num_features={})".format(
|
||||
IR_node.variable_name,
|
||||
dim,
|
||||
IR_node.name,
|
||||
IR_node.layer.attr['_output_shapes'].list.shape[0].dim[-1].size
|
||||
))
|
||||
|
||||
self.add_body(2, "{:<15} = self.{}({})".format(
|
||||
IR_node.variable_name,
|
||||
IR_node.variable_name,
|
||||
self.parent_variable_name(IR_node)
|
||||
))
|
||||
|
||||
|
||||
def emit_Squeeze(self, IR_node):
|
||||
self.add_body(2, "{:<15} = torch.squeeze({})".format(
|
||||
|
@ -450,11 +500,11 @@ class KitModel(nn.Module):
|
|||
|
||||
|
||||
def emit_Pad(self, IR_node):
|
||||
if IR_node.get_attr('mode') == 'constant':
|
||||
if IR_node.get_attr('mode').lower() == 'constant':
|
||||
mode = "mode = 'constant', value = {}".format(0)
|
||||
elif IR_node.get_attr('mode') == 'reflect':
|
||||
elif IR_node.get_attr('mode').lower() == 'reflect':
|
||||
mode = "mode = 'reflect'"
|
||||
elif IR_node.get_attr('mode') == 'SYMMETRIC':
|
||||
elif IR_node.get_attr('mode').upper() == 'SYMMETRIC':
|
||||
mode = "mode = 'replicate'"
|
||||
else:
|
||||
assert False
|
||||
|
@ -565,6 +615,95 @@ class KitModel(nn.Module):
|
|||
return layer""")
|
||||
|
||||
|
||||
def _layer_Scale(self):
|
||||
self.add_body(0, """
|
||||
# from torch.nn.parameter import Parameter
|
||||
|
||||
class _Scale(nn.Module):
|
||||
|
||||
def __init__(self, num_features, affine=True):
|
||||
super(KitModel._Scale, self).__init__()
|
||||
self.num_features = num_features
|
||||
self.affine = affine
|
||||
|
||||
self.running_mean = torch.zeros(num_features)
|
||||
self.running_var = torch.ones(num_features)
|
||||
self.training = False
|
||||
self.eps = 1e-5
|
||||
if self.affine:
|
||||
self.weight = nn.Parameter(torch.Tensor(num_features))
|
||||
self.bias = nn.Parameter(torch.Tensor(num_features))
|
||||
else:
|
||||
self.register_parameter('weight', None)
|
||||
self.register_parameter('bias', None)
|
||||
self.reset_parameters()
|
||||
|
||||
|
||||
def reset_parameters(self):
|
||||
if self.affine:
|
||||
self.weight.data.uniform_()
|
||||
self.bias.data.zero_()
|
||||
|
||||
def _check_input_dim(self, input):
|
||||
raise NotImplementedError
|
||||
|
||||
def forward(self, input):
|
||||
self._check_input_dim(input)
|
||||
|
||||
return F.batch_norm(
|
||||
input, self.running_mean, self.running_var, self.weight, self.bias,
|
||||
self.training,
|
||||
0 , self.eps)
|
||||
|
||||
|
||||
class Scale1d(_Scale):
|
||||
|
||||
def _check_input_dim(self, input):
|
||||
if input.dim() != 2 and input.dim() != 3:
|
||||
raise ValueError('expected 2D or 3D input (got {}D input)'
|
||||
.format(input.dim()))
|
||||
|
||||
|
||||
|
||||
class Scale2d(_Scale):
|
||||
|
||||
|
||||
def _check_input_dim(self, input):
|
||||
if input.dim() != 4:
|
||||
raise ValueError('expected 4D input (got {}D input)'
|
||||
.format(input.dim()))
|
||||
|
||||
|
||||
class Scale3d(_Scale):
|
||||
|
||||
def _check_input_dim(self, input):
|
||||
if input.dim() != 5:
|
||||
raise ValueError('expected 5D input (got {}D input)'
|
||||
.format(input.dim()))
|
||||
|
||||
|
||||
@staticmethod
|
||||
def __scale(dim, name, **kwargs):
|
||||
if dim == 1: layer = KitModel.Scale1d(**kwargs)
|
||||
elif dim == 2: layer = KitModel.Scale2d(**kwargs)
|
||||
elif dim == 3: layer = KitModel.Scale3d(**kwargs)
|
||||
else: raise NotImplementedError()
|
||||
|
||||
if 'scale' in __weights_dict[name]:
|
||||
layer.state_dict()['weight'].copy_(torch.from_numpy(__weights_dict[name]['scale']))
|
||||
else:
|
||||
layer.weight.data.fill_(1)
|
||||
|
||||
if 'bias' in __weights_dict[name]:
|
||||
layer.state_dict()['bias'].copy_(torch.from_numpy(__weights_dict[name]['bias']))
|
||||
else:
|
||||
layer.bias.data.fill_(0)
|
||||
|
||||
return layer""")
|
||||
|
||||
|
||||
|
||||
|
||||
def _layer_LRN(self):
|
||||
self.add_body(0, """
|
||||
class LRN(nn.Module):
|
||||
|
|
|
@ -736,11 +736,11 @@ class TestModels(CorrectnessTest):
|
|||
},
|
||||
|
||||
'coreml' : {
|
||||
'inception_v3' : [CaffeEmit, CoreMLEmit, KerasEmit, MXNetEmit, TensorflowEmit],
|
||||
'mobilenet' : [CaffeEmit, CoreMLEmit, KerasEmit, MXNetEmit, TensorflowEmit],
|
||||
'resnet50' : [CaffeEmit, CoreMLEmit, KerasEmit, MXNetEmit, TensorflowEmit],
|
||||
'tinyyolo' : [CoreMLEmit, KerasEmit, MXNetEmit, TensorflowEmit],
|
||||
'vgg16' : [CaffeEmit, CoreMLEmit, KerasEmit, MXNetEmit, TensorflowEmit],
|
||||
'inception_v3' : [CaffeEmit, CoreMLEmit, KerasEmit, MXNetEmit, PytorchEmit, TensorflowEmit],
|
||||
'mobilenet' : [CaffeEmit, CoreMLEmit, KerasEmit, MXNetEmit, PytorchEmit, TensorflowEmit],
|
||||
'resnet50' : [CaffeEmit, CoreMLEmit, KerasEmit, MXNetEmit, PytorchEmit, TensorflowEmit],
|
||||
'tinyyolo' : [CoreMLEmit, KerasEmit, MXNetEmit, PytorchEmit, TensorflowEmit],
|
||||
'vgg16' : [CaffeEmit, CoreMLEmit, KerasEmit, MXNetEmit, PytorchEmit, TensorflowEmit],
|
||||
},
|
||||
|
||||
'darknet' : {
|
||||
|
|
Загрузка…
Ссылка в новой задаче