Unverified Commit fdd22cba authored by QuanluZhang's avatar QuanluZhang Committed by GitHub
Browse files

[retiarii] document (#3221)

parent fb26187d
Retiarii API Reference
======================
.. contents::
Inline Mutation APIs
--------------------
.. autoclass:: nni.retiarii.nn.pytorch.LayerChoice
:members:
.. autoclass:: nni.retiarii.nn.pytorch.InputChoice
:members:
Graph Mutation APIs
-------------------
.. autoclass:: nni.retiarii.Mutator
:members:
.. autoclass:: nni.retiarii.Model
:members:
.. autoclass:: nni.retiarii.Graph
:members:
.. autoclass:: nni.retiarii.Node
:members:
.. autoclass:: nni.retiarii.Edge
:members:
.. autoclass:: nni.retiarii.Operation
:members:
Trainers
--------
.. autoclass:: nni.retiarii.trainer.PyTorchImageClassificationTrainer
:members:
.. autoclass:: nni.retiarii.trainer.PyTorchMultiModelTrainer
:members:
Oneshot Trainers
----------------
.. autoclass:: nni.retiarii.trainer.pytorch.DartsTrainer
:members:
.. autoclass:: nni.retiarii.trainer.pytorch.EnasTrainer
:members:
.. autoclass:: nni.retiarii.trainer.pytorch.ProxylessTrainer
:members:
.. autoclass:: nni.retiarii.trainer.pytorch.SinglePathTrainer
:members:
Strategies
----------
.. autoclass:: nni.retiarii.strategies.RandomStrategy
:members:
.. autoclass:: nni.retiarii.strategies.TPEStrategy
:members:
Retiarii Experiments
--------------------
.. autoclass:: nni.retiarii.experiment.RetiariiExperiment
:members:
.. autoclass:: nni.retiarii.experiment.RetiariiExeConfig
:members:
Neural Architecture Search with Retiarii (Experimental)
=======================================================
`Retiarii <https://www.usenix.org/system/files/osdi20-zhang_quanlu.pdf>`__ is a new framework to support neural architecture search and hyper-parameter tuning. It allows users to express various search space with high flexibility, to reuse many SOTA search algorithms, and to leverage system level optimizations to speed up the search process. This framework provides the following new user experiences.
* Search space can be expressed directly in user model code. A tuning space can be expressed along defining a model.
* Neural architecture candidates and hyper-parameter candidates are more friendly supported in an experiment.
* The experiment can be launched directly from python code.
*We are working on migrating* `our previous NAS framework <../Overview.rst>`__ *to Retiarii framework. Thus, this feature is still experimental. We recommend users to try the new framework and provide your valuable feedback for us to improve it. The old framework is still supported for now.*
.. contents::
There are mainly two steps to start an experiment for your neural architecture search task. First, define the model space you want to explore. Second, choose a search method to explore your defined model space.
Define your Model Space
-----------------------
Model space is defined by users to express a set of models that users want to explore, and believe good-performing models are included in those models. In this framework, a model space is defined with two parts: a base model and possible mutations on the base model.
Define Base Model
^^^^^^^^^^^^^^^^^
Defining a base model is almost the same as defining a PyTorch (or TensorFlow) model. There are only two small differences.
* Use our wrapped ``nn`` for PyTorch modules instead of ``torch.nn``. Specifically, users can simply replace the code ``import torch.nn as nn`` with ``import nni.retiarii.nn.pytorch as nn``
* Add the decorator ``@blackbox_module`` to some module classes. Below we explain why this decorator is needed and what module classes should be decorated.
**@blackbox_module**: To understand this decorator, we first briefly explain how our framework works: it converts user defined model to a graph representation (called graph IR), each instantiated module is converted to a subgraph. Then user defined mutations are applied to the graph to generate new graphs. Each new graph is then converted back to PyTorch code and executed. ``@blackbox_module`` here means the module will not be converted to a subgraph but is converted to a single graph node. That is, the module will not be unfolded anymore. Users should/can decorate a module class in the following cases:
* When a module class cannot be successfully converted to a subgraph due to some implementation issues. For example, currently our framework does not support adhoc loop, if there is adhoc loop in a module's forward, this class should be decorated as blackbox module. The following ``MyModule`` should be decorated.
.. code-block:: python
@blackbox_module
class MyModule(nn.Module):
def __init__(self):
...
def forward(self, x):
for i in range(10): # <- adhoc loop
...
* The candidate ops in ``LayerChoice`` should be decorated as blackbox module. For example, ``self.op = nn.LayerChoice([Op1(...), Op2(...), Op3(...)])``, where ``Op1``, ``Op2``, ``Op3`` should be decorated.
* When users want to use ``ValueChoice`` in a module's input argument, the module should be decorated as blackbox module. For example, ``self.conv = MyConv(kernel_size=nn.ValueChoice([1, 3, 5]))``, where ``MyConv`` should be decorated.
* If no mutation is targeted on a module, this module *can be* decorated as a blackbox module.
Below is a very simple example of defining a base model, it is almost the same as defining a PyTorch model.
.. code-block:: python
import torch.nn.functional as F
import nni.retiarii.nn.pytorch as nn
class MyModule(nn.Module):
def __init__(self):
super().__init__()
self.conv = nn.Conv2d(32, 1, 5)
self.pool = nn.MaxPool2d(kernel_size=2)
def forward(self, x):
return self.pool(self.conv(x))
class Model(nn.Module):
def __init__(self):
super().__init__()
self.mymodule = MyModule()
def forward(self, x):
return F.relu(self.mymodule(x))
Users can refer to :githublink:`Darts base model <test/retiarii_test/darts/darts_model.py>` and :githublink:`Mnasnet base model <test/retiarii_test/mnasnet/base_mnasnet.py>` for more complicated examples.
Define Model Mutations
^^^^^^^^^^^^^^^^^^^^^^
A base model is only one concrete model not a model space. To define model space, we provide APIs and primitives for users to express how the base model can be mutated.
**Express mutations in an inlined manner**
For easy usability and also backward compatibility, we provide some APIs for users to easily express possible mutations during defining a base model. The APIs can be used just like PyTorch module.
* ``nn.LayerChoice``. It allows users to put several candidate operations (e.g., PyTorch modules), one is chosen in each explored model. *Note that the candidates should be decorated as blackbox module.*
.. code-block:: python
# import nni.retiarii.nn.pytorch as nn
# declared in `__init__`
self.layer = nn.LayerChoice([
ops.PoolBN('max', channels, 3, stride, 1),
ops.SepConv(channels, channels, 3, stride, 1),
nn.Identity()
]))
# invoked in `forward` function
out = self.layer(x)
* ``nn.InputChoice``. It is mainly for choosing (or trying) different connections. It takes several tensors and chooses ``n_chosen`` tensors from them.
.. code-block:: python
# import nni.retiarii.nn.pytorch as nn
# declared in `__init__`
self.input_switch = nn.InputChoice(n_chosen=1)
# invoked in `forward` function, choose one from the three
out = self.input_switch([tensor1, tensor2, tensor3])
* ``nn.ValueChoice``. It is for choosing one value from some candidate values. It can only be used as input argument of blackbox modules and the wrapped ``nn`` modules. *Note that it has not been officially supported.*
.. code-block:: python
# import nni.retiarii.nn.pytorch as nn
# used in `__init__`
self.conv = nn.Conv2d(XX, XX, kernel_size=nn.ValueChoice([1, 3, 5])
self.op = MyOp(nn.ValueChoice([0, 1], nn.ValueChoice([-1, 1]))
Detailed API description and usage can be found `here <./ApiReference.rst>`__\. Example of using these APIs can be found in :githublink:`Darts base model <test/retiarii_test/darts/darts_model.py>`.
**Express mutations with mutators**
Though easy-to-use, inline mutations have limited expressiveness, as it has to be embedded in model definition. To greatly improve expressiveness and flexibility, we provide primitives for users to write *Mutator* to flexibly express how they want to mutate base model. Mutator stands above base model, thus has full ability to edit the model.
Users can instantiate several mutators as below, the mutators will be sequentially applied to the base model one after another to generate a new model during experiment running.
.. code-block:: python
applied_mutators = []
applied_mutators.append(BlockMutator('mutable_0'))
applied_mutators.append(BlockMutator('mutable_1'))
``BlockMutator`` is defined by users to express how to mutate the base model. User defined mutator should inherit ``Mutator`` class, and implement mutation logic in the member function ``mutate``.
.. code-block:: python
from nni.retiarii import Mutator
class BlockMutator(Mutator):
def __init__(self, target: str, candidates: List):
super(BlockMutator, self).__init__()
self.target = target
self.candidate_op_list = candidates
def mutate(self, model):
nodes = model.get_nodes_by_label(self.target)
for node in nodes:
chosen_op = self.choice(self.candidate_op_list)
node.update_operation(chosen_op.type, chosen_op.params)
The input of ``mutate`` is graph IR of the base model (please refer to `here <./ApiReference.rst>`__ for the format and APIs of the IR), users can mutate the graph with its member functions (e.g., ``get_nodes_by_label``, ``update_operation``). The mutation operations can be combined with the API ``self.choice``, in order to express a set of possible mutations. In the above example, the node's operation can be changed to any operation from ``candidate_op_list``.
For mutator to easily target on a node (i.e., PyTorch module), we provide a placeholder module called ``nn.Placeholder``. If you want to mutate a module, you can define this module with ``nn.Placeholder``, and use mutator to mutate this placeholder to give it a real operation.
.. code-block:: python
ph = nn.Placeholder(label='mutable_0',
related_info={
'kernel_size_options': [1, 3, 5],
'n_layer_options': [1, 2, 3, 4],
'exp_ratio': exp_ratio,
'stride': stride
}
)
``label`` is used by mutator to identify this placeholder, ``related_info`` is the information that are required by mutator. As ``related_info`` is a dict, it could include any information that users want to put to pass it to user defined mutator. The complete example code can be found in :githublink:`Mnasnet base model <test/retiarii_test/mnasnet/base_mnasnet.py>`.
Explore the Defined Model Space
-------------------------------
After model space is defined, it is time to explore this model space efficiently. Users can choose proper search and training approach to explore the model space.
Create a Trainer and Exploration Strategy
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Classic search approach:**
In this approach, trainer is for training each explored model, while strategy is for sampling the models. Both trainer and strategy are required to explore the model space.
**Oneshot (Weight-sharing) search approach:**
In this approach, users only need a oneshot trainer, because this trainer takes charge of both search and training.
In the following table, we listed the available trainers and strategies.
.. list-table::
:header-rows: 1
:widths: auto
* - Trainer
- Strategy
- Oneshot Trainer
* - PyTorchImageClassificationTrainer
- TPEStrategy
- DartsTrainer
* - PyTorchMultiModelTrainer
- RandomStrategy
- EnasTrainer
* -
-
- ProxylessTrainer
* -
-
- SinglePathTrainer (RandomTrainer)
There usage and API document can be found `here <./ApiReference>`__\.
Here is a simple example of using trainer and strategy.
.. code-block:: python
trainer = PyTorchImageClassificationTrainer(base_model,
dataset_cls="MNIST",
dataset_kwargs={"root": "data/mnist", "download": True},
dataloader_kwargs={"batch_size": 32},
optimizer_kwargs={"lr": 1e-3},
trainer_kwargs={"max_epochs": 1})
simple_startegy = RandomStrategy()
Users can refer to `this document <./WriteTrainer.rst>`__ for how to write a new trainer, and refer to `this document <./WriteStrategy.rst>`__ for how to write a new strategy.
Set up an Experiment
^^^^^^^^^^^^^^^^^^^^
After all the above are prepared, it is time to start an experiment to do the model search. We design unified interface for users to start their experiment. An example is shown below
.. code-block:: python
exp = RetiariiExperiment(base_model, trainer, applied_mutators, simple_startegy)
exp_config = RetiariiExeConfig('local')
exp_config.experiment_name = 'mnasnet_search'
exp_config.trial_concurrency = 2
exp_config.max_trial_number = 10
exp_config.training_service.use_active_gpu = False
exp.run(exp_config, 8081)
This code starts an NNI experiment. Note that if inlined mutation is used, ``applied_mutators`` should be ``None``.
The complete code of a simple MNIST example can be found :githublink:`here <test/retiarii_test/mnist/test.py>`.
Visualize your experiment
^^^^^^^^^^^^^^^^^^^^^^^^^
Users can visualize their experiment in the same way as visualizing a normal hyper-parameter tuning experiment, please refer to `here <../../Tutorial/WebUI.rst>`__ for details. If users are using oneshot trainer, they can refer to `here <../Visualization.rst>`__ for how to visualize their experiments.
FAQ
---
TBD
Customize A New Strategy
========================
TBD
\ No newline at end of file
Customize A New Trainer
=======================
TBD
\ No newline at end of file
#################
Retiarii Overview
#################
`Retiarii <https://www.usenix.org/system/files/osdi20-zhang_quanlu.pdf>`__ is a new framework to support neural architecture search and hyper-parameter tuning. It allows users to express various search space with high flexibility, to reuse many SOTA search algorithms, and to leverage system level optimizations to speed up the search process. This framework provides the following new user experiences.
.. toctree::
:maxdepth: 2
Quick Start <Tutorial>
Customize a New Trainer <WriteTrainer>
Customize a New Strategy <WriteStrategy>
Retiarii APIs <ApiReference>
\ No newline at end of file
...@@ -21,6 +21,7 @@ For details, please refer to the following tutorials: ...@@ -21,6 +21,7 @@ For details, please refer to the following tutorials:
Write A Search Space <NAS/WriteSearchSpace> Write A Search Space <NAS/WriteSearchSpace>
Classic NAS <NAS/ClassicNas> Classic NAS <NAS/ClassicNas>
One-shot NAS <NAS/one_shot_nas> One-shot NAS <NAS/one_shot_nas>
Retiarii NAS (experimental) <NAS/retiarii/retiarii_index>
Customize a NAS Algorithm <NAS/Advanced> Customize a NAS Algorithm <NAS/Advanced>
NAS Visualization <NAS/Visualization> NAS Visualization <NAS/Visualization>
Search Space Zoo <NAS/SearchSpaceZoo> Search Space Zoo <NAS/SearchSpaceZoo>
......
...@@ -132,7 +132,7 @@ class DartsDiscreteMutator(Mutator): ...@@ -132,7 +132,7 @@ class DartsDiscreteMutator(Mutator):
---------- ----------
model : nn.Module model : nn.Module
The model to apply the mutator. The model to apply the mutator.
parent_mutator : Mutator parent_mutator : nni.nas.pytorch.mutator.Mutator
The mutator that provides ``sample_final`` method, that will be called to get the architecture. The mutator that provides ``sample_final`` method, that will be called to get the architecture.
""" """
def __init__(self, model, parent_mutator): def __init__(self, model, parent_mutator):
......
...@@ -20,7 +20,7 @@ class SPOSSupernetTrainer(Trainer): ...@@ -20,7 +20,7 @@ class SPOSSupernetTrainer(Trainer):
---------- ----------
model : nn.Module model : nn.Module
Model with mutables. Model with mutables.
mutator : Mutator mutator : nni.nas.pytorch.mutator.Mutator
A mutator object that has been initialized with the model. A mutator object that has been initialized with the model.
loss : callable loss : callable
Called with logits and targets. Returns a loss tensor. Called with logits and targets. Returns a loss tensor.
......
...@@ -110,7 +110,7 @@ class BaseMutator(nn.Module): ...@@ -110,7 +110,7 @@ class BaseMutator(nn.Module):
Parameters Parameters
---------- ----------
mutable : LayerChoice mutable : nni.nas.pytorch.mutables.LayerChoice
Module whose forward is called. Module whose forward is called.
args : list of torch.Tensor args : list of torch.Tensor
The arguments of its forward function. The arguments of its forward function.
...@@ -130,7 +130,7 @@ class BaseMutator(nn.Module): ...@@ -130,7 +130,7 @@ class BaseMutator(nn.Module):
Parameters Parameters
---------- ----------
mutable : InputChoice mutable : nni.nas.pytorch.mutables.InputChoice
Mutable that is called. Mutable that is called.
tensor_list : list of torch.Tensor tensor_list : list of torch.Tensor
The arguments mutable is called with. The arguments mutable is called with.
......
...@@ -147,7 +147,7 @@ class Mutator(BaseMutator): ...@@ -147,7 +147,7 @@ class Mutator(BaseMutator):
Parameters Parameters
---------- ----------
mutable : LayerChoice mutable : nni.nas.pytorch.mutables.LayerChoice
Layer choice module. Layer choice module.
args : list of torch.Tensor args : list of torch.Tensor
Inputs Inputs
...@@ -180,7 +180,7 @@ class Mutator(BaseMutator): ...@@ -180,7 +180,7 @@ class Mutator(BaseMutator):
Parameters Parameters
---------- ----------
mutable : InputChoice mutable : nni.nas.pytorch.mutables.InputChoice
Input choice module. Input choice module.
tensor_list : list of torch.Tensor tensor_list : list of torch.Tensor
Tensor list to apply the decision on. Tensor list to apply the decision on.
......
...@@ -28,8 +28,10 @@ class Mutator: ...@@ -28,8 +28,10 @@ class Mutator:
""" """
Mutates graphs in model to generate new model. Mutates graphs in model to generate new model.
`Mutator` class will be used in two places: `Mutator` class will be used in two places:
1. Inherit `Mutator` to implement graph mutation logic. 1. Inherit `Mutator` to implement graph mutation logic.
2. Use `Mutator` subclass to implement NAS strategy. 2. Use `Mutator` subclass to implement NAS strategy.
In scenario 1, the subclass should implement `Mutator.mutate()` interface with `Mutator.choice()`. In scenario 1, the subclass should implement `Mutator.mutate()` interface with `Mutator.choice()`.
In scenario 2, strategy should use constructor or `Mutator.bind_sampler()` to initialize subclass, In scenario 2, strategy should use constructor or `Mutator.bind_sampler()` to initialize subclass,
and then use `Mutator.apply()` to mutate model. and then use `Mutator.apply()` to mutate model.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment