Unverified Commit 403195f0 authored by Yuge Zhang's avatar Yuge Zhang Committed by GitHub
Browse files

Merge branch 'master' into nn-meter

parents 99aa8226 a7278d2d
...@@ -21,6 +21,5 @@ NNI 提供了多种非结构化和结构化剪枝算法。 ...@@ -21,6 +21,5 @@ NNI 提供了多种非结构化和结构化剪枝算法。
:maxdepth: 2 :maxdepth: 2
Pruners <Pruner> Pruners <Pruner>
Dependency Aware Mode <DependencyAware> 依赖感知模式 <DependencyAware>
Model Speedup <ModelSpeedup> 模型加速 <ModelSpeedup>
Automatic Model Pruning with NNI Tuners <AutoPruningUsingTuners>
...@@ -8,10 +8,11 @@ ...@@ -8,10 +8,11 @@
可以使用8位整数表示, 更低的比特位数,例如4/2/1比特, 可以使用8位整数表示, 更低的比特位数,例如4/2/1比特,
是否能够表示权重也是目前非常活跃的研究方向。 是否能够表示权重也是目前非常活跃的研究方向。
一个 Quantizer 是指一种 NNI 实现的量化算法,NNI 提供了多个 Quantizer,如下所示。 你也可以 一个 Quantizer 是指一种 NNI 实现的量化算法,NNI 提供了多个 Quantizer,如下所示。你也可以
使用 NNI 模型压缩的接口来创造你的 Quantizer。 使用 NNI 模型压缩的接口来创造你的 Quantizer。
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
Quantizers <Quantizer> Quantizers <Quantizer>
量化加速 <QuantizationSpeedup>
自定义 NAS 算法
=========================
扩展 One-Shot Trainer
---------------------------------------
如果要在真实任务上使用 Trainer,还需要更多操作,如分布式训练,低精度训练,周期日志,写入 TensorBoard,保存检查点等等。 如前所述,一些 Trainer 支持了上述某些功能。 有两种方法可往已有的 Trainer 中加入功能:继承已有 Trainer 并重载,或复制已有 Trainer 并修改。
无论哪种方法,都需要实现新的 Trainer 基本上,除了新的 Mutator 的概念,实现 One-Shot Trainer 与普通的深度学习 Trainer 相同。 因此,有两处会有所不同:
* 初始化
.. code-block:: python
model = Model()
mutator = MyMutator(model)
* 训练
.. code-block:: python
for _ in range(epochs):
for x, y in data_loader:
mutator.reset() # reset all the choices in model
out = model(x) # like traditional model
loss = criterion(out, y)
loss.backward()
# no difference below
要展示 Mutator 的用途,需要先了解 One-Shot NAS 的工作原理。 通常 One-Shot NAS 会同时优化模型权重和架构权重。 它会反复的对架构采样,或由超网络中的几种架构组成,然后像普通深度学习模型一样训练,将训练后的参数更新到超网络中,然后用指标或损失作为信号来指导架构的采样。 Mutator,在这里用作架构采样,通常会是另一个深度学习模型。 因此,可将其看作一个通过定义参数,并使用优化器进行优化的任何模型。 Mutator 是由一个模型来初始化的。 一旦 Mutator 绑定到了某个模型,就不能重新绑定到另一个模型上。
``mutator.reset()`` 是关键步骤。 这一步确定了模型最终的所有 Choice 重置的结果会一直有效,直到下一次重置来刷新数据。 重置后,模型可看作是普通的模型来进行前向和反向传播。
最后,Mutator 会提供叫做 ``mutator.export()`` 的方法来将模型的架构参数作为 dict 导出。 注意,当前 dict 是从 Mutable 键值到选择张量的映射。 为了存储到 JSON,用户需要将张量显式的转换为 Python list
同时,NNI 提供了工具,能更容易地实现 Trainer 可以参考 `Trainers <./NasReference.rst>`__
实现新的 Mutator
----------------------
这是为了演示 ``mutator.reset()`` ``mutator.export()`` 的伪代码。
.. code-block:: python
def reset(self):
self.apply_on_model(self.sample_search())
.. code-block:: python
def export(self):
return self.sample_final()
重置时,新架构会通过 ``sample_search()`` 采样,并应用到模型上。 然后,对模型进行一步或多步的搜索。 导出时,新架构会通过 ``sample_final()`` 采样,并且对模型不做任何操作。 可用于检查点或导出最终架构。
``sample_search()`` ``sample_final()`` 返回值的要求一致:从 Mutable 键值到张量的映射。 张量可以是 BoolTensor true 表示选择,false 表示没有),或 FloatTensor 将权重应用于每个候选对象。 选定的分支会被计算出来(对于 ``LayerChoice`` ,模型会被调用;对于 ``InputChoice`` ,只有权重),并通过 Choice 的剪枝操作来剪枝模型。 这是 Mutator 实现的示例,大多数算法只需要关心前面部分。
.. code-block:: python
class RandomMutator(Mutator):
def __init__(self, model):
super().__init__(model) # don't forget to call super
# do something else
def sample_search(self):
result = dict()
for mutable in self.mutables: # this is all the mutable modules in user model
# mutables share the same key will be de-duplicated
if isinstance(mutable, LayerChoice):
# decided that this mutable should choose `gen_index`
gen_index = np.random.randint(mutable.length)
result[mutable.key] = torch.tensor([i == gen_index for i in range(mutable.length)],
dtype=torch.bool)
elif isinstance(mutable, InputChoice):
if mutable.n_chosen is None: # n_chosen is None, then choose any number
result[mutable.key] = torch.randint(high=2, size=(mutable.n_candidates,)).view(-1).bool()
# else do something else
return result
def sample_final(self):
return self.sample_search() # use the same logic here. you can do something different
可以在 :githublink:`这里 <nni/nas/pytorch/mutator.py>` 找到随机 mutator 的完整示例。
对于高级用法,例如,需要在 ``LayerChoice`` 执行的时候操作模型,可继承 ``BaseMutator``,并重载 ``on_forward_layer_choice`` 和 ``on_forward_input_choice`` 。这些是 ``LayerChoice`` 和 ``InputChoice`` 对应的回调实现。 还可使用属性 ``mutables`` 来获得模型中所有的 ``LayerChoice`` 和 ``InputChoice``。 详情请参考 :githublink:`这里 <nni/nas/pytorch/>` 。
.. tip::
用于调试的随机 Mutator。 使用
.. code-block:: python
mutator = RandomMutator(model)
mutator.reset()
将立即在搜索空间中将一个可能的候选者设置为活动候选者。
实现分布式 NAS Tuner
-----------------------------------
在学习编写分布式 NAS Tuner前,应先了解如何写出通用的 Tuner。 请参阅这篇 `Customize Tuner <../Tuner/CustomizeTuner.rst>`__ 。
当调用 `nnictl ss_gen <../Tutorial/Nnictl.rst>`_ 时,会生成下面这样的搜索空间文件:
.. code-block:: json
{
"key_name": {
"_type": "layer_choice",
"_value": ["op1_repr", "op2_repr", "op3_repr"]
},
"key_name": {
"_type": "input_choice",
"_value": {
"candidates": ["in1_key", "in2_key", "in3_key"],
"n_chosen": 1
}
}
}
这是 Tuner 在 ``update_search_space`` 中会收到的搜索空间。 Tuner 需要解析搜索空间,并在 ``generate_parameters`` 中生成新的候选。 有效的 "参数" 格式如下:
.. code-block:: json
{
"key_name": {
"_value": "op1_repr",
"_idx": 0
},
"key_name": {
"_value": ["in2_key"],
"_idex": [1]
}
}
和普通超参优化 Tuner 类似,通过 ``generate_parameters`` 来发送。 请参考 `SPOS <./SPOS.rst>`__ 的代码例子来书写用例。
...@@ -4,7 +4,7 @@ Retiarii API 参考 ...@@ -4,7 +4,7 @@ Retiarii API 参考
.. contents:: .. contents::
内联 Mutation API 内联 Mutation API
---------------------------------------- --------------------
.. autoclass:: nni.retiarii.nn.pytorch.LayerChoice .. autoclass:: nni.retiarii.nn.pytorch.LayerChoice
:members: :members:
...@@ -18,8 +18,14 @@ Retiarii API 参考 ...@@ -18,8 +18,14 @@ Retiarii API 参考
.. autoclass:: nni.retiarii.nn.pytorch.ChosenInputs .. autoclass:: nni.retiarii.nn.pytorch.ChosenInputs
:members: :members:
.. autoclass:: nni.retiarii.nn.pytorch.Repeat
:members:
.. autoclass:: nni.retiarii.nn.pytorch.Cell
:members:
图 Mutation API 图 Mutation API
-------------------------------------- -------------------
.. autoclass:: nni.retiarii.Mutator .. autoclass:: nni.retiarii.Mutator
:members: :members:
...@@ -39,38 +45,38 @@ Retiarii API 参考 ...@@ -39,38 +45,38 @@ Retiarii API 参考
.. autoclass:: nni.retiarii.Operation .. autoclass:: nni.retiarii.Operation
:members: :members:
Trainers Evaluators
-------- ----------
.. autoclass:: nni.retiarii.trainer.FunctionalTrainer .. autoclass:: nni.retiarii.evaluator.FunctionalEvaluator
:members: :members:
.. autoclass:: nni.retiarii.trainer.pytorch.lightning.LightningModule .. autoclass:: nni.retiarii.evaluator.pytorch.lightning.LightningModule
:members: :members:
.. autoclass:: nni.retiarii.trainer.pytorch.lightning.Classification .. autoclass:: nni.retiarii.evaluator.pytorch.lightning.Classification
:members: :members:
.. autoclass:: nni.retiarii.trainer.pytorch.lightning.Regression .. autoclass:: nni.retiarii.evaluator.pytorch.lightning.Regression
:members: :members:
Oneshot Trainers Oneshot Trainers
---------------- ----------------
.. autoclass:: nni.retiarii.trainer.pytorch.DartsTrainer .. autoclass:: nni.retiarii.oneshot.pytorch.DartsTrainer
:members: :members:
.. autoclass:: nni.retiarii.trainer.pytorch.EnasTrainer .. autoclass:: nni.retiarii.oneshot.pytorch.EnasTrainer
:members: :members:
.. autoclass:: nni.retiarii.trainer.pytorch.ProxylessTrainer .. autoclass:: nni.retiarii.oneshot.pytorch.ProxylessTrainer
:members: :members:
.. autoclass:: nni.retiarii.trainer.pytorch.SinglePathTrainer .. autoclass:: nni.retiarii.oneshot.pytorch.SinglePathTrainer
:members: :members:
Strategies 探索 Strategies
---------- ----------------------
.. autoclass:: nni.retiarii.strategy.Random .. autoclass:: nni.retiarii.strategy.Random
:members: :members:
...@@ -84,6 +90,9 @@ Strategies ...@@ -84,6 +90,9 @@ Strategies
.. autoclass:: nni.retiarii.strategy.TPEStrategy .. autoclass:: nni.retiarii.strategy.TPEStrategy
:members: :members:
.. autoclass:: nni.retiarii.strategy.PolicyBasedRL
:members:
Retiarii Experiments Retiarii Experiments
-------------------- --------------------
...@@ -92,3 +101,8 @@ Retiarii Experiments ...@@ -92,3 +101,8 @@ Retiarii Experiments
.. autoclass:: nni.retiarii.experiment.pytorch.RetiariiExeConfig .. autoclass:: nni.retiarii.experiment.pytorch.RetiariiExeConfig
:members: :members:
工具
---------
.. autofunction:: nni.retiarii.serialize
\ No newline at end of file
CDARTS
======
介绍
------------
`CDARTS <https://arxiv.org/pdf/2006.10724.pdf>`__ 建立了搜索和评估网络的循环反馈机制。 首先,搜索网络会生成初始结构用于评估,以便优化评估网络的权重。 然后,通过分类中通过的标签,以及评估网络中特征蒸馏的正则化来进一步优化搜索网络中的架构。 重复上述循环来优化搜索和评估网路,从而使结构得到训练,成为最终的评估网络。
在 ``CdartsTrainer`` 的实现中,首先分别实例化了两个 Model 和 Mutator。 第一个 Model 被称为"搜索网络",使用 ``RegularizedDartsMutator`` 来进行变化。它与 ``DartsMutator`` 稍有差别。 第二个 Model 是“评估网络”,它里用前面搜索网络的 Mutator 来创建了一个离散的 Mutator,来每次采样一条路径。 Trainer 会交替训练 Model 和 Mutator。 如果用户对这些 trainers 和 mutators 的详情感兴趣,可以参考 `论文 <https://arxiv.org/pdf/2006.10724.pdf>`__ 。
重现结果
--------------------
这是基于 NNI 平台的 CDARTS,该平台目前支持 CIFAR10 搜索和重新训练。 同时也支持 ImageNet 的搜索和重新训练,并有相应的接口。 在 NNI 上重现的结果略低于论文,但远高于原始 DARTS。 这里展示了在 CIFAR10 上的三个独立实验的结果。
.. list-table::
:header-rows: 1
:widths: auto
* - Runs
- Paper
- NNI
* - 1
- 97.52
- 97.44
* - 2
- 97.53
- 97.48
* - 3
- 97.58
- 97.56
样例
--------
`示例代码 <https://github.com/microsoft/nni/tree/master/examples/nas/cdarts>`__
.. code-block:: bash
# In case NNI code is not cloned. If the code is cloned already, ignore this line and enter code folder.
git clone https://github.com/Microsoft/nni.git
# install apex for distributed training.
git clone https://github.com/NVIDIA/apex
cd apex
python setup.py install --cpp_ext --cuda_ext
# search the best architecture
cd examples/nas/cdarts
bash run_search_cifar.sh
# train the best architecture.
bash run_retrain_cifar.sh
参考
---------
PyTorch
^^^^^^^
.. autoclass:: nni.algorithms.nas.pytorch.cdarts.CdartsTrainer
:members:
.. autoclass:: nni.algorithms.nas.pytorch.cdarts.RegularizedDartsMutator
:members:
.. autoclass:: nni.algorithms.nas.pytorch.cdarts.DartsDiscreteMutator
:members:
.. autoclass:: nni.algorithms.nas.pytorch.cdarts.RegularizedMutatorParallel
:members:
.. role:: raw-html(raw)
:format: html
经典 NAS 算法
======================
在经典 NAS 算法中,每个结构都作为 Trial 来训练,而 NAS 算法来充当 Tuner 因此,训练过程能使用 NNI 中的超参调优框架,Tuner 为下一个 Trial 生成新的结构,Trial 在训练平台中运行。
快速入门
-----------
下例展示了如何使用经典 NAS 算法。 NNI 超参优化非常相似。
.. code-block:: python
model = Net()
# get the chosen architecture from tuner and apply it on model
get_and_apply_next_architecture(model)
train(model) # your code for training the model
acc = test(model) # test the trained model
nni.report_final_result(acc) # report the performance of the chosen architecture
首先,实例化模型。 模型中,搜索空间通过 ``LayerChoice`` ``InputChoice`` 来定义。 然后,调用 ``get_and_apply_next_architecture(model)`` 来获得特定的结构。 此函数会从 Tuner (即,经典的 NAS 算法)中接收结构,并应用到 ``model`` 上。 此时,``model`` 成为了某个结构,不再是搜索空间。 然后可以像普通 PyTorch 模型一样训练此模型。 获得模型精度后,调用 ``nni.report_final_result(acc)`` 来返回给 Tuner
至此,Trial 代码已准备好了。 然后,准备好 NNI Experiment,即搜索空间文件和 Experiment 配置文件。 NNI 超参优化不同的是,要通过运行命令(详情参考 `这里 <../Tutorial/Nnictl.rst>`_) Trial 代码中自动生成搜索空间文件。
``nnictl ss_gen --trial_command="运行 Trial 代码的命令"``
此命令会自动生成 ``nni_auto_gen_search_space.json`` 文件。 然后,将生成的搜索空间文件路径填入 Experiment 配置文件的 ``searchSpacePath`` 字段。 配置文件中的其它字段,可参考 `此教程 <../Tutorial/QuickStart.md>`_
现在我们只支持经典NAS算法中的 :githublink:`PPO Tuner <examples/tuners/random_nas_tuner>` 未来将支持更多经典 NAS 算法。
完整的示例可以参考 :githublink:`PyTorch <examples/nas/classic_nas>` :githublink:`TensorFlow <examples/nas/classic_nas-tf>`
用于调试的独立模式
----------------------------------
为了便于调试,其支持独立运行模式,可直接运行 Trial 命令,而不启动 NNI Experiment 可以通过此方法来检查 Trial 代码是否可正常运行。 在独立模式下,``LayerChoice`` ``InputChoice`` 会选择最开始的候选项。
:raw-html:`<a name="regulaized-evolution-tuner"></a>`
Regularized Evolution Tuner
---------------------------
这是一个用于 NNI 神经网络架构搜索(NAS)接口的 Tuner 它使用了 `evolution 算法 <https://arxiv.org/pdf/1802.01548.pdf>`_
Tuner 首先随机初始化 ``population`` 模型的数量并进行评估。 之后,每次生成新架构时,Tuner 都会从 ``population`` 中随机选择一定数量的 ``sample`` 架构,然后将父模型 ``sample`` 中的最佳模型 mutates 产生子模型。 突变包括隐藏突变和op突变。 隐藏状态突变包括在单元格内形成循环的约束下,用 cell 的另一个隐藏状态替换隐藏状态。 op 变异的行为就像隐藏状态变异一样,只是将一个 op 替换为 op 集中的另一个 op 请注意,不允许将子模型与其父模型保持相同。 评估子模型后,将其添加到 ``population`` 的尾部,然后弹出前一个。
请注意,**试验并发应小于模型的数量** \,否则将引发 NO_MORE_TRIAL 异常。
下面的伪代码总结了整个过程。
.. image:: ../../img/EvoNasTuner.png
:target: ../../img/EvoNasTuner.png
:alt:
百里挑一:一站式神经体系结构搜索的优先路径提取
=======================================================================================
* `论文 <https://papers.nips.cc/paper/2020/file/d072677d210ac4c03ba046120f0802ec-Paper.pdf>`__
* `模型 - Google Drive <https://drive.google.com/drive/folders/1NLGAbBF9bA1IUAxKlk2VjgRXhr6RHvRW?usp=sharing>`__
* `模型 - 百度网盘(提取码:wqw6) <https://pan.baidu.com/s/1TqQNm2s14oEdyNPimw3T9g>`__
* `BibTex <https://scholar.googleusercontent.com/scholar.bib?q=info:ICWVXc_SsKAJ:scholar.google.com/&output=citation&scisdr=CgUmooXfEMfTi0cV5aU:AAGBfm0AAAAAX7sQ_aXoamdKRaBI12tAVN8REq1VKNwM&scisig=AAGBfm0AAAAAX7sQ_RdYtp6BSro3zgbXVJU2MCgsG730&scisf=4&ct=citation&cd=-1&hl=ja>`__
在这项工作中,我们提出了一个简单而有效的架构蒸馏方法。 其核心思想是,为了促进各个模型的收敛,子网在整个训练过程中将进行协作学习和相互传授。 并引入了优先路径的概念,它是指在训练过程中表现出优异性能的候选模型结构。 从优先路径中提取知识可以促进子网的训练。 由于优先路径会根据其性能和复杂性而动态变化,因此最终获得的路径就是百里挑一。 与最近的架构 `MobileNetV3 <https://arxiv.org/abs/1905.02244>`__ 和 `EfficientNet <https://arxiv.org/abs/1905.11946>`__ 系列在相同的配置下比较,Cream 发现的体系结构具有更高的性能。
.. image:: https://raw.githubusercontent.com/microsoft/Cream/main/demo/intro.jpg
重现结果
------------------
ImageNet 的 top-1 准确性。 Cream 搜索算法的 top-1 准确性超过 ImageNet 上的 MobileNetV3 和 EfficientNet-B0 / B1。
如下所示,使用 16 Gpus 的训练比使用 8 Gpus 的训练略胜一筹。
.. list-table::
:header-rows: 1
:widths: auto
* - Model (M Flops)
- 8Gpus
- 16Gpus
* - 14M
- 53.7
- 53.8
* - 43M
- 65.8
- 66.5
* - 114M
- 72.1
- 72.8
* - 287M
- 76.7
- 77.6
* - 481M
- 78.9
- 79.2
* - 604M
- 79.4
- 80.0
.. image:: ../../img/cream_flops100.jpg
:scale: 50%
.. image:: ../../img/cream_flops600.jpg
:scale: 50%
示例
--------
`示例代码 <https://github.com/microsoft/nni/tree/master/examples/nas/cream>`__
请在示例目录下运行下面的脚本。
准备数据
----------------
首先你需要下载 `ImageNet-2012 <http://www.image-net.org/>`__ 到目录 ``./data/imagenet`` 里,然后把验证集移动到子文件夹 ``./data/imagenet/val`` 。 你可以用 `此脚本 <https://raw.githubusercontent.com/soumith/imagenetloader.torch/master/valprep.sh>`__ 来移动验证集。
把 imagenet 数据放在 ``./data`` 里, 如下:
.. code-block:: bash
./data/imagenet/train
./data/imagenet/val
...
快速入门
-----------
1. 搜索
^^^^^^^^^
首先构建搜索环境。
.. code-block:: bash
pip install -r ./requirements
git clone https://github.com/NVIDIA/apex.git
cd apex
python setup.py install --cpp_ext --cuda_ext
搜索架构需要配置参数 ``FLOPS_MINIMUM`` 和 ``FLOPS_MAXIMU`` 以指定所需的模型触发器,例如 [0,600] MB flops。 可以通过修改以下两个参数来指定触发器间隔 ``./configs/train.yaml`` 。
.. code-block:: bash
FLOPS_MINIMUM: 0 # Minimum Flops of Architecture
FLOPS_MAXIMUM: 600 # Maximum Flops of Architecture
例如,如果希望搜索模型 <= 200M 的架构,请将 ``FLOPS_MINIMU`` 和 ``FLOPS_MAXIMU`` 分别设置为 ``0`` 和 ``200`` 。
在指定要搜索的体系结构之后,可以通过运行以下命令立即搜索体系结构:
.. code-block:: bash
python -m torch.distributed.launch --nproc_per_node=8 ./train.py --cfg ./configs/train.yaml
搜索的体系结构需要重新训练并获得最终模型。 最终模型以 ``.pth.tar`` 格式保存。 训练代码不久就会发布。
2. 重新训练
^^^^^^^^^^^
为了训练搜索的架构,需要配置 ``MODEL_SELECTION`` 参数来指定模型触发器。 在 ``./configs/retrain.yaml`` 文件里加上 ``MODEL_SELECTION`` 可以声明训练模型。 您可以从 [14,43,112,287,481,604] 中选择一个,代表不同的 Flops(MB)。
.. code-block:: bash
MODEL_SELECTION: 43 # Retrain 43m model
MODEL_SELECTION: 481 # Retrain 481m model
......
为了训练随机架构,需要设置 ``MODEL_SELECTION`` 为 ``-1`` ,并且设置参数 ``INPUT_ARCH``:
.. code-block:: bash
MODEL_SELECTION: -1 # Train random architectures
INPUT_ARCH: [[0], [3], [3, 3], [3, 1, 3], [3, 3, 3, 3], [3, 3, 3], [0]] # Random Architectures
......
在 ``./configs/retrain.yaml`` 文件里添加 ``MODEL_SELECTION`` 之后,可以使用下面的命令来训练模型。
.. code-block:: bash
python -m torch.distributed.launch --nproc_per_node=8 ./retrain.py --cfg ./configs/retrain.yaml
3. 测试
^^^^^^^^^
要测试我们训练的模型,需要使用 ``./configs/test.yaml`` 中的 ``MODEL_SELECTION`` 来指定要测试的模型。
.. code-block:: bash
MODEL_SELECTION: 43 # test 43m model
MODEL_SELECTION: 481 # test 470m model
......
在指定了模型的触发器之后,需要在 ``./ test.sh`` 中写入恢复模型的路径:
.. code-block:: bash
RESUME_PATH: './43.pth.tar'
RESUME_PATH: './481.pth.tar'
......
我们在 `google drive <https://drive.google.com/drive/folders/1CQjyBryZ4F20Rutj7coF8HWFcedApUn2>`__ 和 `[Models-Baidu Disk (password: wqw6)] <https://pan.baidu.com/s/1TqQNm2s14oEdyNPimw3T9g>`__ 提供了 14M/43M/114M/287M/481M/604M 预训练模型。
下载完预训练模型并且在 ``./configs/test.yaml`` 文件中添加了 ``MODEL_SELECTION`` 和 ``RESUME_PATH`` 之后,可以使用下面的命令来测试模型。
.. code-block:: bash
python -m torch.distributed.launch --nproc_per_node=8 ./test.py --cfg ./configs/test.yaml
...@@ -59,9 +59,6 @@ PyTorch ...@@ -59,9 +59,6 @@ PyTorch
.. autoclass:: nni.algorithms.nas.pytorch.darts.DartsTrainer .. autoclass:: nni.algorithms.nas.pytorch.darts.DartsTrainer
:members: :members:
.. autoclass:: nni.algorithms.nas.pytorch.darts.DartsMutator
:members:
局限性 局限性
----------- -----------
......
...@@ -39,8 +39,5 @@ CIFAR10 Macro/Micro 搜索空间 ...@@ -39,8 +39,5 @@ CIFAR10 Macro/Micro 搜索空间
PyTorch PyTorch
^^^^^^^ ^^^^^^^
.. autoclass:: nni.algorithms.nas.pytorch.enas.EnasTrainer .. autoclass:: nni.algorithms.nas.pytorch.enas.EnasTrainer
:members:
.. autoclass:: nni.algorithms.nas.pytorch.enas.EnasMutator
:members: :members:
执行引擎
=================
执行引擎(Execution engine)用于运行 Retiarii 实验。 NNI 支持三种执行引擎,用户可以根据自己的模型 mutation 定义类型和跨模型优化的需求选择特定的引擎。
* **纯 Python 执行引擎(Pure-python execution engine)** 是默认引擎,它支持 `内联 mutation API <./MutationPrimitives.rst>`__ 表示的模型空间。
* **基于图的执行引擎(Graph-based execution engine)** 支持使用 `内联 mutation APIs <./MutationPrimitives.rst>`__ 和由 `mutators <./Mutators.rst>`__ 表示的模型空间。 它要求用户的模型由 `TorchScript <https://pytorch.org/docs/stable/jit.html>`__ 解析。
* **CGO执行引擎(CGO execution engine)** 具有与 **基于图形的执行引擎** 相同的要求和能力。 但未来将支持跨模型的优化,这使得模型空间的探索变得更快。
纯 Python 执行引擎
----------------------------
纯 Python 执行引擎是默认引擎,如果用户是 NNI NAS 的新手,我们建议使用这个执行引擎。 纯 Python 执行引擎在内联突变 API 的范围内发挥了神奇作用,而不会触及用户模型的其余部分。 因此,它对用户模型的要求最低。
现在需要一个步骤来使用这个引擎。
1. 在整个 PyTorch 模型之外添加 ``@nni.retiarii.model_wrapper`` 装饰器。
.. note:: 在 PyTorch 模型中,您应该始终使用 ``super().__init__()`` 而不是 ``super(MyNetwork, self).__init__()`` ,因为后者在模型装饰器上存在问题。
基于图的执行引擎
----------------------------
对于基于图形的执行引擎,它使用 `TorchScript <https://pytorch.org/docs/stable/jit.html>`__ 将用户定义的模型转换为图形表示(称为图形 IR),其中的每个实例化模块模型转换为子图。 然后将突变应用到图上,生成新的图。 每个新图被转换回 PyTorch 代码并在用户指定的训练服务上执行。
在某些情况下,用户可能会发现 ``@basic_unit`` 非常有帮助。 ``@basic_unit`` 这里意味着模块将不会被转换为子图,而是将其转换为单个图形节点作为基本单元。
``@basic_unit`` 通常在以下情况下使用:
* 当用户想要调整模块的初始化参数时使用 ``valueChoice``,然后用 ``@ basic_unit`` 装饰模块。 例如,在 ``self.conv = MyConv(kernel_size=nn.ValueChoice([1, 3, 5]))`` 中,``MyConv`` 应该被修饰。
* 当模块无法被成功解析为子图,用 ``@basic_unit`` 装饰模块。 解析失败可能是由于复杂的控制流造成的。 目前 Retiarii 不支持 adhoc 循环,如果在一个模块的前向有 adhoc 循环,这个类应该被装饰成可序列化的模块。 例如,下面的 ``MyModule`` 应该被装饰起来。
.. code-block:: python
@basic_unit
class MyModule(nn.Module):
def __init__(self):
...
def forward(self, x):
for i in range(10): # <- adhoc loop
...
* 一些内联突变 API 要求其处理的模块用 ``@basic_unit`` 来装饰。 例如,提供给 ``LayerChoice`` 的用户自定义的模块作为候选操作应该被装饰。
使用基于图的执行引擎需要三个步骤:
1. 如果你的模型中有 ``@nni.retiarii.model_wrapper``,请移除。
2. 将 ``config.execution_engine = 'base'`` 添加到 ``RetiariiExeConfig``。 ``execution_engine`` 的默认值是 'py',即纯 Python 执行引擎。
3. 必要时按照上述准则添加 ``@basic_unit``。
对于导出最佳模型,基于图的执行引擎支持通过运行 ``exp.export_top_models(formatter='code')`` 来导出最佳模型的源代码。
CGO 执行引擎
--------------------
CGO 执行引擎在基于图的执行引擎基础上进行跨模型的优化。 这个执行引擎将在 `v2.4 发布 <https://github.com/microsoft/nni/issues/3813>`__。
Multi-trial NAS 的探索策略
=====================================================
探索策略的用法
-----------------------------
要使用探索策略,用户只需将探索策略实例化,并将实例化的对象传递给 ``RetiariiExperiment``。 示例如下:
.. code-block:: python
import nni.retiarii.strategy as strategy
exploration_strategy = strategy.Random(dedup=True) # dedup=False 如果不希望有重复数据删除
支持的探索策略
--------------------------------
NNI 提供了以下 multi-trial NAS 的探索策略。 用户还可以 `自定义新的探索策略 <./WriteStrategy.rst>`__。
.. list-table::
:header-rows: 1
:widths: auto
* - 名字
- 算法简介
* - `随机策略 <./ApiReference.rst#nni.retiarii.strategy.Random>`__
- 从搜索空间中随机选择模型 (``nni.retiarii.strategy.Random``)
* - `网格搜索 <./ApiReference.rst#nni.retiarii.strategy.GridSearch>`__
- 使用网格搜索算法从用户定义的模型空间中采样新模型。 (``nni.retiarii.strategy.GridSearch``)
* - `正则进化 <./ApiReference.rst#nni.retiarii.strategy.RegularizedEvolution>`__
- 使用 `正则进化算法 <https://arxiv.org/abs/1802.01548>`__ 从生成的模型中生成新模型 (``nni.retiarii.strategy.RegularizedEvolution``)
* - `TPE 策略 <./ApiReference.rst#nni.retiarii.strategy.TPEStrategy>`__
- 使用 `TPE 算法 <https://papers.nips.cc/paper/2011/file/86e8f7ab32cfd12577bc2619bc635690-Paper.pdf>`__ 从用户定义的模型空间中生成新模型 (``nni.retiarii.strategy.TPEStrategy``)
* - `RL 策略 <./ApiReference.rst#nni.retiarii.strategy.PolicyBasedRL>`__
- 使用 `PPO 算法 <https://arxiv.org/abs/1707.06347>`__ 从用户定义的模型空间中生成新模型 (``nni.retiarii.strategy.PolicyBasedRL``)
\ No newline at end of file
FBNet
======
.. note:: 这个 One-Shot NAS 仍然在 NNI NAS 1.0 下实现,将在 v2.4 中迁移到 `Retiarii 框架 <https://github.com/microsoft/nni/issues/3814>`__。
对于 facial landmark 的移动应用,基于 PFLD 模型的基本架构,我们应用 FBNet(Block-wise DNAS)设计了一个在延迟和准确率之间权衡的简洁模型。 参考资料如下:
* `FBNet: Hardware-Aware Efficient ConvNet Design via Differentiable Neural Architecture Search <https://arxiv.org/abs/1812.03443>`__
* `PFLD: A Practical Facial Landmark Detector <https://arxiv.org/abs/1902.10859>`__
FBNet 是一种分块可微分 NAS 方法(Block-wise DNAS),通过使用 Gumbel Softmax 随机采样和可微分训练来选择最佳候选构建块。 在要搜索的每一层(或阶段),并排规划不同的候选块(就像结构重新参数化的有效性一样),从而对超网络进行充分的预训练。 对预训练的超网进一步采样来对子网进行微调,以实现更好的性能。
.. image:: ../../img/fbnet.png
:target: ../../img/fbnet.png
:alt:
PFLD 是一种用于实时应用的轻量级 facial landmark 模型。 通过使用 PeleeNet 的 stem 块、深度卷积的平均池化和 eSE 模块,PLFD 的结构首先被简化加速。
为了在延迟和准确性之间取得更好的平衡,FBNet 被进一步应用于简化的 PFLD,以便在每个特定层搜索最佳块。 搜索空间以 FBNet 空间为基础,并通过使用深度卷积的平均池化和 eSE 模块等对移动部署进行优化。
实验
------------
为了验证 FBNet 应用于 PFLD 的有效性,我们选择具有 106 个 landmark 的开源数据集作为基准:
* `106个点的 Facial Landmark Localization 大挑战 <https://arxiv.org/abs/1905.03469>`__
基线模型表示为 MobileNet-V3 PFLD(`参考基线 <https://github.com/Hsintao/pfld_106_face_landmarks>`__),搜索到的模型表示为 Subnet。 实验结果如下,其中延迟在高通625 CPU(ARMv8)上测试:
.. list-table::
:header-rows: 1
:widths: auto
* - 模型
- 大小
- 延迟
- 验证 NME
* - MobileNet-V3 PFLD
- 1.01MB
- 10ms
- 6.22%
* - Subnet
- 693KB
- 1.60ms
- 5.58%
示例
--------
`示例代码 <https://github.com/microsoft/nni/tree/master/examples/nas/oneshot/pfld>`__
请在示例目录下运行下面的脚本。
此处使用的 Python 依赖如下所示:
.. code-block:: bash
numpy==1.18.5
opencv-python==4.5.1.48
torch==1.6.0
torchvision==0.7.0
onnx==1.8.1
onnx-simplifier==0.3.5
onnxruntime==1.7.0
数据准备
-----------------
首先,您应该将数据集 `106points dataset <https://drive.google.com/file/d/1I7QdnLxAlyG2Tq3L66QYzGhiBEoVfzKo/view?usp=sharing>`__ 下载到路径 ``./data/106points`` 。 数据集包括训练集和测试集:
.. code-block:: bash
./data/106points/train_data/imgs
./data/106points/train_data/list.txt
./data/106points/test_data/imgs
./data/106points/test_data/list.txt
快速入门
-----------
1. 搜索
^^^^^^^^^^
基于简化的 PFLD 架构,以构建超网为例,首先应配置多阶段搜索空间和搜索的超参数。
.. code-block:: bash
from lib.builder import search_space
from lib.ops import PRIMITIVES
from lib.supernet import PFLDInference, AuxiliaryNet
from nni.algorithms.nas.pytorch.fbnet import LookUpTable, NASConfig,
# 超参数配置
# search_space 定义多阶段搜索空间
nas_config = NASConfig(
model_dir="./ckpt_save",
nas_lr=0.01,
mode="mul",
alpha=0.25,
beta=0.6,
search_space=search_space,
)
# 管理信息的查询表
lookup_table = LookUpTable(config=nas_config, primitives=PRIMITIVES)
# 创建超网
pfld_backbone = PFLDInference(lookup_table)
在创建了搜索空间和超参数的超网后,我们可以运行以下命令开始搜索和训练超网。
.. code-block:: bash
python train.py --dev_id "0,1" --snapshot "./ckpt_save" --data_root "./data/106points"
训练过程中会显示验证准确率,准确率最高的模型会被保存为 ``./ckpt_save/supernet/checkpoint_best.pth``。
2. 微调
^^^^^^^^^^^^
在对超网进行预训练后,我们可以运行以下命令对子网进行采样并进行微调:
.. code-block:: bash
python retrain.py --dev_id "0,1" --snapshot "./ckpt_save" --data_root "./data/106points" \
--supernet "./ckpt_save/supernet/checkpoint_best.pth"
训练过程中会显示验证准确率,准确率最高的模型会被保存为 ``./ckpt_save/subnet/checkpoint_best.pth``。
3. 导出
^^^^^^^^^^
在对子网进行微调后,我们可以运行以下命令来导出 ONNX 模型。
.. code-block:: bash
python export.py --supernet "./ckpt_save/supernet/checkpoint_best.pth" \
--resume "./ckpt_save/subnet/checkpoint_best.pth"
ONNX 模型被保存为 ``./output/subnet.onnx``,可以通过使用 `MNN <https://github.com/alibaba/MNN>`__ 进一步转换为移动推理引擎。
我们提供了预训练超网和子网的 checkpoint:
* `超网 <https://drive.google.com/file/d/1TCuWKq8u4_BQ84BWbHSCZ45N3JGB9kFJ/view?usp=sharing>`__
* `子网 <https://drive.google.com/file/d/160rkuwB7y7qlBZNM3W_T53cb6MQIYHIE/view?usp=sharing>`__
* `ONNX 模型 <https://drive.google.com/file/d/1s-v-aOiMv0cqBspPVF3vSGujTbn_T_Uo/view?usp=sharing>`__
\ No newline at end of file
模型 Evaluator
================
模型评估器(model evaluator)用于训练和验证每个生成的模型。
模型评估器的用法
------------------------------------
在 multi-NAS 中,采样模型应该能够在远程机器或训练平台(例如 AzureML、OpenPAI)上执行。 因此,模型及其模型评估器都应该正确序列化。 为了使 NNI 正确地序列化模型评估器,用户应该在他们的一些函数和对象上应用 ``serialize`` 。
.. _serializer:
`serialize <./ApiReference.rst#id7>`__ 允许在另一个进程或机器中重新实例化模型评估器。 它是通过记录用户实例化的评估器的初始化参数来实现的。
Retiarii 提供的评估器相关 API 已经支持序列化,例如 ``pl.Classification``, ``pl.DataLoader``,无需对其应用 ``serialize``。 在以下情况下,用户应该手动使用 ``serialize`` API。
如果评估器 API 的初始化参数(例如 ``pl.Classification``、``pl.DataLoader``)不是原始类型(例如 ``int``, ``string``),它们应该是与 ``serialize`` 一起应用。 如果这些参数的初始化参数不是原始类型,``serialize`` 也应该被应用。 总而言之,如果有必要,``serialize`` 应该被递归应用。
以下是一个示例,``transforms.Compose``, ``transforms.Normalize`` 和 ``MNIST`` 应该通过 ``serialize`` 手动序列化。 ``serialize`` 以一个类 ``cls`` 作为它的第一个参数,它后面的参数是初始化这个类的参数。 ``pl.Classification`` 没有应用 ``serialize`` 因为它已经被 NNI 提供的 API 序列化。
.. code-block:: python
import nni.retiarii.evaluator.pytorch.lightning as pl
from nni.retiarii import serialize
from torchvision import transforms
transform = serialize(transforms.Compose, [serialize(transforms.ToTensor()), serialize(transforms.Normalize, (0.1307,), (0.3081,))])
train_dataset = serialize(MNIST, root='data/mnist', train=True, download=True, transform=transform)
test_dataset = serialize(MNIST, root='data/mnist', train=False, download=True, transform=transform)
evaluator = pl.Classification(train_dataloader=pl.DataLoader(train_dataset, batch_size=100),
val_dataloaders=pl.DataLoader(test_dataset, batch_size=100),
max_epochs=10)
支持的模型评估器
----------------------------------------
NNI 提供了一些常用的模型评估器以方便用户使用。 如果这些模型评估器不满足用户的要求,您可以按照 `教程 <./WriteTrainer.rst>`__ 自定义新的模型评估器。
.. autoclass:: nni.retiarii.evaluator.pytorch.lightning.Classification
:noindex:
.. autoclass:: nni.retiarii.evaluator.pytorch.lightning.Regression
:noindex:
Mutation 原语
===================
为了让用户在他们的 PyTorch/TensorFlow 模型中轻松表达模型空间,NNI 提供了一些内联 mutation API,如下所示。
* `nn.LayerChoice <./ApiReference.rst#nni.retiarii.nn.pytorch.LayerChoice>`__. 它允许用户放置多个候选操作(例如,PyTorch 模块),在每个探索的模型中选择其中一个。
.. code-block:: python
# import nni.retiarii.nn.pytorch as nn
# 在 `__init__` 中声明
self.layer = nn.LayerChoice([
ops.PoolBN('max', channels, 3, stride, 1),
ops.SepConv(channels, channels, 3, stride, 1),
nn.Identity()
]))
# 在 `forward` 函数中调用
out = self.layer(x)
* `nn.InputChoice <./ApiReference.rst#nni.retiarii.nn.pytorch.InputChoice>`__. 它主要用于选择(或尝试)不同的连接。 它会从设置的几个张量中,选择 ``n_chosen`` 个张量。
.. code-block:: python
# import nni.retiarii.nn.pytorch as nn
# 在 `__init__` 中声明
self.input_switch = nn.InputChoice(n_chosen=1)
# 在 `forward` 函数中调用,三者选一
out = self.input_switch([tensor1, tensor2, tensor3])
* `nn.ValueChoice <./ApiReference.rst#nni.retiarii.nn.pytorch.ValueChoice>`__. 它用于从一些候选值中选择一个值。 它只能作为基本单元的输入参数,即 ``nni.retiarii.nn.pytorch`` 中的模块和用 ``@basic_unit`` 装饰的用户定义的模块。
.. code-block:: python
# import nni.retiarii.nn.pytorch as nn
# 在 `__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]))
* `nn.Repeat <./ApiReference.rst#nni.retiarii.nn.pytorch.Repeat>`__. 以可变次数重复某个块
* `nn.Cell <./ApiReference.rst#nni.retiarii.nn.pytorch.Cell>`__. `这种细胞结构在 NAS 文献中被普遍使用 <https://arxiv.org/abs/1611.01578>`__。 具体来说,Cell 由多个 "nodes"(节点)组成。 每个节点是多个运算符的总和。 每个运算符从用户指定的候选者中选择,并从以前的节点和前代(predecessor)中获取一个输入。 前代(Predecessor)指 Cell 的输入。 Cell 的输出是 Cell 中部分节点(目前是所有节点)的串联。
\ No newline at end of file
用 Mutators 表示 Mutations
===============================
除了在 `这里 <./MutationPrimitives.rst>`__ 演示的内联突变 API,NNI 还提供了一种更通用的方法来表达模型空间,即 *突变器(Mutator)*,以涵盖更复杂的模型空间。 那些内联突变 API在底层系统中也是用突变器实现的,这可以看作是模型突变的一个特殊情况。
.. note:: Mutator 和内联突变 API 不能一起使用。
突变器是一段逻辑,用来表达如何突变一个给定的模型。 用户可以自由地编写自己的突变器。 然后用一个基础模型和一个突变器列表来表达一个模型空间。 通过在基础模型上接连应用突变器,来对模型空间中的一个模型进行采样。 示例如下:
.. code-block:: python
applied_mutators = []
applied_mutators.append(BlockMutator('mutable_0'))
applied_mutators.append(BlockMutator('mutable_1'))
``BlockMutator`` 由用户定义,表示如何对基本模型进行突变。
编写 mutator
---------------
用户定义的 Mutator 应该继承 ``Mutator`` 类,并在成员函数 ``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)
``mutate`` 的输入是基本模型的 graph IR(请参考 `这里 <./ApiReference.rst>`__ 获取 IR 的格式和 API),用户可以使用其成员函数(例如, ``get_nodes_by_label``,``update_operation``)对图进行变异。 变异操作可以与 API ``self.choice`` 相结合,以表示一组可能的突变。 在上面的示例中,节点的操作可以更改为 ``candidate_op_list`` 中的任何操作。
使用占位符使突变更容易:``nn.Placeholder``。 如果要更改模型的子图或节点,可以在此模型中定义一个占位符来表示子图或节点。 然后,使用 Mutator 对这个占位符进行变异,使其成为真正的模块。
.. code-block:: python
ph = nn.Placeholder(
label='mutable_0',
kernel_size_options=[1, 3, 5],
n_layer_options=[1, 2, 3, 4],
exp_ratio=exp_ratio,
stride=stride
)
``label`` 被 Mutator 所使用,来识别此占位符。 其他参数是突变器需要的信息。 它们可以从 ``node.operations.parameters`` 作为一个 dict 被访问,包括任何用户想传递给自定义突变器的信息。 完整的示例代码可以在 :githublink:`Mnasnet base model <examples/nas/multi-trial/mnasnet/base_mnasnet.py>` 找到。
开始一个实验与使用内联突变 API 几乎是一样的。 唯一的区别是,应用的突变器应该被传递给 ``RetiariiExperiment``。 示例如下:
.. code-block:: python
exp = RetiariiExperiment(base_model, trainer, applied_mutators, simple_strategy)
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)
One-Shot NAS 算法
=======================
除了 `经典 NAS 算法 <./ClassicNas.rst>`_,还可以使用更先进的 One-Shot NAS 算法来从搜索空间中找到更好的模型。 One-Shot NAS 算法已有了大量的相关工作,如 `SMASH <https://arxiv.org/abs/1708.05344>`__\ , `ENAS <https://arxiv.org/abs/1802.03268>`__\ , `DARTS <https://arxiv.org/abs/1808.05377>`__\ , `FBNet <https://arxiv.org/abs/1812.03443>`__\ , `ProxylessNAS <https://arxiv.org/abs/1812.00332>`__\ , `SPOS <https://arxiv.org/abs/1904.00420>`__\ , `Single-Path NAS <https://arxiv.org/abs/1904.02877>`__\ , `Understanding One-shot <http://proceedings.mlr.press/v80/bender18a>`__ and `GDAS <https://arxiv.org/abs/1910.04465>`__ One-Shot NAS 算法通常会构建一个超网络,其中包含的子网作为此搜索空间的候选项。每一步,会训练一个或多个子网的组合。
当前,NNI 支持数种 One-Shot 方法。 例如,``DartsTrainer`` 使用 SGD 迭代训练体系结构权重和模型权重,``ENASTrainer`` `使用控制器训练模型 <https://arxiv.org/abs/1802.03268>`__ 新的、更高效的 NAS Trainer 在研究界不断的涌现出来,NNI 会在将来的版本中实现其中的一部分。
使用 One-Shot NAS 算法进行搜索
-----------------------------------
每个 One-Shot NAS 算法都实现了 Trainer,可在每种算法说明中找到详细信息。 这是如何使用 ``EnasTrainer`` 的简单示例。
.. code-block:: python
# 与传统模型训练完全相同
model = Net()
dataset_train = CIFAR10(root="./data", train=True, download=True, transform=train_transform)
dataset_valid = CIFAR10(root="./data", train=False, download=True, transform=valid_transform)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), 0.05, momentum=0.9, weight_decay=1.0E-4)
# 这里使用 NAS
def top1_accuracy(output, target):
# 这是ENAS算法要求的计算奖励的函数
batch_size = target.size(0)
_, predicted = torch.max(output.data, 1)
return (predicted == target).sum().item() / batch_size
def metrics_fn(output, target):
# metrics 函数接收 output target 并计算出 dict metrics
return {"acc1": top1_accuracy(output, target)}
from nni.algorithms.nas.pytorch import enas
trainer = enas.EnasTrainer(model,
loss=criterion,
metrics=metrics_fn,
reward_function=top1_accuracy,
optimizer=optimizer,
batch_size=128
num_epochs=10, # 10 epochs
dataset_train=dataset_train,
dataset_valid=dataset_valid,
log_frequency=10) # 10s 打印一次 log
trainer.train() # training
trainer.export(file="model_dir/final_architecture.json") # 把最终的架构导出到文件
``model`` `具有用户定义搜索空间的模型 <./WriteSearchSpace.rst>`__ 然后需要准备搜索数据和模型评估指标。 要从定义的搜索空间中进行搜索,需要实例化 One-Shot 算法,即 Trainer(如,EnasTrainer)。 Trainer 会提供一些可以自定义的参数。 如,损失函数,指标函数,优化器以及数据集。 这些功能可满足大部分需求,NNI 会尽力让内置 Trainer 能够处理更多的模型、任务和数据集。
**注意** ,在使用 One-Shot NAS 算法时,不需要启动 NNI Experiment 不需要 ``nnictl`` ,可直接运行 Python 脚本(即:``train.py`` ),如:``python3 train.py`` 训练完成后,可通过 ``trainer.export()`` 导出找到的最好的模型。
NNI 中每个 Trainer 都用其对应的场景和用法。 一些 Trainer 假定任务是分类任务;一些 Trainer "epoch" 有不同的定义(如:ENAS 的每个 Epoch 一些子步骤加上 Controller 的步骤)。 大部分 Trainer 不支持分布式训练:没有使用 ``DataParallel`` ``DistributedDataParallel`` 来包装模型。 如果通过试用,想要在定制的应用中使用 Trainer,可能需要 `自定义 Trainer <./Advanced.rst#extend-the-ability-of-one-shot-trainers>`__
此外,可以使用 NAS 可视化来显示 One-Shot NAS `请看细节。 <./Visualization.rst>`__
使用导出的架构重新训练
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
搜索阶段后,就该训练找到的架构了。 与很多开源 NAS 算法不同,它们为重新训练专门写了新的模型。 我们发现搜索模型和重新训练模型的过程非常相似,因而可直接将一样的模型代码用到最终模型上。 例如:
.. code-block:: python
model = Net()
apply_fixed_architecture(model, "model_dir/final_architecture.json")
JSON 是从 Mutable 键值到 Choice 的映射。 Choice 可为:
* string: 根据名称来指定候选项。
* number: 根据索引来指定候选项。
* string 数组: 根据名称来指定候选项。
* number 数组: 根据索引来指定候选项。
* boolean 数组: 可直接选定多项的数组。
例如:
.. code-block:: json
{
"LayerChoice1": "conv5x5",
"LayerChoice2": 6,
"InputChoice3": ["layer1", "layer3"],
"InputChoice4": [1, 2],
"InputChoice5": [false, true, false, false, true]
}
应用后,模型会被固定,并准备好进行最终训练。 该模型作为单独的模型来工作,未使用的参数和模块已被剪除。
也可参考 `DARTS <./DARTS.rst>`__ 的重新训练代码。
NAS 参考
=============
.. contents::
Mutables
--------
.. autoclass:: nni.nas.pytorch.mutables.Mutable
:members:
.. autoclass:: nni.nas.pytorch.mutables.LayerChoice
:members:
.. autoclass:: nni.nas.pytorch.mutables.InputChoice
:members:
.. autoclass:: nni.nas.pytorch.mutables.MutableScope
:members:
工具
^^^^^^^^^
.. autofunction:: nni.nas.pytorch.utils.global_mutable_counting
Mutator
--------
.. autoclass:: nni.nas.pytorch.base_mutator.BaseMutator
:members:
.. autoclass:: nni.nas.pytorch.mutator.Mutator
:members:
Random Mutator
^^^^^^^^^^^^^^
.. autoclass:: nni.algorithms.nas.pytorch.random.RandomMutator
:members:
工具
^^^^^^^^^
.. autoclass:: nni.nas.pytorch.utils.StructuredMutableTreeNode
:members:
Trainer
--------
Trainer
^^^^^^^
.. autoclass:: nni.nas.pytorch.base_trainer.BaseTrainer
:members:
.. autoclass:: nni.nas.pytorch.trainer.Trainer
:members:
重新训练
^^^^^^^^^^^
.. autofunction:: nni.nas.pytorch.fixed.apply_fixed_architecture
.. autoclass:: nni.nas.pytorch.fixed.FixedArchitecture
:members:
分布式 NAS
^^^^^^^^^^^^^^^
.. autofunction:: nni.algorithms.nas.pytorch.classic_nas.get_and_apply_next_architecture
.. autoclass:: nni.algorithms.nas.pytorch.classic_nas.mutator.ClassicMutator
:members:
回调
^^^^^^^^^
.. autoclass:: nni.nas.pytorch.callbacks.Callback
:members:
.. autoclass:: nni.nas.pytorch.callbacks.LRSchedulerCallback
:members:
.. autoclass:: nni.nas.pytorch.callbacks.ArchitectureCheckpoint
:members:
.. autoclass:: nni.nas.pytorch.callbacks.ModelCheckpoint
:members:
工具
^^^^^^^^^
.. autoclass:: nni.nas.pytorch.utils.AverageMeterGroup
:members:
.. autoclass:: nni.nas.pytorch.utils.AverageMeter
:members:
.. autofunction:: nni.nas.pytorch.utils.to_device
One-shot NAS
============
在阅读本教程之前,我们强烈建议您先阅读有关如何 `定义一个模型空间 <./QuickStart.rst#id1>`__ 的教程。
使用 One-shot Trainer 进行模型搜素
--------------------------------------------------------------------
对于定义的模型空间,用户可以通过两种方式来探索。 一个是使用探索策略和单架构评估器,正如 `在这里 <./QuickStart.rst#id4>`__ 所演示的那样。 另一种是使用 one-shot trainer,与第一种相比,它消耗的计算资源要少得多。 在本教程中,我们专注于 One-Shot 方法。 One-Shot 方法的原理是将模型空间中的所有模型合并成一个大模型(通常称为超级模型或超级图)。 通过训练和评估整个超级模型,来实现搜索、训练和测试的任务。
我们在此列出已支持的 One-Shot Trainer:
* DARTS trainer
* ENAS trainer
* ProxylessNAS trainer
* Single-path (random) trainer
参见 `API 参考 <./ApiReference.rst>`__ 获得详细用法。 在这里,我们展示了一个例子来手动使用 DARTS Trainer。
.. code-block:: python
from nni.retiarii.oneshot.pytorch import DartsTrainer
trainer = DartsTrainer(
model=model,
loss=criterion,
metrics=lambda output, target: accuracy(output, target, topk=(1,)),
optimizer=optim,
num_epochs=args.epochs,
dataset=dataset_train,
batch_size=args.batch_size,
log_frequency=args.log_frequency,
unrolled=args.unrolled
)
trainer.fit()
final_architecture = trainer.export()
**导出架构的格式**:未来将会支持。
神经网络构搜索在 NNI 上的应用 Retiarii 用于神经网络构搜索
======================================= =======================================
.. Note:: NNI 最新的 NAS 支持都是基于 Retiarii 框架的,仍在使用早期版本 `NNI NAS v1.0 <https://nni.readthedocs.io/zh/v2.2/nas.html>`__ 的用户应尽快将工作迁移到 Retiarii 框架。
.. contents:: .. contents::
动机
----------------------------------------
自动化的神经网络架构(NAS)搜索在寻找更好的模型方面发挥着越来越重要的作用。 最近的研究工作证明了自动化 NAS 的可行性,并发现了一些超越手动设计和调整的模型。 代表算法有 `NASNet <https://arxiv.org/abs/1707.07012>`__\ , `ENAS <https://arxiv.org/abs/1802.03268>`__\ , `DARTS <https://arxiv.org/abs/1806.09055>`__\ , `Network Morphism <https://arxiv.org/abs/1806.10282>`__\ 和 `Evolution <https://arxiv.org/abs/1703.01041>`__。 此外,新的创新不断涌现。
然而,使用现有的 NAS 工作来帮助开发通用的 DNN 模型是相当困难的。 因此,我们设计了 `Retiarii <https://www.usenix.org/system/files/osdi20-zhang_quanlu.pdf>`__,一个全新的 NAS/HPO 框架,并在 NNI 中实施。 它可以帮助用户轻松构建模型空间(或搜索空间,调优空间),并利用现有的 NAS 算法。 该框架还有助于 NAS 创新,用于设计新的 NAS 算法。
概述 概述
-------- --------
自动化的神经网络架构(NAS)搜索在寻找更好的模型方面发挥着越来越重要的作用。 最近的研究工作证明了自动化 NAS 的可行性,并发现了一些超越手动设计和调整的模型。 代表算法有 `NASNet <https://arxiv.org/abs/1707.07012>`__\ , `ENAS <https://arxiv.org/abs/1802.03268>`__\ , `DARTS <https://arxiv.org/abs/1806.09055>`__\ , `Network Morphism <https://arxiv.org/abs/1806.10282>`__\ 和 `Evolution <https://arxiv.org/abs/1703.01041>`__。 此外,新的创新不断涌现。 Retiarii 框架有三个主要特点:
但是,要实现 NAS 算法需要花费大量的精力,并且很难在新算法中重用现有算法的代码。 为了促进 NAS 创新(例如,设计、实现新的 NAS 模型,并列比较不同的 NAS 模型),易于使用且灵活的编程接口非常重要。
以此为动力,NNI 的目标是提供统一的体系结构,以加速 NAS 上的创新,并将最新的算法更快地应用于现实世界中的问题上。 * 提供简单的 API,用于在 PyTorch/TensorFlow 模型中定义模型搜索空间。
* 内置前沿 NAS 算法,用于探索模型搜索空间。
* 实施系统级优化以加快探索。
通过统一的接口,有两种方法来使用神经网络架构搜索。 一种称为 `one-shot NAS <#supported-one-shot-nas-algorithms>`__ ,基于搜索空间构建了一个超级网络,并使用 one-shot 训练来生成性能良好的子模型。 `第二种 <#supported-classic-nas-algorithms>`__ 是经典的搜索方法,搜索空间中每个子模型作为独立的 Trial 运行。 称之为经典的 NAS。 有两种类型的模型空间探索方法:**Multi-trial NAS** 和 **One-shot NAS**。 Mutli-trial NAS 在模型空间中独立训练每个采样模型,而 One-shot NAS 则从一个超级模型中采样。 构建模型空间后,用户可以使用探索方法来探索模型空间。
NNI 还提供了专门的 `可视化工具 <#nas-visualization>`__,用于查看神经网络架构搜索的过程。
支持的经典 NAS 算法 Multi-trial NAS
-------------------------------- -----------------
经典 NAS 算法的过程类似于超参调优,通过 ``nnictl`` 来启动 Experiment,每个子模型会作为 Trial 运行。 不同之处在于,搜索空间文件是通过运行 ``nnictl ss_gen``,从用户模型(已包含搜索空间)中自动生成。 下表列出了经典 NAS 模式支持的算法。 将来版本会支持更多算法 Multi-trial NAS 意味着每个来自模型空间的抽样模型都是独立训练的。 一个典型的 multi-trial NAS 是 `NASNet <https://arxiv.org/abs/1707.07012>`__。 从模型空间中抽取模型的算法被称为探索策略。 NNI支持以下 multi-trial NAS 的探索策略
.. list-table:: .. list-table::
:header-rows: 1 :header-rows: 1
:widths: auto :widths: auto
* - 名称 * - 探索策略名称
- 算法简介 - 算法简介
* - :githublink:`Random Search <examples/tuners/random_nas_tuner>` * - 随机策略
- 从搜索空间中随机选择模型 - 从搜索空间中随机选择模型 (``nni.retiarii.strategy.Random``)
* - `PPO Tuner <../Tuner/BuiltinTuner.rst#PPO-Tuner>`__ * - 网格搜索
- PPO Tuner 是基于 PPO 算法的强化学习 Tuner。 `参考论文 <https://arxiv.org/abs/1707.06347>`__ - 使用网格搜索算法从用户定义的模型空间中采样新模型。 (``nni.retiarii.strategy.GridSearch``)
* - 正则进化
- 使用 `正则进化算法 <https://arxiv.org/abs/1802.01548>`__ 从生成的模型中生成新模型 (``nni.retiarii.strategy.RegularizedEvolution``)
* - TPE 策略
- 使用 `TPE 算法 <https://papers.nips.cc/paper/2011/file/86e8f7ab32cfd12577bc2619bc635690-Paper.pdf>`__ 从用户定义的模型空间中生成新模型 (``nni.retiarii.strategy.TPEStrategy``)
* - RL 策略
- 使用 `PPO 算法 <https://arxiv.org/abs/1707.06347>`__ 从用户定义的模型空间中生成新模型 (``nni.retiarii.strategy.PolicyBasedRL``)
参考 `这里 <ClassicNas.rst>`__ ,了解如何使用经典 NAS 法。 参考 `这里 <./multi_trial_nas.rst>`__ 获取 multi-trial NAS 详细用法。
支持的 One-shot NAS 算法 One-shot NAS
--------------------------------- ---------------------------------
NNI 目前支持下面列出的 One-Shot NAS 算法,并且正在添加更多算法。 用户可以重现算法或在自己的数据集上使用它。 鼓励用户使用 `NNI API <#use-nni-api>`__, 实现其它算法,以使更多人受益。 One-Shot NAS意味着将模型空间构建成一个超级模型,用权重共享的方式训练超级模型,然后从超级模型中不断采样,找到最佳模型。 `DARTS <https://arxiv.org/abs/1806.09055>`__ 是一个典型的 One-Shot NAS。
以下是已经支持的 One-Shot NAS 算法。 未来将支持更多 One-Shot NAS 算法。
.. list-table:: .. list-table::
:header-rows: 1 :header-rows: 1
:widths: auto :widths: auto
* - Name * - One-shot 算法名称
- 算法简介 - 算法简介
* - `ENAS <ENAS.rst>`__ * - `ENAS <ENAS.rst>`__
- `Efficient Neural Architecture Search via Parameter Sharing <https://arxiv.org/abs/1802.03268>`__. 在 ENAS 中,Contoller 学习在大的计算图中搜索最有子图的方式来发现神经网络。 它通过在子模型间共享参数来实现加速和出色的性能指标。 - `Efficient Neural Architecture Search via Parameter Sharing <https://arxiv.org/abs/1802.03268>`__. 在 ENAS 中,Contoller 学习在大的计算图中搜索最有子图的方式来发现神经网络。 它通过在子模型间共享参数来实现加速和出色的性能指标。
* - `DARTS <DARTS.rst>`__ * - `DARTS <DARTS.rst>`__
- `DARTS: Differentiable Architecture Search <https://arxiv.org/abs/1806.09055>`__ 介绍了一种用于双级优化的可区分网络体系结构搜索的新算法。 - `DARTS: Differentiable Architecture Search <https://arxiv.org/abs/1806.09055>`__ 介绍了一种用于双级优化的可区分网络体系结构搜索的新算法。
* - `P-DARTS <PDARTS.rst>`__
- `Progressive Differentiable Architecture Search: Bridging the Depth Gap between Search and Evaluation <https://arxiv.org/abs/1904.12760>`__ 这篇论文是基于 DARTS 的. 它引入了一种有效的算法,可在搜索过程中逐渐增加搜索的深度。
* - `SPOS <SPOS.rst>`__ * - `SPOS <SPOS.rst>`__
- `Single Path One-Shot Neural Architecture Search with Uniform Sampling <https://arxiv.org/abs/1904.00420>`__ 论文构造了一个采用统一的路径采样方法来训练简化的超网络,并使用进化算法来提高搜索神经网络结构的效率。 - `Single Path One-Shot Neural Architecture Search with Uniform Sampling <https://arxiv.org/abs/1904.00420>`__ 论文构造了一个采用统一的路径采样方法来训练简化的超网络,并使用进化算法来提高搜索神经网络结构的效率。
* - `CDARTS <CDARTS.rst>`__
- `Cyclic Differentiable Architecture Search <https://arxiv.org/pdf/2006.10724.pdf>`__ 在搜索和评估网络之间建立循环反馈机制。 通过引入的循环的可微分架构搜索框架将两个网络集成为一个架构。
* - `ProxylessNAS <Proxylessnas.rst>`__ * - `ProxylessNAS <Proxylessnas.rst>`__
- `ProxylessNAS: Direct Neural Architecture Search on Target Task and Hardware <https://arxiv.org/abs/1812.00332>`__. 它删除了代理,直接从大规模目标任务和目标硬件平台进行学习。 - `ProxylessNAS: Direct Neural Architecture Search on Target Task and Hardware <https://arxiv.org/abs/1812.00332>`__. 它删除了代理,直接从大规模目标任务和目标硬件平台进行学习。
* - `TextNAS <TextNAS.rst>`__
- `TextNAS: A Neural Architecture Search Space tailored for Text Representation <https://arxiv.org/pdf/1912.10729.pdf>`__. 这是专门用于文本表示的神经网络架构搜索算法。
* - `Cream <Cream.rst>`__
- `Cream of the Crop: Distilling Prioritized Paths For One-Shot Neural Architecture Search <https://papers.nips.cc/paper/2020/file/d072677d210ac4c03ba046120f0802ec-Paper.pdf>`__. 一种新的 NAS 算法,无需使用进化算法即可提取搜索空间中的优先路径。 在 ImageNet 上的性能具有竞争力,特别是对于小模型(例如: FLOPs < 200 M 时)。
One-shot 算法 **独立运行,不需要 nnictl**。 NNI 支持 PyTorch 和 TensorFlow 2.x。
这是运行示例的一些常见依赖项。 PyTorch 需要高于 1.2 才能使用 ``BoolTensor``。
* tensorboard
* PyTorch 1.2+
* git
参考 `这里 <NasGuide.rst>`__,了解如何使用 one-shot NAS 算法。
一次性 NAS 可以通过可视化工具来查看。 点 `这里 <./Visualization.rst>`__ 了解更多细节。
搜索空间集合 参考 `这里 <ClassicNas.rst>`__ ,了解如何使用经典 NAS 算法。
----------------
NNI 提供了一些预定义的、可被重用的搜索空间。 通过堆叠这些抽取出的单元,用户可以快速复现 NAS 模型。
搜索空间集合包含了以下 NAS 单元:
* `DartsCell <./SearchSpaceZoo.rst#DartsCell>`__
* `ENAS micro <./SearchSpaceZoo.rst#ENASMicroLayer>`__
* `ENAS macro <./SearchSpaceZoo.rst#ENASMacroLayer>`__
* `NAS Bench 201 <./SearchSpaceZoo.rst#nas-bench-201>`__
使用 NNI API 来编写搜索空间
----------------------------------------
在两种场景下需要用于设计和搜索模型的编程接口。
#. 在设计神经网络时,可能在层、子模型或连接上有多种选择,并且无法确定是其中一种或某些的组合的结果最好。 因此,需要简单的方法来表达候选的层或子模型。
#. 在神经网络上应用 NAS 时,需要统一的方式来表达架构的搜索空间,这样不必为不同的搜索算法来更改代码。
为了使用 NNI NAS, 建议用户先通读这篇文档 `the tutorial of NAS API for building search space <./WriteSearchSpace.rst>`__。
NAS 可视化
-----------------
为了帮助用户跟踪指定搜索空间下搜索模型的过程和状态,开发了此可视化工具。 它将搜索空间可视化为超网络,并显示子网络、层和操作的重要性,同时还能显示重要性是如何在搜索过程中变化的。 请参阅 `the document of NAS visualization <./Visualization.rst>`__ 。
参考和反馈 参考和反馈
---------------------- ----------------------
* `快速入门 <./QuickStart.rst>`__ ;
* 在Github 中 `提交此功能的 Bug <https://github.com/microsoft/nni/issues/new?template=bug-report.rst>`__; * `构建模型空间 <./construct_space.rst>`__ ;
* `Retiarii: 一个探索性的深度学习框架 <https://www.usenix.org/system/files/osdi20-zhang_quanlu.pdf>`__ ;
* 在 Github 中 `提交 Bug 报告 <https://github.com/microsoft/nni/issues/new?template=bug-report.rst>`__;
* 在Github 中 `提交新功能或请求改进 <https://github.com/microsoft/nni/issues/new?template=enhancement.rst>`__。 * 在Github 中 `提交新功能或请求改进 <https://github.com/microsoft/nni/issues/new?template=enhancement.rst>`__。
P-DARTS
=======
示例
--------
:githublink:`示例代码 <examples/nas/pdarts>`
.. code-block:: bash
#如果未克隆 NNI 代码。 如果代码已被克隆,请忽略此行并直接进入代码目录。
git clone https://github.com/Microsoft/nni.git
# 搜索最优结构
cd examples/nas/pdarts
python3 search.py
# 训练最优架构,跟 darts 一样的步骤
cd ../darts
python3 retrain.py --arc-checkpoint ../pdarts/checkpoints/epoch_2.json
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