From 2bc5a5931655ddd7c2679a4bcc8ea4c22bad21d4 Mon Sep 17 00:00:00 2001 From: Bruce Tsai Date: Thu, 21 Feb 2019 15:24:46 +0800 Subject: [PATCH 1/6] Add support of mxnet_to_coreml * Add PReLU * Add group support in Convolution * Skip _minus_scalar and _mul_scalar --- tools/coreml/converter/_layers.py | 36 +++++++++++++++++++++- tools/coreml/converter/_mxnet_converter.py | 3 ++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/tools/coreml/converter/_layers.py b/tools/coreml/converter/_layers.py index 8f4bc1a8a02c..69f0c38c0875 100644 --- a/tools/coreml/converter/_layers.py +++ b/tools/coreml/converter/_layers.py @@ -220,6 +220,39 @@ def convert_activation(net, node, module, builder): output_name = output_name) +def convert_leakyrelu(net, node, module, builder): + """Convert an leakyrelu layer from mxnet to coreml. + + Parameters + ---------- + network: net + A mxnet network object. + + layer: node + Node to convert. + + module: module + An module for MXNet + + builder: NeuralNetworkBuilder + A neural network builder object. + """ + input_name, output_name = _get_input_output_name(net, node) + name = node['name'] + inputs = node['inputs'] + args, _ = module.get_params() + mx_non_linearity = _get_attrs(node)['act_type'] + if mx_non_linearity == 'prelu': + non_linearity = 'PRELU' + params = args[_get_node_name(net, inputs[1][0])].asnumpy() + else: + raise TypeError('Unknown activation type %s' % mx_non_linearity) + builder.add_activation(name = name, + non_linearity = non_linearity, + input_name = input_name, + output_name = output_name, + params = params) + def convert_elementwise_add(net, node, module, builder): """Convert an elementwise add layer from mxnet to coreml. @@ -335,6 +368,7 @@ def convert_convolution(net, node, module, builder): border_mode = "valid" n_filters = int(param['num_filter']) + n_groups = int(param['num_group']) W = args[_get_node_name(net, inputs[1][0])].asnumpy() if has_bias: @@ -361,7 +395,7 @@ def convert_convolution(net, node, module, builder): stride_height=stride_height, stride_width=stride_width, border_mode=border_mode, - groups=1, + groups=n_groups, W=W, b=Wb, has_bias=has_bias, diff --git a/tools/coreml/converter/_mxnet_converter.py b/tools/coreml/converter/_mxnet_converter.py index 2d91eb86a965..c5fd37b34aed 100644 --- a/tools/coreml/converter/_mxnet_converter.py +++ b/tools/coreml/converter/_mxnet_converter.py @@ -38,11 +38,14 @@ 'elemwise_add' : _layers.convert_elementwise_add, 'Reshape' : _layers.convert_reshape, 'Deconvolution' : _layers.convert_deconvolution, + 'LeakyReLU' : _layers.convert_leakyrelu, } _MXNET_SKIP_LAYERS = [ '_MulScalar', 'Dropout', + '_minus_scalar', + '_mul_scalar', ] def _mxnet_remove_batch(input_data): From a1e937d547a52894b0c02891592487ec4679910a Mon Sep 17 00:00:00 2001 From: Bruce Tsai Date: Mon, 25 Feb 2019 09:20:00 +0800 Subject: [PATCH 2/6] Comply with code style * Add spare line between comment and code * Add two spare lines between functions * Change "Convert an leakyrelu" to "Convert a leakyrelu" --- tools/coreml/converter/_layers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/coreml/converter/_layers.py b/tools/coreml/converter/_layers.py index 69f0c38c0875..ef3a2a7398ef 100644 --- a/tools/coreml/converter/_layers.py +++ b/tools/coreml/converter/_layers.py @@ -221,7 +221,7 @@ def convert_activation(net, node, module, builder): def convert_leakyrelu(net, node, module, builder): - """Convert an leakyrelu layer from mxnet to coreml. + """Convert a leakyrelu layer from mxnet to coreml. Parameters ---------- @@ -237,6 +237,7 @@ def convert_leakyrelu(net, node, module, builder): builder: NeuralNetworkBuilder A neural network builder object. """ + input_name, output_name = _get_input_output_name(net, node) name = node['name'] inputs = node['inputs'] @@ -253,6 +254,7 @@ def convert_leakyrelu(net, node, module, builder): output_name = output_name, params = params) + def convert_elementwise_add(net, node, module, builder): """Convert an elementwise add layer from mxnet to coreml. From cb980670b2b61cbe2f88a9dcf007a5381e760717 Mon Sep 17 00:00:00 2001 From: Bruce Tsai Date: Wed, 27 Feb 2019 16:47:19 +0800 Subject: [PATCH 3/6] Check if num_group exists in param before assigning value --- tools/coreml/converter/_layers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/coreml/converter/_layers.py b/tools/coreml/converter/_layers.py index ef3a2a7398ef..cf9fc6d4d6fa 100644 --- a/tools/coreml/converter/_layers.py +++ b/tools/coreml/converter/_layers.py @@ -370,7 +370,7 @@ def convert_convolution(net, node, module, builder): border_mode = "valid" n_filters = int(param['num_filter']) - n_groups = int(param['num_group']) + n_groups = int(param['num_group']) if 'num_group' in param else 1 W = args[_get_node_name(net, inputs[1][0])].asnumpy() if has_bias: From c94bbb82eddf718853b973ad7a48f41980bb69df Mon Sep 17 00:00:00 2001 From: Bruce Tsai Date: Wed, 27 Feb 2019 16:51:42 +0800 Subject: [PATCH 4/6] Add test cases for test_mxnet_converter * Add test_tiny_prelu_leakyrelu_random_input * Add test_really_tiny_conv_random_input_multi_group * Add test_tiny_conv_random_input_multi_group * Modify test_conv_random by adding num_group --- tools/coreml/test/test_mxnet_converter.py | 51 +++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tools/coreml/test/test_mxnet_converter.py b/tools/coreml/test/test_mxnet_converter.py index bc850690a572..26c553ebd710 100644 --- a/tools/coreml/test/test_mxnet_converter.py +++ b/tools/coreml/test/test_mxnet_converter.py @@ -192,6 +192,15 @@ def test_tiny_tanh_activation_random_input(self): net = mx.sym.Activation(net, name='tanh1', act_type="tanh") self._test_mxnet_model(net, input_shape=input_shape, mode='random') + def test_tiny_prelu_leakyrelu_random_input(self): + np.random.seed(1988) + input_shape = (1, 10) + net = mx.sym.Variable('data') + net = mx.sym.FullyConnected(data=net, name='fc1', num_hidden=5) + gamma = mx.sym.Variable('gamma') + net = mx.sym.LeakyReLU(net, gamma=gamma, name='prelu1', act_type="prelu") + self._test_mxnet_model(net, input_shape=input_shape, mode='random') + def test_really_tiny_conv_random_input(self): np.random.seed(1988) input_shape = (1, 1, 10, 10) @@ -408,6 +417,26 @@ def test_really_tiny_conv_random_input_multi_filter(self): ) self._test_mxnet_model(net, input_shape=input_shape, mode='random') + def test_really_tiny_conv_random_input_multi_group(self): + np.random.seed(1988) + input_shape = (1, 16, 10, 10) + num_filter = 16 + num_group = 4 + kernel = (1, 1) + stride = (1, 1) + pad = (0, 0) + net = mx.sym.Variable('data') + net = mx.symbol.Convolution( + data=net, + num_filter=num_filter, + num_group=num_group, + kernel=kernel, + stride=stride, + pad=pad, + name='conv_1' + ) + self._test_mxnet_model(net, input_shape=input_shape, mode='random') + def test_tiny_conv_random_3d_input(self): np.random.seed(1988) input_shape = (1, 3, 10, 10) @@ -444,10 +473,31 @@ def test_tiny_conv_random_input_multi_filter(self): ) self._test_mxnet_model(net, input_shape=input_shape, mode='random') + def test_tiny_conv_random_input_multi_group(self): + np.random.seed(1988) + input_shape = (1, 16, 10, 10) + num_filter = 16 + num_group = 4 + kernel = (5, 5) + stride = (1, 1) + pad = (0, 0) + net = mx.sym.Variable('data') + net = mx.symbol.Convolution( + data=net, + num_filter=num_filter, + num_group=num_group, + kernel=kernel, + stride=stride, + pad=pad, + name='conv_1' + ) + self._test_mxnet_model(net, input_shape=input_shape, mode='random') + def test_conv_random(self): np.random.seed(1988) input_shape = (1, 3, 10, 10) num_filter = 64 + num_group = 1 kernel = (5, 5) stride = (1, 1) pad = (0, 0) @@ -455,6 +505,7 @@ def test_conv_random(self): net = mx.symbol.Convolution( data=net, num_filter=num_filter, + num_group=num_group, kernel=kernel, stride=stride, pad=pad, From 01899336f88500f0881cceab318bcb893cf44866 Mon Sep 17 00:00:00 2001 From: Bruce Tsai Date: Thu, 28 Feb 2019 08:53:37 +0800 Subject: [PATCH 5/6] Rollback to not setting num_group --- tools/coreml/test/test_mxnet_converter.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/coreml/test/test_mxnet_converter.py b/tools/coreml/test/test_mxnet_converter.py index 26c553ebd710..13a892f2b17d 100644 --- a/tools/coreml/test/test_mxnet_converter.py +++ b/tools/coreml/test/test_mxnet_converter.py @@ -497,7 +497,6 @@ def test_conv_random(self): np.random.seed(1988) input_shape = (1, 3, 10, 10) num_filter = 64 - num_group = 1 kernel = (5, 5) stride = (1, 1) pad = (0, 0) @@ -505,7 +504,6 @@ def test_conv_random(self): net = mx.symbol.Convolution( data=net, num_filter=num_filter, - num_group=num_group, kernel=kernel, stride=stride, pad=pad, From da9f7cbaa4ca10c314778c363d7d674fac23d101 Mon Sep 17 00:00:00 2001 From: Bruce Tsai Date: Thu, 28 Feb 2019 09:18:21 +0800 Subject: [PATCH 6/6] Add "leaky" and "elu" in converter * Add support of "leaky" and "elu" * Add unit test for "leaky" and "elu" --- tools/coreml/converter/_layers.py | 10 +++++++++- tools/coreml/test/test_mxnet_converter.py | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tools/coreml/converter/_layers.py b/tools/coreml/converter/_layers.py index cf9fc6d4d6fa..6590b13b108f 100644 --- a/tools/coreml/converter/_layers.py +++ b/tools/coreml/converter/_layers.py @@ -243,7 +243,15 @@ def convert_leakyrelu(net, node, module, builder): inputs = node['inputs'] args, _ = module.get_params() mx_non_linearity = _get_attrs(node)['act_type'] - if mx_non_linearity == 'prelu': + if mx_non_linearity == 'elu': + non_linearity = 'ELU' + slope = _get_attrs(node)['slope'] if 'slope' in _get_attrs(node) else 0.25 + params = slope + elif mx_non_linearity == 'leaky': + non_linearity = 'LEAKYRELU' + slope = _get_attrs(node)['slope'] if 'slope' in _get_attrs(node) else 0.25 + params = [slope] + elif mx_non_linearity == 'prelu': non_linearity = 'PRELU' params = args[_get_node_name(net, inputs[1][0])].asnumpy() else: diff --git a/tools/coreml/test/test_mxnet_converter.py b/tools/coreml/test/test_mxnet_converter.py index 13a892f2b17d..192090588fde 100644 --- a/tools/coreml/test/test_mxnet_converter.py +++ b/tools/coreml/test/test_mxnet_converter.py @@ -192,6 +192,24 @@ def test_tiny_tanh_activation_random_input(self): net = mx.sym.Activation(net, name='tanh1', act_type="tanh") self._test_mxnet_model(net, input_shape=input_shape, mode='random') + def test_tiny_elu_leakyrelu_random_input(self): + np.random.seed(1988) + input_shape = (1, 10) + net = mx.sym.Variable('data') + net = mx.sym.FullyConnected(data=net, name='fc1', num_hidden=5) + slope = 0.1 + net = mx.sym.LeakyReLU(net, name='elu1', act_type="elu", slope=slope) + self._test_mxnet_model(net, input_shape=input_shape, mode='random') + + def test_tiny_leaky_leakyrelu_random_input(self): + np.random.seed(1988) + input_shape = (1, 10) + net = mx.sym.Variable('data') + net = mx.sym.FullyConnected(data=net, name='fc1', num_hidden=5) + slope = 0.1 + net = mx.sym.LeakyReLU(net, name='leaky1', act_type="leaky", slope=slope) + self._test_mxnet_model(net, input_shape=input_shape, mode='random') + def test_tiny_prelu_leakyrelu_random_input(self): np.random.seed(1988) input_shape = (1, 10)