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

add Chinese translation (#661)

parent b38c0431
###########################
Python API 参考
###########################
Trial
------------------------
.. autofunction:: nni.get_next_parameter
.. autofunction:: nni.get_current_parameter
.. autofunction:: nni.report_intermediate_result
.. autofunction:: nni.report_final_result
.. autofunction:: nni.get_sequence_id
Tuner
------------------------
.. autoclass:: nni.tuner.Tuner
:members:
.. autoclass:: nni.hyperopt_tuner.hyperopt_tuner.HyperoptTuner
:members:
.. autoclass:: nni.evolution_tuner.evolution_tuner.EvolutionTuner
:members:
.. autoclass:: nni.gridsearch_tuner.gridsearch_tuner.GridSearchTuner
:members:
.. autoclass:: nni.smac_tuner.smac_tuner.SMACTuner
:members:
Assessor
------------------------
.. autoclass:: nni.assessor.Assessor
:members:
.. autoclass:: nni.curvefitting_assessor.curvefitting_assessor.CurvefittingAssessor
:members:
.. autoclass:: nni.medianstop_assessor.medianstop_assessor.MedianstopAssessor
:members:
Advisor
------------------------
.. autoclass:: nni.hyperband_advisor.hyperband_advisor.Hyperband
\ No newline at end of file
# NNI 中使用 scikit-learn
[scikit-learn](https://github.com/scikit-learn/scikit-learn) (sklearn) 是数据挖掘和分析的流行工具。 它支持多种机器学习模型,如线性回归,逻辑回归,决策树,支持向量机等。 提高 scikit-learn 的效率是非常有价值的课题。
NNI 支持多种调优算法,可以为 scikit-learn 搜索最佳的模型和超参,并支持本机、远程服务器组、云等各种环境。
## 1. 如何运行此样例
安装 NNI 包,并使用命令行工具 `nnictl` 来启动 Experiment。 有关安装和环境准备的内容,参考[这里](QuickStart.md)。 安装完 NNI 后,进入相应的目录,输入下列命令即可启动 Experiment:
```bash
nnictl create --config ./config.yml
```
## 2. 样例概述
### 2.1 分类
此样例使用了数字数据集,由 1797 张 8x8 的图片组成,每张图片都是一个手写数字。目标是将这些图片分到 10 个类别中。
在此样例中,使用了 SVC 作为模型,并选择了一些参数,包括 `"C", "keral", "degree", "gamma" 和 "coef0"`。 关于这些参数的更多信息,可参考[这里](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html)
### 2.2 回归
此样例使用了波士顿房价数据,数据集由波士顿各地区房价所组成,还包括了房屋的周边信息,例如:犯罪率 (CRIM),非零售业务的面积 (INDUS),房主年龄 (AGE) 等等。这些信息可用来预测波士顿的房价。 本例中,尝试了不同的回归模型,包括 `"LinearRegression", "SVR", "KNeighborsRegressor", "DecisionTreeRegressor"` 和一些参数,如 `"svr_kernel", "knr_weights"`。 关于这些模型算法和参数的更多信息,可参考[这里](https://scikit-learn.org/stable/supervised_learning.html#supervised-learning)
## 3. 如何在 NNI 中使用 sklearn
只需要如下几步,即可在 sklearn 代码中使用 NNI。
* **第一步**
准备 search_space.json 文件来存储选择的搜索空间。 例如,如果要在不同的模型中选择:
```json
{
"model_name":{"_type":"choice","_value":["LinearRegression", "SVR", "KNeighborsRegressor", "DecisionTreeRegressor"]}
}
```
如果要选择不同的模型和参数,可以将它们放到同一个 search_space.json 文件中。
```json
{
"model_name":{"_type":"choice","_value":["LinearRegression", "SVR", "KNeighborsRegressor", "DecisionTreeRegressor"]},
"svr_kernel": {"_type":"choice","_value":["linear", "poly", "rbf"]},
"knr_weights": {"_type":"choice","_value":["uniform", "distance"]}
}
```
在 Python 代码中,可以将这些值作为一个 dict,读取到 Python 代码中。
* **第二步**
在代码最前面,加上 `import nni` 来导入 NNI 包。 首先,要使用 `nni.get_next_parameter()` 函数从 NNI 中获取参数。 然后在代码中使用这些参数。 例如,如果定义了如下的 search_space.json:
```json
{
"C": {"_type":"uniform","_value":[0.1, 1]},
"keral": {"_type":"choice","_value":["linear", "rbf", "poly", "sigmoid"]},
"degree": {"_type":"choice","_value":[1, 2, 3, 4]},
"gamma": {"_type":"uniform","_value":[0.01, 0.1]},
"coef0 ": {"_type":"uniform","_value":[0.01, 0.1]}
}
```
就会获得像下面一样的 dict:
```python
params = {
'C': 1.0,
'keral': 'linear',
'degree': 3,
'gamma': 0.01,
'coef0': 0.01
}
```
就可以使用这些变量来编写 scikit-learn 的代码。
* **第三步**
完成训练后,可以得到模型分数,如:精度,召回率,均方差等等。 NNI 会将分数发送给 Tuner 算法,并据此生成下一组参数,所以需要将分数返回给 NNI。NNI 会开始下一个 Trial 任务。
只需要在训练结束后调用 `nni.report_final_result(score)`,就可以将分数传给 NNI。 如果训练过程中有中间分数,也可以使用 `nni.report_intemediate_result(score)` 返回给 NNI。 注意, 可以不返回中间分数,但必须返回最终的分数。
\ No newline at end of file
NNI 支持的训练平台介绍
=====================================
.. toctree::
本机<tutorial_1_CR_exp_local_api>
远程<RemoteMachineMode>
OpenPAI<PAIMode>
Kubeflow<KubeflowMode>
FrameworkController<FrameworkControllerMode>
\ No newline at end of file
#################
Tuner(调参器)
#################
NNI 能用简单快速的方法来配置超参调优算法,称之为 **Tuner**。
Tuner 从 Trial 接收指标结果,来评估一组超参或网络结构的性能。 然后 Tuner 会将下一组超参或网络结构的配置发送给新的 Trial。
在 NNI 中,有两种方法来选择调优算法:可以使用内置的 Tuner,也可以自定义 Tuner。 另外,也可以使用 Advisor,它同时支持 Tuner 和 Assessor 的功能。
详细信息,参考以下教程:
.. toctree::
内置 Tuner<Builtin_Tuner>
自定义 Tuner<Customize_Tuner>
自定义 Advisor<Customize_Advisor>
\ No newline at end of file
# **教程:使用 NNI API 在本地创建和运行 Experiment**
本教程会使用 [~/examples/trials/mnist] 样例来解释如何在本地使用 NNI API 来创建并运行 Experiment。
> 在开始前
要有一个使用卷积层对 MNIST 分类的代码,如 `mnist_before.py`
> 第一步:更新模型代码
对代码进行以下改动来启用 NNI API:
1.1 声明 NNI API
在 Trial 代码中通过 `import nni` 来导入 NNI API。
1.2 获取预定义的参数
参考下列代码片段:
RECEIVED_PARAMS = nni.get_next_parameter()
来获得 Tuner 分配的超参值。 `RECEIVED_PARAMS` 是一个对象,例如:
{"conv_size": 2, "hidden_size": 124, "learning_rate": 0.0307, "dropout_rate": 0.2029}
1.3 返回结果
使用 API:
`nni.report_intermediate_result(accuracy)`
返回 `accuracy` 的值给 Assessor。
使用 API:
`nni.report_final_result(accuracy)`
返回 `accuracy` 的值给 Tuner。
将改动保存到 `mnist.py` 文件中。
**注意**
accuracy - 如果使用 NNI 内置的 Tuner/Assessor,那么 `accuracy` 必须是数值(如 float, int)。在定制 Tuner/Assessor 时 `accuracy` 可以是任何类型的 Python 对象。
Assessor(评估器)- 会根据 Trial 的历史值(即其中间结果),来决定这次 Trial 是否应该提前终止。
Tuner(调参器) - 会根据探索的历史(所有 Trial 的最终结果)来生成下一组参数、架构。
> 第二步:定义搜索空间
`Step 1.2 获取预定义的参数` 中使用的超参定义在 `search_space.json` 文件中:
{
"dropout_rate":{"_type":"uniform","_value":[0.1,0.5]},
"conv_size":{"_type":"choice","_value":[2,3,5,7]},
"hidden_size":{"_type":"choice","_value":[124, 512, 1024]},
"learning_rate":{"_type":"uniform","_value":[0.0001, 0.1]}
}
参考 [SearchSpaceSpec.md](./SearchSpaceSpec.md) 进一步了解搜索空间。
> 第三步:定义 Experiment
>
> > 3.1 启用 NNI API 模式
要启用 NNI 的 API 模式,需要将 useAnnotation 设置为 *false*,并提供搜索空间文件的路径(即第一步中定义的文件):
useAnnotation: false
searchSpacePath: /path/to/your/search_space.json
在 NNI 中运行 Experiment,只需要:
* 可运行的 Trial 的代码
* 实现或选择 Tuner
* 准备 YAML 的 Experiment 配置文件
* (可选) 实现或选择 Assessor
**准备 Trial**
> 在克隆代码后,可以在 ~/nni/examples 中找到一些样例,运行 `ls examples/trials` 查看所有 Trial 样例。
先从 NNI 提供的简单 Trial 样例,如 MNIST 开始。 NNI 样例在代码目录的 examples 中,运行 `ls ~/nni/examples/trials` 可以看到所有 Experiment 的样例。 执行下面的命令可轻松运行 NNI 的 mnist 样例:
python ~/nni/examples/trials/mnist-annotation/mnist.py
上面的命令会写在 YAML 文件中。 参考[这里](Trials.md)来写出自己的 Experiment 代码。
**准备 Tuner**: NNI 支持多种流行的自动机器学习算法,包括:Random Search(随机搜索),Tree of Parzen Estimators (TPE),Evolution(进化算法)等等。 也可以实现自己的 Tuner(参考[这里](Customize_Tuner.md))。下面使用了 NNI 内置的 Tuner:
tuner:
builtinTunerName: TPE
classArgs:
optimize_mode: maximize
*builtinTunerName* 用来指定 NNI 中的 Tuner,*classArgs* 是传入到 Tuner的参数(内置 Tuner 在[这里](Builtin_Tuner.md)),*optimization_mode* 表明需要最大化还是最小化 Trial 的结果。
**准备配置文件**:实现 Trial 的代码,并选择或实现自定义的 Tuner 后,就要准备 YAML 配置文件了。 NNI 为每个 Trial 样例都提供了演示的配置文件,用命令`cat ~/nni/examples/trials/mnist-annotation/config.yml` 来查看其内容。 大致内容如下:
authorName: your_name
experimentName: auto_mnist
# 并发运行数量
trialConcurrency: 2
# Experiment 运行时间
maxExecDuration: 3h
# 可为空,即数量不限
maxTrialNum: 100
# 可选值为: local, remote
trainingServicePlatform: local
# 可选值为: true, false
useAnnotation: true
tuner:
builtinTunerName: TPE
classArgs:
optimize_mode: maximize
trial:
command: python mnist.py
codeDir: ~/nni/examples/trials/mnist-annotation
gpuNum: 0
因为这个 Trial 代码使用了 NNI Annotation 的方法(参考[这里](AnnotationSpec.md) ),所以*useAnnotation* 为 true。 *command* 是运行 Trial 代码所需要的命令,*codeDir* 是 Trial 代码的相对位置。 命令会在此目录中执行。 同时,也需要提供每个 Trial 进程所需的 GPU 数量。
完成上述步骤后,可通过下列命令来启动 Experiment:
nnictl create --config ~/nni/examples/trials/mnist-annotation/config.yml
参考[这里](NNICTLDOC.md)来了解 *nnictl* 命令行工具的更多用法。
## 查看 Experiment 结果
Experiment 应该一直在运行。 除了 *nnictl* 以外,还可以通过 NNI 的网页来查看 Experiment 进程,进行控制和其它一些有意思的功能。
## 使用多个本地 GPU 加快搜索速度
下列步骤假设本机有 4 块 NVIDIA GPUs,参考 [tensorflow with GPU support](https://www.tensorflow.org/install/gpu)。 演示启用了 4 个并发的 Trial 任务,每个 Trial 任务使用了 1 块 GPU。
**准备配置文件**:NNI 提供了演示用的配置文件,使用 `cat examples/trials/mnist-annotation/config_gpu.yml` 来查看。 trailConcurrency 和 gpuNum 与基本配置文件不同:
...
# 可同时运行的 Trial 数量
trialConcurrency: 4
...
trial:
command: python mnist.py
codeDir: ~/nni/examples/trials/mnist-annotation
gpuNum: 1
用下列命令运行 Experiment:
nnictl create --config ~/nni/examples/trials/mnist-annotation/config_gpu.yml
可以用 *nnictl* 命令行工具或网页界面来跟踪训练过程。 *nvidia_smi* 命令行工具能在训练过程中查看 GPU 使用情况。
\ No newline at end of file
# 自定义 Assessor
*Assessor 从 Trial 中接收中间结果,并决定此 Trial 是否应该终止。 一旦 Trial 满足提前终止条件,Assessor 将终止此 Trial。*
因此,如果要自定义 Assessor,需要:
**1) 继承于 Assessor 基类,创建 Assessor 类**
```python
from nni.assessor import Assessor
class CustomizedAssessor(Assessor):
def __init__(self, ...):
...
```
**2) 实现评估 Trial 的函数**
```python
from nni.assessor import Assessor, AssessResult
class CustomizedAssessor(Assessor):
def __init__(self, ...):
...
def assess_trial(self, trial_history):
"""
决定是否应该终止 Trial。 必须重载。
trial_history: 中间结果列表对象。
返回 AssessResult.Good 或 AssessResult.Bad。
"""
# 代码实现于此处。
...
```
**3) 实现脚本来运行 Assessor**
```python
import argparse
import CustomizedAssessor
def main():
parser = argparse.ArgumentParser(description='parse command line parameters.')
# 在这里解析 Assessor 的参数。
...
FLAGS, unparsed = parser.parse_known_args()
tuner = CustomizedAssessor(...)
tuner.run()
main()
```
注意 2) 中, 对象 `trial_history``report_intermediate_result` 函数返回给 Assessor 的完全一致。
也可以重载 Assessor 的 `run` 函数来控制过程逻辑。
更多样例,可参考:
> - [Base-Assessor](https://msrasrg.visualstudio.com/NeuralNetworkIntelligenceOpenSource/_git/Default?_a=contents&path=%2Fsrc%2Fsdk%2Fpynni%2Fnni%2Fassessor.py&version=GBadd_readme)
\ No newline at end of file
# 如何在 NNI 中实现 Trial 的代码?
*Trial 从 Tuner 中接收超参和架构配置,并将中间结果发送给 Assessor,最终结果发送给Tuner 。*
当用户需要在 NNI 上运行 Trial 时,需要:
**1) 写好原始的训练代码**
Trial 的代码可以是任何能在本机运行的机器学习代码。 这里使用 `mnist-keras. py` 作为样例:
```python
import argparse
import logging
import keras
import numpy as np
from keras import backend as K
from keras.datasets import mnist
from keras.layers import Conv2D, Dense, Flatten, MaxPooling2D
from keras.models import Sequential
K.set_image_data_format('channels_last')
H, W = 28, 28
NUM_CLASSES = 10
def create_mnist_model(hyper_params, input_shape=(H, W, 1), num_classes=NUM_CLASSES):
layers = [
Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=input_shape),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D(pool_size=(2, 2)),
Flatten(),
Dense(100, activation='relu'),
Dense(num_classes, activation='softmax')
]
model = Sequential(layers)
if hyper_params['optimizer'] == 'Adam':
optimizer = keras.optimizers.Adam(lr=hyper_params['learning_rate'])
else:
optimizer = keras.optimizers.SGD(lr=hyper_params['learning_rate'], momentum=0.9)
model.compile(loss=keras.losses.categorical_crossentropy, optimizer=optimizer, metrics=['accuracy'])
return model
def load_mnist_data(args):
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = (np.expand_dims(x_train, -1).astype(np.float) / 255.)[:args.num_train]
x_test = (np.expand_dims(x_test, -1).astype(np.float) / 255.)[:args.num_test]
y_train = keras.utils.to_categorical(y_train, NUM_CLASSES)[:args.num_train]
y_test = keras.utils.to_categorical(y_test, NUM_CLASSES)[:args.num_test]
return x_train, y_train, x_test, y_test
class SendMetrics(keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs={}):
pass
def train(args, params):
x_train, y_train, x_test, y_test = load_mnist_data(args)
model = create_mnist_model(params)
model.fit(x_train, y_train, batch_size=args.batch_size, epochs=args.epochs, verbose=1,
validation_data=(x_test, y_test), callbacks=[SendMetrics()])
_, acc = model.evaluate(x_test, y_test, verbose=0)
def generate_default_params():
return {
'optimizer': 'Adam',
'learning_rate': 0.001
}
if __name__ == '__main__':
PARSER = argparse.ArgumentParser()
PARSER.add_argument("--batch_size", type=int, default=200, help="batch size", required=False)
PARSER.add_argument("--epochs", type=int, default=10, help="Train epochs", required=False)
PARSER.add_argument("--num_train", type=int, default=1000, help="Number of train samples to be used, maximum 60000", required=False)
PARSER.add_argument("--num_test", type=int, default=1000, help="Number of test samples to be used, maximum 10000", required=False)
ARGS, UNKNOWN = PARSER.parse_known_args()
PARAMS = generate_default_params()
train(ARGS, PARAMS)
```
**2) 从 Tuner 获取配置**
导入 `NNI` 并用 `nni.get_next_parameter()` 来接收参数。 注意代码中的 **10**, **24****25** 行。
```python
import argparse
import logging
import keras
import numpy as np
from keras import backend as K
from keras.datasets import mnist
from keras.layers import Conv2D, Dense, Flatten, MaxPooling2D
from keras.models import Sequential
import nni
...
if __name__ == '__main__':
PARSER = argparse.ArgumentParser()
PARSER.add_argument("--batch_size", type=int, default=200, help="batch size", required=False)
PARSER.add_argument("--epochs", type=int, default=10, help="Train epochs", required=False)
PARSER.add_argument("--num_train", type=int, default=1000, help="Number of train samples to be used, maximum 60000", required=False)
PARSER.add_argument("--num_test", type=int, default=1000, help="Number of test samples to be used, maximum 10000", required=False)
ARGS, UNKNOWN = PARSER.parse_known_args()
PARAMS = generate_default_params()
RECEIVED_PARAMS = nni.get_next_parameter()
PARAMS.update(RECEIVED_PARAMS)
train(ARGS, PARAMS)
```
**3) 发送中间结果**
`nni.report_intermediate_result` 将中间结果发送给 Assessor。 注意第 **5** 行。
```python
...
class SendMetrics(keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs={}):
nni.report_intermediate_result(logs)
def train(args, params):
x_train, y_train, x_test, y_test = load_mnist_data(args)
model = create_mnist_model(params)
model.fit(x_train, y_train, batch_size=args.batch_size, epochs=args.epochs, verbose=1,
validation_data=(x_test, y_test), callbacks=[SendMetrics()])
_, acc = model.evaluate(x_test, y_test, verbose=0)
...
```
**4) 发送最终结果**
`nni.report_final_result` 将最终结果发送给 Tuner。 注意第 **15** 行。
```python
...
class SendMetrics(keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs={}):
nni.report_intermediate_result(logs)
def train(args, params):
x_train, y_train, x_test, y_test = load_mnist_data(args)
model = create_mnist_model(params)
model.fit(x_train, y_train, batch_size=args.batch_size, epochs=args.epochs, verbose=1,
validation_data=(x_test, y_test), callbacks=[SendMetrics()])
_, acc = model.evaluate(x_test, y_test, verbose=0)
nni.report_final_result(acc)
...
```
这是完整的样例:
```python
import argparse
import logging
import keras
import numpy as np
from keras import backend as K
from keras.datasets import mnist
from keras.layers import Conv2D, Dense, Flatten, MaxPooling2D
from keras.models import Sequential
import nni
LOG = logging.getLogger('mnist_keras')
K.set_image_data_format('channels_last')
H, W = 28, 28
NUM_CLASSES = 10
def create_mnist_model(hyper_params, input_shape=(H, W, 1), num_classes=NUM_CLASSES):
'''
创建简单的卷积模型
'''
layers = [
Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=input_shape),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D(pool_size=(2, 2)),
Flatten(),
Dense(100, activation='relu'),
Dense(num_classes, activation='softmax')
]
model = Sequential(layers)
if hyper_params['optimizer'] == 'Adam':
optimizer = keras.optimizers.Adam(lr=hyper_params['learning_rate'])
else:
optimizer = keras.optimizers.SGD(lr=hyper_params['learning_rate'], momentum=0.9)
model.compile(loss=keras.losses.categorical_crossentropy, optimizer=optimizer, metrics=['accuracy'])
return model
def load_mnist_data(args):
'''
加载 MNIST 数据集
'''
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = (np.expand_dims(x_train, -1).astype(np.float) / 255.)[:args.num_train]
x_test = (np.expand_dims(x_test, -1).astype(np.float) / 255.)[:args.num_test]
y_train = keras.utils.to_categorical(y_train, NUM_CLASSES)[:args.num_train]
y_test = keras.utils.to_categorical(y_test, NUM_CLASSES)[:args.num_test]
LOG.debug('x_train shape: %s', (x_train.shape,))
LOG.debug('x_test shape: %s', (x_test.shape,))
return x_train, y_train, x_test, y_test
class SendMetrics(keras.callbacks.Callback):
'''
Keras 回调来返回中间结果给 NNI
'''
def on_epoch_end(self, epoch, logs={}):
'''
在每个 epoch 结束时运行
'''
LOG.debug(logs)
nni.report_intermediate_result(logs)
def train(args, params):
'''
训练模型
'''
x_train, y_train, x_test, y_test = load_mnist_data(args)
model = create_mnist_model(params)
model.fit(x_train, y_train, batch_size=args.batch_size, epochs=args.epochs, verbose=1,
validation_data=(x_test, y_test), callbacks=[SendMetrics()])
_, acc = model.evaluate(x_test, y_test, verbose=0)
LOG.debug('Final result is: %d', acc)
nni.report_final_result(acc)
def generate_default_params():
'''
生成默认超参
'''
return {
'optimizer': 'Adam',
'learning_rate': 0.001
}
if __name__ == '__main__':
PARSER = argparse.ArgumentParser()
PARSER.add_argument("--batch_size", type=int, default=200, help="batch size", required=False)
PARSER.add_argument("--epochs", type=int, default=10, help="Train epochs", required=False)
PARSER.add_argument("--num_train", type=int, default=1000, help="Number of train samples to be used, maximum 60000", required=False)
PARSER.add_argument("--num_test", type=int, default=1000, help="Number of test samples to be used, maximum 10000", required=False)
ARGS, UNKNOWN = PARSER.parse_known_args()
try:
# 从 Tuner 中获取参数
RECEIVED_PARAMS = nni.get_next_parameter()
LOG.debug(RECEIVED_PARAMS)
PARAMS = generate_default_params()
PARAMS.update(RECEIVED_PARAMS)
# 训练
train(ARGS, PARAMS)
except Exception as e:
LOG.exception(e)
raise
```
\ No newline at end of file
此样例需要安装 Pytorch。 Pytorch 安装包需要选择所基于的 Python 和 CUDA 版本。
以下是 python==3.5 和 cuda == 8.0 下的环境样例,使用下列命令来安装 Pytorch: python3 -m pip install http://download.pytorch.org/whl/cu80/torch-0.4.1-cp35-cp35m-linux_x86_64.whl python3 -m pip install torchvision
\ No newline at end of file
# 在阅读理解上使用自动模型架构搜索
该样例展示了如何使用遗传算法为阅读理解任务找到好的模型架构。
## 搜索空间
对于阅读理解项目,注意力和循环神经网络(RNN)模块已经被证明非常有效。 使用的搜索空间如下:
1. IDENTITY (Effectively 表示继续训练)。
2. INSERT-RNN-LAYER (插入 LSTM。 在 Experiment 中比较了 GRU 和 LSTM 的性能后,我们决定在这里采用 LSTM。)
3. REMOVE-RNN-LAYER
4. INSERT-ATTENTION-LAYER (插入注意力层。)
5. REMOVE-ATTENTION-LAYER
6. ADD-SKIP (在随机层之间一致).
7. REMOVE-SKIP (移除随机跳过).
![ga-squad-logo](../../../../examples/trials/ga_squad/ga_squad.png)
## 新版本
另一个时间更快,性能更好的版本正在开发中。 很快将发布。
# 如何运行此样例?
## 在本机或远程上运行此样例
### 使用下载脚本来下载数据
执行下列命令来下载所需要的数据:
chmod +x ./download.sh
./download.sh
### 手动下载
1. 在 https://rajpurkar.github.io/SQuAD-explorer/ 下载 "dev-v1.1.json" 和 "train-v1.1.json"。
```bash
wget https://rajpurkar.github.io/SQuAD-explorer/dataset/train-v1.1.json
wget https://rajpurkar.github.io/SQuAD-explorer/dataset/dev-v1.1.json
```
2. 在 https://nlp.stanford.edu/projects/glove/ 下载 "glove.840B.300d.txt"。
```bash
wget http://nlp.stanford.edu/data/glove.840B.300d.zip
unzip glove.840B.300d.zip
```
### 更新配置
修改 `nni/examples/trials/ga_squad/config.yml`,以下是默认配置:
authorName: default
experimentName: example_ga_squad
trialConcurrency: 1
maxExecDuration: 1h
maxTrialNum: 1
#可选项: local, remote
trainingServicePlatform: local
#可选项: true, false
useAnnotation: false
tuner:
codeDir: ~/nni/examples/tuners/ga_customer_tuner
classFileName: customer_tuner.py
className: CustomerTuner
classArgs:
optimize_mode: maximize
trial:
command: python3 trial.py
codeDir: ~/nni/examples/trials/ga_squad
gpuNum: 0
在 "trial" 部分中,如果需要使用 GPU 来进行架构搜索,可将 `gpuNum``0` 改为 `1`。 根据训练时长,可以增加 `maxTrialNum``maxExecDuration`
`trialConcurrency` 是并发运行的 Trial 的数量。如果将 `gpuNum` 设置为 1,则需要与 GPU 数量一致。
### 提交任务
nnictl create --config ~/nni/examples/trials/ga_squad/config.yml
## 在 OpenPAI 上运行此样例
根据上传大小的限制,仅上传源代码,并在训练过程中下载数据。 本 Experiment 需要的内存 `memoryMB >= 32G`,训练过程可能需要数小时。
### 更新配置
修改 `nni/examples/trials/ga_squad/config_pai.yml`,以下是默认配置:
authorName: default
experimentName: example_ga_squad
trialConcurrency: 1
maxExecDuration: 1h
maxTrialNum: 10
#可选项: local, remote, pai
trainingServicePlatform: pai
#可选项: true, false
useAnnotation: false
# nni_manager 的 ip
nniManagerIp: 10.10.10.10
tuner:
codeDir: ../../tuners/ga_customer_tuner
classFileName: customer_tuner.py
className: CustomerTuner
classArgs:
optimize_mode: maximize
trial:
command: chmod +x ./download.sh && ./download.sh && python3 trial.py
codeDir: .
gpuNum: 0
cpuNum: 1
memoryMB: 32869
#在 OpenPAI 上运行 NNI 任务的 Docker 映像
image: msranni/nni:latest
#在 OpenPAI 的 hdfs 目录上存储数据的目录,如:'hdfs://host:port/directory'
dataDir: hdfs://10.10.10.10:9000/username/nni
#在 OpenPAI 的 hdfs 目录上存储输出的目录,如:'hdfs://host:port/directory'
outputDir: hdfs://10.10.10.10:9000/username/nni
paiConfig:
#登录 OpenPAI 的用户名
userName: username
#登录 OpenPAI 的密码
passWord: password
# OpenPAI 的 RESTful 服务器地址
host: 10.10.10.10
将默认值改为个人账户和服务器信息。 包括 `nniManagerIp`, `dataDir`, `outputDir`, `userName`, `passWord``host`
在 "trial" 部分中,如果需要使用 GPU 来进行架构搜索,可将 `gpuNum``0` 改为 `1`。 根据训练时长,可以增加 `maxTrialNum``maxExecDuration`
`trialConcurrency` 是并发运行的 Trial 的数量。如果将 `gpuNum` 设置为 1,则需要与 GPU 数量一致。
### 提交任务
nnictl create --config ~/nni/examples/trials/ga_squad/config_pai.yml
# 关于此 Trial 的技术细节
## 实现方法
基于进化算法架构的问答和其它样例一样,有两个部分:Trial 和 Tuner。
### Trial
Trial 有大量的文件、函数和类。 这里只简单介绍最重要的文件:
* `attention.py` 包含了 Tensorflow 注意力算法的实现。
* `data.py` 包含了数据处理函数。
* `evaluate.py` 包含了评估脚本。
* `graph.py` 包含了计算图的定义。
* `rnn.py` 包含了 TensorFlow 的 GRU 实现。
* `train_model.py` 是整个文档模型的封装。
这些文件中,`trial.py``graph_to_tf.py` 非常特别。
`graph_to_tf.py` 有一个叫做 `graph_to_network`的函数,其框架代码如下:
def graph_to_network(input1,
input2,
input1_lengths,
input2_lengths,
graph,
dropout_rate,
is_training,
num_heads=1,
rnn_units=256):
topology = graph.is_topology()
layers = dict()
layers_sequence_lengths = dict()
num_units = input1.get_shape().as_list()[-1]
layers[0] = input1*tf.sqrt(tf.cast(num_units, tf.float32)) + \
positional_encoding(input1, scale=False, zero_pad=False)
layers[1] = input2*tf.sqrt(tf.cast(num_units, tf.float32))
layers[0] = dropout(layers[0], dropout_rate, is_training)
layers[1] = dropout(layers[1], dropout_rate, is_training)
layers_sequence_lengths[0] = input1_lengths
layers_sequence_lengths[1] = input2_lengths
for _, topo_i in enumerate(topology):
if topo_i == '|':
continue
if graph.layers[topo_i].graph_type == LayerType.input.value:
# ......
elif graph.layers[topo_i].graph_type == LayerType.attention.value:
# ......
# 处理更多层
正如我们看到的,这个函数实际上是个编译器。它将内部模型的 DAG 配置`图`(在`模型配置格式`章节介绍)转换为 Tensorflow 的计算图。
topology = graph.is_topology()
将内部图表示进行拓扑排序,代码在下列循环中:
for _, topo_i in enumerate(topology):
执行实际转换,将每层映射为 TensorFlow 计算图中的一部分。
### Tuner
Tuner 比 Trial 代码简单很多。 它们共用了同样的 `graph.py`。 此外,Tuner 有 `customer_tuner.py`,其中最重要的类是 `CustomerTuner`
class CustomerTuner(Tuner):
# ......
def generate_parameters(self, parameter_id):
"""将一组 Trial 图配置作为序列化对象返回。
parameter_id : int
"""
if len(self.population) <= 0:
logger.debug("the len of poplution lower than zero.")
raise Exception('The population is empty')
pos = -1
for i in range(len(self.population)):
if self.population[i].result == None:
pos = i
break
if pos != -1:
indiv = copy.deepcopy(self.population[pos])
self.population.pop(pos)
temp = json.loads(graph_dumps(indiv.config))
else:
random.shuffle(self.population)
if self.population[0].result > self.population[1].result:
self.population[0] = self.population[1]
indiv = copy.deepcopy(self.population[0])
self.population.pop(1)
indiv.mutation()
graph = indiv.config
temp = json.loads(graph_dumps(graph))
# ......
重载函数 `generate_parameters` 实现了简单的变异算法。 代码如下:
if self.population[0].result > self.population[1].result:
self.population[0] = self.population[1]
indiv = copy.deepcopy(self.population[0])
控制突变过程。 它会在种群中随机取出两个个体,对更好结果的一个保留数据,并突变另一个。
## 模型配置格式
这是模型配置的样例,在架构搜索过程中,从 Tuner 传入 Trial 的代码。
{
"max_layer_num": 50,
"layers": [
{
"input_size": 0,
"type": 3,
"output_size": 1,
"input": [],
"size": "x",
"output": [4, 5],
"is_delete": false
},
{
"input_size": 0,
"type": 3,
"output_size": 1,
"input": [],
"size": "y",
"output": [4, 5],
"is_delete": false
},
{
"input_size": 1,
"type": 4,
"output_size": 0,
"input": [6],
"size": "x",
"output": [],
"is_delete": false
},
{
"input_size": 1,
"type": 4,
"output_size": 0,
"input": [5],
"size": "y",
"output": [],
"is_delete": false
},
{"Comment": "More layers will be here for actual graphs."}
]
}
每个模型配置都有一个 "layers" 部分,这是层定义的 JSON 列表。 每层的定义也是一个 JSON 对象:
* `type` 是层的类型。 0, 1, 2, 3, 4 对应注意力、自注意力、RNN、输入和输出层。
* `size` 是输出的长度。 "x", "y" 对应文档长度和问题长度。
* `input_size` 是该层的输入数量。
* `input` 表示输入层的索引。
* `output` 是输出层的索引,该层会作为这些层的输入。
* `is_delete` 表示此层是否可用。
\ No newline at end of file
## Kaggle 比赛 [TGS Salt Identification Chanllenge](https://www.kaggle.com/c/tgs-salt-identification-challenge) 第 33 名的解决方案
此样例展示了如何在没有任何代码改动的情况下通过 NNI 来为竞赛代码使用自动机器学习。 要在 NNI 上运行此代码,首先需要单独运行它,然后配置 config.yml:
nnictl create --config config.yml
此代码仍然能够单独运行,但需要至少一周来重现竞赛的结果。
[解决方案概述](https://www.kaggle.com/c/tgs-salt-identification-challenge/discussion/69593)
准备:
下载完整的数据,运行 preprocess.py 来准备数据。
阶段 1:
将目录 0-3 训练 100 个 epoch,对于每个目录,训练三个模型:
python3 train.py --ifolds 0 --epochs 100 --model_name UNetResNetV4
python3 train.py --ifolds 0 --epochs 100 --model_name UNetResNetV5 --layers 50
python3 train.py --ifolds 0 --epochs 100 --model_name UNetResNetV6
阶段 2:
使用余弦退火学习率调度器运行 300 次 epoch 来微调阶段 1 的模型:
python3 train.py --ifolds 0 --epochs 300 --lrs cosine --lr 0.001 --min_lr 0.0001 --model_name UNetResNetV4
阶段 3:
用深度通道微调阶段 2 的模型:
python3 train.py --ifolds 0 --epochs 300 --lrs cosine --lr 0.001 --min_lr 0.0001 --model_name UNetResNetV4 --depths
阶段 4:
为每个模型进行预测,组合结果生成伪标签。
阶段 5:
用伪标签微调阶段 3 的模型
python3 train.py --ifolds 0 --epochs 300 --lrs cosine --lr 0.001 --min_lr 0.0001 --model_name UNetResNetV4 --depths --pseudo
阶段 6: 将所有阶段 3 和阶段 5 的模型组合起来。
\ No newline at end of file
# 在 NNI 中用网络形态算法来进行自动模型结构搜索
Network Morphism (网络形态)是内置的 Tuner,它使用了网络形态技术来搜索和评估新的网络结构。 该样例展示了如何使用它来为深度学习找到好的模型架构。
## 如何运行此样例?
### 1. 训练框架支持
网络形态当前基于框架,还没有实现与框架脱离的方法。 当前支持 Pytorch 和 Keras。 如果熟悉 JSON 中间格式,可以在自定义的训练框架中生成自己的模型。 随后,我们会将中间结果从 JSON 转换为 ONNX,从而能够成为[标准的中间表示](https://github.com/onnx/onnx/blob/master/docs/IR.md)
### 2. 安装需求
```bash
# 安装依赖包
cd examples/trials/network_morphism/
pip install -r requirements.txt
```
### 3. 更新配置
修改 `examples/trials/network_morphism/cifar10/config.yml` 来适配自己的任务。注意,searchSpacePath 在配置中不需要。 默认配置:
```yaml
authorName: default
experimentName: example_cifar10-network-morphism
trialConcurrency: 1
maxExecDuration: 48h
maxTrialNum: 200
#可选项: local, remote, pai
trainingServicePlatform: local
#可选项: true, false
useAnnotation: false
tuner:
#可选项: TPE, Random, Anneal, Evolution, BatchTuner, NetworkMorphism
#SMAC (SMAC 需要通过 nnictl 安装)
builtinTunerName: NetworkMorphism
classArgs:
#可选项: maximize, minimize
optimize_mode: maximize
#当前仅支持视觉领域
task: cv
#修改来适配自己的图像大小
input_width: 32
#修改来适配自己的图像通道
input_channel: 3
#修改来适配自己的分类数量
n_output_node: 10
trial:
# 自己的命令
command: python3 cifar10_keras.py
codeDir: .
gpuNum: 0
```
在 "trial" 部分中,如果需要使用 GPU 来进行架构搜索,可将 `gpuNum``0` 改为 `1`。 根据训练时长,可以增加 `maxTrialNum``maxExecDuration`
`trialConcurrency` 是并发运行的 Trial 的数量。如果将 `gpuNum` 设置为 1,则需要与 GPU 数量一致。
### 4. 在代码中调用 "json\_to\_graph()" 函数
修改代码来调用 "json\_to\_graph()" 函数来从收到的 JSON 字符串生成一个 Pytorch 或 Keras 模型。 简单样例:
```python
import nni
from nni.networkmorphism_tuner.graph import json_to_graph
def build_graph_from_json(ir_model_json):
"""从 JSON 生成 Pytorch 模型
"""
graph = json_to_graph(ir_model_json)
model = graph.produce_torch_model()
return model
# 从网络形态 Tuner 中获得下一组参数
RCV_CONFIG = nni.get_next_parameter()
# 调用函数来生成 Pytorch 或 Keras 模型
net = build_graph_from_json(RCV_CONFIG)
# 训练过程
# ....
# 将最终精度返回给 NNI
nni.report_final_result(best_acc)
```
### 5. 提交任务
```bash
# 可以使用命令行工具 "nnictl" 来创建任务
# 最终会成功提交一个网络形态任务到 NNI
nnictl create --config config.yml
```
## Trial 样例
下面的代码可在 `examples/trials/network_morphism/` 中找到。 可参考此代码来更新自己的任务。 希望它对你有用。
### FashionMNIST
`Fashion-MNIST` 是来自 [Zalando](https://jobs.zalando.com/tech/) 文章的图片 — 有 60,000 个样例的训练集和 10,000 个样例的测试集。 每个样例是 28x28 的灰度图,分为 10 个类别。 由于 MNIST 数据集过于简单,该数据集现在开始被广泛使用,用来替换 MNIST 作为基准数据集。
这里有两个样例,[FashionMNIST-keras.py](./FashionMNIST/FashionMNIST_keras.py)[FashionMNIST-pytorch.py](./FashionMNIST/FashionMNIST_pytorch.py)。 注意,在 `config.yml` 中,需要为此数据集修改 `input_width` 为 28,以及 `input_channel` 为 1。
### Cifar10
`CIFAR-10` 数据集 [Canadian Institute For Advanced Research](https://www.cifar.ca/) 是广泛用于机器学习和视觉算法训练的数据集。 它是机器学习领域最广泛使用的数据集之一。 CIFAR-10 数据集包含了 60,000 张 32x32 的彩色图片,分为 10 类。
这里有两个样例,[cifar10-keras.py](./cifar10/cifar10_keras.py)[cifar10-pytorch.py](./cifar10/cifar10_pytorch.py)。 在 `config.yml` 中,该数据集 `input_width` 的值是 32,并且 `input_channel` 是 3。
\ No newline at end of file
**在 NNI 中运行 ENAS**
===
来自贡献者的 [enas-nni](https://github.com/countif/enas_nni) 可运行在 NNI 中。 非常感谢!
欢迎更多志愿者加入我们!
\ No newline at end of file
# 如何使用 ga_customer_tuner?
此定制的 Tuner 仅适用于代码 "~/nni/examples/trials/ga_squad", 输入 `cd ~/nni/examples/trials/ga_squad` 查看 readme.md 来了解 ga_squad 的更多信息。
# 配置
如果要在 Experiment 中使用 ga_customer_tuner 可按照下列格式来配置:
tuner:
codeDir: ~/nni/examples/tuners/ga_customer_tuner
classFileName: customer_tuner.py
className: CustomerTuner
classArgs:
optimize_mode: maximize
\ No newline at end of file
# 如何使用 ga_customer_tuner?
此定制的 Tuner 仅适用于代码 "~/nni/examples/trials/ga_squad", 输入 `cd ~/nni/examples/trials/ga_squad` 查看 readme.md 来了解 ga_squad 的更多信息。
# 配置
如果要在 Experiment 中使用 ga_customer_tuner 可按照下列格式来配置:
tuner:
codeDir: ~/nni/examples/tuners/ga_customer_tuner
classFileName: customer_tuner.py
className: CustomerTuner
classArgs:
optimize_mode: maximize
\ No newline at end of file
## 安装
为当前用户安装:
pip install --user -e .
为所有用户安装:
pip install -e .
## 测试
python setup.py test
\ No newline at end of file
# NNI 中的 Curve Fitting Assessor
## 1. 介绍
Curve Fitting Assessor 是一个 LPA (learning, predicting, assessing,即学习、预测、评估) 的算法。 如果预测的Trial X 在 step S 比性能最好的 Trial 要差,就会提前终止它。
此算法中,使用了 12 条曲线来拟合学习曲线,从[参考论文](http://aad.informatik.uni-freiburg.de/papers/15-IJCAI-Extrapolation_of_Learning_Curves.pdf)中选择了大量的参数曲线模型。 学习曲线的形状与先验知识是一致的:都是典型的递增的、饱和的函数。
<p align="center">
<img src="./learning_curve.PNG" alt="drawing"/>
</p>
所有学习曲线模型被合并到了单个,更强大的模型中。 合并的模型通过加权线性混合:
<p align="center">
<img src="./f_comb.gif" alt="drawing"/>
</p>
合并后的参数向量
<p align="center">
<img src="./expression_xi.gif" alt="drawing"/>
</p>
假设增加一个高斯噪声,且噪声参数初始化为最大似然估计。
通过学习历史数据来确定新的组合参数向量的最大概率值。 用这样的方法来预测后面的 Trial 性能,并停止不好的 Trial 来节省计算资源。
具体来说,该算法有学习、预测和评估三个阶段。
* 步骤 1:学习。 从当前 Trial 的历史中学习,并从贝叶斯角度决定 \xi 。 首先,使用最小二乘法 (由 `fit_theta` 实现) 来节省时间。 获得参数后,过滤曲线并移除异常点(由 `filter_curve` 实现)。 最后,使用 MCMC 采样方法 (由 `mcmc_sampling` 实现) 来调整每个曲线的权重。 至此,确定了 \xi 中的所有参数。
* 步骤 2:预测。 用 \xi 和混合模型公式,在目标位置(例如 epoch 的总数)来计算期望的最终结果精度(由 `f_comb` 实现)。
* 步骤 3:如果拟合结果没有收敛,预测结果会是 `None`,并返回 `AssessResult.Good`,待下次有了更多精确信息后再次预测。 此外,会通过 `predict()` 函数获得正数。如果该值大于 __历史最好结果__ * `THRESHOLD`(默认为 0.95),则返回 `AssessResult.Good`,否则返回 `AssessResult.Bad`
下图显示了此算法在 MNIST Trial 历史数据上结果。其中绿点表示 Assessor 获得的数据,蓝点表示将来,但未知的数据,红色线条是 Curve fitting Assessor 的预测曲线。
<p align="center">
<img src="./example_of_curve_fitting.PNG" alt="drawing"/>
</p>
## 2. 用法
要使用 Curve Fitting Assessor,需要在 Experiment 的 YAML 配置文件进行如下改动。
assessor:
builtinAssessorName: Curvefitting
classArgs:
# (必须) epoch 的总数。
# 需要此数据来决定需要预测的点。
epoch_num: 20
# (可选项) choice: maximize, minimize
# 如果选择了 minimize 模式,需要调整阈值为 >= 1.0 (例如:threshold=1.1)
* optimize_mode 的默认值是 maximize
optimize_mode: maximize
# (可选项) 决定 Trial 是否应被停止的次数
# 为了节省资源,只有在大于 start_step(default=6) 的精度点才开始预测。
# 只有在收到 start_step 个中间结果之后。
# start_step 的默认值是 6。
start_step: 6
# (可选) 决定是否提前终止的阈值。
# 例如,如果 threshold = 0.95, optimize_mode = maximize,最好的历史结果是 0.9,那么会在 Trial 的预测值低于 0.95 * 0.9 = 0.855 时停止。
* 阈值的默认值是 0.95。
threshold: 0.95
## 3. 文件结构
Assessor 有大量的文件、函数和类。 这里只简单介绍最重要的文件:
* `curvefunctions.py` 包含了所有函数表达式和默认参数。
* `modelfactory.py` 包括学习和预测部分,并实现了相应的计算部分。
* `curvefitting_assessor.py` 是接收 Trial 历史数据并评估是否需要提前终止的 Assessor。
## 4. TODO
* 进一步提高预测精度,并在更多模型上测试。
\ No newline at end of file
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