You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by GitBox <gi...@apache.org> on 2018/08/15 23:25:00 UTC

[GitHub] piiswrong closed pull request #11254: add blocklist

piiswrong closed pull request #11254: add blocklist
URL: https://github.com/apache/incubator-mxnet/pull/11254
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/docs/api/python/gluon/gluon.md b/docs/api/python/gluon/gluon.md
index 9bf866d21a1..69c1d3b886d 100644
--- a/docs/api/python/gluon/gluon.md
+++ b/docs/api/python/gluon/gluon.md
@@ -18,7 +18,7 @@ Based on the the [Gluon API specification](https://github.com/gluon-api/gluon-ap
 1. Simple, Easy-to-Understand Code: Gluon offers a full set of plug-and-play neural network building blocks, including predefined layers, optimizers, and initializers.
 2. Flexible, Imperative Structure: Gluon does not require the neural network model to be rigidly defined, but rather brings the training algorithm and model closer together to provide flexibility in the development process.
 3. Dynamic Graphs: Gluon enables developers to define neural network models that are dynamic, meaning they can be built on the fly, with any structure, and using any of Python’s native control flow.
-4. High Performance: Gluon provides all of the above benefits without impacting the training speed that the underlying engine provides. 
+4. High Performance: Gluon provides all of the above benefits without impacting the training speed that the underlying engine provides.
 
 **Examples**
 
@@ -28,8 +28,8 @@ Use plug-and-play neural network building blocks, including predefined layers, o
 
 ```
 net = gluon.nn.Sequential()
-# When instantiated, Sequential stores a chain of neural network layers. 
-# Once presented with data, Sequential executes each layer in turn, using 
+# When instantiated, Sequential stores a chain of neural network layers.
+# Once presented with data, Sequential executes each layer in turn, using
 # the output of one layer as the input for the next
 with net.name_scope():
     net.add(gluon.nn.Dense(256, activation="relu")) # 1st layer (256 nodes)
@@ -76,7 +76,7 @@ with net.name_scope():
     net.add(nn.Dense(256, activation="relu"))
     net.add(nn.Dense(128, activation="relu"))
     net.add(nn.Dense(2))
-    
+
 net.hybridize()
 ```
 
@@ -116,6 +116,8 @@ net.hybridize()
 
     Block
     HybridBlock
+    BlockList
+    HybridBlockList
     SymbolBlock
     nn.Sequential
     nn.HybridSequential
diff --git a/python/mxnet/gluon/block.py b/python/mxnet/gluon/block.py
index 7406a5d6c75..85e7bdf7ad8 100644
--- a/python/mxnet/gluon/block.py
+++ b/python/mxnet/gluon/block.py
@@ -18,13 +18,14 @@
 # coding: utf-8
 # pylint: disable= arguments-differ
 """Base container class for all neural network models."""
-__all__ = ['Block', 'HybridBlock', 'SymbolBlock']
+__all__ = ['Block', 'HybridBlock', 'BlockList', 'HybridBlockList', 'SymbolBlock']
 
 import threading
 import copy
 import warnings
 import re
-from collections import OrderedDict
+from collections import OrderedDict, Iterable
+
 
 from .. import symbol, ndarray, initializer
 from ..symbol import Symbol
@@ -180,7 +181,7 @@ def __repr__(self):
         s = '{name}(\n{modstr}\n)'
         modstr = '\n'.join(['  ({key}): {block}'.format(key=key,
                                                         block=_indent(block.__repr__(), 2))
-                            for key, block in self.__dict__.items() if isinstance(block, Block)])
+                            for key, block in self._children.items()])
         return s.format(name=self.__class__.__name__, modstr=modstr)
 
     def __setattr__(self, name, value):
@@ -409,7 +410,7 @@ def apply(self, fn):
         Parameters
         ----------
         fn : callable
-            Function to be applied to each submodule, of form `fn(block)`.
+            Function to be applied to each sub-block, of form `fn(block)`.
 
         Returns
         -------
@@ -841,6 +842,140 @@ def hybrid_forward(self, F, x, *args, **kwargs):
         # pylint: disable= invalid-name
         raise NotImplementedError
 
