Unverified Commit 226aaef9 authored by Chi Song's avatar Chi Song Committed by GitHub
Browse files

Chinese Translation (#1914)

parent ae81ec47
......@@ -69,7 +69,7 @@ Python 包使用 setuptools 安装,所以安装路径依赖于 Python 配置
| ------------------ | ------------------------------------------ |
| `easy-install` | 安装依赖项,生成,安装 NNI,并编辑 `~/.bashrc` |
| `dev-easy-install` | 安装依赖项,生成,将 NNI 作为符号链接来安装,并编辑 `~/.bashrc` |
| `install` | 安装 Python 包,Node.js 模块,NNI 脚本和例 |
| `install` | 安装 Python 包,Node.js 模块,NNI 脚本和例 |
| `dev-install` | 将 Python 和 Node.js 模块作为符号链接安装,然后安装 scripts |
| `pip-install` | 安装依赖项,生成,安装 NNI,但不安装 Python 包 |
......
......@@ -19,7 +19,7 @@ NNI 管理自动机器学习 (AutoML) 的 Experiment,**调度运行**由调优
* 想要更容易**实现或试验新的自动机器学习算法**的研究员或数据科学家,包括:超参调优算法,神经网络搜索算法以及模型压缩算法。
* 在机器学习平台中**支持自动机器学习**
### **NNI v1.2 已发布! &nbsp;[<img width="48" src="docs/img/release_icon.png" />](#nni-released-reminder)**
### **NNI v1.3 已发布! &nbsp;[<img width="48" src="docs/img/release_icon.png" />](#nni-released-reminder)**
## **NNI 功能一览**
......@@ -224,7 +224,7 @@ Linux 和 MacOS
*`python >= 3.5` 的环境中运行命令: `git``wget`,确保安装了这两个组件。
```bash
git clone -b v1.2 https://github.com/Microsoft/nni.git
git clone -b v1.3 https://github.com/Microsoft/nni.git
cd nni
source install.sh
```
......@@ -234,7 +234,7 @@ Windows
*`python >=3.5` 的环境中运行命令: `git``PowerShell`,确保安装了这两个组件。
```bash
git clone -b v1.2 https://github.com/Microsoft/nni.git
git clone -b v1.3 https://github.com/Microsoft/nni.git
cd nni
powershell -ExecutionPolicy Bypass -file install.ps1
```
......@@ -250,7 +250,7 @@ Windows 上参考 [Windows 上使用 NNI](docs/zh_CN/Tutorial/NniOnWindows.md)
* 通过克隆源代码下载示例。
```bash
git clone -b v1.2 https://github.com/Microsoft/nni.git
git clone -b v1.3 https://github.com/Microsoft/nni.git
```
Linux 和 MacOS
......@@ -326,7 +326,7 @@ You can use these commands to get more information about the experiment
* [如何调试](docs/zh_CN/Tutorial/HowToDebug.md)
* [自定义 Tuner](docs/zh_CN/Tuner/CustomizeTuner.md)
* [实现定制的训练平台](docs/zh_CN/TrainingService/HowToImplementTrainingService.md)
* [在 NNI 上实现新的 NAS Trainer](https://github.com/microsoft/nni/blob/master/docs/en_US/NAS/NasInterface.md#implement-a-new-nas-trainer-on-nni)
* [在 NNI 上实现新的 NAS Trainer](https://github.com/microsoft/nni/blob/master/docs/zh_CN/NAS/NasInterface.md#implement-a-new-nas-trainer-on-nni)
* [自定义 Advisor](docs/zh_CN/Tuner/CustomizeAdvisor.md)
## **其它代码库和参考**
......@@ -349,6 +349,7 @@ You can use these commands to get more information about the experiment
* [使用 NNI 为 SPTAG 自动调参](docs/zh_CN/CommunitySharings/SptagAutoTune.md)
* [使用 NNI 为 scikit-learn 查找超参](https://towardsdatascience.com/find-thy-hyper-parameters-for-scikit-learn-pipelines-using-microsoft-nni-f1015b1224c1)
* **博客** - [AutoML 工具(Advisor,NNI 与 Google Vizier)的对比](http://gaocegege.com/Blog/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/katib-new#%E6%80%BB%E7%BB%93%E4%B8%8E%E5%88%86%E6%9E%90) 作者:[@gaocegege](https://github.com/gaocegege) - kubeflow/katib 的设计与实现的总结与分析章节
* **Blog (中文)** - [NNI 2019 新功能汇总](https://mp.weixin.qq.com/s/7_KRT-rRojQbNuJzkjFMuA) by @squirrelsc
## **反馈**
......
# 高级神经网络架构搜索教程
目前,许多 NAS(Neural Architecture Search,神经网络架构搜索)算法都在 Trial 上使用了 **权重共享(weight sharing)** 的方法来加速训练过程。 例如,[ENAS](https://arxiv.org/abs/1802.03268) 与以前的 [NASNet](https://arxiv.org/abs/1707.07012) 算法相比,通过'*子模型间的参数共享(parameter sharing between child models)*'提高了 1000 倍的效率。 而例如 [DARTS](https://arxiv.org/abs/1806.09055), [Network Morphism](https://arxiv.org/abs/1806.10282), 和 [Evolution](https://arxiv.org/abs/1703.01041) 等算法也利用或者隐式的利用了权重共享。
本教程介绍了如何使用权重共享。
## 权重共享
推荐通过 NFS (Network File System)进行权重共享,它是轻量、相对高效的多机共享文件方案。 欢迎社区来共享更多高效的技术。
### 通过 NFS 文件使用权重共享
使用 NFS 配置(见下文),Trial 代码可以通过读写文件来共享模型权重。 建议使用 Tuner 的存储路径:
```yaml
tuner:
codeDir: path/to/customer_tuner
classFileName: customer_tuner.py
className: CustomerTuner
classArgs:
...
save_dir_root: /nfs/storage/path/
```
并让 Tuner 来决定在什么路径读写权重文件,通过 `nni.get_next_parameters()` 来获取路径:
<img src="https://user-images.githubusercontent.com/23273522/51817667-93ebf080-2306-11e9-8395-b18b322062bc.png" alt="drawing" width="700" />
例如,在 Tensorflow 中:
```python
# 保存 models
saver = tf.train.Saver()
saver.save(sess, os.path.join(params['save_path'], 'model.ckpt'))
# 读取 models
tf.init_from_checkpoint(params['restore_path'])
```
超参中的 `'save_path'``'restore_path'` 可以通过 Tuner 来管理。
### NFS 配置
NFS 使用了客户端/服务器架构。通过一个 NFS 服务器来提供物理存储,远程计算机上的 Trial 使用 NFS 客户端来读写文件,操作上和本地文件相同。
#### NFS 服务器
如果有足够的存储空间,并能够让 NNI 的 Trial 通过**远程机器**来连接,NFS 服务可以安装在任何计算机上。 通常,可以选择一台远程服务器作为 NFS 服务。
在 Ubuntu 上,可通过 `apt-get` 安装 NFS 服务:
```bash
sudo apt-get install nfs-kernel-server
```
假设 `/tmp/nni/shared` 是物理存储位置,然后运行:
```bash
mkdir -p /tmp/nni/shared
sudo echo "/tmp/nni/shared *(rw,sync,no_subtree_check,no_root_squash)" >> /etc/exports
sudo service nfs-kernel-server restart
```
可以通过命令 `sudo showmount -e localhost` 来检查上述目录是否通过 NFS 成功导出了
#### NFS 客户端
为了通过 NFS 访问远程共享文件,需要安装 NFS 客户端。 例如,在 Ubuntu 上运行:
```bash
sudo apt-get install nfs-common
```
然后创建并装载上共享目录:
```bash
mkdir -p /mnt/nfs/nni/
sudo mount -t nfs 10.10.10.10:/tmp/nni/shared /mnt/nfs/nni
```
实际使用时,IP `10.10.10.10` 需要替换为 NFS 服务器的真实地址。
## Trial 依赖控制的异步 Dispatcher 模式
多机间启用权重的 Trial,一般是通过**先写后读**的方式来保持一致性。 子节点在父节点的 Trial 完成训练前,不应该读取父节点模型。 要解决这个问题,要通过 `multiThread: true` 来启用**异步调度模式**。在 `config.yml` 中,每次收到 `NEW_TRIAL` 请求,分派一个新的 Trial 时,Tuner 线程可以决定是否阻塞当前线程。 例如:
```python
def generate_parameters(self, parameter_id):
self.thread_lock.acquire()
indiv = # 新 Trial 的配置
self.events[parameter_id] = threading.Event()
self.thread_lock.release()
if indiv.parent_id is not None:
self.events[indiv.parent_id].wait()
def receive_trial_result(self, parameter_id, parameters, reward):
self.thread_lock.acquire()
# 处理 Trial 结果的配置
self.thread_lock.release()
self.events[parameter_id].set()
```
## 样例
详细内容参考:[简单的参数共享样例](https://github.com/Microsoft/nni/tree/master/test/async_sharing_test)。 基于已有的 [ga_squad](https://github.com/Microsoft/nni/tree/master/examples/trials/ga_squad) 样例,还提供了新的 [样例](https://github.com/Microsoft/nni/tree/master/examples/trials/weight_sharing/ga_squad)
\ No newline at end of file
# 神经网络架构搜索的 NNI 编程接口(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) 等。 新的算法还在不断涌现。 然而,实现这些算法需要很大的工作量,且很难重用其它算法的代码库来实现。
要促进 NAS 创新(例如,设计实现新的 NAS 模型,并列比较不同的 NAS 模型),易于使用且灵活的编程接口非常重要。
<a name="ProgInterface"></a>
## 编程接口
在两种场景下需要用于设计和搜索模型的新的编程接口。 1) 在设计神经网络时,层、子模型或连接有多个可能,并且不确定哪一个或哪种组合表现最好。 如果有一种简单的方法来表达想要尝试的候选层、子模型,将会很有价值。 2) 研究自动化 NAS 时,需要统一的方式来表达神经网络架构的搜索空间, 并在不改变 Trial 代码的情况下来使用不同的搜索算法。
本文基于 [NNI Annotation](../Tutorial/AnnotationSpec.md) 实现了简单灵活的编程接口 。 通过以下示例来详细说明。
### 示例:为层选择运算符
在设计此模型时,第四层的运算符有多个可能的选择,会让模型有更好的表现。 如图所示,在模型代码中可以对第四层使用 Annotation。 此 Annotation 中,共有五个字段:
![](../../img/example_layerchoice.png)
* **layer_choice**:它是函数调用的 list,每个函数都要在代码或导入的库中实现。 函数的输入参数格式为:`def XXX (input, arg2, arg3, ...)`,其中输入是包含了两个元素的 list。 其中一个是 `fixed_inputs` 的 list,另一个是 `optional_inputs` 中选择输入的 list。 `conv``pool` 是函数示例。 对于 list 中的函数调用,无需写出第一个参数(即 input)。 注意,只会从这些函数调用中选择一个来执行。
* **fixed_inputs** :它是变量的 list,可以是前一层输出的张量。 也可以是此层之前的另一个 `nni.mutable_layer``layer_output`,或此层之前的其它 Python 变量。 list 中的所有变量将被输入 `layer_choice` 中选择的函数(作为输入 list 的第一个元素)。
* **optional_inputs** :它是变量的 list,可以是前一层的输出张量。 也可以是此层之前的另一个 `nni.mutable_layer``layer_output`,或此层之前的其它 Python 变量。 只有 `optional_input_size` 变量被输入 `layer_choice` 到所选的函数 (作为输入 list 的第二个元素)。
* **optional_input_size** :它表示从 `input_candidates` 中选择多少个输入。 它可以是一个数字,也可以是一个范围。 范围 [1, 3] 表示选择 1、2 或 3 个输入。
* **layer_output** :表示输出的名称。本例中,表示 `layer_choice` 选择的函数的返回值。 这是一个变量名,可以在随后的 Python 代码或 `nni.mutable_layer` 中使用。
此示例有两种写 Annotation 的方法。 对于上面的示例,输入函数的形式是 `[[], [out3]]` 。 对于下面的示例,输入的形式是 `[[out3], []]`
**调试**`nnictl trial codegen` 命令可帮助调试 NAS 编程接口。 如果 Experiment `YYY` 中的 Trial 的 `XXX` 出错了,可以运行 `nnictl trial codegen YYY --trial_id XXX` 在当前目录下生成这个 Trial 的可执行代码。 通过运行此代码,可以不需要 NNI 就能调试 Trial 失败的原因。 此命令会编译 Trial 代码,并用实际选择的层次和输入来替换 NNI 的 NAS 代码。
### 示例:为层选择输入的连接
设计层的连接对于制作高性能模型至关重要。 通过此接口,可选择一个层可以采用哪些连接来作为输入。 可以从一组连接中选择几个。 下面的示例从三个候选输入中为 `concat` 这个函数选择两个输入 。 `concat` 还会使用 `fixed_inputs` 获取其上一层的输出 。
![](../../img/example_connectchoice.png)
### 示例:同时选择运算符和连接
此示例从三个运算符中选择一个,并为其选择两个连接作为输入。 由于输入会有多个变量,,在函数的开头需要调用 `concat`
![](../../img/example_combined.png)
### 示例:[ENAS](https://arxiv.org/abs/1802.03268) 宏搜索空间
为了证明编程接口带来的便利,使用该接口来实现 “ENAS + 宏搜索空间” 的 Trial 代码。 左图是 ENAS 论文中的宏搜索空间。
![](../../img/example_enas.png)
## 统一的 NAS 搜索空间说明
通过上面的 Annotation 更新 Trial 代码后,即在代码中隐式指定了神经网络架构的搜索空间。 基于该代码,NNI 将自动生成一个搜索空间文件,可作为调优算法的输入。 搜索空间文件遵循以下 JSON 格式。
```javascript
{
"mutable_1": {
"_type": "mutable_layer",
"_value": {
"layer_1": {
"layer_choice": ["conv(ch=128)", "pool", "identity"],
"optional_inputs": ["out1", "out2", "out3"],
"optional_input_size": 2
},
"layer_2": {
...
}
}
}
}
```
相应生成的神经网络结构(由调优算法生成)如下:
```javascript
{
"mutable_1": {
"layer_1": {
"chosen_layer": "pool",
"chosen_inputs": ["out1", "out3"]
},
"layer_2": {
...
}
}
}
```
通过对搜索空间格式和体系结构选择 (choice) 表达式的说明,可以自由地在 NNI 上实现神经体系结构搜索的各种或通用的调优算法。 接下来的工作会提供一个通用的 NAS 算法。
## 支持 One-Shot NAS
One-Shot NAS 是流行的,能在有限的时间和资源预算内找到较好的神经网络结构的方法。 本质上,它会基于搜索空间来构建完整的图,并使用梯度下降最终找到最佳子图。 它有不同的训练方法,如:[training subgraphs (per mini-batch)](https://arxiv.org/abs/1802.03268)[training full graph through dropout](http://proceedings.mlr.press/v80/bender18a/bender18a.pdf),以及 [training with architecture weights (regularization)](https://arxiv.org/abs/1806.09055)
如上所示,NNI 支持通用的 NAS。 从用户角度来看,One-Shot NAS 和 NAS 具有相同的搜索空间规范,因此,它们可以使用相同的编程接口,只是在训练模式上有所不同。 NNI 提供了四种训练模式:
***classic_mode***: [上文](#ProgInterface)对此模式有相应的描述,每个子图是一个 Trial 任务。 要使用此模式,需要启用 NNI Annotation,并在 Experiment 配置文件中为 NAS 指定一个 Tuner。 [这里](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-nas)是如何实现 Trial 和配置文件的例子。 [这里](https://github.com/microsoft/nni/tree/master/examples/tuners/random_nas_tuner)是一个简单的 NAS Tuner。
***enas_mode***: 参考 [ENAS 论文](https://arxiv.org/abs/1802.03268)的训练方法。 它基于神经网络架构搜索空间来构建全图,每个 mini-batch 只激活一个子图。 [详细说明](#ENASMode)。 (当前仅支持 TensorFlow)。
要使用 enas_mode,需要在配置的 `trial` 部分增加如下字段。
```diff
trial:
command: 运行 Trial 的命令
codeDir: Trial 代码的目录
gpuNum: 每个 Trial 所需要的 GPU 数量
+ #choice: classic_mode, enas_mode, oneshot_mode
+ nasMode: enas_mode
```
与 classic_mode 类似,在 enas_mode 中,需要为 NAS 指定 Tuner,其会从 Tuner(或者论文中的术语:Controller)中接收子图。 由于 Trial 任务要从 Tuner 中接收多个子图,每个子图用于一个 mini-batch,需要在 Trial 代码中增加两行来接收下一个子图(`nni.training_update`),并返回当前子图的结果。 示例如下:
```python
for _ in range(num):
# 接收并启用一个新的子图
"""@nni.training_update(tf=tf, session=self.session)"""
loss, _ = self.session.run([loss_op, train_op])
# 返回这个 mini-batch 的损失值
"""@nni.report_final_result(loss)"""
```
在这里,`nni.training_update`用来在全图上进行更新。 在 enas_mode 中,更新表示接收一个子图,并在下一个 mini-batch 中启用它。 在 darts_mode 中,更新表示训练架构权重(参考 darts_mode 中的详细说明)。 在 enas_mode 中,需要将导入的 TensorFlow 包传入 `tf`,并将会话传入 `session`
***oneshot_mode***: 遵循[论文](http://proceedings.mlr.press/v80/bender18a/bender18a.pdf)中的训练方法。 与 enas_mode 通过训练大量子图来训练全图有所不同,oneshot_mode 中构建了全图,并将 dropout 添加到候选的输入以及候选的输出操作中。 然后像其它深度学习模型一样进行训练。 [详细说明](#OneshotMode)。 (当前仅支持 TensorFlow)。
要使用 oneshot_mode,需要在配置的 `trial` 部分增加如下字段。 在此模式中,不需要使用 Tuner,只需要在配置文件中添加任意一个Tuner。 此外,也不需要增加 `nni.training_update`,因为在训练过程中不需要更新。
```diff
trial:
command: 运行 Trial 的命令
codeDir: Trial 代码的目录
gpuNum: 每个 Trial 所需要的 GPU 数量
+ #choice: classic_mode, enas_mode, oneshot_mode
+ nasMode: oneshot_mode
```
***darts_mode***: 参考 [论文](https://arxiv.org/abs/1806.09055)中的训练方法。 与 oneshot_mode 类似。 有两个不同之处,首先 darts_mode 只将架构权重添加到候选操作的输出中,另外是交错的来训练模型权重和架构权重。 [详细说明](#DartsMode)
要使用 darts_mode,需要在配置的 `trial` 部分增加如下字段。 在此模式中,不需要使用 Tuner,只需要在配置文件中添加任意一个Tuner。
```diff
trial:
command: 运行 Trial 的命令
codeDir: Trial 代码的目录
gpuNum: 每个 Trial 所需要的 GPU 数量
+ #choice: classic_mode, enas_mode, oneshot_mode
+ nasMode: darts_mode
```
在使用 darts_mode 时,需要按照如下所示调用 `nni.training_update`,来更新架构权重。 更新架构权重时,和训练数据一样也需要`损失值`(即, `feed_dict`)。
```python
for _ in range(num):
# 训练架构权重
"""@nni.training_update(tf=tf, session=self.session, loss=loss, feed_dict=feed_dict)"""
loss, _ = self.session.run([loss_op, train_op])
```
**注意**:对于 enas_mode、oneshot_mode、以及 darts_mode,NNI 仅能在训练阶段时有用。 NNI 不处理它们的推理阶段。 对于 enas_mode,推理阶段需要通过 Controller 来生成新的子图。 对于 oneshot_mode,推理阶段会随机采样生成新的子图,并选择其中好的子图。 对于 darts_mode,推理过程会根据架构权重来修剪掉一些候选的操作。
<a name="ENASMode"></a>
### enas_mode
在 enas_mode 中,编译后的 Trial 代码会构建完整的图形(而不是子图),会接收所选择的架构,并在完整的图形上对此体系结构进行小型的批处理训练,然后再请求另一个架构。 通过 [NNI 多阶段 Experiment](./MultiPhase.md) 来支持。
具体来说,使用 TensorFlow 的 Trial,通过 TensorFlow 变量来作为信号,并使用 TensorFlow 的条件函数来控制搜索空间(全图)来提高灵活性。这意味着根据这些信号,可以变为不同的多个子图。 [这里](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-nas/enas_mode)是 enas_mode 的示例。
<a name="OneshotMode"></a>
### oneshot_mode
下图展示了 Dropout 通过 `nni.mutable_layers` 添加在全图的位置,输入的是 1-k 个候选输入,4 个操作是候选的操作。
![](../../img/oneshot_mode.png)
[论文](http://proceedings.mlr.press/v80/bender18a/bender18a.pdf)中的建议,应该为每层的输入实现 Dropout 方法。 当 0 < r < 1 是模型超参的取值范围(默认值为 0.01),k 是某层可选超参的数量,Dropout 比率设为 r^(1/k)。 fan-in 越高,每个输入被丢弃的可能性越大。 但某层丢弃所有可选输入的概率是常数,与 fan-in 无关。 假设 r = 0.05。 如果某层有 k = 2 个可选的输入,每个输入都会以独立的 0.051/2 ≈ 0.22 的概率被丢弃,也就是说有 0.78 的概率被保留。 如果某层有 k = 7 个可选的输入,每个输入都会以独立的 0.051/7 ≈ 0.65 的概率被丢弃,也就是说有 0.35 的概率被保留。 在这两种情况下,丢弃所有可选输入的概率是 5%。 候选操作的输出会通过同样的方法被丢弃。 [这里](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-nas/oneshot_mode)是 oneshot_mode 的示例。
<a name="DartsMode"></a>
### darts_mode
下图显示了通过 `nni.mutable_layers` 在全图中为某层加入架构权重,每个候选操作的输出会乘以架构权重。
![](../../img/darts_mode.png)
`nni.training_update` 中,TensorFlow 的 MomentumOptimizer 通过传递的 `loss``feed_dict` 来训练架构权重。 [这里](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-nas/darts_mode)是 darts_mode 的示例。
### [**待实现**] One-Shot NAS 的多 Trial 任务。
One-Shot NAS 通常只有一个带有完整图的 Trial 任务。 但是,同时运行多个 Trial 任务会很有用。 例如,在 enas_mode 中,多个 Trial 任务可以共享全图的权重来加速模型训练或收敛。 一些 One-Shot 不够稳定,运行多个 Trial 任务可以提升找到更好模型的概率。
NNI 原生支持运行多个 Trial 任务。 下图显示了 NNI 上如何运行多个 Trial 任务。
![](../../img/one-shot_training.png)
=============================================================
## NNI 上 NAS 的系统设计
### Experiment 执行的基本流程
NNI 的 Annotation 编译器会将 Trial 代码转换为可以接收架构选择并构建相应模型(如图)的代码。 NAS 的搜索空间可以看作是一个完整的图(在这里,完整的图意味着允许所有提供的操作符和连接来构建图),调优算法所选择的是其子图。 默认情况下,编译时 Trial 代码仅构建并执行子图。
![](../../img/nas_on_nni.png)
上图显示了 Trial 代码如何在 NNI 上运行。 `nnictl` 处理 Trial 代码,并生成搜索空间文件和编译后的 Trial 代码。 前者会输入 Tuner,后者会在 Trial 代码运行时使用。
[使用 NAS 的简单示例](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-nas)
### [**待实现**] 权重共享
在所选择的架构(即 Trial)之间共享权重可以加速模型搜索。 例如,适当地继承已完成 Trial 的权重可加速新 Trial 的收敛。 One-shot NAS(例如,ENAS,Darts)更为激进,不同架构(即子图)的训练会在完整图中共享相同的权重。
![](../../img/nas_weight_share.png)
权重分配(转移)在加速 NAS 中有关键作用,而找到有效的权重共享方式仍是热门的研究课题。 NNI 提供了一个键值存储,用于存储和加载权重。 Tuner 和 Trial 使用 KV 客户端库来访问存储。
NNI 上的权重共享示例。
## 通用的 NAS 调优算法
与超参数调优一样,NAS 也需要相对通用的算法。 通用编程接口使其更容易。 这是 NAS 上[基于 PPO 算法的 RL Tuner](https://github.com/microsoft/nni/tree/master/src/sdk/pynni/nni/ppo_tuner)。 期待社区努力设计和实施更好的 NAS 调优算法。
## [**待实现**] 导出最佳神经网络架构和代码
Experiment 完成后,可通过 `nnictl experiment export --code` 来导出用最好的神经网络结构和 Trial 代码。
## 结论和未来的工作
如本文所示,不同的 NAS 算法和执行模式,可通过相同的编程接口来支持。
在这一领域有许多系统和机器学习方向的有趣的研究主题。
\ No newline at end of file
......@@ -13,9 +13,9 @@ NNI 提供了先进的调优算法,使用上也很简单。 下面是内置 As
## 用法
要使用 NNI 内置的 Assessor,需要在 `config.yml` 文件中添加 **builtinAssessorName****classArgs**。 这一节会介绍推荐的场景、参数等详细用法以及例。
要使用 NNI 内置的 Assessor,需要在 `config.yml` 文件中添加 **builtinAssessorName****classArgs**。 这一节会介绍推荐的场景、参数等详细用法以及例。
注意:参考例中的格式来创建新的 `config.yml` 文件。
注意:参考例中的格式来创建新的 `config.yml` 文件。
<a name="MedianStop"></a>
......@@ -32,7 +32,7 @@ NNI 提供了先进的调优算法,使用上也很简单。 下面是内置 As
* **optimize_mode** (*maximize 或 minimize, 可选, 默认值为 maximize*) - 如果为 'maximize', Assessor 会在结果小于期望值时**终止** Trial。 如果为 'minimize',Assessor 会在结果大于期望值时**终止** Trial。
* **start_step** (*int, 可选, 默认值为 0*) - 只有收到 start_step 个中间结果后,才开始判断是否一个 Trial 应该被终止。
**使用例:**
**使用例:**
```yaml
# config.yml
......@@ -63,7 +63,7 @@ assessor:
* **threshold** (*float, 可选, 默认值为 0.95*) - 用来确定提前终止较差结果的阈值。 例如,如果 threshold = 0.95, optimize_mode = maximize,最好的历史结果是 0.9,那么会在 Trial 的预测值低于 0.95 * 0.9 = 0.855 时停止。
* **gap** (*int, 可选, 默认值为 1*) - Assessor 两次评估之间的间隔次数。 例如:如果 gap = 2, start_step = 6,就会评估第 6, 8, 10, 12... 个中间结果。
**使用例:**
**使用例:**
```yaml
# config.yml
......
......@@ -54,7 +54,9 @@ assessor:
注意在 **2** 中, `trial_history` 对象与 Trial 通过 `report_intermediate_result` 函数返回给 Assessor 的对象完全一致。
更多样例,可参考:
Assessor 的工作目录是`<home>/nni/experiments/<experiment_id>/log` 可从环境变量 `NNI_LOG_DIRECTORY` 中获取。
更多示例,可参考:
> * [medianstop-assessor](https://github.com/Microsoft/nni/tree/master/src/sdk/pynni/nni/medianstop_assessor)
> * [curvefitting-assessor](https://github.com/Microsoft/nni/tree/master/src/sdk/pynni/nni/curvefitting_assessor)
\ No newline at end of file
# Compressor
# 使用 NNI 进行模型压缩
随着更多层和节点大型神经网络的使用,降低其存储和计算成本变得至关重要,尤其是对于某些实时应用程序。 模型压缩可用于解决此问题。
我们很高兴的宣布,基于 NNI 的模型压缩工具发布了 Alpha 版本。该版本仍处于试验阶段,根据用户反馈会进行改进。 诚挚邀请您使用、反馈,或更多贡献。
我们很高兴的宣布,基于 NNI 的模型压缩工具发布了试用版本。该版本仍处于试验阶段,根据用户反馈会进行改进。 诚挚邀请您使用、反馈,或更多贡献。
NNI 提供了易于使用的工具包来帮助用户设计并使用压缩算法。 其使用了统一的接口来支持 TensorFlow 和 PyTorch。 只需要添加几行代码即可压缩模型。 NNI 中也内置了一些流程的模型压缩算法。 用户还可以通过 NNI 强大的自动调参功能来找到最好的压缩后的模型,详见[自动模型压缩](./AutoCompression.md)。 另外,用户还能使用 NNI 的接口,轻松定制新的压缩算法,详见[教程](#customize-new-compression-algorithms)
NNI 提供了易于使用的工具包来帮助用户设计并使用压缩算法。 当前支持基于 PyTorch 的统一接口。 只需要添加几行代码即可压缩模型。 NNI 中也内置了一些流程的模型压缩算法。 用户还可以通过 NNI 强大的自动调参功能来找到最好的压缩后的模型,详见[自动模型压缩](./AutoCompression.md)。 另外,用户还能使用 NNI 的接口,轻松定制新的压缩算法,详见[教程](#customize-new-compression-algorithms)
模型压缩方面的综述可参考:[Recent Advances in Efficient Computation of Deep Convolutional Neural Networks](https://arxiv.org/pdf/1802.00939.pdf)
## 支持的算法
......@@ -10,22 +13,31 @@ NNI 提供了几种压缩算法,包括剪枝和量化算法:
**剪枝**
| 名称 | 算法简介 |
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| [Level Pruner](./Pruner.md#level-pruner) | 根据权重的绝对值,来按比例修剪权重。 |
| [AGP Pruner](./Pruner.md#agp-pruner) | 自动的逐步剪枝(是否剪枝的判断:基于对模型剪枝的效果)[参考论文](https://arxiv.org/abs/1710.01878) |
| [L1Filter Pruner](./Pruner.md#l1filter-pruner) | 剪除卷积层中最不重要的过滤器 (PRUNING FILTERS FOR EFFICIENT CONVNETS)[参考论文](https://arxiv.org/abs/1608.08710) |
| [Slim Pruner](./Pruner.md#slim-pruner) | 通过修剪 BN 层中的缩放因子来修剪卷积层中的通道 (Learning Efficient Convolutional Networks through Network Slimming)[参考论文](https://arxiv.org/abs/1708.06519) |
| [Lottery Ticket Pruner](./Pruner.md#agp-pruner) | "The Lottery Ticket Hypothesis: Finding Sparse, Trainable Neural Networks" 提出的剪枝过程。 它会反复修剪模型。 [参考论文](https://arxiv.org/abs/1803.03635) |
| [FPGM Pruner](./Pruner.md#fpgm-pruner) | Filter Pruning via Geometric Median for Deep Convolutional Neural Networks Acceleration [参考论文](https://arxiv.org/pdf/1811.00250.pdf) |
剪枝算法通过删除冗余权重或层通道来压缩原始网络,从而降低模型复杂性并解决过拟合问题。
| 名称 | 算法简介 |
| ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| [Level Pruner](./Pruner.md#level-pruner) | 根据权重的绝对值,来按比例修剪权重。 |
| [AGP Pruner](./Pruner.md#agp-pruner) | 自动的逐步剪枝(是否剪枝的判断:基于对模型剪枝的效果)[参考论文](https://arxiv.org/abs/1710.01878) |
| [Lottery Ticket Pruner](./Pruner.md#agp-pruner) | "The Lottery Ticket Hypothesis: Finding Sparse, Trainable Neural Networks" 提出的剪枝过程。 它会反复修剪模型。 [参考论文](https://arxiv.org/abs/1803.03635) |
| [FPGM Pruner](./Pruner.md#fpgm-pruner) | Filter Pruning via Geometric Median for Deep Convolutional Neural Networks Acceleration [参考论文](https://arxiv.org/pdf/1811.00250.pdf) |
| [L1Filter Pruner](./Pruner.md#l1filter-pruner) | 在卷积层中具有最小 L1 权重规范的剪枝过滤器(用于 Efficient Convnets 的剪枝过滤器) [参考论文](https://arxiv.org/abs/1608.08710) |
| [L2Filter Pruner](./Pruner.md#l2filter-pruner) | 在卷积层中具有最小 L2 权重规范的剪枝过滤器 |
| [ActivationAPoZRankFilterPruner](./Pruner.md#ActivationAPoZRankFilterPruner) | 基于指标 APoZ(平均百分比零)的剪枝过滤器,该指标测量(卷积)图层激活中零的百分比。 [参考论文](https://arxiv.org/abs/1607.03250) |
| [ActivationMeanRankFilterPruner](./Pruner.md#ActivationMeanRankFilterPruner) | 基于计算输出激活最小平均值指标的剪枝过滤器 |
| [Slim Pruner](./Pruner.md#slim-pruner) | 通过修剪 BN 层中的缩放因子来修剪卷积层中的通道 (Learning Efficient Convolutional Networks through Network Slimming) [参考论文](https://arxiv.org/abs/1708.06519) |
**量化**
量化算法通过减少表示权重或激活所需的精度位数来压缩原始网络,这可以减少计算和推理时间。
| 名称 | 算法简介 |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Naive Quantizer](./Quantizer.md#naive-quantizer) | 默认将权重量化为 8 位 |
| [QAT Quantizer](./Quantizer.md#qat-quantizer) | 为 Efficient Integer-Arithmetic-Only Inference 量化并训练神经网络。 [参考论文](http://openaccess.thecvf.com/content_cvpr_2018/papers/Jacob_Quantization_and_Training_CVPR_2018_paper.pdf) |
| [DoReFa Quantizer](./Quantizer.md#dorefa-quantizer) | DoReFa-Net: 通过低位宽的梯度算法来训练低位宽的卷积神经网络。 [参考论文](https://arxiv.org/abs/1606.06160) |
| [BNN Quantizer](./Quantizer.md#BNN-Quantizer) | 二进制神经网络:使用权重和激活限制为 +1 或 -1 的深度神经网络。 [参考论文](https://arxiv.org/abs/1602.02830) |
## 内置压缩算法的用法
......@@ -57,17 +69,46 @@ pruner.compress()
实例化压缩算法时,会传入 `config_list`。 配置说明如下。
### 压缩算法中的用户配置
压缩模型时,用户可能希望指定稀疏率,为不同类型的操作指定不同的比例,排除某些类型的操作,或仅压缩某类操作。 配置规范可用于表达此类需求。 可将其视为一个 Python 的 `list` 对象,其中每个元素都是一个 `dict` 对象。
压缩模型时,用户可能希望指定稀疏率,为不同类型的操作指定不同的比例,排除某些类型的操作,或仅压缩某类操作。 配置规范可用于表达此类需求。 可将其视为一个 Python 的 `list` 对象,其中每个元素都是一个 `dict` 对象。 在每个 `dict` 中,有一些 NNI 压缩算法支持的键值:
`list` 中的 `dict` 会依次被应用,也就是说,如果一个操作出现在两个配置里,后面的 `dict` 会覆盖前面的配置。
#### 通用键值
在每个 `dict` 中,有一些 NNI 压缩算法支持的键值:
* __op_types__:指定要压缩的操作类型。 'default' 表示使用算法的默认设置。
* __op_names__:指定需要压缩的操作的名称。 如果没有设置此字段,操作符不会通过名称筛选。
* __exclude__:默认为 False。 如果此字段为 True,表示要通过类型和名称,将一些操作从压缩中排除。
`dict` 还有一些其它键值,由特定的压缩算法所使用。 例如:
#### 量化算法的键值
**如果使用量化算法,则需要设置更多键值。 如果使用剪枝算法,则可以忽略这些键值**
`list` 中的 `dict` 会依次被应用,也就是说,如果一个操作出现在两个配置里,后面的 `dict` 会覆盖前面的配置。
* __quant_types__ : 字符串列表。
要应用量化的类型,当前支持 "权重","输入","输出"。 "权重"是指将量化操作应用到 module 的权重参数上。 "输入" 是指对 module 的 forward 方法的输入应用量化操作。 "输出"是指将量化运法应用于模块 forward 方法的输出,在某些论文中,这种方法称为"激活"。
* __quant_bits__ : int 或 dict {str : int}
量化的位宽,键是量化类型,值是量化位宽度,例如:
```
{
quant_bits: {
'weight': 8,
'output': 4,
},
}
```
当值为 int 类型时,所有量化类型使用相同的位宽。 例如:
```
{
quant_bits: 8, # 权重和输出的位宽都为 8 bits
}
```
#### 为每个压缩算法指定的其他键
`dict` 还有一些其它键值,由特定的压缩算法所使用。 例如, [Level Pruner](./Pruner.md#level-pruner) 需要 `sparsity` 键,用于指定修剪的量。
#### 示例
配置的简单示例如下:
```python
......@@ -178,11 +219,9 @@ class YourPruner(nni.compression.tensorflow.Pruner):
定制量化算法的接口与剪枝算法类似。 唯一的不同是使用 `quantize_weight` 替换了 `calc_mask``quantize_weight` 直接返回量化后的权重,而不是 mask。这是因为对于量化算法,量化后的权重不能通过应用 mask 来获得。
```python
# TensorFlow 中定制 Quantizer。
# PyTorch 的 Quantizer,只需将
# nni.compression.tensorflow.Quantizer 替换为
# nni.compression.torch.Quantizer
class YourQuantizer(nni.compression.tensorflow.Quantizer):
from nni.compression.torch.compressor import Quantizer
class YourQuantizer(Quantizer):
def __init__(self, model, config_list):
"""
建议使用 NNI 定义的规范来配置
......@@ -231,27 +270,71 @@ class YourQuantizer(nni.compression.tensorflow.Quantizer):
Parameters
----------
inputs : Tensor
需要量化的输入
需要量化的张量
config : dict
输入量化的配置
输入量化的配置
"""
# 实现生成 `new_input`
# 生成 `new_input` 的代码
return new_input
# Pytorch 版本不需要 sess 参数
def update_epoch(self, epoch_num, sess):
def update_epoch(self, epoch_num):
pass
# Pytorch 版本不需要 sess 参数
def step(self, sess):
def step(self):
"""
根据需要可基于 bind_model 方法中的模型或权重进行操作
Can do some processing based on the model or weights binded
in the func bind_model
"""
pass
```
#### 定制 backward 函数
有时,量化操作必须自定义 backward 函数,例如 [Straight-Through Estimator](https://stackoverflow.com/questions/38361314/the-concept-of-straight-through-estimator-ste),可如下定制 backward 函数:
```python
from nni.compression.torch.compressor import Quantizer, QuantGrad, QuantType
class ClipGrad(QuantGrad):
@staticmethod
def quant_backward(tensor, grad_output, quant_type):
"""
此方法应被子类重载来提供定制的 backward 函数,
默认实现是 Straight-Through Estimator
Parameters
----------
tensor : Tensor
量化操作的输入
grad_output : Tensor
量化操作输出的梯度
quant_type : QuantType
量化类型,可为 `QuantType.QUANT_INPUT`, `QuantType.QUANT_WEIGHT`, `QuantType.QUANT_OUTPUT`,
可为不同的类型定义不同的行为。
Returns
-------
tensor
量化输入的梯度
"""
# 对于 quant_output 函数,如果张量的绝对值大于 1,则将梯度设置为 0
if quant_type == QuantType.QUANT_OUTPUT:
grad_output[torch.abs(tensor) > 1] = 0
return grad_output
class YourQuantizer(Quantizer):
def __init__(self, model, config_list):
super().__init__(model, config_list)
# 定制 backward 函数来重载默认的 backward 函数
self.quant_grad = ClipGrad
```
### 使用用户自定义的压缩算法
如果不定制 `QuantGrad`,默认的 backward 为 Straight-Through Estimator。 _即将推出_...
__[TODO]__ ...
## **参考和反馈**
* 在 GitHub 中[提交此功能的 Bug](https://github.com/microsoft/nni/issues/new?template=bug-report.md)
* 在 GitHub 中[提交新功能或改进请求](https://github.com/microsoft/nni/issues/new?template=enhancement.md)
* 了解 NNI 中[特征工程的更多信息](https://github.com/microsoft/nni/blob/master/docs/zh_CN/FeatureEngineering/Overview.md)
* 了解 NNI 中[ NAS 的更多信息](https://github.com/microsoft/nni/blob/master/docs/zh_CN/NAS/Overview.md)
* 了解如何[使用 NNI 进行超参数调优](https://github.com/microsoft/nni/blob/master/docs/zh_CN/Tuner/BuiltinTuner.md)
NNI Compressor 中的 Pruner
===
支持的剪枝算法
* [Level Pruner](#level-pruner)
* [AGP Pruner](#agp-pruner)
* [Lottery Ticket 假设](#lottery-ticket-hypothesis)
* [Slim Pruner](#slim-pruner)
* [具有权重等级的 Filter Pruners](#weightrankfilterpruner)
* [FPGM Pruner](#fpgm-pruner)
* [L1Filter Pruner](#l1filter-pruner)
* [L2Filter Pruner](#l2filter-pruner)
* [具有激活等级的 Filter Pruners](#activationrankfilterpruner)
* [APoZ Rank Pruner](#activationapozrankfilterpruner)
* [Activation Mean Rank Pruner](#activationmeanrankfilterpruner)
## Level Pruner
这是个基本的一次性 Pruner:可设置目标稀疏度(以分数表示,0.6 表示会剪除 60%)。
......@@ -10,7 +23,7 @@ NNI Compressor 中的 Pruner
### 用法
TensorFlow 代码
```
```python
from nni.compression.tensorflow import LevelPruner
config_list = [{ 'sparsity': 0.8, 'op_types': ['default'] }]
pruner = LevelPruner(model_graph, config_list)
......@@ -18,7 +31,7 @@ pruner.compress()
```
PyTorch 代码
```
```python
from nni.compression.torch import LevelPruner
config_list = [{ 'sparsity': 0.8, 'op_types': ['default'] }]
pruner = LevelPruner(model, config_list)
......@@ -37,8 +50,6 @@ pruner.compress()
### 用法
通过下列代码,可以在 10 个 Epoch 中将权重稀疏度从 0% 剪枝到 80%。
首先,导入 Pruner 来为模型添加遮盖。
TensorFlow 代码
```python
from nni.compression.tensorflow import AGP_Pruner
......@@ -68,7 +79,7 @@ pruner = AGP_Pruner(model, config_list)
pruner.compress()
```
其次,在训练代码中每完成一个 Epoch,更新一下 Epoch 数值。
在训练代码中每完成一个 Epoch,更新一下 Epoch 数值。
TensorFlow 代码
```python
......@@ -130,12 +141,45 @@ for _ in pruner.get_prune_iterations():
* **sparsity:** 压缩完成后的最终稀疏度。
***
## FPGM Pruner
## Slim Pruner
这是一次性的 Pruner,在 ['Learning Efficient Convolutional Networks through Network Slimming'](https://arxiv.org/pdf/1708.06519.pdf) 中提出,作者 Zhuang Liu, Jianguo Li, Zhiqiang Shen, Gao Huang, Shoumeng Yan 以及 Changshui Zhang。
![](../../img/slim_pruner.png)
> Slim Pruner **会遮盖卷据层通道之后 BN 层对应的缩放因子**,训练时在缩放因子上的 L1 正规化应在批量正规化 (BN) 层之后来做。BN 层的缩放因子在修剪时,是**全局排序的**,因此稀疏模型能自动找到给定的稀疏度。
### 用法
PyTorch 代码
```python
from nni.compression.torch import SlimPruner
config_list = [{ 'sparsity': 0.8, 'op_types': ['BatchNorm2d'] }]
pruner = SlimPruner(model, config_list)
pruner.compress()
```
#### Slim Pruner 的用户配置
- **sparsity:**,指定压缩的稀疏度。
- **op_types:** 在 Slim Pruner 中仅支持 BatchNorm2d。
## WeightRankFilterPruner
WeightRankFilterPruner 是一系列的 Pruner,在卷积层权重上,用最小的重要性标准修剪过滤器,来达到预设的网络稀疏度。
### FPGM Pruner
这是一种一次性的 Pruner,FPGM Pruner 是论文 [Filter Pruning via Geometric Median for Deep Convolutional Neural Networks Acceleration](https://arxiv.org/pdf/1811.00250.pdf) 的实现
具有最小几何中位数的 FPGMPruner 修剪过滤器
![](../../img/fpgm_fig1.png)
> 以前的方法使用 “smaller-norm-less-important” 准则来修剪卷积神经网络中规范值较小的。 本文中,分析了基于规范的准则,并指出其所依赖的两个条件不能总是满足:(1) 过滤器的规范偏差应该较大;(2) 过滤器的最小规范化值应该很小。 为了解决此问题,提出了新的过滤器修建方法,即 Filter Pruning via Geometric Median (FPGM),可不考虑这两个要求来压缩模型。 与以前的方法不同,FPGM 通过修剪冗余的,而不是相关性更小的部分来压缩 CNN 模型。
### 用法
首先,导入 Pruner 来为模型添加遮盖。
#### 用法
TensorFlow 代码
```python
......@@ -159,7 +203,7 @@ pruner.compress()
```
注意:FPGM Pruner 用于修剪深度神经网络中的卷积层,因此 `op_types` 字段仅支持卷积层。
另外,需要在每个 epoch 开始的地方添加下列代码来更新 epoch 的编号。
需要在每个 epoch 开始的地方添加下列代码来更新 epoch 的编号。
TensorFlow 代码
```python
......@@ -176,9 +220,9 @@ pruner.update_epoch(epoch)
***
## L1Filter Pruner
### L1Filter Pruner
这是一种一次性的 Pruner,由 ['PRUNING FILTERS FOR EFFICIENT CONVNETS'](https://arxiv.org/abs/1608.08710) 提出,作者 Hao Li, Asim Kadav, Igor Durdanovic, Hanan Samet 和 Hans Peter Graf。
这是一种一次性的 Pruner,由 ['PRUNING FILTERS FOR EFFICIENT CONVNETS'](https://arxiv.org/abs/1608.08710) 提出,作者 Hao Li, Asim Kadav, Igor Durdanovic, Hanan Samet 和 Hans Peter Graf。 [重现的实验结果](l1filterpruner.md)
![](../../img/l1filter_pruner.png)
......@@ -191,7 +235,11 @@ pruner.update_epoch(epoch)
> 3. 修剪 ![](http://latex.codecogs.com/gif.latex?m) 具有最小求和值及其相应特征图的筛选器。 在 下一个卷积层中,被剪除的特征图所对应的内核也被移除。
> 4. 为第 ![](http://latex.codecogs.com/gif.latex?i) 和 ![](http://latex.codecogs.com/gif.latex?i+1) 层创建新的内核举证,并保留剩余的内核 权重,并复制到新模型中。
```
#### 用法
PyTorch 代码
```python
from nni.compression.torch import L1FilterPruner
config_list = [{ 'sparsity': 0.8, 'op_types': ['Conv2d'] }]
pruner = L1FilterPruner(model, config_list)
......@@ -201,28 +249,91 @@ pruner.compress()
#### L1Filter Pruner 的用户配置
- **sparsity:**,指定压缩的稀疏度。
- **op_types:** 在 L1Filter Pruner 中仅支持 Conv2d。
- **op_types:** 在 L1Filter Pruner 中仅支持 Conv1d 和 Conv2d。
## Slim Pruner
***
这是一次性的 Pruner,在 ['Learning Efficient Convolutional Networks through Network Slimming'](https://arxiv.org/pdf/1708.06519.pdf) 中提出,作者 Zhuang Liu, Jianguo Li, Zhiqiang Shen, Gao Huang, Shoumeng Yan 以及 Changshui Zhang。
### L2Filter Pruner
![](../../img/slim_pruner.png)
这是一种结构化剪枝算法,用于修剪权重的最小 L2 规范筛选器。 它被实现为一次性修剪器。
> Slim Pruner **会遮盖卷据层通道之后 BN 层对应的缩放因子**,训练时在缩放因子上的 L1 正规化应在批量正规化 (BN) 层之后来做。BN 层的缩放因子在修剪时,是**全局排序的**,因此稀疏模型能自动找到给定的稀疏度。
#### 用法
### 用法
PyTorch 代码
```python
from nni.compression.torch import L2FilterPruner
config_list = [{ 'sparsity': 0.8, 'op_types': ['Conv2d'] }]
pruner = L2FilterPruner(model, config_list)
pruner.compress()
```
#### L2Filter Pruner 的用户配置
- **sparsity:**,指定压缩的稀疏度。
- **op_types:** 在 L2Filter Pruner 中仅支持 Conv1d 和 Conv2d。
## ActivationRankFilterPruner
ActivationRankFilterPruner 是一系列的 Pruner,从卷积层激活的输出,用最小的重要性标准修剪过滤器,来达到预设的网络稀疏度。
### ActivationAPoZRankFilterPruner
我们将其实现为一次性剪枝器,它基于 `APoZ` 修剪卷积层,参考论文 [Network Trimming: A Data-Driven Neuron Pruning Approach towards Efficient Deep Architectures](https://arxiv.org/abs/1607.03250)。 基于迭代剪枝的 `APoZ` 将在以后的版本中支持。
APoZ 定义为:
![](../../img/apoz.png)
#### 用法
PyTorch 代码
```python
from nni.compression.torch import ActivationAPoZRankFilterPruner
config_list = [{
'sparsity': 0.5,
'op_types': ['Conv2d']
}]
pruner = ActivationAPoZRankFilterPruner(model, config_list, statistics_batch_num=1)
pruner.compress()
```
from nni.compression.torch import SlimPruner
config_list = [{ 'sparsity': 0.8, 'op_types': ['BatchNorm2d'] }]
pruner = SlimPruner(model, config_list)
注意:ActivationAPoZRankFilterPruner 用于修剪深度神经网络中的卷积层,因此 `op_types` 字段仅支持卷积层。
查看示例进一步了解
#### ActivationAPoZRankFilterPruner 的用户配置
- **sparsity:** 卷积过滤器要修剪的百分比。
- **op_types:** 在 ActivationAPoZRankFilterPruner 中仅支持 Conv2d。
***
### ActivationMeanRankFilterPruner
其实现为一次性修剪器,基于 `平均激活` 准则来修剪卷积层,在论文 [Pruning Convolutional Neural Networks for Resource Efficient Inference](https://arxiv.org/abs/1611.06440) 的 2.2 节中有说明。 本文中提到的其他修剪标准将在以后的版本中支持。
#### 用法
PyTorch 代码
```python
from nni.compression.torch import ActivationMeanRankFilterPruner
config_list = [{
'sparsity': 0.5,
'op_types': ['Conv2d']
}]
pruner = ActivationMeanRankFilterPruner(model, config_list)
pruner.compress()
```
#### Slim Pruner 的用户配置
注意:ActivationMeanRankFilterPruner 用于修剪深度神经网络中的卷积层,因此 `op_types` 字段仅支持卷积层。
- **sparsity:**,指定压缩的稀疏度。
- **op_types:** 在 Slim Pruner 中仅支持 BatchNorm2d。
查看示例进一步了解
#### ActivationMeanRankFilterPruner 的用户配置
- **sparsity:** 卷积过滤器要修剪的百分比。
- **op_types:** 在 ActivationMeanRankFilterPruner 中仅支持 Conv2d。
***
\ No newline at end of file
NNI Compressor 中的 Quantizer
===
## Naive Quantizer
Naive Quantizer 将 Quantizer 权重默认设置为 8 位,可用它来测试量化算法。
### 用法
Tensorflow
```python
nni.compressors.tensorflow.NaiveQuantizer(model_graph).compress()
tensorflow ```python nni.compression.tensorflow.NaiveQuantizer(model_graph).compress()
```
PyTorch
```python
nni.compressors.torch.NaiveQuantizer(model).compress()
pytorch
```python nni.compression.torch.NaiveQuantizer(model).compress()
```
***
......@@ -27,7 +23,7 @@ nni.compressors.torch.NaiveQuantizer(model).compress()
PyTorch 代码
```python
from nni.compressors.torch import QAT_Quantizer
from nni.compression.torch import QAT_Quantizer
model = Mnist()
config_list = [{
......@@ -49,9 +45,13 @@ quantizer.compress()
查看示例进一步了解
#### QAT Quantizer 的用户配置
* **quant_types:**: 字符串列表 要应用的量化类型,当前支持 'weight', 'input', 'output'
* **quant_bits:** int 或 {str : int} 的 dict 量化的位长,主键是量化类型,键值为长度,例如。 {'weight', 8}, 当类型为 int 时,所有量化类型都用同样的位长
* **quant_start_step:** int 在运行到某步骤前,对模型禁用量化。这让网络在进入更稳定的 状态后再激活量化,这样不会配除掉一些分数显著的值,默认为 0
压缩算法所需的常见配置可在[通用配置](./Overview.md#User-configuration-for-a-compression-algorithm)中找到。
此算法所需的配置:
* **quant_start_step:** int
在运行到某步骤前,对模型禁用量化。这让网络在进入更稳定的 状态后再激活量化,这样不会配除掉一些分数显著的值,默认为 0
### 注意
当前不支持批处理规范化折叠。
......@@ -63,17 +63,14 @@ quantizer.compress()
### 用法
要实现 DoReFa Quantizer,在训练代码前加入以下代码。
TensorFlow 代码
```python
from nni.compressors.tensorflow import DoReFaQuantizer
config_list = [{ 'q_bits': 8, 'op_types': 'default' }]
quantizer = DoReFaQuantizer(tf.get_default_graph(), config_list)
quantizer.compress()
```
PyTorch 代码
```python
from nni.compressors.torch import DoReFaQuantizer
config_list = [{ 'q_bits': 8, 'op_types': 'default' }]
from nni.compression.torch import DoReFaQuantizer
config_list = [{
'quant_types': ['weight'],
'quant_bits': 8,
'op_types': 'default'
}]
quantizer = DoReFaQuantizer(model, config_list)
quantizer.compress()
```
......@@ -81,4 +78,52 @@ quantizer.compress()
查看示例进一步了解
#### DoReFa Quantizer 的用户配置
* **q_bits:** 指定需要被量化的位数。
压缩算法所需的常见配置可在[通用配置](./Overview.md#User-configuration-for-a-compression-algorithm)中找到。
此算法所需的配置:
## BNN Quantizer
在 [Binarized Neural Networks: Training Deep Neural Networks with Weights and Activations Constrained to +1 or -1](https://arxiv.org/abs/1602.02830) 中,
> 引入了一种训练二进制神经网络(BNN)的方法 - 神经网络在运行时使用二进制权重。 在训练时,二进制权重和激活用于计算参数梯度。 在 forward 过程中,BNN 会大大减少内存大小和访问,并将大多数算术运算替换为按位计算,可显著提高能源效率。
### 用法
PyTorch 代码
```python
from nni.compression.torch import BNNQuantizer
model = VGG_Cifar10(num_classes=10)
configure_list = [{
'quant_bits': 1,
'quant_types': ['weight'],
'op_types': ['Conv2d', 'Linear'],
'op_names': ['features.0', 'features.3', 'features.7', 'features.10', 'features.14', 'features.17', 'classifier.0', 'classifier.3']
}, {
'quant_bits': 1,
'quant_types': ['output'],
'op_types': ['Hardtanh'],
'op_names': ['features.6', 'features.9', 'features.13', 'features.16', 'features.20', 'classifier.2', 'classifier.5']
}]
quantizer = BNNQuantizer(model, configure_list)
model = quantizer.compress()
```
可以查看示例 [examples/model_compress/BNN_quantizer_cifar10.py](https://github.com/microsoft/nni/tree/master/examples/model_compress/BNN_quantizer_cifar10.py) 了解更多信息。
#### BNN Quantizer 的用户配置
压缩算法所需的常见配置可在[通用配置](./Overview.md#User-configuration-for-a-compression-algorithm)中找到。
此算法所需的配置:
### 实验
我们实现了 [Binarized Neural Networks: Training Deep Neural Networks with Weights and Activations Constrained to +1 or -1](https://arxiv.org/abs/1602.02830) 中的一个实验,对 CIFAR-10 上的 **VGGNet** 进行了量化操作。 我们的实验结果如下:
| 模型 | 精度 |
| ------ | ------ |
| VGGNet | 86.93% |
实验代码可在 [examples/model_compress/BNN_quantizer_cifar10.py](https://github.com/microsoft/nni/tree/master/examples/model_compress/BNN_quantizer_cifar10.py)
\ No newline at end of file
NNI Compressor 中的 L1FilterPruner
NN I 上的 L1FilterPruner
===
## 1. 介绍
## 介绍
L1FilterPruner 是在卷积层中用来修剪过滤器的通用剪枝算法。
......@@ -18,25 +18,9 @@ L1FilterPruner 是在卷积层中用来修剪过滤器的通用剪枝算法。
> 3. 修剪 ![](http://latex.codecogs.com/gif.latex?m) 具有最小求和值及其相应特征图的筛选器。 在 下一个卷积层中,被剪除的特征图所对应的内核也被移除。
> 4. 为第 ![](http://latex.codecogs.com/gif.latex?i) 和 ![](http://latex.codecogs.com/gif.latex?i+1) 层创建新的内核举证,并保留剩余的内核 权重,并复制到新模型中。
## 2. 用法
## 实验
PyTorch 代码
```
from nni.compression.torch import L1FilterPruner
config_list = [{ 'sparsity': 0.8, 'op_types': ['Conv2d'], 'op_names': ['conv1', 'conv2'] }]
pruner = L1FilterPruner(model, config_list)
pruner.compress()
```
#### L1Filter Pruner 的用户配置
- **sparsity:**,指定压缩的稀疏度。
- **op_types:** 在 L1Filter Pruner 中仅支持 Conv2d。
## 3. 实验
我们实现了 ['PRUNING FILTERS FOR EFFICIENT CONVNETS'](https://arxiv.org/abs/1608.08710) 中的一项实验, 即论文中,在 CIFAR-10 数据集上修剪 **VGG-16****VGG-16-pruned-A**,其中大约剪除了 $64\%$ 的参数。 我们的实验结果如下:
我们通过 **L1FilterPruner** 实现了 ['PRUNING FILTERS FOR EFFICIENT CONVNETS'](https://arxiv.org/abs/1608.08710) 中的一项实验, 即论文中,在 CIFAR-10 数据集上修剪 **VGG-16****VGG-16-pruned-A**,其中大约剪除了 $64\%$ 的参数。 我们的实验结果如下:
| 模型 | 错误率(论文/我们的) | 参数量 | 剪除率 |
| --------------- | ----------- | -------- | ----- |
......
# 特征工程
# NNI 中的特征工程
我们很高兴的宣布,基于 NNI 的特征工程工具发布了 Alpha 版本。该版本仍处于试验阶段,根据使用反馈会进行改进。 诚挚邀请您使用、反馈,或更多贡献。
我们很高兴的宣布,基于 NNI 的特征工程工具发布了试用版本。该版本仍处于试验阶段,根据使用反馈会进行改进。 诚挚邀请您使用、反馈,或更多贡献。
当前支持以下特征选择器:
- [GradientFeatureSelector](./GradientFeatureSelector.md)
......@@ -253,4 +253,11 @@ print("Pipeline Score: ", pipeline.score(X_train, y_train))
此基准测试可在[这里](https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/)下载
代码参考 `/examples/feature_engineering/gradient_feature_selector/benchmark_test.py`
代码参考 `/examples/feature_engineering/gradient_feature_selector/benchmark_test.py`
## **参考和反馈**
* 在 GitHub 中[提交此功能的 Bug](https://github.com/microsoft/nni/issues/new?template=bug-report.md)
* 在 GitHub 中[提交新功能或改进请求](https://github.com/microsoft/nni/issues/new?template=enhancement.md)
* 了解 NNI 中[神经网络结构搜索的更多信息](https://github.com/microsoft/nni/blob/master/docs/zh_CN/NAS/Overview.md)
* 了解 NNI 中[模型自动压缩的更多信息](https://github.com/microsoft/nni/blob/master/docs/zh_CN/Compressor/Overview.md)
* 了解如何[使用 NNI 进行超参数调优](https://github.com/microsoft/nni/blob/master/docs/zh_CN/Tuner/BuiltinTuner.md)
......@@ -105,3 +105,10 @@ python3 retrain.py --arc-checkpoint ../pdarts/checkpoints/epoch_2.json
2. 在神经网络上应用 NAS 时,需要统一的方式来表达架构的搜索空间,这样不必为不同的搜索算法来更改代码。
NNI 提出的 API 在[这里](https://github.com/microsoft/nni/tree/master/src/sdk/pynni/nni/nas/pytorch)[这里](https://github.com/microsoft/nni/tree/master/examples/nas/darts)包含了基于此 API 的 NAS 实现示例。
## **参考和反馈**
* 在 GitHub 中[提交此功能的 Bug](https://github.com/microsoft/nni/issues/new?template=bug-report.md)
* 在 GitHub 中[提交新功能或改进请求](https://github.com/microsoft/nni/issues/new?template=enhancement.md)
* 了解 NNI 中[特征工程的更多信息](https://github.com/microsoft/nni/blob/master/docs/zh_CN/FeatureEngineering/Overview.md)
* 了解 NNI 中[模型自动压缩的更多信息](https://github.com/microsoft/nni/blob/master/docs/zh_CN/Compressor/Overview.md)
* 了解如何[使用 NNI 进行超参数调优](https://github.com/microsoft/nni/blob/master/docs/zh_CN/Tuner/BuiltinTuner.md)
# P-DARTS
## 示例
[示例代码](https://github.com/microsoft/nni/tree/master/examples/nas/pdarts)
```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
```
# 单路径 One-Shot (SPOS)
## 介绍
[Single Path One-Shot Neural Architecture Search with Uniform Sampling](https://arxiv.org/abs/1904.00420) 中提出的 one-shot NAS 方法,通过构造简化的通过统一路径采样方法训练的超网络来解决 One-Shot 模型训练的问题。这样所有架构(及其权重)都得到了完全且平等的训练。 然后,采用进化算法无需任何微调即可有效的搜索出性能最佳的体系结构。
在 NNI 上的实现基于 [官方 Repo](https://github.com/megvii-model/SinglePathOneShot). 实现了一个训练超级网络的 Trainer,以及一个利用 NNI 框架能力来加速进化搜索阶段的进化 Tuner。 还展示了
## 示例
此示例是论文中的搜索空间,使用 flops 限制来执行统一的采样方法。
[示例代码](https://github.com/microsoft/nni/tree/master/examples/nas/spos)
### 必需组件
由于使用了 DALI 来加速 ImageNet 的数据读取,需要 NVIDIA DALI >= 0.16。 [安装指南](https://docs.nvidia.com/deeplearning/sdk/dali-developer-guide/docs/installation.html)
[这里](https://1drv.ms/u/s!Am_mmG2-KsrnajesvSdfsq_cN48?e=aHVppN) ( [Megvii](https://github.com/megvii-model) 维护) 下载 flops 查找表。 将 `op_flops_dict.pkl``checkpoint-150000.pth.tar` (如果不需要重新训练超网络) 放到 `data` 目录中。
准备标准格式的 ImageNet (参考[这里的脚本](https://gist.github.com/BIGBALLON/8a71d225eff18d88e469e6ea9b39cef4))。 将其链接到 `data/imagenet` 会更方便。
准备好后,应具有以下代码结构:
```
spos
├── architecture_final.json
├── blocks.py
├── config_search.yml
├── data
│ ├── imagenet
│ │ ├── train
│ │ └── val
│ └── op_flops_dict.pkl
├── dataloader.py
├── network.py
├── readme.md
├── scratch.py
├── supernet.py
├── tester.py
├── tuner.py
└── utils.py
```
### 步骤 1. 训练超网络
```
python supernet.py
```
会将检查点导出到 `checkpoints` 目录中,为下一步做准备。
注意:数据加载的官方 Repo [与通常的方法有所不同](https://github.com/megvii-model/SinglePathOneShot/issues/5),使用了 BGR 张量,以及 0 到 255 之间的值来与自己的深度学习框架对齐。 选项 `--spos-preprocessing` 会模拟原始的使用行为,并能使用预训练的检查点。
### 步骤 2. 进化搜索
单路径 One-Shot 利用进化算法来搜索最佳架构。 tester 负责通过训练图像的子集来测试采样的体系结构,重新计算所有批处理规范,并在完整的验证集上评估架构。
为了使 Tuner 识别 flops 限制并能计算 flops,在 `tuner.py` 中创建了新的 `EvolutionWithFlops` Tuner,其继承于 SDK 中的 tuner。
要为 NNI 框架准备好搜索空间,首先运行
```
nnictl ss_gen -t "python tester.py"
```
将生成 `nni_auto_gen_search_space.json` 文件,这是搜索空间的序列化形式。
默认情况下,它将使用前面下载的 `checkpoint-150000.pth.tar`。 如果要使用从自行训练的检查点,在 `config_search.yml` 中的命令上指定 `---checkpoint`
然后使用进化 Tuner 搜索。
```
nnictl create --config config_search.yml
```
从每个 Epoch 导出的最终架构可在 Tuner 工作目录下的 `checkpoints` 中找到,默认值为 `$HOME/nni/experiments/your_experiment_id/log`
### 步骤 3. 从头开始训练
```
python scratch.py
```
默认情况下,它将使用 `architecture_final.json`. 该体系结构由官方仓库提供(转换成了 NNI 格式)。 通过 `--fixed-arc` 选项,可使用任何结构(例如,步骤 2 中找到的结构)。
## 参考
### PyTorch
```eval_rst
.. autoclass:: nni.nas.pytorch.spos.SPOSEvolution
:members:
.. automethod:: __init__
.. autoclass:: nni.nas.pytorch.spos.SPOSSupernetTrainer
:members:
.. automethod:: __init__
.. autoclass:: nni.nas.pytorch.spos.SPOSSupernetTrainingMutator
:members:
.. automethod:: __init__
```
## 已知的局限
* 仅支持 Block 搜索。 尚不支持通道搜索。
* 仅提供 GPU 版本。
## 当前重现结果
重现中。 由于官方版本和原始论文之间的不同,我们将当前结果与官方 Repo(我们运行的结果)和论文进行了比较。
* 进化阶段几乎与官方 Repo 一致。 进化算法显示出了收敛趋势,在搜索结束时达到约 65% 的精度。 但此结果与论文不一致。 详情参考[此 issue](https://github.com/megvii-model/SinglePathOneShot/issues/6)
* 重新训练阶段未匹配。 我们的重新训练代码,使用了作者发布的架构,获得了 72.14% 的准确率,与官方发布的 73.61%,和原始论文中的 74.3% 有一定差距。
......@@ -37,7 +37,7 @@ Experiment 的运行过程为:Tuner 接收搜索空间并生成配置。 这
>
> 第二步:[改动模型代码](TrialExample/Trials.md)
>
> 第三步:[>定义 Experiment 配置](Tutorial/ExperimentConfig.md)
> 第三步:[定义 Experiment 配置](Tutorial/ExperimentConfig.md)
<p align="center">
<img src="https://user-images.githubusercontent.com/23273522/51816627-5d13db80-2302-11e9-8f3e-627e260203d5.jpg" alt="绘图"/>
......
......@@ -246,7 +246,7 @@
* 修复了在某些极端条件下,不能正确存储任务的取消状态。
* 修复在使用 SMAC Tuner 时,解析搜索空间的错误。
* 修复 CIFAR-10 例中的 broken pipe 问题。
* 修复 CIFAR-10 例中的 broken pipe 问题。
* 为本地训练和 NNI 管理器添加单元测试。
* 为远程服务器、OpenPAI 和 Kubeflow 训练平台在 Azure 中增加集成测试。
* 在 OpenPAI 客户端中支持 Pylon 路径。
......@@ -284,7 +284,7 @@
* [FrameworkController 训练平台](TrainingService/FrameworkControllerMode.md):支持使用在 Kubernetes 上使用 FrameworkController 运行。
* FrameworkController 是 Kubernetes 上非常通用的控制器(Controller),能用来运行基于各种机器学习框架的分布式作业,如 TensorFlow,Pytorch, MXNet 等。
* NNI 为作业定义了统一而简单的规范。
* 如何使用 FrameworkController 的 MNIST 例。
* 如何使用 FrameworkController 的 MNIST 例。
#### 改进用户体验
......@@ -324,7 +324,7 @@
### 新示例
* [FashionMnist](https://github.com/microsoft/nni/tree/master/examples/trials/network_morphism),使用 network morphism Tuner
* 使用 PyTorch 的[分布式 MNIST ](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-distributed-pytorch)
* 使用 PyTorch 的[分布式 MNIST ](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-distributed-pytorch)
## 发布 0.4 - 12/6/2018
......@@ -332,7 +332,7 @@
* [Kubeflow 训练平台](TrainingService/KubeflowMode.md)
* 支持 tf-operator
* 使用 Kubeflow 的[分布式 Trial ](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-distributed/dist_mnist.py)
* 使用 Kubeflow 的[分布式 Trial ](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-distributed/dist_mnist.py)
* [遍历搜索 Tuner](Tuner/GridsearchTuner.md)
* [Hyperband Tuner](Tuner/HyperbandAdvisor.md)
* 支持在 MAC 上运行 NNI Experiment
......@@ -372,7 +372,7 @@
### API 的新功能和更新
* <span style="color:red"><strong>不兼容的改动</strong></span>:nn.get_parameters() 改为 nni.get_next_parameter。 所有以前版本的例将无法在 v0.3 上运行,需要重新克隆 NNI 代码库获取新例。 如果在自己的代码中使用了 NNI,也需要相应的更新。
* <span style="color:red"><strong>不兼容的改动</strong></span>:nn.get_parameters() 改为 nni.get_next_parameter。 所有以前版本的例将无法在 v0.3 上运行,需要重新克隆 NNI 代码库获取新例。 如果在自己的代码中使用了 NNI,也需要相应的更新。
* 新 API **nni.get_sequence_id()**。 每个 Trial 任务都会被分配一个唯一的序列数字,可通过 nni.get_sequence_id() API 来获取。
......@@ -400,9 +400,9 @@
docker pull msranni/nni:latest
```
* 新的 Trial 例:[NNI Sklearn ](https://github.com/microsoft/nni/tree/master/examples/trials/sklearn)
* 新的 Trial 例:[NNI Sklearn ](https://github.com/microsoft/nni/tree/master/examples/trials/sklearn)
* 新的竞赛例:[Kaggle Competition TGS Salt](https://github.com/microsoft/nni/tree/master/examples/trials/kaggle-tgs-salt)
* 新的竞赛例:[Kaggle Competition TGS Salt](https://github.com/microsoft/nni/tree/master/examples/trials/kaggle-tgs-salt)
### 其它
......@@ -420,7 +420,7 @@
* [SMAC](https://www.cs.ubc.ca/~hutter/papers/10-TR-SMAC.pdf) 基于 Sequential Model-Based Optimization (SMBO). 它会利用使用过的结果好的模型(高斯随机过程模型),并将随机森林引入到 SMBO 中,来处理分类参数。 NNI 的 SMAC 通过包装 [SMAC3](https://github.com/automl/SMAC3) 来支持。
* 支持将 NNI 安装在 [conda](https://conda.io/docs/index.html) 和 Python 虚拟环境中。
* 其它
* 更新 ga squad 例与相关文档
* 更新 ga squad 例与相关文档
* 用户体验改善及 Bug 修复
## 发布 0.1.0 - 9/10/2018 (首个版本)
......
......@@ -32,7 +32,7 @@
参考[Kubeflow 训练平台](KubeflowMode.md)的设计,FrameworkController 训练平台与其类似。
##
##
FrameworkController 配置文件的格式如下:
......@@ -93,7 +93,7 @@ frameworkcontrollerConfig:
注意:如果用 FrameworkController 模式运行,需要在 YAML 文件中显式设置 `trainingServicePlatform: frameworkcontroller`
FrameworkController 模式的 Trial 配置格式,是 FrameworkController 官方配置的简化版。参考 [frameworkcontroller 的 tensorflow ](https://github.com/Microsoft/frameworkcontroller/blob/master/example/framework/scenario/tensorflow/cpu/tensorflowdistributedtrainingwithcpu.yaml) 了解详情。
FrameworkController 模式的 Trial 配置格式,是 FrameworkController 官方配置的简化版。参考 [frameworkcontroller 的 tensorflow ](https://github.com/Microsoft/frameworkcontroller/blob/master/example/framework/scenario/tensorflow/cpu/tensorflowdistributedtrainingwithcpu.yaml) 了解详情。
frameworkcontroller 模式中的 Trial 配置使用以下主键:
......
# **教程:使用 NNI API 在本地创建和运行 Experiment**
本教程会使用 [~/examples/trials/mnist-tfv1] 例来解释如何在本地使用 NNI API 来创建并运行 Experiment。
本教程会使用 [~/examples/trials/mnist-tfv1] 例来解释如何在本地使用 NNI API 来创建并运行 Experiment。
> 在开始前
......@@ -78,9 +78,9 @@
**准备 Trial**
> 在克隆代码后,可以在 ~/nni/examples 中找到一些例,运行 `ls examples/trials` 查看所有 Trial 例。
> 在克隆代码后,可以在 ~/nni/examples 中找到一些例,运行 `ls examples/trials` 查看所有 Trial 例。
先从 NNI 提供的简单 Trial 例,如 MNIST 开始。 NNI 例在代码目录的 examples 中,运行 `ls ~/nni/examples/trials` 可以看到所有 Experiment 的例。 执行下面的命令可轻松运行 NNI 的 mnist 例:
先从 NNI 提供的简单 Trial 例,如 MNIST 开始。 NNI 例在代码目录的 examples 中,运行 `ls ~/nni/examples/trials` 可以看到所有 Experiment 的例。 执行下面的命令可轻松运行 NNI 的 mnist 例:
python ~/nni/examples/trials/mnist-annotation/mnist.py
......@@ -97,7 +97,7 @@
*builtinTunerName* 用来指定 NNI 中的 Tuner,*classArgs* 是传入到 Tuner的参数(内置 Tuner 在[这里](../Tuner/BuiltinTuner.md)),*optimization_mode* 表明需要最大化还是最小化 Trial 的结果。
**准备配置文件**:实现 Trial 的代码,并选择或实现自定义的 Tuner 后,就要准备 YAML 配置文件了。 NNI 为每个 Trial 例都提供了演示的配置文件,用命令`cat ~/nni/examples/trials/mnist-annotation/config.yml` 来查看其内容。 大致内容如下:
**准备配置文件**:实现 Trial 的代码,并选择或实现自定义的 Tuner 后,就要准备 YAML 配置文件了。 NNI 为每个 Trial 例都提供了演示的配置文件,用命令`cat ~/nni/examples/trials/mnist-annotation/config.yml` 来查看其内容。 大致内容如下:
```yaml
authorName: your_name
......
......@@ -36,10 +36,13 @@ trial:
cpuNum: 1
memoryMB: 8196
image: msranni/nni:latest
# 配置访问的 OpenPAI 集群
virtualCluster: default
nniManagerNFSMountPath: /home/user/mnt
containerNFSMountPath: /mnt/data/user
# 配置要访问的 OpenPAI 集群
paiConfig:
userName: your_pai_nni_user
passWord: your_pai_password
token: your_pai_token
host: 10.1.1.1
```
......@@ -56,56 +59,12 @@ paiConfig:
* [Docker Hub](https://hub.docker.com/) 上有预制的 NNI Docker 映像 [nnimsra/nni](https://hub.docker.com/r/msranni/nni/)。 它包含了用来启动 NNI Experiment 所依赖的所有 Python 包,Node 模块和 JavaScript。 生成此 Docker 映像的文件在[这里](https://github.com/Microsoft/nni/tree/master/deployment/docker/Dockerfile)。 可以直接使用此映像,或参考它来生成自己的映像。
* virtualCluster
* 可选。 设置 OpenPAI 的 virtualCluster,即虚拟集群。 如果未设置此参数,将使用默认(default)虚拟集群。
* shmMB
* 可选。 设置 OpenPAI 的 shmMB,即 Docker 中的共享内存。
* authFile
* 可选。在使用 pai 模式时,为私有 Docker 仓库设置认证文件,[见参考文档](https://github.com/microsoft/pai/blob/2ea69b45faa018662bc164ed7733f6fdbb4c42b3/docs/faq.md#q-how-to-use-private-docker-registry-job-image-when-submitting-an-openpai-job)。提供 authFile 的本地路径即可, NNI 会上传此文件。
* portList
* 可选。 设置 OpenPAI 的 portList。指定了容器中使用的端口列表,[参考文档](https://github.com/microsoft/pai/blob/b2324866d0280a2d22958717ea6025740f71b9f0/docs/job_tutorial.md#specification)
示例如下:
portList:
- label: test
beginAt: 8080
portNumber: 2
假设需要在 MNIST 示例中使用端口来运行 TensorBoard。 第一步是编写 `mnist.py` 的包装脚本 `launch_pai.sh`
```bash
export TENSORBOARD_PORT=PAI_PORT_LIST_${PAI_CURRENT_TASK_ROLE_NAME}_0_tensorboard
tensorboard --logdir . --port ${!TENSORBOARD_PORT} &
python3 mnist.py
```
portList 的配置部分如下:
```yaml
trial:
command: bash launch_pai.sh
portList:
- label: tensorboard
beginAt: 0
portNumber: 1
```
NNI 支持 OpenPAI 中的两种认证授权方法,即密码和 Token,[参考](https://github.com/microsoft/pai/blob/b6bd2ab1c8890f91b7ac5859743274d2aa923c22/docs/rest-server/API.md#2-authentication)。 认证在 `paiConfig` 字段中配置。
密码认证的 `paiConfig` 配置如下:
paiConfig:
userName: your_pai_nni_user
passWord: your_pai_password
host: 10.1.1.1
Token 认证的 `paiConfig` 配置如下:
paiConfig:
userName: your_pai_nni_user
token: your_pai_token
host: 10.1.1.1
* nniManagerNFSMountPath
* 必填。 在 nniManager 计算机上设置挂载的路径。
* containerNFSMountPath
* 必填。 在 OpenPAI 的容器中设置挂载路径。
* paiStoragePlugin
* 必填。 设置 PAI 中使用的存储插件的名称。
完成并保存 NNI Experiment 配置文件后(例如可保存为:exp_pai.yml),运行以下命令:
......@@ -126,9 +85,7 @@ Token 认证的 `paiConfig` 配置如下:
## 数据管理
如果训练数据集不大,可放在 codeDir 中,NNI会将其上传到 HDFS,或者构建 Docker 映像来包含数据。 如果数据集非常大,则不可放在 codeDir 中,可参考此[指南](https://github.com/microsoft/pai/blob/master/docs/user/storage.md)来将数据目录挂载到容器中。
如果要将 Trial 的其它输出保存到 HDFS 上,如模型文件等,需要在 Trial 代码中使用 `NNI_OUTPUT_DIR` 来保存输出文件。NNI 的 SDK 会将文件从 Trial 容器的 `NNI_OUTPUT_DIR` 复制到 HDFS 上,目标路径为:`hdfs://host:port/{username}/nni/{experiments}/{experimentId}/trials/{trialId}/nnioutput`
使用 NNI 启动 Experiment 前,应在 nniManager 计算机中设置相应的挂载数据的路径。 OpenPAI 有自己的存储(NFS、AzureBlob ...),在 PAI 中使用的存储将在启动作业时挂载到容器中。 应通过 `paiStoragePlugin` 字段选择 OpenPAI 中的存储类型。 然后,应将存储挂载到 nniManager 计算机上,并在配置文件中设置 `nniManagerNFSMountPath`,NNI会生成 bash 文件并将 `codeDir` 中的数据拷贝到 `nniManagerNFSMountPath` 文件夹中,然后启动 Trial 任务。 `nniManagerNFSMountPath` 中的数据会同步到 OpenPAI 存储中,并挂载到 OpenPAI 的容器中。 容器中的数据路径在 `containerNFSMountPath` 设置,NNI 将进入该文件夹,运行脚本启动 Trial 任务。
## 版本校验
......
**在 OpenPAIYarn 上运行 Experiment**
===
原始的 `pai` 模式改为了 `paiYarn` 模式,这是基于 Yarn 的分布式训练平台。
## 设置环境
参考[指南](../Tutorial/QuickStart.md)安装 NNI。
## 运行 Experiment
`examples/trials/mnist-annotation` 为例。 NNI 的 YAML 配置文件如下:
```yaml
authorName: your_name
experimentName: auto_mnist
# 并发运行的 Trial 数量
trialConcurrency: 2
# Experiment 的最长持续运行时间
maxExecDuration: 3h
# 空表示一直运行
maxTrialNum: 100
# 可选项: local, remote, pai, paiYarn
trainingServicePlatform: paiYarn
# 搜索空间文件
searchSpacePath: search_space.json
# 可选项: true, false
useAnnotation: true
tuner:
builtinTunerName: TPE
classArgs:
optimize_mode: maximize
trial:
command: python3 mnist.py
codeDir: ~/nni/examples/trials/mnist-annotation
gpuNum: 0
cpuNum: 1
memoryMB: 8196
image: msranni/nni:latest
# 配置访问的 OpenpaiYarn 集群
paiYarnConfig:
userName: your_paiYarn_nni_user
passWord: your_paiYarn_password
host: 10.1.1.1
```
注意:如果用 paiYarn 模式运行,需要在 YAML 文件中设置 `trainingServicePlatform: paiYarn`
[本机模式](LocalMode.md),以及[远程计算机模式](RemoteMachineMode.md)相比,paiYarn 模式的 Trial 有额外的配置:
* cpuNum
* 必填。 Trial 程序的 CPU 需求,必须为正数。
* memoryMB
* 必填。 Trial 程序的内存需求,必须为正数。
* image
* 必填。 在 paiYarn 模式中,Trial 程序由 OpenpaiYarn 在 [Docker 容器](https://www.docker.com/)中安排运行。 此键用来指定 Trial 程序的容器使用的 Docker 映像。
* [Docker Hub](https://hub.docker.com/) 上有预制的 NNI Docker 映像 [nnimsra/nni](https://hub.docker.com/r/msranni/nni/)。 它包含了用来启动 NNI Experiment 所依赖的所有 Python 包,Node 模块和 JavaScript。 生成此 Docker 映像的文件在[这里](https://github.com/Microsoft/nni/tree/master/deployment/docker/Dockerfile)。 可以直接使用此映像,或参考它来生成自己的映像。
* virtualCluster
* 可选。 设置 OpenPAIYarn 的 virtualCluster,即虚拟集群。 如果未设置此参数,将使用默认(default)虚拟集群。
* shmMB
* 可选。 设置 OpenPAIYarn 的 shmMB,即 Docker 中的共享内存。
* authFile
* 可选。在使用 paiYarn 模式时,为私有 Docker 仓库设置认证文件,[见参考文档](https://github.com/microsoft/paiYarn/blob/2ea69b45faa018662bc164ed7733f6fdbb4c42b3/docs/faq.md#q-how-to-use-private-docker-registry-job-image-when-submitting-an-openpaiYarn-job)。提供 authFile 的本地路径即可, NNI 会上传此文件。
* portList
* 可选。 设置 OpenPAIYarn 的 portList。指定了容器中使用的端口列表,[参考文档](https://github.com/microsoft/paiYarn/blob/b2324866d0280a2d22958717ea6025740f71b9f0/docs/job_tutorial.md#specification)<br /> 示例如下: NNI 中的配置架构如下所示:
```
portList:
- label: test
beginAt: 8080
portNumber: 2
```
假设需要在 MNIST 示例中使用端口来运行 TensorBoard。 第一步是编写 `mnist.py` 的包装脚本 `launch_paiYarn.sh`
```bash
export TENSORBOARD_PORT=paiYarn_PORT_LIST_${paiYarn_CURRENT_TASK_ROLE_NAME}_0_tensorboard
tensorboard --logdir . --port ${!TENSORBOARD_PORT} &
python3 mnist.py
```
portList 的配置部分如下:
```yaml
trial:
command: bash launch_paiYarn.sh
portList:
- label: tensorboard
beginAt: 0
portNumber: 1
```
NNI 支持 OpenPAIYarn 中的两种认证授权方法,即密码和 paiYarn Token,[参考](https://github.com/microsoft/paiYarn/blob/b6bd2ab1c8890f91b7ac5859743274d2aa923c22/docs/rest-server/API.md#2-authentication)。 授权配置在 `paiYarnConfig` 字段中。 密码认证的 `paiYarnConfig` 配置如下:
```
paiYarnConfig:
userName: your_paiYarn_nni_user
passWord: your_paiYarn_password
host: 10.1.1.1
```
Token 认证的 `paiYarnConfig` 配置如下:
```
paiYarnConfig:
userName: your_paiYarn_nni_user
token: your_paiYarn_token
host: 10.1.1.1
```
完成并保存 NNI Experiment 配置文件后(例如可保存为:exp_paiYarn.yml),运行以下命令:
```
nnictl create --config exp_paiYarn.yml
```
来在 paiYarn 模式下启动 Experiment。 NNI 会为每个 Trial 创建 OpenPAIYarn 作业,作业名称的格式为 `nni_exp_{experiment_id}_trial_{trial_id}`。 可以在 OpenPAIYarn 集群的网站中看到 NNI 创建的作业,例如: ![](../../img/nni_paiYarn_joblist.jpg)
注意:paiYarn 模式下,NNIManager 会启动 RESTful 服务,监听端口为 NNI 网页服务器的端口加1。 例如,如果网页端口为`8080`,那么 RESTful 服务器会监听在 `8081`端口,来接收运行在 Kubernetes 中的 Trial 作业的指标。 因此,需要在防火墙中启用端口 `8081` 的 TCP 协议,以允许传入流量。
当一个 Trial 作业完成后,可以在 NNI 网页的概述页面(如:http://localhost:8080/oview)中查看 Trial 的信息。
在 Trial 列表页面中展开 Trial 信息,点击如下的 logPath: ![](../../img/nni_webui_joblist.jpg)
接着将会打开 HDFS 的 WEB 界面,并浏览到 Trial 的输出文件: ![](../../img/nni_trial_hdfs_output.jpg)
在输出目录中可以看到三个文件:stderr, stdout, 以及 trial.log
## 数据管理
如果训练数据集不大,可放在 codeDir 中,NNI会将其上传到 HDFS,或者构建 Docker 映像来包含数据。 如果数据集非常大,则不可放在 codeDir 中,可参考此[指南](https://github.com/microsoft/paiYarn/blob/master/docs/user/storage.md)来将数据目录挂载到容器中。
如果要将 Trial 的其它输出保存到 HDFS 上,如模型文件等,需要在 Trial 代码中使用 `NNI_OUTPUT_DIR` 来保存输出文件。NNI 的 SDK 会将文件从 Trial 容器的 `NNI_OUTPUT_DIR` 复制到 HDFS 上,目标路径为:`hdfs://host:port/{username}/nni/{experiments}/{experimentId}/trials/{trialId}/nnioutput`。
## 版本校验
从 0.6 开始,NNI 支持版本校验。确保 NNIManager 与 trialKeeper 的版本一致,避免兼容性错误。 检查策略:
1. 0.6 以前的 NNIManager 可与任何版本的 trialKeeper 一起运行,trialKeeper 支持向后兼容。
2. 从 NNIManager 0.6 开始,与 triakKeeper 的版本必须一致。 例如,如果 NNIManager 是 0.6 版,则 trialKeeper 也必须是 0.6 版。
3. 注意,只有版本的前两位数字才会被检查。例如,NNIManager 0.6.1 可以和 trialKeeper 的 0.6 或 0.6.2 一起使用,但不能与 trialKeeper 的 0.5.1 或 0.7 版本一起使用。
如果 Experiment 无法运行,而且不能确认是否是因为版本不匹配造成的,可以在 Web 界面检查是否有相关的错误消息。 ![](../../img/version_check.png)
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