This is an automated email from the ASF dual-hosted git repository.
marcoabreu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git
The following commit(s) were added to refs/heads/master by this push:
new 815f36c [MXNET-707] Add unit test for mxnet to coreml converter
(#11952)
815f36c is described below
commit 815f36ce8b4ed16fe27d500f5c8c930cd10cee5c
Author: Lin Yuan <[email protected]>
AuthorDate: Tue Oct 16 10:16:15 2018 -0700
[MXNET-707] Add unit test for mxnet to coreml converter (#11952)
* Add unittest to coreml converter
* Add unittest to coreml converter
* Add docstring and remove unused method
* updated test and removed unittest folder
* remove unittest
* Add coreml test to CI
* fix lint
* install mxnet-to-coreml for testing
* exclude test that takes too long
* linting to 100 max line width
---
.travis.yml | 2 +
ci/travis/install.sh | 2 +-
tools/coreml/test/test_mxnet_converter.py | 69 +++++++++++++++++--------------
tools/coreml/test/test_mxnet_image.py | 45 ++++++++++----------
tools/coreml/test/test_mxnet_models.py | 48 +++++++++++----------
5 files changed, 92 insertions(+), 74 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index d6b38f4..b5c323f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -34,7 +34,9 @@ script:
- export MXNET_STORAGE_FALLBACK_LOG_VERBOSE=0
- mv make/osx.mk config.mk
- make -j 2
+
# We ignore several tests to avoid possible timeouts on large PRs.
# This lowers our test coverage, but is required for consistent Travis runs.
# These tests will be tested in a variety of environments in Jenkins based
tests.
- python -m nose --with-timer
--exclude-test=test_sparse_operator.test_elemwise_binary_ops
--exclude-test=test_gluon_model_zoo.test_models
--exclude-test=test_random.test_shuffle
--exclude-test=test_operator.test_broadcast_binary_op
--exclude-test=test_operator.test_pick
--exclude-test=test_profiler.test_continuous_profile_and_instant_marker
--exclude-test=test_metric_perf.test_metric_performance
--exclude-test=test_operator.test_order --verbose tests/python/unittest/
+ - python2 -m nose --verbose tools/coreml/test --exclude-test=test_mxnet_image
diff --git a/ci/travis/install.sh b/ci/travis/install.sh
index ae95976..6412849 100644
--- a/ci/travis/install.sh
+++ b/ci/travis/install.sh
@@ -22,5 +22,5 @@ export HOMEBREW_NO_AUTO_UPDATE=1
if [ ${TRAVIS_OS_NAME} == "osx" ]; then
brew install opencv
- python -m pip install --user nose numpy cython scipy requests mock
nose-timer nose-exclude
+ python -m pip install --user nose numpy cython scipy requests mock
nose-timer nose-exclude mxnet-to-coreml
fi
diff --git a/tools/coreml/test/test_mxnet_converter.py
b/tools/coreml/test/test_mxnet_converter.py
index 6020041..5d26c5f 100644
--- a/tools/coreml/test/test_mxnet_converter.py
+++ b/tools/coreml/test/test_mxnet_converter.py
@@ -18,15 +18,12 @@
import unittest
import mxnet as mx
import numpy as np
-import sys
-import os
-current_working_directory = os.getcwd()
-sys.path.append(current_working_directory + "/..")
-sys.path.append(current_working_directory + "/../converter/")
-import _mxnet_converter as mxnet_converter
+
+from converter._mxnet_converter import convert
from collections import namedtuple
from converter import utils
+
def _mxnet_remove_batch(input_data):
for blob in input_data:
input_data[blob] = np.reshape(input_data[blob],
input_data[blob].shape[1:])
@@ -39,7 +36,8 @@ def _get_mxnet_module(net, data_shapes, mode, label_names,
input_names=None):
"""
mx.random.seed(1993)
- mod = utils.create_module(sym=net, data_shapes=data_shapes,
label_shapes=input_names, label_names=label_names)
+ mod = utils.create_module(sym=net, data_shapes=data_shapes,
label_shapes=input_names,
+ label_names=label_names)
if mode == 'random':
mod.init_params(
@@ -62,11 +60,14 @@ def _get_mxnet_module(net, data_shapes, mode, label_names,
input_names=None):
class SingleLayerTest(unittest.TestCase):
"""
Unit test class for testing where converter is able to convert individual
layers or not.
- In order to do so, it converts model and generates preds on both CoreML
and MXNet and check they are the same.
+ In order to do so, it converts model and generates preds on both CoreML
and MXNet and check
+ they are the same.
"""
- def _test_mxnet_model(self, net, input_shape, mode, class_labels=None,
coreml_mode=None, label_names=None, delta=1e-3,
+ def _test_mxnet_model(self, net, input_shape, mode, class_labels=None,
+ coreml_mode=None, label_names=None, delta=1e-2,
pre_processing_args=None, input_name='data'):
- """ Helper method that convert the CoreML model into CoreML and
compares the predictions over random data.
+ """ Helper method that convert the CoreML model into CoreML and
compares the predictions
+ over random data.
Parameters
----------
@@ -89,7 +90,7 @@ class SingleLayerTest(unittest.TestCase):
The name of the input variable to the symbolic graph.
"""
- data_shapes=[(input_name, input_shape)]
+ data_shapes = [(input_name, input_shape)]
mod = _get_mxnet_module(net, data_shapes, mode, label_names)
@@ -100,7 +101,7 @@ class SingleLayerTest(unittest.TestCase):
mxnet_preds = mod.get_outputs()[0].asnumpy().flatten()
# Get predictions from coreml
- coreml_model = mxnet_converter.convert(
+ coreml_model = convert(
model=mod,
class_labels=class_labels,
mode=coreml_mode,
@@ -112,7 +113,7 @@ class SingleLayerTest(unittest.TestCase):
# Check prediction accuracy
self.assertEquals(len(mxnet_preds), len(coreml_preds))
for i in range(len(mxnet_preds)):
- self.assertAlmostEquals(mxnet_preds[i], coreml_preds[i], delta =
delta)
+ self.assertAlmostEquals(mxnet_preds[i], coreml_preds[i],
delta=delta)
def test_tiny_inner_product_zero_input(self):
np.random.seed(1988)
@@ -140,7 +141,7 @@ class SingleLayerTest(unittest.TestCase):
input_shape = (1, 10)
net = mx.sym.Variable('data')
net = mx.sym.FullyConnected(data=net, name='fc1', num_hidden=5)
- self._test_mxnet_model(net, input_shape=input_shape, mode='ones')
+ self._test_mxnet_model(net, input_shape=input_shape, mode='ones',
delta=0.05)
def test_tiny_inner_product_random_input(self):
np.random.seed(1988)
@@ -162,7 +163,8 @@ class SingleLayerTest(unittest.TestCase):
net = mx.sym.Variable('data')
net = mx.sym.FullyConnected(data=net, name='fc1', num_hidden=5)
net = mx.sym.SoftmaxOutput(net, name='softmax')
- self._test_mxnet_model(net, input_shape=input_shape, mode='random',
label_names=['softmax_label'])
+ self._test_mxnet_model(net, input_shape=input_shape, mode='random',
+ label_names=['softmax_label'])
def test_tiny_relu_activation_random_input(self):
np.random.seed(1988)
@@ -228,7 +230,7 @@ class SingleLayerTest(unittest.TestCase):
pad=pad,
name='conv_1'
)
- self._test_mxnet_model(net, input_shape=input_shape, mode='ones')
+ self._test_mxnet_model(net, input_shape=input_shape, mode='ones',
delta=0.05)
def test_tiny_conv_random_input(self):
np.random.seed(1988)
@@ -481,7 +483,8 @@ class SingleLayerTest(unittest.TestCase):
net = mx.sym.Flatten(data=net, name='flatten1')
net = mx.sym.FullyConnected(data=net, name='fc1', num_hidden=5)
net = mx.sym.SoftmaxOutput(net, name='softmax')
- self._test_mxnet_model(net, input_shape=input_shape, mode='random',
label_names=['softmax_label'])
+ self._test_mxnet_model(net, input_shape=input_shape, mode='random',
+ label_names=['softmax_label'])
def test_transpose(self):
np.random.seed(1988)
@@ -516,10 +519,8 @@ class SingleLayerTest(unittest.TestCase):
net = mx.sym.Variable('data')
net = mx.sym.FullyConnected(data=net, name='fc1', num_hidden=5)
net = mx.sym.SoftmaxOutput(net, name='softmax')
- mod = _get_mxnet_module(net,
- data_shapes=[('data', input_shape)],
- mode='random',
- label_names=['softmax_label'])
+ mod = _get_mxnet_module(net, data_shapes=[('data', input_shape)],
+ mode='random', label_names=['softmax_label'])
# Generate some dummy data
input_data = np.random.uniform(-0.1, 0.1, input_shape)
@@ -529,14 +530,15 @@ class SingleLayerTest(unittest.TestCase):
kwargs = {'input_shape': {'data': input_shape}}
# Get predictions from coreml
- coreml_model = mxnet_converter.convert(
+ coreml_model = convert(
model=mod,
-
class_labels=['Category1','Category2','Category3','Category4','Category5'],
+ class_labels=['Category1', 'Category2', 'Category3', 'Category4',
'Category5'],
mode='classifier',
**kwargs
)
- prediction = coreml_model.predict(_mxnet_remove_batch({'data':
input_data}))
+ prediction = coreml_model.predict(
+ _mxnet_remove_batch({'data': input_data}))
self.assertEqual(prediction['classLabel'], 'Category3')
def test_really_tiny_deconv_random_input(self):
@@ -579,7 +581,7 @@ class SingleLayerTest(unittest.TestCase):
name='deconv_1'
)
# Test the mxnet model
- self._test_mxnet_model(net, input_shape=input_shape, mode='ones')
+ self._test_mxnet_model(net, input_shape=input_shape, mode='ones',
delta=0.05)
def test_tiny_deconv_random_input(self):
np.random.seed(1988)
@@ -915,7 +917,6 @@ class SingleLayerTest(unittest.TestCase):
def test_batch_norm_no_global_stats(self):
""" This test should throw an exception since converter doesn't support
- conversion of MXNet models that use local batch stats (i.e.
use_global_stats=False). The reason for this is CoreML doesn't
support
local batch stats.
"""
@@ -943,12 +944,17 @@ class SingleLayerTest(unittest.TestCase):
net = mx.sym.Variable('data')
net = mx.sym.FullyConnected(data=net, name='fc1', num_hidden=5)
net = mx.sym.SoftmaxOutput(net, name='softmax')
- self._test_mxnet_model(net, input_shape=input_shape, mode='random',
label_names=['softmax_label'],
- pre_processing_args={'red_bias':0,
'blue_bias':0, 'green_bias':0, 'image_scale':1})
+ self._test_mxnet_model(net, input_shape=input_shape, mode='random',
+ label_names=['softmax_label'],
+ pre_processing_args={'red_bias': 0,
+ 'blue_bias': 0,
+ 'green_bias': 0,
+ 'image_scale': 1})
def test_different_input_variables(self):
"""
- Verifying the behavior when input variable name is different than the
standard name - 'data'.
+ Verifying the behavior when input variable name is different than the
+ standard name - 'data'.
"""
np.random.seed(1988)
input_shape = (1, 10)
@@ -958,12 +964,13 @@ class SingleLayerTest(unittest.TestCase):
def test_really_tiny_conv_optional_params(self):
"""
- Verifying the behavior of a convolutional layer when stride and pad
are not provided.
+ Verifying the behavior of a convolutional layer when stride and pad
+ are not provided.
"""
np.random.seed(1988)
input_shape = (1, 1, 10, 10)
num_filter = 1
- kernel = (1 ,1)
+ kernel = (1, 1)
# Define a model
net = mx.sym.Variable('data')
diff --git a/tools/coreml/test/test_mxnet_image.py
b/tools/coreml/test/test_mxnet_image.py
index e754519..b3165f0 100644
--- a/tools/coreml/test/test_mxnet_image.py
+++ b/tools/coreml/test/test_mxnet_image.py
@@ -1,4 +1,4 @@
-from __future__ import print_function
+
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
@@ -16,19 +16,13 @@ from __future__ import print_function
# specific language governing permissions and limitations
# under the License.
+import unittest
import mxnet as mx
import numpy as np
-import unittest
-import sys
-import os
from six.moves import xrange
-
-current_working_directory = os.getcwd()
-sys.path.append(current_working_directory + "/..")
-sys.path.append(current_working_directory + "/../converter/")
-import _mxnet_converter as mxnet_converter
-from converter.utils import load_model
+from converter._mxnet_converter import convert
+from converter import utils
VAL_DATA = 'data/val-5k-256.rec'
URL = 'http://data.mxnet.io/data/val-5k-256.rec'
@@ -44,7 +38,7 @@ def read_image(data_val, label_name):
label_width=1,
preprocess_threads=4,
batch_size=32,
- data_shape=(3,224,224),
+ data_shape=(3, 224, 224),
label_name=label_name,
rand_corp=False,
rand_mirror=False,
@@ -80,11 +74,11 @@ class ImageNetTest(unittest.TestCase):
epoch_num=epoch,
data_shapes=data.provide_data,
label_shapes=data.provide_label,
- label_names=[label_name,]
+ label_names=[label_name, ]
)
input_shape = (1, 3, 224, 224)
- coreml_model = mxnet_converter.convert(mod, input_shape={'data':
input_shape})
+ coreml_model = convert(mod, input_shape={'data': input_shape})
mxnet_acc = []
mxnet_top_5_acc = []
@@ -104,34 +98,43 @@ class ImageNetTest(unittest.TestCase):
mxnet_predict = mxnet_preds[i]
label = label_numpy[i]
mxnet_acc.append(is_correct_top_one(mxnet_predict, label))
- mxnet_top_5_acc.append(is_correct_top_five(mxnet_predict,
label))
+ mxnet_top_5_acc.append(is_correct_top_five(mxnet_predict,
+ label))
coreml_acc.append(is_correct_top_one(coreml_predict, label))
- coreml_top_5_acc.append(is_correct_top_five(coreml_predict,
label))
+ coreml_top_5_acc.append(is_correct_top_five(coreml_predict,
+ label))
num_batch += 1
- if (num_batch == 5): break # we only use a subset of the batches.
+ if (num_batch == 5):
+ break # we only use a subset of the batches.
print("MXNet acc %s" % np.mean(mxnet_acc))
print("Coreml acc %s" % np.mean(coreml_acc))
print("MXNet top 5 acc %s" % np.mean(mxnet_top_5_acc))
print("Coreml top 5 acc %s" % np.mean(coreml_top_5_acc))
self.assertAlmostEqual(np.mean(mxnet_acc), np.mean(coreml_acc),
delta=1e-4)
- self.assertAlmostEqual(np.mean(mxnet_top_5_acc),
np.mean(coreml_top_5_acc), delta=1e-4)
+ self.assertAlmostEqual(np.mean(mxnet_top_5_acc),
+ np.mean(coreml_top_5_acc),
+ delta=1e-4)
def test_squeezenet(self):
print("Testing Image Classification with Squeezenet")
- self._test_image_prediction(model_name='squeezenet_v1.1', epoch=0,
label_name='prob_label')
+ self._test_image_prediction(model_name='squeezenet_v1.1', epoch=0,
+ label_name='prob_label')
def test_inception_with_batch_normalization(self):
print("Testing Image Classification with Inception/BatchNorm")
- self._test_image_prediction(model_name='Inception-BN', epoch=126,
label_name='softmax_label')
+ self._test_image_prediction(model_name='Inception-BN', epoch=126,
+ label_name='softmax_label')
def test_resnet18(self):
print("Testing Image Classification with ResNet18")
- self._test_image_prediction(model_name='resnet-18', epoch=0,
label_name='softmax_label')
+ self._test_image_prediction(model_name='resnet-18', epoch=0,
+ label_name='softmax_label')
def test_vgg16(self):
print("Testing Image Classification with vgg16")
- self._test_image_prediction(model_name='vgg16', epoch=0,
label_name='prob_label')
+ self._test_image_prediction(model_name='vgg16', epoch=0,
+ label_name='prob_label')
if __name__ == '__main__':
diff --git a/tools/coreml/test/test_mxnet_models.py
b/tools/coreml/test/test_mxnet_models.py
index 7c294db..8dd319a 100644
--- a/tools/coreml/test/test_mxnet_models.py
+++ b/tools/coreml/test/test_mxnet_models.py
@@ -19,21 +19,16 @@ from __future__ import print_function
import unittest
import mxnet as mx
import numpy as np
-import sys
-import os
-from collections import namedtuple
+from collections import namedtuple
+from converter._mxnet_converter import convert
from six.moves import xrange
-current_working_directory = os.getcwd()
-sys.path.append(current_working_directory + "/..")
-sys.path.append(current_working_directory + "/../converter/")
-import _mxnet_converter as mxnet_converter
-
def _mxnet_remove_batch(input_data):
for blob in input_data:
- input_data[blob] = np.reshape(input_data[blob],
input_data[blob].shape[1:])
+ input_data[blob] = np.reshape(input_data[blob], input_data[blob]
+ .shape[1:])
return input_data
@@ -53,9 +48,10 @@ def _kl_divergence(distribution1, distribution2):
class ModelsTest(unittest.TestCase):
"""
- Unit test class that tests converter on entire MXNet models .
- In order to test each unit test converts MXNet model into CoreML model
using the converter, generate predictions
- on both MXNet and CoreML and verifies that predictions are same (or
similar).
+ Unit test class that tests converter on entire MXNet models.
+ In order to test each unit test converts MXNet model into CoreML model
using the converter,
+ generate predictions on both MXNet and CoreML and verifies that
predictions are same
+ (or similar).
"""
def _load_model(self, model_name, epoch_num, input_shape):
sym, arg_params, aux_params = mx.model.load_checkpoint(model_name,
epoch_num)
@@ -76,8 +72,10 @@ class ModelsTest(unittest.TestCase):
)
return mod
- def _test_model(self, model_name, epoch_num, input_shape=(1, 3, 224, 224),
files=None):
- """ Tests whether the converted CoreML model's preds are equal to
MXNet preds for a given model or not.
+ def _test_model(self, model_name, epoch_num, input_shape=(1, 3, 224, 224),
+ files=None):
+ """ Tests whether the converted CoreML model's preds are equal to MXNet
+ preds for a given model or not.
Parameters
----------
@@ -88,10 +86,12 @@ class ModelsTest(unittest.TestCase):
Epoch number of model we would like to load.
input_shape: tuple
- The shape of the input data in the form of (batch_size, channels,
height, width)
+ The shape of the input data in the form of (batch_size, channels,
+ height, width)
files: list of strings
- List of URLs pertaining to files that need to be downloaded in
order to use the model.
+ List of URLs pertaining to files that need to be downloaded in
+ order to use the model.
"""
if files is not None:
@@ -106,17 +106,22 @@ class ModelsTest(unittest.TestCase):
input_shape=input_shape
)
- coreml_model = mxnet_converter.convert(module, input_shape={'data':
input_shape})
+ coreml_model = convert(module, input_shape={'data': input_shape})
# Get predictions from MXNet and coreml
- div=[] # For storing KL divergence for each input.
+ div = [] # For storing KL divergence for each input.
for _ in xrange(1):
np.random.seed(1993)
- input_data = {'data': np.random.uniform(0, 1,
input_shape).astype(np.float32)}
+ input_data = {'data': np.random.uniform(0, 1, input_shape)
+ .astype(np.float32)}
Batch = namedtuple('Batch', ['data'])
- module.forward(Batch([mx.nd.array(input_data['data'])]),
is_train=False)
+ module.forward(Batch([mx.nd.array(input_data['data'])]),
+ is_train=False)
mxnet_pred = module.get_outputs()[0].asnumpy().flatten()
- coreml_pred =
coreml_model.predict(_mxnet_remove_batch(input_data)).values()[0].flatten()
+ coreml_pred = coreml_model \
+ .predict(_mxnet_remove_batch(input_data)) \
+ .values()[0] \
+ .flatten()
self.assertEqual(len(mxnet_pred), len(coreml_pred))
div.append(_kl_divergence(mxnet_pred, coreml_pred))
@@ -138,6 +143,7 @@ class ModelsTest(unittest.TestCase):
files=["http://data.mxnet.io/models/imagenet/resnet/50-layers/resnet-50-symbol.json",
"http://data.mxnet.io/models/imagenet/resnet/50-layers/resnet-50-0000.params"])
+ @unittest.skip("Model is too big for unit test")
def test_pred_vgg16(self):
self._test_model(model_name='vgg16', epoch_num=0,
files=["http://data.mxnet.io/models/imagenet/vgg/vgg16-symbol.json",