+
+class BlockList(Block):
+    r"""Holds sub-blocks in a list.
+
+    BlockList can be indexed like a regular Python list, but blocks it
+    contains are properly registered, and will be visible by all Block methods::
+
+        class MyBlock(gluon.Block):
+            def __init__(self):
+                super(MyBlock, self).__init__()
+                self.blocks = gluon.BlockList([nn.Dense(10) for i in range(10)])
+
+            def forward(self, x):
+                # BlockList can act as an iterable, or be indexed using ints
+                for i, l in enumerate(self.blocks):
+                    x = self.blocks[i // 2](x) + l(x)
+                return x
+
+    Parameters
+    ----------
+    blocks : iterable, optional
+        an iterable of blocks to add
+    """
+    def __init__(self, blocks=None, **kwargs):
+        super(BlockList, self).__init__(**kwargs)
+        if blocks is not None:
+            self += blocks
+
+    def __getitem__(self, idx):
+        if isinstance(idx, slice):
+            return BlockList(list(self._children.values())[idx])
+        else:
+            return self._children.values()[idx]
+
+    def __setitem__(self, idx, block):
+        self.register_child(block, str(idx))
+
+    def __len__(self):
+        return len(self._children)
+
+    def __iter__(self):
+        return iter(self._children.values())
+
+    def __iadd__(self, blocks):
+        return self.extend(blocks)
+
+    def append(self, block):
+        r"""Appends a given block to the end of the list.
+        Arguments:
+            block (nn.Module): block to append
+        """
+        self.register_child(block, str(len(self)))
+        return self
+
+    def extend(self, blocks):
+        r"""Appends blocks from a Python iterable to the end of the list.
+        Arguments:
+            blocks (iterable): iterable of blocks to append
+        """
+        if not isinstance(blocks, Iterable):
+            raise TypeError("BlockList.extend should be called with an "
+                            "iterable, but got " + type(blocks).__name__)
+        offset = len(self)
+        for i, block in enumerate(blocks):
+            self.register_child(block, str(offset + i))
+        return self
+
+
+class HybridBlockList(HybridBlock):
+    r"""Holds hybrid sub-blocks in a list.
+
+    HybridBlockList can be indexed like a regular Python list, but blocks it
+    contains are properly registered, and will be visible by all Block methods::
+
+        class MyBlock(gluon.HybridBlock):
+            def __init__(self):
+                super(MyBlock, self).__init__()
+                self.blocks = gluon.HybridBlockList([nn.Dense(10) for i in range(10)])
+
+            def hybrid_forward(self, F, x):
+                # HybridBlockList can act as an iterable, or be indexed using ints
+                for i, l in enumerate(self.blocks):
+                    x = self.blocks[i // 2](x) + l(x)
+                return x
+
+    Parameters
+    ----------
+    blocks : iterable, optional
+        an iterable of hybrid blocks to add
+    """
+    def __init__(self, blocks=None, **kwargs):
+        super(HybridBlockList, self).__init__(**kwargs)
+        if blocks is not None:
+            self += blocks
+
+    def __getitem__(self, idx):
+        if isinstance(idx, slice):
+            return HybridBlockList(list(self._children.values())[idx])
+        else:
+            return self._children.values()[idx]
+
+    def __setitem__(self, idx, block):
+        self.register_child(block, str(idx))
+
+    def __len__(self):
+        return len(self._children)
+
+    def __iter__(self):
+        return iter(self._children.values())
+
+    def __iadd__(self, blocks):
+        return self.extend(blocks)
+
+    def append(self, block):
+        r"""Appends a given block to the end of the list.
+        Arguments:
+            block (nn.Module): block to append
+        """
+        self.register_child(block, str(len(self)))
+        return self
+
+    def extend(self, blocks):
+        r"""Appends blocks from a Python iterable to the end of the list.
+        Arguments:
+            blocks (iterable): iterable of blocks to append
+        """
+        if not isinstance(blocks, Iterable):
+            raise TypeError("HybridBlockList.extend should be called with an "
+                            "iterable, but got " + type(blocks).__name__)
+        offset = len(self)
+        for i, block in enumerate(blocks):
+            self.register_child(block, str(offset + i))
+        return self
+
 def _common_prefix(names):
     """Get the common prefix for all names"""
     if not names:
diff --git a/tests/python/unittest/test_gluon.py b/tests/python/unittest/test_gluon.py
index ced3063448b..2e43c526391 100644
--- a/tests/python/unittest/test_gluon.py
+++ b/tests/python/unittest/test_gluon.py
@@ -1252,6 +1252,41 @@ def test_summary():
     assert_raises(AssertionError, net.summary, mx.nd.ones((32, 3, 224, 224)))
 
 
+def test_block_list():
+    class MyBlock(gluon.Block):
+        def __init__(self):
+            super(MyBlock, self).__init__()
+            self.blocks = gluon.BlockList([gluon.nn.Dense(10) for i in range(5)])
+            self.blocks.append(gluon.nn.Dense(10))
+            self.blocks.extend([gluon.nn.Dense(10) for i in range(4)])
+
+        def forward(self, x):
+            # BlockList can act as an iterable, or be indexed using ints
+            for i, l in enumerate(self.blocks):
+                x = self.blocks[i // 2](x) + l(x)
+            return x
+
+    block = MyBlock()
+    block.initialize()
+    block(mx.nd.zeros((10, 10))).asnumpy()
+
+    class MyHybridBlock(gluon.HybridBlock):
+        def __init__(self):
+            super(MyHybridBlock, self).__init__()
+            self.blocks = gluon.HybridBlockList([nn.Dense(10) for i in range(10)])
+
+        def hybrid_forward(self, F, x):
+            # HybridBlockList can act as an iterable, or be indexed using ints
+            for i, l in enumerate(self.blocks):
+                x = self.blocks[i // 2](x) + l(x)
+            return x
+
+    block = MyHybridBlock()
+    block.initialize()
+    block(mx.nd.zeros((10, 10))).asnumpy()
+    block.hybridize()
+    block(mx.nd.zeros((10, 10))).asnumpy()
+
 if __name__ == '__main__':
     import nose
     nose.runmodule()


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services