You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by zh...@apache.org on 2019/06/02 18:00:21 UTC

[incubator-mxnet] branch master updated: [BUGFIX] fix unknown parameter shapes when np_shape is turned on. (#15097)

This is an automated email from the ASF dual-hosted git repository.

zhasheng 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 360f8d0  [BUGFIX] fix unknown parameter shapes when np_shape is turned on. (#15097)
360f8d0 is described below

commit 360f8d003dc58762922095f0e53100fd2d429a60
Author: Da Zheng <zh...@gmail.com>
AuthorDate: Sun Jun 2 10:59:58 2019 -0700

    [BUGFIX] fix unknown parameter shapes when np_shape is turned on. (#15097)
    
    * fix.
    
    * add test.
    
    * fix test.
    
    * check unknown shape correctly.
    
    * fix test.
    
    * fix.
    
    * fix.
    
    * add more comments.
    
    * add doc.
---
 python/mxnet/gluon/parameter.py     | 22 ++++++++++++++++++----
 python/mxnet/gluon/utils.py         | 18 ++++++++++++++++++
 python/mxnet/util.py                |  4 ++++
 tests/python/unittest/test_gluon.py | 16 ++++++++++++++++
 4 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/python/mxnet/gluon/parameter.py b/python/mxnet/gluon/parameter.py
index 086dbc0..83edbaf 100644
--- a/python/mxnet/gluon/parameter.py
+++ b/python/mxnet/gluon/parameter.py
@@ -30,7 +30,8 @@ from ..base import mx_real_t, MXNetError
 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
 tensor_types = (symbol.Symbol, ndarray.NDArray)
@@ -156,7 +157,20 @@ class Parameter(object):
 
     @property
     def shape(self):
-        return self._shape
+        """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():
+            # 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
 
     @shape.setter
     def shape(self, new_shape):
@@ -269,7 +283,7 @@ class Parameter(object):
             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."%(
@@ -380,7 +394,7 @@ class Parameter(object):
             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/gluon/utils.py b/python/mxnet/gluon/utils.py
index 8615422..3957b74 100644
--- a/python/mxnet/gluon/utils.py
+++ b/python/mxnet/gluon/utils.py
@@ -38,6 +38,7 @@ except ImportError:
 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,20 @@ class HookHandle(object):
 
     def __exit__(self, ptype, value, trace):
         self.detach()
+
+def shape_is_known(shape):
+    """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
+    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 29f5b78..5bc1dc8 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
diff --git a/tests/python/unittest/test_gluon.py b/tests/python/unittest/test_gluon.py
index efa04f4..08e4c52 100644
--- a/tests/python/unittest/test_gluon.py
+++ b/tests/python/unittest/test_gluon.py
@@ -2726,6 +2726,22 @@ def test_slice_activation_reshape_activation():
             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)
+
+    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
     nose.runmodule()