Unverified Commit 8c203f30 authored by SparkSnail's avatar SparkSnail Committed by GitHub
Browse files

Merge pull request #211 from microsoft/master

merge master
parents 7c1ab114 483232c8
...@@ -14,6 +14,6 @@ Metis 属于基于序列的贝叶斯优化 (SMBO) 的类别,它也基于贝叶 ...@@ -14,6 +14,6 @@ Metis 属于基于序列的贝叶斯优化 (SMBO) 的类别,它也基于贝叶
它会标识出下一个超参的候选项。 这是通过对隐含信息的探索、挖掘和重采样来实现的。 它会标识出下一个超参的候选项。 这是通过对隐含信息的探索、挖掘和重采样来实现的。
注意,搜索空间仅支持 `choice`, `quniform`, `uniform``randint` 此 Tuner 搜索空间仅接受 `quniform``uniform``randint` 和数值的 `choice` 类型
更多详情,参考论文:https://www.microsoft.com/en-us/research/publication/metis-robustly-tuning-tail-latencies-cloud-systems/ 更多详情,参考论文:https://www.microsoft.com/en-us/research/publication/metis-robustly-tuning-tail-latencies-cloud-systems/
\ No newline at end of file
# NNI 中的 PPO Tuner
## PPOTuner
这是通常用于 NAS 接口的 NNI Tuner,使用了 [PPO 算法](https://arxiv.org/abs/1707.06347)。 此实现继承了[这里](https://github.com/openai/baselines/tree/master/baselines/ppo2)的主要逻辑,(即 OpenAI 的 PPO2),并为 NAS 场景做了适配。
它能成功调优 [mnist-nas 示例](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-nas),结果如下:
![](../../img/ppo_mnist.png)
我们也使用 NAS 接口和 PPO Tuner 调优了[ ENAS 论文中为图片分类所做的宏分类](https://github.com/microsoft/nni/tree/master/examples/trials/nas_cifar10)(Trial 中 Epoch 限定为 8)。 [enas 论文](https://arxiv.org/pdf/1802.03268.pdf)中的图 7 展示了搜索空间:
![](../../img/enas_search_space.png)
上图是某个选定的架构,用来展示搜索空间。 每个方块是一层,其操作可从 6 个操作中选择。 每条虚线是直通连接,每个方块都可以有 0 或 1 条直通连接获得前面层的输出。 **注意**,在原始的宏搜索空间中,每个方块层可选择任意条直通连接,在此实现中,仅允许 0 或 1条。
结果如下图所示([配置文件](https://github.com/microsoft/nni/blob/master/examples/trials/nas_cifar10/config_ppo.yml)):
![](../../img/ppo_cifar10.png)
\ No newline at end of file
...@@ -2,12 +2,10 @@ ...@@ -2,12 +2,10 @@
创建 Experiment 所需要的配置文件。 配置文件的路径会传入 `nnictl` 命令。 配置文件的格式为 YAML。 本文介绍了配置文件的内容,并提供了一些示例和模板。 创建 Experiment 所需要的配置文件。 配置文件的路径会传入 `nnictl` 命令。 配置文件的格式为 YAML。 本文介绍了配置文件的内容,并提供了一些示例和模板。
- [Experiment(实验)配置参考](#Experiment-config-reference) - [Experiment(实验)配置参考](#experiment-config-reference)
- [模板](#Template) - [模板](#template)
- [说明](#Configuration-spec) - [说明](#configuration-spec)
- [样例](#Examples) - [样例](#examples)
<a name="Template"></a>
## 模板 ## 模板
...@@ -19,27 +17,27 @@ experimentName: ...@@ -19,27 +17,27 @@ experimentName:
trialConcurrency: trialConcurrency:
maxExecDuration: maxExecDuration:
maxTrialNum: maxTrialNum:
#可选项: local, remote, pai, kubeflow # 可选项: local, remote, pai, kubeflow
trainingServicePlatform: trainingServicePlatform:
searchSpacePath: searchSpacePath:
#可选项: true, false, 默认值: false # 可选项: true, false, 默认值: false
useAnnotation: useAnnotation:
#可选项: true, false, 默认值: false # 可选项: true, false, 默认值: false
multiPhase: multiPhase:
#可选项: true, false, 默认值: false # 可选项: true, false, 默认值: false
multiThread: multiThread:
tuner: tuner:
#可选项: TPE, Random, Anneal, Evolution # 可选项: TPE, Random, Anneal, Evolution
builtinTunerName: builtinTunerName:
classArgs: classArgs:
#可选项: maximize, minimize # 可选项: maximize, minimize
optimize_mode: optimize_mode:
gpuNum: gpuIndices:
trial: trial:
command: command:
codeDir: codeDir:
gpuNum: gpuNum:
#在本地使用时,machineList 可为空 # 在本机模式下,machineList 可为空
machineList: machineList:
- ip: - ip:
port: port:
...@@ -70,18 +68,18 @@ tuner: ...@@ -70,18 +68,18 @@ tuner:
classArgs: classArgs:
#可选项: maximize, minimize #可选项: maximize, minimize
optimize_mode: optimize_mode:
gpuNum: gpuIndices:
assessor: assessor:
#可选项: Medianstop #可选项: Medianstop
builtinAssessorName: builtinAssessorName:
classArgs: classArgs:
#可选项: maximize, minimize #可选项: maximize, minimize
optimize_mode: optimize_mode:
gpuNum: gpuIndices:
trial: trial:
command: command:
codeDir: codeDir:
gpuNum: gpuIndices:
#在本地使用时,machineList 可为空 #在本地使用时,machineList 可为空
machineList: machineList:
- ip: - ip:
...@@ -112,18 +110,18 @@ tuner: ...@@ -112,18 +110,18 @@ tuner:
classArgs: classArgs:
#可选项: maximize, minimize #可选项: maximize, minimize
optimize_mode: optimize_mode:
gpuNum: gpuIndices:
assessor: assessor:
#可选项: Medianstop #可选项: Medianstop
builtinAssessorName: builtinAssessorName:
classArgs: classArgs:
#可选项: maximize, minimize #可选项: maximize, minimize
optimize_mode: optimize_mode:
gpuNum: gpuIndices:
trial: trial:
command: command:
codeDir: codeDir:
gpuNum: gpuIndices:
#在本地使用时,machineList 可为空 #在本地使用时,machineList 可为空
machineList: machineList:
- ip: - ip:
...@@ -132,8 +130,6 @@ machineList: ...@@ -132,8 +130,6 @@ machineList:
passwd: passwd:
``` ```
<a name="Configuration"></a>
## 说明 ## 说明
- **authorName** - **authorName**
...@@ -264,11 +260,11 @@ machineList: ...@@ -264,11 +260,11 @@ machineList:
- **builtinTunerName** - **builtinTunerName**
**builtinTunerName** 指定系统 Tuner 的名,NNI SDK 提供了多 Tuner,如:{**TPE**, **Random**, **Anneal**, **Evolution**, **BatchTuner**, **GridSearch**} **builtinTunerName** 指定系统 Tuner 的名,NNI SDK 提供了多个内置 Tuner,详情参考[这里](../Tuner/BuiltinTuner.md)
- **classArgs** - **classArgs**
**classArgs** 指定了 Tuner 算法的参数。 如果 **builtinTunerName** 是{**TPE**, **Random**, **Anneal**, **Evolution**},用户需要设置 **optimize_mode** **classArgs** 指定了 Tuner 算法的参数。 参考[此文件](../Tuner/BuiltinTuner.md)来了解内置 Tuner 的配置参数
- **codeDir**, **classFileName**, **className****classArgs** - **codeDir**, **classFileName**, **className****classArgs**
...@@ -288,17 +284,17 @@ machineList: ...@@ -288,17 +284,17 @@ machineList:
**classArgs** 指定了 Tuner 算法的参数。 **classArgs** 指定了 Tuner 算法的参数。
- **gpuNum** - **gpuIndices**
__gpuNum__ 指定了运行 Tuner 进程的 GPU 数量。 此字段的值必须是正整数。 如果此字段没有设置,NNI不会在脚本中添加 `CUDA_VISIBLE_DEVICES` (也就是说,不会通过 `CUDA_VISIBLE_DEVICES` 来控制 GPU 在 Trial 中是否可见),也不会管理 GPU 资源。 __gpuIndices__ 指定了 Tuner 进程可使用的 GPU。 可以指定单个或多个 GPU 索引,多个索引间使用逗号(,)隔开,例如:`1``0,1,3`。 如果没设置此字段,脚本中的 `CUDA_VISIBLE_DEVICES` 会为空 '',即 Tuner 中找不到 GPU。
注意: 只能使用一种方法来指定 Tuner,例如:设置 {tunerName, optimizationMode} 或 {tunerCommand, tunerCwd},不能同时设置两者。
- **includeIntermediateResults** - **includeIntermediateResults**
如果 __includeIntermediateResults__ 为 true,最后一个 Assessor 的中间结果会被发送给 Tuner 作为最终结果。 __includeIntermediateResults__ 的默认值为 false。 如果 __includeIntermediateResults__ 为 true,最后一个 Assessor 的中间结果会被发送给 Tuner 作为最终结果。 __includeIntermediateResults__ 的默认值为 false。
注意:用户只能用一种方法来指定 Tuner,指定 `builtinTunerName``classArgs`,或指定 `codeDir``classFileName``className` 以及 `classArgs`
- **Assessor** - **Assessor**
...@@ -310,7 +306,7 @@ machineList: ...@@ -310,7 +306,7 @@ machineList:
- **builtinAssessorName** - **builtinAssessorName**
**builtinAssessorName** 指定了系统 Assessor 的名称, NNI 内置的 Assessor 有 {**Medianstop**,等等} **builtinAssessorName** 指定了内置 Assessor 的名称,NNI SDK 提供了多个内置的 Assessor,详情参考[这里](../Assessor/BuiltinAssessor.md)
- **classArgs** - **classArgs**
...@@ -334,11 +330,48 @@ machineList: ...@@ -334,11 +330,48 @@ machineList:
**classArgs** 指定了 Assessor 算法的参数。 **classArgs** 指定了 Assessor 算法的参数。
- **gpuNum** 注意:用户只能用一种方法来指定 Assessor,指定 `builtinAssessorName``classArgs`,或指定 `codeDir``classFileName``className` 以及 `classArgs`。 如果不需要使用 Assessor,此字段可为空。
- **Advisor**
- 说明
**Advisor** 指定了 Experiment 的 Advisor 算法。有两种方法可设置 Advisor。 一种方法是使用 SDK 提供的 Advisor ,需要设置 **builtinAdvisorName****classArgs**。 另一种方法,是使用用户自定义的 Advisor,需要设置 **codeDirectory****classFileName****className****classArgs**
- **builtinAdvisorName****classArgs**
- **builtinAdvisorName**
**builtinAdvisorName** 指定了内置 Advisor 的名称,NNI SDK 提供了多个[内置的 Advisor](../Tuner/BuiltinTuner.md)
- **classArgs**
**classArgs** 指定了 Advisor 算法的参数。 参考[此文件](../Tuner/BuiltinTuner.md)来了解内置 Advisor 的配置参数。
- **codeDir**, **classFileName**, **className****classArgs**
- **codeDir**
**codeDir** 指定 Advisor 代码的目录。
- **classFileName**
**classFileName** 指定 Advisor 文件名。
**gpuNum** 指定了运行 Assessor 进程的 GPU 数量。 此字段的值必须是正整数。 - **className**
**className** 指定 Advisor 类名。
注意: 只能使用一种方法来指定 Assessor,例如:设置 {assessorName, optimizationMode} 或 {assessorCommand, assessorCwd},不能同时设置。如果不需要使用 Assessor,可将其置为空。 - **classArgs**
**classArgs** 指定了 Advisor 算法的参数。
- **gpuIndices**
__gpuIndices__ 指定了 Advisor 进程可使用的 GPU。 可以指定单个或多个 GPU 索引,多个索引间使用逗号(,)隔开,例如:`1``0,1,3`。 如果没设置此字段,脚本中的 `CUDA_VISIBLE_DEVICES` 会为空 '',即 Tuner 中找不到 GPU。
注意:用户只能用一种方法来指定 Advisor ,指定 `builtinAdvisorName``classArgs`,或指定 `codeDir``classFileName``className` 以及 `classArgs`
- **trial (local, remote)** - **trial (local, remote)**
...@@ -568,8 +601,6 @@ machineList: ...@@ -568,8 +601,6 @@ machineList:
**host** 是 OpenPAI 的主机地址。 **host** 是 OpenPAI 的主机地址。
<a name="Examples"></a>
## 样例 ## 样例
- **本机模式** - **本机模式**
...@@ -592,7 +623,6 @@ machineList: ...@@ -592,7 +623,6 @@ machineList:
classArgs: classArgs:
#可选项: maximize, minimize #可选项: maximize, minimize
optimize_mode: maximize optimize_mode: maximize
gpuNum: 0
trial: trial:
command: python3 mnist.py command: python3 mnist.py
codeDir: /nni/mnist codeDir: /nni/mnist
...@@ -618,14 +648,12 @@ machineList: ...@@ -618,14 +648,12 @@ machineList:
classArgs: classArgs:
#可选项: maximize, minimize #可选项: maximize, minimize
optimize_mode: maximize optimize_mode: maximize
gpuNum: 0
assessor: assessor:
#可选项: Medianstop #可选项: Medianstop
builtinAssessorName: Medianstop builtinAssessorName: Medianstop
classArgs: classArgs:
#可选项: maximize, minimize #可选项: maximize, minimize
optimize_mode: maximize optimize_mode: maximize
gpuNum: 0
trial: trial:
command: python3 mnist.py command: python3 mnist.py
codeDir: /nni/mnist codeDir: /nni/mnist
...@@ -652,7 +680,6 @@ machineList: ...@@ -652,7 +680,6 @@ machineList:
classArgs: classArgs:
#可选项: maximize, minimize #可选项: maximize, minimize
optimize_mode: maximize optimize_mode: maximize
gpuNum: 0
assessor: assessor:
codeDir: /nni/assessor codeDir: /nni/assessor
classFileName: myassessor.py classFileName: myassessor.py
...@@ -660,7 +687,6 @@ machineList: ...@@ -660,7 +687,6 @@ machineList:
classArgs: classArgs:
#choice: maximize, minimize #choice: maximize, minimize
optimize_mode: maximize optimize_mode: maximize
gpuNum: 0
trial: trial:
command: python3 mnist.py command: python3 mnist.py
codeDir: /nni/mnist codeDir: /nni/mnist
...@@ -688,7 +714,6 @@ machineList: ...@@ -688,7 +714,6 @@ machineList:
classArgs: classArgs:
#可选项: maximize, minimize #可选项: maximize, minimize
optimize_mode: maximize optimize_mode: maximize
gpuNum: 0
trial: trial:
command: python3 mnist.py command: python3 mnist.py
codeDir: /nni/mnist codeDir: /nni/mnist
...@@ -762,16 +787,16 @@ machineList: ...@@ -762,16 +787,16 @@ machineList:
trialConcurrency: 1 trialConcurrency: 1
maxExecDuration: 1h maxExecDuration: 1h
maxTrialNum: 1 maxTrialNum: 1
#可选项: local, remote, pai, kubeflow # 可选项: local, remote, pai, kubeflow
trainingServicePlatform: kubeflow trainingServicePlatform: kubeflow
searchSpacePath: search_space.json searchSpacePath: search_space.json
#可选项: true, false # 可选项: true, false
useAnnotation: false useAnnotation: false
tuner: tuner:
#可选项: TPE, Random, Anneal, Evolution # 可选项: TPE, Random, Anneal, Evolution
builtinTunerName: TPE builtinTunerName: TPE
classArgs: classArgs:
#可选项: maximize, minimize # 可选项: maximize, minimize
optimize_mode: maximize optimize_mode: maximize
trial: trial:
codeDir: . codeDir: .
...@@ -797,23 +822,22 @@ machineList: ...@@ -797,23 +822,22 @@ machineList:
trialConcurrency: 1 trialConcurrency: 1
maxExecDuration: 1h maxExecDuration: 1h
maxTrialNum: 1 maxTrialNum: 1
#可选项: local, remote, pai, kubeflow # 可选项: local, remote, pai, kubeflow
trainingServicePlatform: kubeflow trainingServicePlatform: kubeflow
searchSpacePath: search_space.json searchSpacePath: search_space.json
#可选项: true, false # 可选项: true, false
useAnnotation: false useAnnotation: false
#nniManagerIp: 10.10.10.10 #nniManagerIp: 10.10.10.10
tuner: tuner:
#可选项: TPE, Random, Anneal, Evolution # 可选项: TPE, Random, Anneal, Evolution
builtinTunerName: TPE builtinTunerName: TPE
classArgs: classArgs:
#可选项: maximize, minimize # 可选项: maximize, minimize
optimize_mode: maximize optimize_mode: maximize
assessor: assessor:
builtinAssessorName: Medianstop builtinAssessorName: Medianstop
classArgs: classArgs:
optimize_mode: maximize optimize_mode: maximize
gpuNum: 0
trial: trial:
codeDir: . codeDir: .
worker: worker:
......
...@@ -10,6 +10,7 @@ nnictl 支持的命令: ...@@ -10,6 +10,7 @@ nnictl 支持的命令:
* [nnictl create](#create) * [nnictl create](#create)
* [nnictl resume](#resume) * [nnictl resume](#resume)
* [nnictl view](#view)
* [nnictl stop](#stop) * [nnictl stop](#stop)
* [nnictl update](#update) * [nnictl update](#update)
* [nnictl trial](#trial) * [nnictl trial](#trial)
...@@ -104,6 +105,35 @@ nnictl 支持的命令: ...@@ -104,6 +105,35 @@ nnictl 支持的命令:
nnictl resume [experiment_id] --port 8088 nnictl resume [experiment_id] --port 8088
``` ```
<a name="view"></a>
![](https://placehold.it/15/1589F0/000000?text=+) `nnictl view`
* 说明
使用此命令查看已停止的 Experiment。
* 用法
```bash
nnictl view [OPTIONS]
```
* 选项
| 参数及缩写 | 是否必需 | 默认值 | 说明 |
| ---------- | ----- | --- | -------------------------------- |
| id | True | | 要查看的 Experiment 标识 |
| --port, -p | False | | 要查看的 Experiment 使用的 RESTful 服务端口 |
* 示例
> 在指定的端口 8088 上查看 Experiment
```bash
nnictl view [experiment_id] --port 8088
```
<a name="stop"></a> <a name="stop"></a>
![](https://placehold.it/15/1589F0/000000?text=+) `nnictl stop` ![](https://placehold.it/15/1589F0/000000?text=+) `nnictl stop`
...@@ -125,7 +155,7 @@ nnictl 支持的命令: ...@@ -125,7 +155,7 @@ nnictl 支持的命令:
| --port, -p | False | | 要停止的 Experiment 使用的 RESTful 服务端口 | | --port, -p | False | | 要停止的 Experiment 使用的 RESTful 服务端口 |
| --all, -a | False | | 停止所有 Experiment | | --all, -a | False | | 停止所有 Experiment |
* 详细信息及 * 详细信息及
1. 如果没有指定 id,并且当前有运行的 Experiment,则会停止该 Experiment,否则会输出错误信息。 1. 如果没有指定 id,并且当前有运行的 Experiment,则会停止该 Experiment,否则会输出错误信息。
...@@ -183,7 +213,7 @@ nnictl 支持的命令: ...@@ -183,7 +213,7 @@ nnictl 支持的命令:
| id | False | | 需要设置的 Experiment 的 id | | id | False | | 需要设置的 Experiment 的 id |
| --filename, -f | True | | 新的搜索空间文件名 | | --filename, -f | True | | 新的搜索空间文件名 |
* *
`使用 'examples/trials/mnist/search_space.json' 来更新 Experiment 的搜索空间` `使用 'examples/trials/mnist/search_space.json' 来更新 Experiment 的搜索空间`
...@@ -207,7 +237,7 @@ nnictl 支持的命令: ...@@ -207,7 +237,7 @@ nnictl 支持的命令:
| 参数及缩写 | 是否必需 | 默认值 | 说明 | | 参数及缩写 | 是否必需 | 默认值 | 说明 |
| ----------- | ----- | --- | --------------------- | | ----------- | ----- | --- | --------------------- |
| id | False | | 需要设置的 Experiment 的 id | | id | False | | 需要设置的 Experiment 的 ID |
| --value, -v | True | | 允许同时运行的 Trial 的数量 | | --value, -v | True | | 允许同时运行的 Trial 的数量 |
* 样例 * 样例
...@@ -232,12 +262,12 @@ nnictl 支持的命令: ...@@ -232,12 +262,12 @@ nnictl 支持的命令:
* 选项 * 选项
| 参数及缩写 | 是否必需 | 默认值 | 说明 | | 参数及缩写 | 是否必需 | 默认值 | 说明 |
| ----------- | ----- | --- | ----------------------------------------------------------------------- | | ----------- | ----- | --- | ------------------------------------------------------------------------ |
| id | False | | 需要设置的 Experiment 的 id | | id | False | | 需要设置的 Experiment 的 ID |
| --value, -v | True | | Experiment 持续时间如没有单位,则为秒。 后缀可以为 's' 即秒 (默认值), 'm' 即分钟, 'h' 即小时或 'd' 即天。 | | --value, -v | True | | Experiment 持续时间如没有单位,则为秒。 后缀可以为 's' 即秒 (默认值), 'm' 即分钟, 'h' 即小时或 'd' 即天。 |
* *
> 修改 Experiment 的执行时间 > 修改 Experiment 的执行时间
...@@ -264,7 +294,7 @@ nnictl 支持的命令: ...@@ -264,7 +294,7 @@ nnictl 支持的命令:
| id | False | | 需要设置的 Experiment 的 id | | id | False | | 需要设置的 Experiment 的 id |
| --value, -v | True | | 需要设置的 maxtrialnum 的数量 | | --value, -v | True | | 需要设置的 maxtrialnum 的数量 |
* *
> 更新 Experiment 的 Trial 数量 > 更新 Experiment 的 Trial 数量
...@@ -312,7 +342,7 @@ nnictl 支持的命令: ...@@ -312,7 +342,7 @@ nnictl 支持的命令:
| id | False | | Trial 的 Experiment ID | | id | False | | Trial 的 Experiment ID |
| --trial_id, -T | True | | 需要终止的 Trial 的 ID。 | | --trial_id, -T | True | | 需要终止的 Trial 的 ID。 |
* *
> 结束 Trial 任务 > 结束 Trial 任务
...@@ -378,7 +408,7 @@ nnictl 支持的命令: ...@@ -378,7 +408,7 @@ nnictl 支持的命令:
| 参数及缩写 | 是否必需 | 默认值 | 说明 | | 参数及缩写 | 是否必需 | 默认值 | 说明 |
| ----- | ----- | --- | --------------------- | | ----- | ----- | --- | --------------------- |
| id | False | | 需要设置的 Experiment 的 id | | id | False | | 需要设置的 Experiment 的 ID |
* **nnictl experiment list** * **nnictl experiment list**
...@@ -439,7 +469,7 @@ nnictl 支持的命令: ...@@ -439,7 +469,7 @@ nnictl 支持的命令:
| --filename, -f | True | | 文件的输出路径 | | --filename, -f | True | | 文件的输出路径 |
| --type | True | | 输出文件类型,仅支持 "csv" 和 "json" | | --type | True | | 输出文件类型,仅支持 "csv" 和 "json" |
* *
> 将 Experiment 中所有 Trial 数据导出为 JSON 格式 > 将 Experiment 中所有 Trial 数据导出为 JSON 格式
...@@ -478,7 +508,7 @@ nnictl 支持的命令: ...@@ -478,7 +508,7 @@ nnictl 支持的命令:
] ]
``` ```
最顶层列表的每个元素都是一个例。 对于内置的 Tuner 和 Advisor,每个样本至少需要两个主键:`parameter``value``parameter` 必须与 Experiment 的搜索空间相匹配,`parameter` 中的所有的主键(或超参)都必须与搜索空间中的主键相匹配。 否则, Tuner 或 Advisor 可能会有无法预期的行为。 `Value` 应当遵循与 `nni.report_final_result` 的输入值一样的规则,即要么时一个数字,或者是包含 `default` 主键的 dict。 对于自定义的 Tuner 或 Advisor,根据实现的不同,此文件可以是任意的 JSON 内容(例如,`import_data`)。 最顶层列表的每个元素都是一个例。 对于内置的 Tuner 和 Advisor,每个样本至少需要两个主键:`parameter``value``parameter` 必须与 Experiment 的搜索空间相匹配,`parameter` 中的所有的主键(或超参)都必须与搜索空间中的主键相匹配。 否则, Tuner 或 Advisor 可能会有无法预期的行为。 `Value` 应当遵循与 `nni.report_final_result` 的输入值一样的规则,即要么时一个数字,或者是包含 `default` 主键的 dict。 对于自定义的 Tuner 或 Advisor,根据实现的不同,此文件可以是任意的 JSON 内容(例如,`import_data`)。
也可以用 [nnictl experiment export](#export) 命令导出 Experiment 已经运行过的 Trial 超参和结果。 也可以用 [nnictl experiment export](#export) 命令导出 Experiment 已经运行过的 Trial 超参和结果。
...@@ -489,7 +519,7 @@ nnictl 支持的命令: ...@@ -489,7 +519,7 @@ nnictl 支持的命令:
内置 Advisor: BOHB 内置 Advisor: BOHB
``` ```
*如果要将数据导入到 BOHB Advisor,建议像 NNI 一样,增加 "TRIAL_BUDGET" 参数,否则,BOHB 会使用 max_budget 作为 "TRIAL_BUDGET"。 例如下:* *如果要将数据导入到 BOHB Advisor,建议像 NNI 一样,增加 "TRIAL_BUDGET" 参数,否则,BOHB 会使用 max_budget 作为 "TRIAL_BUDGET"。 例如下:*
```json ```json
[ [
...@@ -497,7 +527,7 @@ nnictl 支持的命令: ...@@ -497,7 +527,7 @@ nnictl 支持的命令:
] ]
``` ```
* *
> 将数据导入运行中的 Experiment > 将数据导入运行中的 Experiment
...@@ -563,7 +593,7 @@ nnictl 支持的命令: ...@@ -563,7 +593,7 @@ nnictl 支持的命令:
| 参数及缩写 | 是否必需 | 默认值 | 说明 | | 参数及缩写 | 是否必需 | 默认值 | 说明 |
| ---------- | ----- | --- | --------------------- | | ---------- | ----- | --- | --------------------- |
| id | False | | 需要设置的 Experiment 的 ID | | id | False | | 需要设置的 Experiment 的 id |
| --head, -h | False | | 显示 stdout 开始的若干行 | | --head, -h | False | | 显示 stdout 开始的若干行 |
| --tail, -t | False | | 显示 stdout 结尾的若干行 | | --tail, -t | False | | 显示 stdout 结尾的若干行 |
| --path, -p | False | | 显示 stdout 文件的路径 | | --path, -p | False | | 显示 stdout 文件的路径 |
...@@ -592,7 +622,7 @@ nnictl 支持的命令: ...@@ -592,7 +622,7 @@ nnictl 支持的命令:
| 参数及缩写 | 是否必需 | 默认值 | 说明 | | 参数及缩写 | 是否必需 | 默认值 | 说明 |
| ---------- | ----- | --- | --------------------- | | ---------- | ----- | --- | --------------------- |
| id | False | | 需要设置的 Experiment 的 ID | | id | False | | 需要设置的 Experiment 的 id |
| --head, -h | False | | 显示 stderr 开始的若干行 | | --head, -h | False | | 显示 stderr 开始的若干行 |
| --tail, -t | False | | 显示 stderr 结尾的若干行 | | --tail, -t | False | | 显示 stderr 结尾的若干行 |
| --path, -p | False | | 显示 stderr 文件的路径 | | --path, -p | False | | 显示 stderr 文件的路径 |
...@@ -641,7 +671,7 @@ nnictl 支持的命令: ...@@ -641,7 +671,7 @@ nnictl 支持的命令:
| 参数及缩写 | 是否必需 | 默认值 | 说明 | | 参数及缩写 | 是否必需 | 默认值 | 说明 |
| -------------- | ----- | ---- | --------------------- | | -------------- | ----- | ---- | --------------------- |
| id | False | | 需要设置的 Experiment 的 id | | id | False | | 需要设置的 Experiment 的 id |
| --trial_id, -T | False | | Trial 的 id | | --trial_id, -T | False | | Trial 的 ID |
| --port | False | 6006 | Tensorboard 进程的端口 | | --port | False | 6006 | Tensorboard 进程的端口 |
* 详细说明 * 详细说明
......
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
已知的局限: 已知的局限:
* 注意 Metis Tuner 当前仅支持在 `choice`使用数值。 * GP Tuner 和 Metis Tuner 的搜索空间只支持**数值**,(`choice` 类型在其它 Tuner 中可以使用数值,如:字符串等)。 GP Tuner 和 Metis Tuner 都使用了高斯过程的回归(Gaussian Process Regressor, GPR)。 GPR 基于计算不同点距离的和函数来进行预测,其无法计算非数值值的距离
* 请注意,对于嵌套搜索空间: * 请注意,对于嵌套搜索空间:
......
...@@ -50,6 +50,9 @@ Assessor(评估器) ...@@ -50,6 +50,9 @@ Assessor(评估器)
Advisor Advisor
------------------------ ------------------------
.. autoclass:: nni.msg_dispatcher_base.MsgDispatcherBase
:members:
.. autoclass:: nni.hyperband_advisor.hyperband_advisor.Hyperband .. autoclass:: nni.hyperband_advisor.hyperband_advisor.Hyperband
:members: :members:
......
...@@ -20,7 +20,7 @@ configure_list = [{ ...@@ -20,7 +20,7 @@ configure_list = [{
'start_epoch': 0, 'start_epoch': 0,
'end_epoch': 10, 'end_epoch': 10,
'frequency': 1, 'frequency': 1,
'op_type': 'default' 'op_types': ['default']
}] }]
pruner = AGP_Pruner(configure_list) pruner = AGP_Pruner(configure_list)
``` ```
......
# 运行模型压缩示例
以 PyTorch 剪枝为例:
```bash
python main_torch_pruner.py
```
此示例使用了 AGP Pruner。 初始化 Pruner 需要通过以下两种方式来提供配置。
- 读取 `configure_example.yaml`,这样代码会更整洁,但配置会比较复杂。
- 直接在代码中配置
此例在代码中配置了模型压缩:
```python
configure_list = [{
'initial_sparsity': 0,
'final_sparsity': 0.8,
'start_epoch': 0,
'end_epoch': 10,
'frequency': 1,
'op_types': ['default']
}]
pruner = AGP_Pruner(configure_list)
```
当调用 `pruner(model)` 时,模型会被嵌入掩码操作。 例如,某层以权重作为输入,可在权重和层操作之间插入一个操作,此操作以权重为输入,并将其应用掩码后输出。 因此,计算过程中,只要通过此操作,就会应用掩码。 还可以**不做任何改动**,来对模型进行微调。
```python
for epoch in range(10):
# update_epoch 来让 Pruner 知道 Epoch 的数量,从而能够在训练过程中调整掩码。
pruner.update_epoch(epoch)
print('# Epoch {} #'.format(epoch))
train(model, device, train_loader, optimizer)
test(model, device, test_loader)
```
微调完成后,被修剪过的权重可通过以下代码获得:
```
masks = pruner.mask_list
layer_name = xxx
mask = masks[layer_name]
```
...@@ -6,4 +6,4 @@ AGPruner: ...@@ -6,4 +6,4 @@ AGPruner:
frequency: 1 frequency: 1
initial_sparsity: 0.05 initial_sparsity: 0.05
final_sparsity: 0.8 final_sparsity: 0.8
op_type: 'default' op_types: ['default']
...@@ -91,7 +91,7 @@ def main(): ...@@ -91,7 +91,7 @@ def main():
'start_epoch': 0, 'start_epoch': 0,
'end_epoch': 10, 'end_epoch': 10,
'frequency': 1, 'frequency': 1,
'op_type': 'default' 'op_types': ['default']
}] }]
pruner = AGP_Pruner(configure_list) pruner = AGP_Pruner(configure_list)
# if you want to load from yaml file # if you want to load from yaml file
......
...@@ -82,7 +82,7 @@ def main(): ...@@ -82,7 +82,7 @@ def main():
'''you can change this to DoReFaQuantizer to implement it '''you can change this to DoReFaQuantizer to implement it
DoReFaQuantizer(configure_list).compress(tf.get_default_graph()) DoReFaQuantizer(configure_list).compress(tf.get_default_graph())
''' '''
configure_list = [{'q_bits':8, 'op_type':'default'}] configure_list = [{'q_bits':8, 'op_types':['default']}]
quantizer = QAT_Quantizer(configure_list) quantizer = QAT_Quantizer(configure_list)
quantizer(tf.get_default_graph()) quantizer(tf.get_default_graph())
# you can also use compress(model) or compress_default_graph() # you can also use compress(model) or compress_default_graph()
......
...@@ -76,7 +76,7 @@ def main(): ...@@ -76,7 +76,7 @@ def main():
'start_epoch': 0, 'start_epoch': 0,
'end_epoch': 10, 'end_epoch': 10,
'frequency': 1, 'frequency': 1,
'op_type': 'default' 'op_types': ['default']
}] }]
pruner = AGP_Pruner(configure_list) pruner = AGP_Pruner(configure_list)
......
...@@ -68,7 +68,7 @@ def main(): ...@@ -68,7 +68,7 @@ def main():
'''you can change this to DoReFaQuantizer to implement it '''you can change this to DoReFaQuantizer to implement it
DoReFaQuantizer(configure_list).compress(model) DoReFaQuantizer(configure_list).compress(model)
''' '''
configure_list = [{'q_bits':8, 'op_type':'default'}] configure_list = [{'q_bits':8, 'op_types':['default']}]
quantizer = QAT_Quantizer(configure_list) quantizer = QAT_Quantizer(configure_list)
quantizer(model) quantizer(model)
# you can also use compress(model) method # you can also use compress(model) method
......
**在 NNI 中运行神经网络架构搜索** # 在 NNI 中运行神经网络架构搜索
===
参考 [NNI-NAS-Example](https://github.com/Crysple/NNI-NAS-Example),来使用贡献者提供的 NAS 接口。 参考 [NNI-NAS-Example](https://github.com/Crysple/NNI-NAS-Example),来使用贡献者提供的 NAS 接口。
谢谢可爱的贡献者! 此目录中包含了 Trial 代码,并提供了示例的配置文件来展示如何使用 PPO Tuner 来调优此 Trial 代码。
欢迎越来越多的人加入我们! 运行下列代码来准备数据集 `cd data && . download.sh`.
\ No newline at end of file
感谢可爱的志愿者,欢迎更多的人加入我们!
\ No newline at end of file
...@@ -19,16 +19,14 @@ ...@@ -19,16 +19,14 @@
# ================================================================================================== # ==================================================================================================
# pylint: disable=wildcard-import
from .trial import * from .trial import *
from .smartparam import * from .smartparam import *
from .nas_utils import training_update from .nas_utils import training_update
class NoMoreTrialError(Exception): class NoMoreTrialError(Exception):
def __init__(self,ErrorInfo): def __init__(self, ErrorInfo):
super().__init__(self) super().__init__(self)
self.errorinfo=ErrorInfo self.errorinfo = ErrorInfo
def __str__(self): def __str__(self):
return self.errorinfo return self.errorinfo
\ No newline at end of file
...@@ -27,9 +27,10 @@ import logging ...@@ -27,9 +27,10 @@ import logging
import json import json
import importlib import importlib
from .common import enable_multi_thread, enable_multi_phase
from .constants import ModuleName, ClassName, ClassArgs, AdvisorModuleName, AdvisorClassName from .constants import ModuleName, ClassName, ClassArgs, AdvisorModuleName, AdvisorClassName
from nni.common import enable_multi_thread, enable_multi_phase from .msg_dispatcher import MsgDispatcher
from nni.msg_dispatcher import MsgDispatcher
logger = logging.getLogger('nni.main') logger = logging.getLogger('nni.main')
logger.debug('START') logger.debug('START')
...@@ -44,7 +45,7 @@ def augment_classargs(input_class_args, classname): ...@@ -44,7 +45,7 @@ def augment_classargs(input_class_args, classname):
input_class_args[key] = value input_class_args[key] = value
return input_class_args return input_class_args
def create_builtin_class_instance(classname, jsonstr_args, is_advisor = False): def create_builtin_class_instance(classname, jsonstr_args, is_advisor=False):
if is_advisor: if is_advisor:
if classname not in AdvisorModuleName or \ if classname not in AdvisorModuleName or \
importlib.util.find_spec(AdvisorModuleName[classname]) is None: importlib.util.find_spec(AdvisorModuleName[classname]) is None:
...@@ -130,55 +131,15 @@ def main(): ...@@ -130,55 +131,15 @@ def main():
if args.advisor_class_name: if args.advisor_class_name:
# advisor is enabled and starts to run # advisor is enabled and starts to run
if args.advisor_class_name in AdvisorModuleName: _run_advisor(args)
dispatcher = create_builtin_class_instance(
args.advisor_class_name,
args.advisor_args, True)
else:
dispatcher = create_customized_class_instance(
args.advisor_directory,
args.advisor_class_filename,
args.advisor_class_name,
args.advisor_args)
if dispatcher is None:
raise AssertionError('Failed to create Advisor instance')
try:
dispatcher.run()
except Exception as exception:
logger.exception(exception)
raise
else: else:
# tuner (and assessor) is enabled and starts to run # tuner (and assessor) is enabled and starts to run
tuner = None tuner = _create_tuner(args)
assessor = None
if args.tuner_class_name in ModuleName:
tuner = create_builtin_class_instance(
args.tuner_class_name,
args.tuner_args)
else:
tuner = create_customized_class_instance(
args.tuner_directory,
args.tuner_class_filename,
args.tuner_class_name,
args.tuner_args)
if tuner is None:
raise AssertionError('Failed to create Tuner instance')
if args.assessor_class_name: if args.assessor_class_name:
if args.assessor_class_name in ModuleName: assessor = _create_assessor(args)
assessor = create_builtin_class_instance( else:
args.assessor_class_name, assessor = None
args.assessor_args)
else:
assessor = create_customized_class_instance(
args.assessor_directory,
args.assessor_class_filename,
args.assessor_class_name,
args.assessor_args)
if assessor is None:
raise AssertionError('Failed to create Assessor instance')
dispatcher = MsgDispatcher(tuner, assessor) dispatcher = MsgDispatcher(tuner, assessor)
try: try:
...@@ -193,6 +154,59 @@ def main(): ...@@ -193,6 +154,59 @@ def main():
assessor._on_error() assessor._on_error()
raise raise
def _run_advisor(args):
if args.advisor_class_name in AdvisorModuleName:
dispatcher = create_builtin_class_instance(
args.advisor_class_name,
args.advisor_args, True)
else:
dispatcher = create_customized_class_instance(
args.advisor_directory,
args.advisor_class_filename,
args.advisor_class_name,
args.advisor_args)
if dispatcher is None:
raise AssertionError('Failed to create Advisor instance')
try:
dispatcher.run()
except Exception as exception:
logger.exception(exception)
raise
def _create_tuner(args):
if args.tuner_class_name in ModuleName:
tuner = create_builtin_class_instance(
args.tuner_class_name,
args.tuner_args)
else:
tuner = create_customized_class_instance(
args.tuner_directory,
args.tuner_class_filename,
args.tuner_class_name,
args.tuner_args)
if tuner is None:
raise AssertionError('Failed to create Tuner instance')
return tuner
def _create_assessor(args):
if args.assessor_class_name in ModuleName:
assessor = create_builtin_class_instance(
args.assessor_class_name,
args.assessor_args)
else:
assessor = create_customized_class_instance(
args.assessor_directory,
args.assessor_class_filename,
args.assessor_class_name,
args.assessor_args)
if assessor is None:
raise AssertionError('Failed to create Assessor instance')
return assessor
if __name__ == '__main__': if __name__ == '__main__':
try: try:
main() main()
......
...@@ -31,7 +31,6 @@ class AssessResult(Enum): ...@@ -31,7 +31,6 @@ class AssessResult(Enum):
Bad = False Bad = False
class Assessor(Recoverable): class Assessor(Recoverable):
# pylint: disable=no-self-use,unused-argument
def assess_trial(self, trial_job_id, trial_history): def assess_trial(self, trial_job_id, trial_history):
"""Determines whether a trial should be killed. Must override. """Determines whether a trial should be killed. Must override.
...@@ -46,21 +45,20 @@ class Assessor(Recoverable): ...@@ -46,21 +45,20 @@ class Assessor(Recoverable):
trial_job_id: identifier of the trial (str). trial_job_id: identifier of the trial (str).
success: True if the trial successfully completed; False if failed or terminated. success: True if the trial successfully completed; False if failed or terminated.
""" """
pass
def load_checkpoint(self): def load_checkpoint(self):
"""Load the checkpoint of assessr. """Load the checkpoint of assessr.
path: checkpoint directory for assessor path: checkpoint directory for assessor
""" """
checkpoin_path = self.get_checkpoint_path() checkpoin_path = self.get_checkpoint_path()
_logger.info('Load checkpoint ignored by assessor, checkpoint path: %s' % checkpoin_path) _logger.info('Load checkpoint ignored by assessor, checkpoint path: %s', checkpoin_path)
def save_checkpoint(self): def save_checkpoint(self):
"""Save the checkpoint of assessor. """Save the checkpoint of assessor.
path: checkpoint directory for assessor path: checkpoint directory for assessor
""" """
checkpoin_path = self.get_checkpoint_path() checkpoin_path = self.get_checkpoint_path()
_logger.info('Save checkpoint ignored by assessor, checkpoint path: %s' % checkpoin_path) _logger.info('Save checkpoint ignored by assessor, checkpoint path: %s', checkpoin_path)
def _on_exit(self): def _on_exit(self):
pass pass
......
...@@ -100,7 +100,7 @@ class BatchTuner(Tuner): ...@@ -100,7 +100,7 @@ class BatchTuner(Tuner):
data: data:
a list of dictionarys, each of which has at least two keys, 'parameter' and 'value' a list of dictionarys, each of which has at least two keys, 'parameter' and 'value'
""" """
if len(self.values) == 0: if not self.values:
logger.info("Search space has not been initialized, skip this data import") logger.info("Search space has not been initialized, skip this data import")
return return
......
...@@ -51,7 +51,7 @@ def create_parameter_id(): ...@@ -51,7 +51,7 @@ def create_parameter_id():
int int
parameter id parameter id
""" """
global _next_parameter_id # pylint: disable=global-statement global _next_parameter_id
_next_parameter_id += 1 _next_parameter_id += 1
return _next_parameter_id - 1 return _next_parameter_id - 1
...@@ -80,7 +80,7 @@ def create_bracket_parameter_id(brackets_id, brackets_curr_decay, increased_id=- ...@@ -80,7 +80,7 @@ def create_bracket_parameter_id(brackets_id, brackets_curr_decay, increased_id=-
return params_id return params_id
class Bracket(object): class Bracket:
""" """
A bracket in BOHB, all the information of a bracket is managed by A bracket in BOHB, all the information of a bracket is managed by
an instance of this class. an instance of this class.
...@@ -98,7 +98,7 @@ class Bracket(object): ...@@ -98,7 +98,7 @@ class Bracket(object):
max_budget : float max_budget : float
The largest budget to consider. Needs to be larger than min_budget! The largest budget to consider. Needs to be larger than min_budget!
The budgets will be geometrically distributed The budgets will be geometrically distributed
:math:`a^2 + b^2 = c^2 \sim \eta^k` for :math:`k\in [0, 1, ... , num\_subsets - 1]`. :math:`a^2 + b^2 = c^2 \\sim \\eta^k` for :math:`k\\in [0, 1, ... , num\\_subsets - 1]`.
optimize_mode: str optimize_mode: str
optimize mode, 'maximize' or 'minimize' optimize mode, 'maximize' or 'minimize'
""" """
...@@ -169,7 +169,7 @@ class Bracket(object): ...@@ -169,7 +169,7 @@ class Bracket(object):
If we have generated new trials after this trial end, we will return a new trial parameters. If we have generated new trials after this trial end, we will return a new trial parameters.
Otherwise, we will return None. Otherwise, we will return None.
""" """
global _KEY # pylint: disable=global-statement global _KEY
self.num_finished_configs[i] += 1 self.num_finished_configs[i] += 1
logger.debug('bracket id: %d, round: %d %d, finished: %d, all: %d', logger.debug('bracket id: %d, round: %d %d, finished: %d, all: %d',
self.s, self.i, i, self.num_finished_configs[i], self.num_configs_to_run[i]) self.s, self.i, i, self.num_finished_configs[i], self.num_configs_to_run[i])
...@@ -377,8 +377,10 @@ class BOHB(MsgDispatcherBase): ...@@ -377,8 +377,10 @@ class BOHB(MsgDispatcherBase):
if self.curr_s < 0: if self.curr_s < 0:
logger.info("s < 0, Finish this round of Hyperband in BOHB. Generate new round") logger.info("s < 0, Finish this round of Hyperband in BOHB. Generate new round")
self.curr_s = self.s_max self.curr_s = self.s_max
self.brackets[self.curr_s] = Bracket(s=self.curr_s, s_max=self.s_max, eta=self.eta, self.brackets[self.curr_s] = Bracket(
max_budget=self.max_budget, optimize_mode=self.optimize_mode) s=self.curr_s, s_max=self.s_max, eta=self.eta,
max_budget=self.max_budget, optimize_mode=self.optimize_mode
)
next_n, next_r = self.brackets[self.curr_s].get_n_r() next_n, next_r = self.brackets[self.curr_s].get_n_r()
logger.debug( logger.debug(
'new SuccessiveHalving iteration, next_n=%d, next_r=%d', next_n, next_r) 'new SuccessiveHalving iteration, next_n=%d, next_r=%d', next_n, next_r)
...@@ -599,7 +601,7 @@ class BOHB(MsgDispatcherBase): ...@@ -599,7 +601,7 @@ class BOHB(MsgDispatcherBase):
logger.debug('bracket id = %s, metrics value = %s, type = %s', s, value, data['type']) logger.debug('bracket id = %s, metrics value = %s, type = %s', s, value, data['type'])
s = int(s) s = int(s)
# add <trial_job_id, parameter_id> to self.job_id_para_id_map here, # add <trial_job_id, parameter_id> to self.job_id_para_id_map here,
# because when the first parameter_id is created, trial_job_id is not known yet. # because when the first parameter_id is created, trial_job_id is not known yet.
if data['trial_job_id'] in self.job_id_para_id_map: if data['trial_job_id'] in self.job_id_para_id_map:
assert self.job_id_para_id_map[data['trial_job_id']] == data['parameter_id'] assert self.job_id_para_id_map[data['trial_job_id']] == data['parameter_id']
...@@ -643,14 +645,14 @@ class BOHB(MsgDispatcherBase): ...@@ -643,14 +645,14 @@ class BOHB(MsgDispatcherBase):
""" """
_completed_num = 0 _completed_num = 0
for trial_info in data: for trial_info in data:
logger.info("Importing data, current processing progress %s / %s" %(_completed_num, len(data))) logger.info("Importing data, current processing progress %s / %s", _completed_num, len(data))
_completed_num += 1 _completed_num += 1
assert "parameter" in trial_info assert "parameter" in trial_info
_params = trial_info["parameter"] _params = trial_info["parameter"]
assert "value" in trial_info assert "value" in trial_info
_value = trial_info['value'] _value = trial_info['value']
if not _value: if not _value:
logger.info("Useless trial data, value is %s, skip this trial data." %_value) logger.info("Useless trial data, value is %s, skip this trial data.", _value)
continue continue
budget_exist_flag = False budget_exist_flag = False
barely_params = dict() barely_params = dict()
...@@ -662,7 +664,7 @@ class BOHB(MsgDispatcherBase): ...@@ -662,7 +664,7 @@ class BOHB(MsgDispatcherBase):
barely_params[keys] = _params[keys] barely_params[keys] = _params[keys]
if not budget_exist_flag: if not budget_exist_flag:
_budget = self.max_budget _budget = self.max_budget
logger.info("Set \"TRIAL_BUDGET\" value to %s (max budget)" %self.max_budget) logger.info("Set \"TRIAL_BUDGET\" value to %s (max budget)", self.max_budget)
if self.optimize_mode is OptimizeMode.Maximize: if self.optimize_mode is OptimizeMode.Maximize:
reward = -_value reward = -_value
else: else:
......
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import logging import logging
import traceback
import ConfigSpace import ConfigSpace
import ConfigSpace.hyperparameters import ConfigSpace.hyperparameters
...@@ -39,7 +38,7 @@ import statsmodels.api as sm ...@@ -39,7 +38,7 @@ import statsmodels.api as sm
logger = logging.getLogger('BOHB_Advisor') logger = logging.getLogger('BOHB_Advisor')
class CG_BOHB(object): class CG_BOHB:
def __init__(self, configspace, min_points_in_model=None, def __init__(self, configspace, min_points_in_model=None,
top_n_percent=15, num_samples=64, random_fraction=1/3, top_n_percent=15, num_samples=64, random_fraction=1/3,
bandwidth_factor=3, min_bandwidth=1e-3): bandwidth_factor=3, min_bandwidth=1e-3):
...@@ -77,8 +76,8 @@ class CG_BOHB(object): ...@@ -77,8 +76,8 @@ class CG_BOHB(object):
self.min_points_in_model = len(self.configspace.get_hyperparameters())+1 self.min_points_in_model = len(self.configspace.get_hyperparameters())+1
if self.min_points_in_model < len(self.configspace.get_hyperparameters())+1: if self.min_points_in_model < len(self.configspace.get_hyperparameters())+1:
logger.warning('Invalid min_points_in_model value. Setting it to %i'%(len(self.configspace.get_hyperparameters())+1)) logger.warning('Invalid min_points_in_model value. Setting it to %i', len(self.configspace.get_hyperparameters()) + 1)
self.min_points_in_model =len(self.configspace.get_hyperparameters())+1 self.min_points_in_model = len(self.configspace.get_hyperparameters()) + 1
self.num_samples = num_samples self.num_samples = num_samples
self.random_fraction = random_fraction self.random_fraction = random_fraction
...@@ -107,9 +106,9 @@ class CG_BOHB(object): ...@@ -107,9 +106,9 @@ class CG_BOHB(object):
self.kde_models = dict() self.kde_models = dict()
def largest_budget_with_model(self): def largest_budget_with_model(self):
if len(self.kde_models) == 0: if not self.kde_models:
return(-float('inf')) return -float('inf')
return(max(self.kde_models.keys())) return max(self.kde_models.keys())
def sample_from_largest_budget(self, info_dict): def sample_from_largest_budget(self, info_dict):
"""We opted for a single multidimensional KDE compared to the """We opted for a single multidimensional KDE compared to the
...@@ -162,11 +161,11 @@ class CG_BOHB(object): ...@@ -162,11 +161,11 @@ class CG_BOHB(object):
val = minimize_me(vector) val = minimize_me(vector)
if not np.isfinite(val): if not np.isfinite(val):
logger.warning('sampled vector: %s has EI value %s'%(vector, val)) logger.warning('sampled vector: %s has EI value %s', vector, val)
logger.warning("data in the KDEs:\n%s\n%s"%(kde_good.data, kde_bad.data)) logger.warning("data in the KDEs:\n%s\n%s", kde_good.data, kde_bad.data)
logger.warning("bandwidth of the KDEs:\n%s\n%s"%(kde_good.bw, kde_bad.bw)) logger.warning("bandwidth of the KDEs:\n%s\n%s", kde_good.bw, kde_bad.bw)
logger.warning("l(x) = %s"%(l(vector))) logger.warning("l(x) = %s", l(vector))
logger.warning("g(x) = %s"%(g(vector))) logger.warning("g(x) = %s", g(vector))
# right now, this happens because a KDE does not contain all values for a categorical parameter # right now, this happens because a KDE does not contain all values for a categorical parameter
# this cannot be fixed with the statsmodels KDE, so for now, we are just going to evaluate this one # this cannot be fixed with the statsmodels KDE, so for now, we are just going to evaluate this one
...@@ -181,19 +180,15 @@ class CG_BOHB(object): ...@@ -181,19 +180,15 @@ class CG_BOHB(object):
best_vector = vector best_vector = vector
if best_vector is None: if best_vector is None:
logger.debug("Sampling based optimization with %i samples failed -> using random configuration"%self.num_samples) logger.debug("Sampling based optimization with %i samples failed -> using random configuration", self.num_samples)
sample = self.configspace.sample_configuration().get_dictionary() sample = self.configspace.sample_configuration().get_dictionary()
info_dict['model_based_pick'] = False info_dict['model_based_pick'] = False
else: else:
logger.debug('best_vector: {}, {}, {}, {}'.format(best_vector, best, l(best_vector), g(best_vector))) logger.debug('best_vector: %s, %s, %s, %s', best_vector, best, l(best_vector), g(best_vector))
for i, hp_value in enumerate(best_vector): for i, _ in enumerate(best_vector):
if isinstance( hp = self.configspace.get_hyperparameter(self.configspace.get_hyperparameter_by_idx(i))
self.configspace.get_hyperparameter( if isinstance(hp, ConfigSpace.hyperparameters.CategoricalHyperparameter):
self.configspace.get_hyperparameter_by_idx(i)
),
ConfigSpace.hyperparameters.CategoricalHyperparameter
):
best_vector[i] = int(np.rint(best_vector[i])) best_vector[i] = int(np.rint(best_vector[i]))
sample = ConfigSpace.Configuration(self.configspace, vector=best_vector).get_dictionary() sample = ConfigSpace.Configuration(self.configspace, vector=best_vector).get_dictionary()
...@@ -224,12 +219,12 @@ class CG_BOHB(object): ...@@ -224,12 +219,12 @@ class CG_BOHB(object):
# If no model is available, sample from prior # If no model is available, sample from prior
# also mix in a fraction of random configs # also mix in a fraction of random configs
if len(self.kde_models.keys()) == 0 or np.random.rand() < self.random_fraction: if not self.kde_models.keys() or np.random.rand() < self.random_fraction:
sample = self.configspace.sample_configuration() sample = self.configspace.sample_configuration()
info_dict['model_based_pick'] = False info_dict['model_based_pick'] = False
if sample is None: if sample is None:
sample, info_dict= self.sample_from_largest_budget(info_dict) sample, info_dict = self.sample_from_largest_budget(info_dict)
sample = ConfigSpace.util.deactivate_inactive_hyperparameters( sample = ConfigSpace.util.deactivate_inactive_hyperparameters(
configuration_space=self.configspace, configuration_space=self.configspace,
...@@ -245,10 +240,10 @@ class CG_BOHB(object): ...@@ -245,10 +240,10 @@ class CG_BOHB(object):
for i in range(array.shape[0]): for i in range(array.shape[0]):
datum = np.copy(array[i]) datum = np.copy(array[i])
nan_indices = np.argwhere(np.isnan(datum)).flatten() nan_indices = np.argwhere(np.isnan(datum)).flatten()
while(np.any(nan_indices)): while np.any(nan_indices):
nan_idx = nan_indices[0] nan_idx = nan_indices[0]
valid_indices = np.argwhere(np.isfinite(array[:,nan_idx])).flatten() valid_indices = np.argwhere(np.isfinite(array[:, nan_idx])).flatten()
if len(valid_indices) > 0: if valid_indices:
# pick one of them at random and overwrite all NaN values # pick one of them at random and overwrite all NaN values
row_idx = np.random.choice(valid_indices) row_idx = np.random.choice(valid_indices)
datum[nan_indices] = array[row_idx, nan_indices] datum[nan_indices] = array[row_idx, nan_indices]
...@@ -260,8 +255,8 @@ class CG_BOHB(object): ...@@ -260,8 +255,8 @@ class CG_BOHB(object):
else: else:
datum[nan_idx] = np.random.randint(t) datum[nan_idx] = np.random.randint(t)
nan_indices = np.argwhere(np.isnan(datum)).flatten() nan_indices = np.argwhere(np.isnan(datum)).flatten()
return_array[i,:] = datum return_array[i, :] = datum
return(return_array) return return_array
def new_result(self, loss, budget, parameters, update_model=True): def new_result(self, loss, budget, parameters, update_model=True):
""" """
...@@ -305,7 +300,7 @@ class CG_BOHB(object): ...@@ -305,7 +300,7 @@ class CG_BOHB(object):
# a) if not enough points are available # a) if not enough points are available
if len(self.configs[budget]) <= self.min_points_in_model - 1: if len(self.configs[budget]) <= self.min_points_in_model - 1:
logger.debug("Only %i run(s) for budget %f available, need more than %s \ logger.debug("Only %i run(s) for budget %f available, need more than %s \
-> can't build model!"%(len(self.configs[budget]), budget, self.min_points_in_model+1)) -> can't build model!", len(self.configs[budget]), budget, self.min_points_in_model+1)
return return
# b) during warnm starting when we feed previous results in and only update once # b) during warnm starting when we feed previous results in and only update once
if not update_model: if not update_model:
...@@ -345,5 +340,5 @@ class CG_BOHB(object): ...@@ -345,5 +340,5 @@ class CG_BOHB(object):
} }
# update probs for the categorical parameters for later sampling # update probs for the categorical parameters for later sampling
logger.debug('done building a new model for budget %f based on %i/%i split\nBest loss for this budget:%f\n' logger.debug('done building a new model for budget %f based on %i/%i split\nBest loss for this budget:%f\n',
%(budget, n_good, n_bad, np.min(train_losses))) budget, n_good, n_bad, np.min(train_losses))
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