Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

Improve hybridblock doc #11236

Merged
merged 2 commits into from
Jun 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 25 additions & 13 deletions docs/tutorials/gluon/hybrid.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ net(x)
Hybrid execution can be activated by simply calling `.hybridize()` on the top
level layer. The first forward call after activation will try to build a
computation graph from `hybrid_forward` and cache it. On subsequent forward
calls the cached graph instead of `hybrid_forward` will be invoked:
calls the cached graph, instead of `hybrid_forward`, will be invoked:

```python
net.hybridize()
Expand All @@ -105,23 +105,35 @@ Hybridize will speed up execution and save memory. If the top level layer is
not a `HybridBlock`, you can still call `.hybridize()` on it and Gluon will try
to hybridize its children layers instead.

`hybridize` also accepts several options for performance tuning. For example, you
can do

```python
net.hybridize(static_alloc=True)
# or
net.hybridize(static_alloc=True, static_shape=True)
```

Please refer to the [API manual](https://mxnet.incubator.apache.org/api/python/gluon/gluon.html?highlight=hybridize#mxnet.gluon.Block.hybridize)
for details.

## Serializing trained model for deployment

Models implemented as `HybridBlock` can be easily serialized for deployment
using other language front-ends like C, C++ and Scala. To this end, we simply
forward the model with symbolic variables instead of NDArrays and save the
output Symbol(s):
Models implemented as `HybridBlock` can be easily serialized. The serialized
model can be loaded back later or used for deployment
with other language front-ends like C, C++ and Scala. To this end, we simply
use `export` and `SymbolBlock.imports`:

```python
x = mx.sym.var('data')
y = net(x)
print(y)
y.save('model.json')
net.save_parameters('model.params')
net.export('model', epoch=1)
```

If your network outputs more than one value, you can use `mx.sym.Group` to
combine them into a grouped Symbol and then save. The saved json and params
files can then be loaded with C, C++ and Scala interface for prediction.
Two files `model-symbol.json` and `model-0001.params` are saved on disk.
You can use other language bindings to load them. You can also load them back
to gluon with `SymbolBlock`:

```python
net2 = gluon.SymbolBlock.imports('model-symbol.json', ['data'], 'model-0001.params')
```

<!-- INSERT SOURCE DOWNLOAD BUTTONS -->
38 changes: 34 additions & 4 deletions python/mxnet/gluon/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ def forward(self, x):


Child :py:class:`Block` assigned this way will be registered and :py:meth:`collect_params`
will collect their Parameters recursively.
will collect their Parameters recursively. You can also manually register
child blocks with :py:meth:`register_child`.

Parameters
----------
Expand Down Expand Up @@ -310,6 +311,8 @@ def _collect_params_with_prefix(self, prefix=''):
def save_parameters(self, filename):
"""Save parameters to file.

Parameters
----------
filename : str
Path to file.
"""
Expand All @@ -336,6 +339,8 @@ def load_parameters(self, filename, ctx=None, allow_missing=False,
ignore_extra=False):
"""Load parameters from file.

Parameters
----------
filename : str
Path to parameter file.
ctx : Context or list of Context, default cpu()
Expand Down Expand Up @@ -635,9 +640,31 @@ def _summary_hook(block, _, outputs):
class HybridBlock(Block):
"""`HybridBlock` supports forwarding with both Symbol and NDArray.

`HybridBlock` is similar to `Block`, with a few differences::

import mxnet as mx
from mxnet.gluon import HybridBlock, nn

class Model(HybridBlock):
def __init__(self, **kwargs):
super(Model, self).__init__(**kwargs)
# use name_scope to give child Blocks appropriate names.
with self.name_scope():
self.dense0 = nn.Dense(20)
self.dense1 = nn.Dense(20)

def hybrid_forward(self, F, x):
x = F.relu(self.dense0(x))
return F.relu(self.dense1(x))

model = Model()
model.initialize(ctx=mx.cpu(0))
model.hybridize()
model(mx.nd.zeros((10, 10), ctx=mx.cpu(0)))

Forward computation in :py:class:`HybridBlock` must be static to work with :py:class:`Symbol` s,
i.e. you cannot call :py:meth:`NDArray.asnumpy`, :py:attr:`NDArray.shape`,
:py:attr:`NDArray.dtype`, etc on tensors.
:py:attr:`NDArray.dtype`, `NDArray` indexing (`x[i]`) etc on tensors.
Also, you cannot use branching or loop logic that bases on non-constant
expressions like random numbers or intermediate results, since they change
the graph structure for each iteration.
Expand All @@ -647,9 +674,12 @@ class HybridBlock(Block):
representing the forward computation and cache it. On subsequent forwards,
the cached graph will be used instead of :py:meth:`hybrid_forward`.

Refer `Hybrid tutorial <http://mxnet.io/tutorials/gluon/hybrid.html>`_ to see
the end-to-end usage.
Please see references for detailed tutorial.

References
----------
`Hybrid - Faster training and easy deployment
<http://mxnet.io/tutorials/gluon/hybrid.html>`_
"""
def __init__(self, prefix=None, params=None):
super(HybridBlock, self).__init__(prefix=prefix, params=params)
Expand Down
14 changes: 11 additions & 3 deletions python/mxnet/gluon/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@ def initialize(self, init=None, ctx=None, default_init=initializer.Uniform(),
def reset_ctx(self, ctx):
"""Re-assign Parameter to other contexts.

Parameters
----------
ctx : Context or list of Context, default ``context.current_context()``.
Assign Parameter to given context. If ctx is a list of Context, a
copy will be made for each context.
Expand Down Expand Up @@ -587,8 +589,8 @@ def __init__(self, **kwargs):
super(Block, self).__init__(**kwargs)
self.const = self.params.get_constant('const', [[1,2],[3,4]])

Parameter
---------
Parameters
----------
name : str
Name of the parameter.
value : array-like
Expand Down Expand Up @@ -739,7 +741,7 @@ def get_constant(self, name, value=None):
found, :py:func:`get` will create a new :py:class:`Constant` with key-word
arguments and insert it to self.

Constants
Parameters
----------
name : str
Name of the desired Constant. It will be prepended with this dictionary's
Expand Down Expand Up @@ -814,6 +816,8 @@ def zero_grad(self):
def reset_ctx(self, ctx):
"""Re-assign all Parameters to other contexts.

Parameters
----------
ctx : Context or list of Context, default :py:meth:`context.current_context()`.
Assign Parameter to given context. If ctx is a list of Context, a
copy will be made for each context.
Expand Down Expand Up @@ -846,6 +850,8 @@ def setattr(self, name, value):
def save(self, filename, strip_prefix=''):
"""Save parameters to file.

Parameters
----------
filename : str
Path to parameter file.
strip_prefix : str, default ''
Expand All @@ -870,6 +876,8 @@ def load(self, filename, ctx=None, allow_missing=False,
ignore_extra=False, restore_prefix=''):
"""Load parameters from file.

Parameters
----------
filename : str
Path to parameter file.
ctx : Context or list of Context
Expand Down