From 31244922d7861fef484fc315e633b4a0cc7e6ff6 Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Fri, 29 Jun 2018 16:08:16 +0200 Subject: [PATCH 01/15] Fix MXPredReshape in the c_predict_api. --- src/c_api/c_predict_api.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/c_api/c_predict_api.cc b/src/c_api/c_predict_api.cc index becb0cb364f6..d84a89ab2133 100644 --- a/src/c_api/c_predict_api.cc +++ b/src/c_api/c_predict_api.cc @@ -140,6 +140,7 @@ int MXPredCreatePartialOut(const char* symbol_json_str, } sym = nnvm::Symbol::CreateGroup(out_syms); } + ret->sym = sym; // load the parameters std::unordered_map arg_params, aux_params; @@ -214,6 +215,7 @@ int MXPredCreatePartialOut(const char* symbol_json_str, } Context ctx = Context::Create(static_cast(dev_type), dev_id); + ret->ctx = ctx; std::vector arg_arrays, aux_arrays; for (size_t i = 0; i < arg_shapes.size(); ++i) { @@ -231,6 +233,7 @@ int MXPredCreatePartialOut(const char* symbol_json_str, aux_arrays.push_back(nd); } ret->arg_arrays = arg_arrays; + ret->aux_arrays = aux_arrays; // bind { std::map ctx_map; @@ -309,7 +312,6 @@ int MXPredReshape(mx_uint num_input_nodes, << " shape has been changed, only allow to change the shape of input data."; } } - p->arg_arrays.clear(); for (size_t i=0; i < aux_names.size(); ++i) { TShape newShape = aux_shapes[i]; @@ -319,7 +321,6 @@ int MXPredReshape(mx_uint num_input_nodes, << " shape has been changed, only allow to change the shape of input data."; } ret->aux_arrays = p->aux_arrays; - p->aux_arrays.clear(); // bind { From b7adcc694f98a76f9eae91422c41fee6a387075b Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Sun, 8 Jul 2018 03:03:04 +0200 Subject: [PATCH 02/15] Add unittest for the C predict API. --- amalgamation/python/mxnet_predict.py | 33 ++++++++++ tests/python/unittest/test_predictor.py | 88 +++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 tests/python/unittest/test_predictor.py diff --git a/amalgamation/python/mxnet_predict.py b/amalgamation/python/mxnet_predict.py index 627f375e1411..b72d0e89035b 100644 --- a/amalgamation/python/mxnet_predict.py +++ b/amalgamation/python/mxnet_predict.py @@ -159,6 +159,39 @@ def forward(self, **kwargs): mx_uint(v.size))) _check_call(_LIB.MXPredForward(self.handle)) + def reshape(self, input_shapes): + """Change the input shape of the predictor. + + Parameters + ---------- + input_shapes : dict of str to tuple + The new shape of input data. + + Examples + -------- + >>> predictor.reshape({'data':data_shape_tuple}) + """ + indptr = [0] + sdata = [] + keys = [] + for k, v in input_shapes.items(): + if not isinstance(v, tuple): + raise ValueError("Expect input_shapes to be dict str->tuple") + keys.append(c_str(k)) + sdata.extend(v) + indptr.append(len(sdata)) + + new_handle = PredictorHandle() + _check_call(_LIB.MXPredReshape( + mx_uint(len(indptr) - 1), + c_array(ctypes.c_char_p, keys), + c_array(mx_uint, indptr), + c_array(mx_uint, sdata), + self.handle, + ctypes.byref(new_handle))) + _check_call(_LIB.MXPredFree(self.handle)) + self.handle = new_handle + def get_output(self, index): """Get the index-th output. diff --git a/tests/python/unittest/test_predictor.py b/tests/python/unittest/test_predictor.py new file mode 100644 index 000000000000..88929a54f664 --- /dev/null +++ b/tests/python/unittest/test_predictor.py @@ -0,0 +1,88 @@ +# 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 +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +from __future__ import print_function +import numpy as np +import mxnet as mx +import mxnet.ndarray as nd +from mxnet import gluon +from mxnet.test_utils import assert_almost_equal +from common import setup_module, with_seed, teardown + +import sys, os +curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__))) +sys.path.append("../../../amalgamation/python/") +from mxnet_predict import Predictor, load_ndarray_file + +@with_seed() +def test_predictor(): + prefix = 'test_predictor_simple_dense' + symbol_file = "%s-symbol.json" % prefix + param_file = "%s-0000.params" % prefix + + # two inputs with different batch sizes + input1 = np.random.uniform(size=(1,3)) + input2 = np.random.uniform(size=(3,3)) + + # define a simple model + block = gluon.nn.HybridSequential() + block.add(gluon.nn.Dense(7)) + block.add(gluon.nn.Dense(3)) + block.hybridize() + block.initialize() + out1 = block.forward(nd.array(input1)) + out2 = block.forward(nd.array(input2)) + block.export(prefix) + + # create a predictor + predictor = Predictor(open(symbol_file, "r").read(), + open(param_file, "rb").read(), + {'data':input1.shape}) + + # forward and get output + predictor.forward(data=input1) + predictor_out1 = predictor.get_output(0) + assert_almost_equal(out1.asnumpy(), predictor_out1) + + # reshape + predictor.reshape({'data':input2.shape}) + predictor.forward(data=input2) + predictor_out2 = predictor.get_output(0) + assert_almost_equal(out2.asnumpy(), predictor_out2) + + # destroy the predictor + del predictor + +@with_seed() +def test_load_ndarray(): + + nd_file = 'test_predictor_load_ndarray.params' + a = nd.random.uniform(shape=(7, 3)) + b = nd.random.uniform(shape=(7,)) + nd_data = {'a':a, 'b':b} + nd.save(nd_file, nd_data) + + # test load_ndarray_file + nd_load = load_ndarray_file(open(nd_file, "rb").read()) + assert(set(nd_data.keys()) == set(nd_load.keys())) + for k in nd_data.keys(): + assert_almost_equal(nd_data[k].asnumpy(), nd_load[k]) + + +if __name__ == '__main__': + import nose + nose.runmodule() From d2d14f0dd68817b2bcf4ce39fb0e7924a979ee18 Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Sun, 8 Jul 2018 17:03:42 +0200 Subject: [PATCH 03/15] Fix path in the test. --- tests/python/unittest/test_predictor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/python/unittest/test_predictor.py b/tests/python/unittest/test_predictor.py index 88929a54f664..fd942fe2d158 100644 --- a/tests/python/unittest/test_predictor.py +++ b/tests/python/unittest/test_predictor.py @@ -25,7 +25,8 @@ import sys, os curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__))) -sys.path.append("../../../amalgamation/python/") +amalgamation_path = os.path.join(curr_path, '..', '..', '..', 'amalgamation', 'python') +sys.path.append(amalgamation_path) from mxnet_predict import Predictor, load_ndarray_file @with_seed() From e10b42005fb207aedc1ab4842ba1728021da1887 Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Sun, 8 Jul 2018 19:01:22 +0200 Subject: [PATCH 04/15] Fix for Windows. --- amalgamation/python/mxnet_predict.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/amalgamation/python/mxnet_predict.py b/amalgamation/python/mxnet_predict.py index b72d0e89035b..89b45fa82fef 100644 --- a/amalgamation/python/mxnet_predict.py +++ b/amalgamation/python/mxnet_predict.py @@ -51,10 +51,12 @@ def c_array(ctype, values): def _find_lib_path(): """Find mxnet library.""" curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__))) - api_path = os.path.join(curr_path, '../../lib/') + api_path = os.path.join(curr_path, '..', '..', 'lib') dll_path = [curr_path, api_path] dll_path = [os.path.join(p, 'libmxnet.so') for p in dll_path] + \ - [os.path.join(p, 'libmxnet_predict.so') for p in dll_path] + [os.path.join(p, 'libmxnet_predict.so') for p in dll_path] + \ + [os.path.join(p, 'libmxnet.dll') for p in dll_path] + \ + [os.path.join(p, 'libmxnet_predict.dll') for p in dll_path] lib_path = [p for p in dll_path if os.path.exists(p) and os.path.isfile(p)] if len(lib_path) == 0: raise RuntimeError('Cannot find the files.\n' + From 1841b65d7ce16348b9aeed4a4e5ec7e9654fc864 Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Sun, 8 Jul 2018 23:43:46 +0200 Subject: [PATCH 05/15] Try again to fix for Windows. --- amalgamation/python/mxnet_predict.py | 22 +++++++++++----------- tests/python/unittest/test_predictor.py | 3 +-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/amalgamation/python/mxnet_predict.py b/amalgamation/python/mxnet_predict.py index 89b45fa82fef..082915c17dc6 100644 --- a/amalgamation/python/mxnet_predict.py +++ b/amalgamation/python/mxnet_predict.py @@ -51,17 +51,17 @@ def c_array(ctype, values): def _find_lib_path(): """Find mxnet library.""" curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__))) - api_path = os.path.join(curr_path, '..', '..', 'lib') - dll_path = [curr_path, api_path] - dll_path = [os.path.join(p, 'libmxnet.so') for p in dll_path] + \ - [os.path.join(p, 'libmxnet_predict.so') for p in dll_path] + \ - [os.path.join(p, 'libmxnet.dll') for p in dll_path] + \ - [os.path.join(p, 'libmxnet_predict.dll') for p in dll_path] - lib_path = [p for p in dll_path if os.path.exists(p) and os.path.isfile(p)] - if len(lib_path) == 0: - raise RuntimeError('Cannot find the files.\n' + - 'List of candidates:\n' + str('\n'.join(dll_path))) - return lib_path + amalgamation_lib_path = os.path.join(curr_path, '../../lib/libmxnet_predict.so') + if os.path.exists(amalgamation_lib_path) and os.path.isfile(amalgamation_lib_path): + lib_path = [amalgamation_lib_path] + return lib_path + else: + # from python/setup.py + libinfo_py = os.path.join(curr_path, '../../python/mxnet/libinfo.py') + libinfo = {'__file__': libinfo_py} + exec(compile(open(libinfo_py, "rb").read(), libinfo_py, 'exec'), libinfo, libinfo) + lib_path = libinfo['find_lib_path']() + return lib_path def _load_lib(): diff --git a/tests/python/unittest/test_predictor.py b/tests/python/unittest/test_predictor.py index fd942fe2d158..151b31f73abe 100644 --- a/tests/python/unittest/test_predictor.py +++ b/tests/python/unittest/test_predictor.py @@ -25,8 +25,7 @@ import sys, os curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__))) -amalgamation_path = os.path.join(curr_path, '..', '..', '..', 'amalgamation', 'python') -sys.path.append(amalgamation_path) +sys.path.append(os.path.join(curr_path, "../../../amalgamation/python/")) from mxnet_predict import Predictor, load_ndarray_file @with_seed() From 39cd1986d6496a6985f1e7f9bccb383e365cb66b Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Tue, 10 Jul 2018 01:59:42 +0200 Subject: [PATCH 06/15] One more try to fix test on Windows. --- amalgamation/python/mxnet_predict.py | 9 +++++++-- tests/python/unittest/test_predictor.py | 10 +++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/amalgamation/python/mxnet_predict.py b/amalgamation/python/mxnet_predict.py index 082915c17dc6..c72eba74f447 100644 --- a/amalgamation/python/mxnet_predict.py +++ b/amalgamation/python/mxnet_predict.py @@ -56,8 +56,13 @@ def _find_lib_path(): lib_path = [amalgamation_lib_path] return lib_path else: - # from python/setup.py - libinfo_py = os.path.join(curr_path, '../../python/mxnet/libinfo.py') + libinfo_search_pathes = sys.path + [os.path.join(curr_path, '../../python/')] + libinfo_search_pathes = [os.path.join(p, 'mxnet/libinfo.py') for p in libinfo_search_pathes] + libinfo_pathes = [p for p in libinfo_search_pathes if os.path.exists(p) and os.path.isfile(p)] + if len(libinfo_pathes) == 0: + raise RuntimeError('Cannot find libinfo.py.\n' + + 'List of candidates:\n' + str('\n'.join(libinfo_search_pathes))) + libinfo_py = libinfo_pathes[0] libinfo = {'__file__': libinfo_py} exec(compile(open(libinfo_py, "rb").read(), libinfo_py, 'exec'), libinfo, libinfo) lib_path = libinfo['find_lib_path']() diff --git a/tests/python/unittest/test_predictor.py b/tests/python/unittest/test_predictor.py index 151b31f73abe..a85ceaee9122 100644 --- a/tests/python/unittest/test_predictor.py +++ b/tests/python/unittest/test_predictor.py @@ -16,6 +16,11 @@ # under the License. from __future__ import print_function +import sys, os +curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__))) +sys.path.append(os.path.join(curr_path, "../../../amalgamation/python/")) +from mxnet_predict import Predictor, load_ndarray_file + import numpy as np import mxnet as mx import mxnet.ndarray as nd @@ -23,11 +28,6 @@ from mxnet.test_utils import assert_almost_equal from common import setup_module, with_seed, teardown -import sys, os -curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__))) -sys.path.append(os.path.join(curr_path, "../../../amalgamation/python/")) -from mxnet_predict import Predictor, load_ndarray_file - @with_seed() def test_predictor(): prefix = 'test_predictor_simple_dense' From 6f0a3de0537f2ba61bd5c31f65c09fb920c47df0 Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Wed, 11 Jul 2018 22:56:51 +0200 Subject: [PATCH 07/15] Try again with CI. --- tests/python/unittest/test_predictor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/python/unittest/test_predictor.py b/tests/python/unittest/test_predictor.py index a85ceaee9122..109533726140 100644 --- a/tests/python/unittest/test_predictor.py +++ b/tests/python/unittest/test_predictor.py @@ -69,7 +69,6 @@ def test_predictor(): @with_seed() def test_load_ndarray(): - nd_file = 'test_predictor_load_ndarray.params' a = nd.random.uniform(shape=(7, 3)) b = nd.random.uniform(shape=(7,)) From 57e71357bd1b053f5cc777d713e3756b2f65a8c0 Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Tue, 17 Jul 2018 12:32:37 +0200 Subject: [PATCH 08/15] Try importing from mxnet first if cannot find the amalgamation lib. --- amalgamation/python/mxnet_predict.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/amalgamation/python/mxnet_predict.py b/amalgamation/python/mxnet_predict.py index c72eba74f447..9097bd30ae78 100644 --- a/amalgamation/python/mxnet_predict.py +++ b/amalgamation/python/mxnet_predict.py @@ -56,17 +56,19 @@ def _find_lib_path(): lib_path = [amalgamation_lib_path] return lib_path else: - libinfo_search_pathes = sys.path + [os.path.join(curr_path, '../../python/')] - libinfo_search_pathes = [os.path.join(p, 'mxnet/libinfo.py') for p in libinfo_search_pathes] - libinfo_pathes = [p for p in libinfo_search_pathes if os.path.exists(p) and os.path.isfile(p)] - if len(libinfo_pathes) == 0: - raise RuntimeError('Cannot find libinfo.py.\n' + - 'List of candidates:\n' + str('\n'.join(libinfo_search_pathes))) - libinfo_py = libinfo_pathes[0] - libinfo = {'__file__': libinfo_py} - exec(compile(open(libinfo_py, "rb").read(), libinfo_py, 'exec'), libinfo, libinfo) - lib_path = libinfo['find_lib_path']() - return lib_path + try: + from mxnet.libinfo import find_lib_path + lib_path = find_lib_path() + return lib_path + except ImportError: + libinfo_path = os.path.join(curr_path, '../../python/mxnet/libinfo.py') + if os.path.exists(libinfo_path) and os.path.isfile(libinfo_path): + libinfo = {'__file__': libinfo_py} + exec(compile(open(libinfo_py, "rb").read(), libinfo_py, 'exec'), libinfo, libinfo) + lib_path = libinfo['find_lib_path']() + return lib_path + else: + raise RuntimeError('Cannot find libinfo.py at %s.\n' % libinfo_path) def _load_lib(): From f3d70681421e627d7ad8481fb6254ede2190d945 Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Fri, 27 Jul 2018 00:13:47 +0200 Subject: [PATCH 09/15] Add a log message when libmxnet_predict.so is not found. --- amalgamation/python/mxnet_predict.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/amalgamation/python/mxnet_predict.py b/amalgamation/python/mxnet_predict.py index 9097bd30ae78..ca72e9affaa1 100644 --- a/amalgamation/python/mxnet_predict.py +++ b/amalgamation/python/mxnet_predict.py @@ -26,6 +26,7 @@ import os import sys import ctypes +import logging import numpy as np __all__ = ["Predictor", "load_ndarray_file"] @@ -56,6 +57,7 @@ def _find_lib_path(): lib_path = [amalgamation_lib_path] return lib_path else: + logging.info('Cannot find libmxnet_predict.so. Will search for MXNet library using libinfo.py then.') try: from mxnet.libinfo import find_lib_path lib_path = find_lib_path() @@ -68,7 +70,7 @@ def _find_lib_path(): lib_path = libinfo['find_lib_path']() return lib_path else: - raise RuntimeError('Cannot find libinfo.py at %s.\n' % libinfo_path) + raise RuntimeError('Cannot find libinfo.py at %s.' % libinfo_path) def _load_lib(): From fc3304c5a98452418189bc59c99ec06ea6ad59ba Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Sat, 11 Aug 2018 23:30:04 +0200 Subject: [PATCH 10/15] Set specific rtol and atol values. --- tests/python/unittest/test_predictor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/python/unittest/test_predictor.py b/tests/python/unittest/test_predictor.py index 109533726140..e634fa1ffbf7 100644 --- a/tests/python/unittest/test_predictor.py +++ b/tests/python/unittest/test_predictor.py @@ -62,7 +62,7 @@ def test_predictor(): predictor.reshape({'data':input2.shape}) predictor.forward(data=input2) predictor_out2 = predictor.get_output(0) - assert_almost_equal(out2.asnumpy(), predictor_out2) + assert_almost_equal(out2.asnumpy(), predictor_out2, rtol=1e-5, atol=1e-6) # destroy the predictor del predictor @@ -79,7 +79,7 @@ def test_load_ndarray(): nd_load = load_ndarray_file(open(nd_file, "rb").read()) assert(set(nd_data.keys()) == set(nd_load.keys())) for k in nd_data.keys(): - assert_almost_equal(nd_data[k].asnumpy(), nd_load[k]) + assert_almost_equal(nd_data[k].asnumpy(), nd_load[k], rtol=1e-5, atol=1e-6) if __name__ == '__main__': From ff4b6d99e7255bcaff8d5e4405f519e8a7f84704 Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Sat, 11 Aug 2018 23:32:53 +0200 Subject: [PATCH 11/15] Fix missing rtol and atol values. --- tests/python/unittest/test_predictor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/unittest/test_predictor.py b/tests/python/unittest/test_predictor.py index e634fa1ffbf7..fc2fbf600cbc 100644 --- a/tests/python/unittest/test_predictor.py +++ b/tests/python/unittest/test_predictor.py @@ -56,7 +56,7 @@ def test_predictor(): # forward and get output predictor.forward(data=input1) predictor_out1 = predictor.get_output(0) - assert_almost_equal(out1.asnumpy(), predictor_out1) + assert_almost_equal(out1.asnumpy(), predictor_out1, rtol=1e-5, atol=1e-6) # reshape predictor.reshape({'data':input2.shape}) From c457243727a4d87d212205d5c616ff569bb9aa60 Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Sun, 12 Aug 2018 03:33:48 +0200 Subject: [PATCH 12/15] Empty commit. From 0d3c9a21359415c36931de2e5817b5d64f855f12 Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Sun, 12 Aug 2018 15:38:58 +0200 Subject: [PATCH 13/15] Try again with CI. From a6e20ccc8868b654f27cf4e2bc5d364b9bbfa903 Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Sun, 12 Aug 2018 18:45:06 +0200 Subject: [PATCH 14/15] One more try with CI. From bb444a6f7d93370b758f4cdef19ca867e0e3ed4f Mon Sep 17 00:00:00 2001 From: Huilin Qu Date: Mon, 13 Aug 2018 23:21:52 +0200 Subject: [PATCH 15/15] Retry CI.