From a5b9104da44a1505e3e17625dc0d97c28957d0c5 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 29 May 2019 21:22:16 +0000 Subject: [PATCH 1/9] add test. --- tests/python/unittest/test_gluon.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/python/unittest/test_gluon.py b/tests/python/unittest/test_gluon.py index efa04f4fa47a..616d1fde05be 100644 --- a/tests/python/unittest/test_gluon.py +++ b/tests/python/unittest/test_gluon.py @@ -2726,6 +2726,23 @@ def hybrid_forward(self, F, x): net = Net(act0, act1, shape, slice) check_layer_forward_withinput(net, x) +@with_seed() +def test_np_shape_parameters(): + class Foo(gluon.Block): + def __init__(self, **kwargs): + super(Foo, self).__init__(**kwargs) + self.dense = gluon.nn.Dense(16) + def forward(self, x): + return self.dense(x) + + mx.set_np_compat(True) + z = mx.nd.zeros((2,2016)) + print(z.shape) + foo = Foo() + foo.initialize() + print(foo(z).shape) + mx.set_np_compat(False) + if __name__ == '__main__': import nose nose.runmodule() From c5898e2e769801998a2ffe47d7ed886ec82907b0 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 29 May 2019 21:16:51 +0000 Subject: [PATCH 2/9] fix. --- python/mxnet/gluon/parameter.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/python/mxnet/gluon/parameter.py b/python/mxnet/gluon/parameter.py index 086dbc07a043..b6b58a24f8fe 100644 --- a/python/mxnet/gluon/parameter.py +++ b/python/mxnet/gluon/parameter.py @@ -31,6 +31,7 @@ from ..context import Context, cpu from .. import autograd from .utils import _indent, _brief_print_list +from .. import is_np_shape # pylint: disable= invalid-name tensor_types = (symbol.Symbol, ndarray.NDArray) @@ -156,7 +157,10 @@ def grad_req(self, req): @property def shape(self): - return self._shape + if is_np_shape(): + return tuple(i if i != 0 else -1 for i in self._shape) + else: + return self._shape @shape.setter def shape(self, new_shape): From e62857bacaab81bd121e1f32344d0b85b42892a5 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 29 May 2019 21:35:58 +0000 Subject: [PATCH 3/9] fix test. --- tests/python/unittest/test_gluon.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/python/unittest/test_gluon.py b/tests/python/unittest/test_gluon.py index 616d1fde05be..3a50aaf658f1 100644 --- a/tests/python/unittest/test_gluon.py +++ b/tests/python/unittest/test_gluon.py @@ -2729,19 +2729,19 @@ def hybrid_forward(self, F, x): @with_seed() def test_np_shape_parameters(): class Foo(gluon.Block): - def __init__(self, **kwargs): - super(Foo, self).__init__(**kwargs) - self.dense = gluon.nn.Dense(16) - def forward(self, x): - return self.dense(x) + def __init__(self, **kwargs): + super(Foo, self).__init__(**kwargs) + self.dense = gluon.nn.Dense(16) + def forward(self, x): + return self.dense(x) - mx.set_np_compat(True) + mx.set_np_shape(True) z = mx.nd.zeros((2,2016)) print(z.shape) foo = Foo() foo.initialize() print(foo(z).shape) - mx.set_np_compat(False) + mx.set_np_shape(False) if __name__ == '__main__': import nose From e7fe925e87f9dc54c4083ca34a03919db1eb230b Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 29 May 2019 22:47:28 +0000 Subject: [PATCH 4/9] check unknown shape correctly. --- python/mxnet/gluon/parameter.py | 6 +++--- python/mxnet/util.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/python/mxnet/gluon/parameter.py b/python/mxnet/gluon/parameter.py index b6b58a24f8fe..9d6f8a3bb88b 100644 --- a/python/mxnet/gluon/parameter.py +++ b/python/mxnet/gluon/parameter.py @@ -30,7 +30,7 @@ from .. import symbol, ndarray, initializer, context from ..context import Context, cpu from .. import autograd -from .utils import _indent, _brief_print_list +from .utils import _indent, _brief_print_list, shape_is_known from .. import is_np_shape # pylint: disable= invalid-name @@ -273,7 +273,7 @@ def _finish_deferred_init(self): return init, ctx, default_init, data = self._deferred_init self._deferred_init = () - assert self.shape is not None and np.prod(self.shape) > 0, \ + assert shape_is_known(self.shape), \ "Cannot initialize Parameter '%s' because it has " \ "invalid shape: %s. Please specify in_units, " \ "in_channels, etc for `Block`s."%( @@ -384,7 +384,7 @@ def initialize(self, init=None, ctx=None, default_init=initializer.Uniform(), ctx = [ctx] if init is None: init = default_init if self.init is None else self.init - if not self.shape or np.prod(self.shape) <= 0: + if not shape_is_known(self.shape): if self._allow_deferred_init: self._deferred_init = (init, ctx, default_init, None) return diff --git a/python/mxnet/util.py b/python/mxnet/util.py index 29f5b78e454e..97442b126d6a 100644 --- a/python/mxnet/util.py +++ b/python/mxnet/util.py @@ -245,3 +245,17 @@ def _with_np_shape(*args, **kwargs): return func(*args, **kwargs) return _with_np_shape + +def shape_is_known(shape): + """Check whether a shape is completely known w/ or w/o np semantics.""" + if shape is None: + return False + unknown_dim_size = -1 if is_np_shape() else 0 + if len(shape) == 0: + return unknown_dim_size == -1 + for dim_size in shape: + if dim_size == unknown_dim_size: + return False + assert dim_size > unknown_dim_size, "shape dimension size cannot be less than {}, while " \ + "received {}".format(unknown_dim_size, dim_size) + return True From caafac9814b5aa4fd969f6cc0e6912fb278a21bd Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 29 May 2019 22:48:58 +0000 Subject: [PATCH 5/9] fix test. --- tests/python/unittest/test_gluon.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/python/unittest/test_gluon.py b/tests/python/unittest/test_gluon.py index 3a50aaf658f1..08e4c52a5826 100644 --- a/tests/python/unittest/test_gluon.py +++ b/tests/python/unittest/test_gluon.py @@ -2735,13 +2735,12 @@ def __init__(self, **kwargs): def forward(self, x): return self.dense(x) - mx.set_np_shape(True) - z = mx.nd.zeros((2,2016)) - print(z.shape) - foo = Foo() - foo.initialize() - print(foo(z).shape) - mx.set_np_shape(False) + with mx.np_shape(True): + z = mx.nd.zeros((2,2016)) + print(z.shape) + foo = Foo() + foo.initialize() + print(foo(z).shape) if __name__ == '__main__': import nose From 97b367e0e2e17fddddce48472bb9d75389629a0a Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 29 May 2019 22:49:50 +0000 Subject: [PATCH 6/9] fix. --- python/mxnet/gluon/parameter.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/mxnet/gluon/parameter.py b/python/mxnet/gluon/parameter.py index 9d6f8a3bb88b..b33bfc8a435c 100644 --- a/python/mxnet/gluon/parameter.py +++ b/python/mxnet/gluon/parameter.py @@ -157,7 +157,9 @@ def grad_req(self, req): @property def shape(self): - if is_np_shape(): + if self._shape is None: + return None + elif is_np_shape(): return tuple(i if i != 0 else -1 for i in self._shape) else: return self._shape From 34ebc2d3170518cf73e7f6e0bd8bc33181891647 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 29 May 2019 23:31:11 +0000 Subject: [PATCH 7/9] fix. --- python/mxnet/gluon/utils.py | 15 +++++++++++++++ python/mxnet/util.py | 14 -------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/python/mxnet/gluon/utils.py b/python/mxnet/gluon/utils.py index 861542220927..83a5674c45bd 100644 --- a/python/mxnet/gluon/utils.py +++ b/python/mxnet/gluon/utils.py @@ -38,6 +38,7 @@ class requests_failed_to_import(object): import numpy as np from .. import ndarray +from ..util import is_np_shape def split_data(data, num_slice, batch_axis=0, even_split=True): """Splits an NDArray into `num_slice` slices along `batch_axis`. @@ -412,3 +413,17 @@ def __enter__(self): def __exit__(self, ptype, value, trace): self.detach() + +def shape_is_known(shape): + """Check whether a shape is completely known w/ or w/o np semantics.""" + if shape is None: + return False + unknown_dim_size = -1 if is_np_shape() else 0 + if len(shape) == 0: + return unknown_dim_size == -1 + for dim_size in shape: + if dim_size == unknown_dim_size: + return False + assert dim_size > unknown_dim_size, "shape dimension size cannot be less than {}, while " \ + "received {}".format(unknown_dim_size, dim_size) + return True diff --git a/python/mxnet/util.py b/python/mxnet/util.py index 97442b126d6a..29f5b78e454e 100644 --- a/python/mxnet/util.py +++ b/python/mxnet/util.py @@ -245,17 +245,3 @@ def _with_np_shape(*args, **kwargs): return func(*args, **kwargs) return _with_np_shape - -def shape_is_known(shape): - """Check whether a shape is completely known w/ or w/o np semantics.""" - if shape is None: - return False - unknown_dim_size = -1 if is_np_shape() else 0 - if len(shape) == 0: - return unknown_dim_size == -1 - for dim_size in shape: - if dim_size == unknown_dim_size: - return False - assert dim_size > unknown_dim_size, "shape dimension size cannot be less than {}, while " \ - "received {}".format(unknown_dim_size, dim_size) - return True From fab40d40e11c6d83c2c14c19d695e41940b9d25f Mon Sep 17 00:00:00 2001 From: Da Zheng Date: Sat, 1 Jun 2019 23:07:02 -0700 Subject: [PATCH 8/9] add more comments. --- python/mxnet/gluon/parameter.py | 3 +++ python/mxnet/gluon/utils.py | 5 ++++- python/mxnet/util.py | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/python/mxnet/gluon/parameter.py b/python/mxnet/gluon/parameter.py index b33bfc8a435c..04150d9a6fd2 100644 --- a/python/mxnet/gluon/parameter.py +++ b/python/mxnet/gluon/parameter.py @@ -160,6 +160,9 @@ def shape(self): if self._shape is None: return None elif is_np_shape(): + # Parameters shouldn't be zero-size. If one of its dimension is 0, + # it means the parameter isn't initialized. In the NumPy semantics, + # the unknown dimension should be marked with -1. return tuple(i if i != 0 else -1 for i in self._shape) else: return self._shape diff --git a/python/mxnet/gluon/utils.py b/python/mxnet/gluon/utils.py index 83a5674c45bd..3957b7402688 100644 --- a/python/mxnet/gluon/utils.py +++ b/python/mxnet/gluon/utils.py @@ -415,7 +415,10 @@ def __exit__(self, ptype, value, trace): self.detach() def shape_is_known(shape): - """Check whether a shape is completely known w/ or w/o np semantics.""" + """Check whether a shape is completely known with or without np semantics. + + Please see the doc of is_np_shape for more details. + """ if shape is None: return False unknown_dim_size = -1 if is_np_shape() else 0 diff --git a/python/mxnet/util.py b/python/mxnet/util.py index 29f5b78e454e..5bc1dc809c88 100644 --- a/python/mxnet/util.py +++ b/python/mxnet/util.py @@ -89,6 +89,10 @@ def is_np_shape(): the shapes of zero-size tensors. This is turned off by default for keeping backward compatibility. + In the NumPy shape semantics, `-1` indicates an unknown size. For example, + `(-1, 2, 2)` means that the size of the first dimension is unknown. Its size + may be inferred during shape inference. + Please note that this is designed as an infrastructure for the incoming MXNet-NumPy operators. Legacy operators registered in the modules `mx.nd` and `mx.sym` are not guaranteed to behave like their counterparts From 83a7e7c4859531e8e6b78600c01fbc0e603bf4b1 Mon Sep 17 00:00:00 2001 From: Da Zheng Date: Sat, 1 Jun 2019 23:29:20 -0700 Subject: [PATCH 9/9] add doc. --- python/mxnet/gluon/parameter.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/mxnet/gluon/parameter.py b/python/mxnet/gluon/parameter.py index 04150d9a6fd2..83edbaf210a7 100644 --- a/python/mxnet/gluon/parameter.py +++ b/python/mxnet/gluon/parameter.py @@ -157,6 +157,11 @@ def grad_req(self, req): @property def shape(self): + """The shape of the parameter. + + By default, an unknown dimension size is 0. However, when the NumPy semantic + is turned on, unknown dimension size is -1. + """ if self._shape is None: return None elif is_np_shape():