Commit 3be88922 authored by suiguoxin's avatar suiguoxin
Browse files

Merge branch 'master' of git://github.com/microsoft/nni

parents b92c4ab2 5a058baf
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
此样例演示了 NNI 如何支持嵌套的搜索空间。 搜索空间文件示了如何定义嵌套的搜索空间。 此样例演示了 NNI 如何支持嵌套的搜索空间。 搜索空间文件示了如何定义嵌套的搜索空间。
`代码目录: examples/trials/mnist-cascading-search-space/` `代码目录: examples/trials/mnist-nested-search-space/`
<a name="mnist-kubeflow-tf"></a> <a name="mnist-kubeflow-tf"></a>
**用 Kubeflow 运行分布式的 MNIST (tensorflow)** **用 Kubeflow 运行分布式的 MNIST (tensorflow)**
......
# Windows 本地模式(测试中 # Windows 上的 NNI(实验阶段的功能
当前 Windows 下仅支持本机模式。 推荐 Windows 10 的 1809 版,其经过了测试。 当前 Windows 支持本机、远程和 OpenPAI 模式。 推荐 Windows 10 的 1809 版,其经过了测试。
## **在 Windows 上安装** ## **在 Windows 上安装**
...@@ -25,15 +25,15 @@ Set-ExecutionPolicy -ExecutionPolicy Unrestricted ...@@ -25,15 +25,15 @@ Set-ExecutionPolicy -ExecutionPolicy Unrestricted
先决条件: `python >=3.5`, `git`, `PowerShell` 先决条件: `python >=3.5`, `git`, `PowerShell`
```bash ```bash
git clone -b v0.7 https://github.com/Microsoft/nni.git git clone -b v0.8 https://github.com/Microsoft/nni.git
cd nni cd nni
powershell ./install.ps1 powershell -file install.ps1
``` ```
运行完以上脚本后,从命令行使用 **config_windows.yml** 来启动 Experiment,完成安装验证。 运行完以上脚本后,从命令行使用 **config_windows.yml** 来启动 Experiment,完成安装验证。
```bash ```bash
nnictl create --config nni/examples/trials/mnist/config_windows.yml nnictl create --config nni\examples\trials\mnist\config_windows.yml
``` ```
同样,其它示例的 YAML 配置中也需将 Trial 命令的 `python3` 替换为 `python` 同样,其它示例的 YAML 配置中也需将 Trial 命令的 `python3` 替换为 `python`
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
## 安装 ## 安装
当前支持 Linux,MacOS 和 Windows(本机模式),在 Ubuntu 16.04 或更高版本,MacOS 10.14.1 以及 Windows 10.1809 上进行了测试。 在 `python >= 3.5` 的环境中,只需要运行 `pip install` 即可完成安装。 当前支持 Linux,MacOS 和 Windows,在 Ubuntu 16.04 或更高版本,MacOS 10.14.1 以及 Windows 10.1809 上进行了测试。 在 `python >= 3.5` 的环境中,只需要运行 `pip install` 即可完成安装。
#### Linux 和 MacOS #### Linux 和 MacOS
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#### Windows #### Windows
如果选择 Windows 本机模式并使用 PowerShell 运行脚本,需要首次以管理员身份在 PowerShell 环境中运行以下命令。 如果 Windows 上使用 NNI,首次使用 PowerShell ,需要以管理员身份运行下列命令。
```bash ```bash
Set-ExecutionPolicy -ExecutionPolicy Unrestricted Set-ExecutionPolicy -ExecutionPolicy Unrestricted
...@@ -161,7 +161,7 @@ trial: ...@@ -161,7 +161,7 @@ trial:
从命令行使用 **config_windows.yml** 文件启动 MNIST Experiment 。 从命令行使用 **config_windows.yml** 文件启动 MNIST Experiment 。
**注意**:如果使用 Windows 本机模式,则需要在 config.yml 文件中,将 `python3` 改为 `python`,或者使用 config_windows.yml 来开始 Experiment。 **注意**:如果使用 Windows,则需要在 config.yml 文件中,将 `python3` 改为 `python`,或者使用 config_windows.yml 来开始 Experiment。
```bash ```bash
nnictl create --config nni/examples/trials/mnist/config_windows.yml nnictl create --config nni/examples/trials/mnist/config_windows.yml
......
...@@ -56,6 +56,10 @@ machineList: ...@@ -56,6 +56,10 @@ machineList:
passwd: bob123 passwd: bob123
``` ```
可以使用不同系统来在远程计算机上运行 Experiment。
#### Linux 和 macOS
填好 `machineList` 部分,然后运行: 填好 `machineList` 部分,然后运行:
```bash ```bash
...@@ -64,6 +68,16 @@ nnictl create --config ~/nni/examples/trials/mnist-annotation/config_remote.yml ...@@ -64,6 +68,16 @@ nnictl create --config ~/nni/examples/trials/mnist-annotation/config_remote.yml
来启动 Experiment。 来启动 Experiment。
#### Windows
填好 `machineList` 部分,然后运行:
```bash
nnictl create --config %userprofile%\nni\examples\trials\mnist-annotation\config_remote.yml
```
来启动 Experiment。
## 版本校验 ## 版本校验
从 0.6 开始,NNI 支持版本校验,详情参考[这里](PaiMode.md) 从 0.6 开始,NNI 支持版本校验,详情参考[这里](PaiMode.md)
\ No newline at end of file
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
* 表示变量的值是选项之一。 这里的 'options' 是一个数组。 选项的每个元素都是字符串。 也可以是嵌套的子搜索空间。此子搜索空间仅在相应的元素选中后才起作用。 该子搜索空间中的变量可看作是条件变量。 * 表示变量的值是选项之一。 这里的 'options' 是一个数组。 选项的每个元素都是字符串。 也可以是嵌套的子搜索空间。此子搜索空间仅在相应的元素选中后才起作用。 该子搜索空间中的变量可看作是条件变量。
* 这是个简单的 [nested] 搜索空间定义的[示例](../../examples/trials/mnist-cascading-search-space/search_space.json)。 如果选项列表中的元素是 dict,则它是一个子搜索空间,对于内置的 Tuner,必须在此 dict 中添加键 “_name”,这有助于标识选中的元素。 相应的,这是从 NNI 中获得的嵌套搜索空间定义的[示例](../../examples/trials/mnist-cascading-search-space/sample.json)。 以下 Tuner 支持嵌套搜索空间: * 这是个简单的 [nested] 搜索空间定义的[示例](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-nested-search-space/search_space.json)。 如果选项列表中的元素是 dict,则它是一个子搜索空间,对于内置的 Tuner,必须在此 dict 中添加键 “_name”,这有助于标识选中的元素。 相应的,这是从 NNI 中获得的嵌套搜索空间定义的[示例](https://github.com/microsoft/nni/tree/master/examples/trials/mnist-nested-search-space/sample.json)。 以下 Tuner 支持嵌套搜索空间:
* Random Search(随机搜索) * Random Search(随机搜索)
* TPE * TPE
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
* 查看 Experiment 的配置和搜索空间内容。 * 查看 Experiment 的配置和搜索空间内容。
* 支持下载 Experiment 结果。 * 支持下载 Experiment 结果。
* 支持导出 nni-manager 和 dispatcher 的日志文件。
* 如果有任何问题,可以点击 “Feedback” 告诉我们。
![](../img/webui-img/over1.png) ![](../img/webui-img/over1.png)
...@@ -52,6 +54,14 @@ ...@@ -52,6 +54,14 @@
![](../img/webui-img/detail-local.png) ![](../img/webui-img/detail-local.png)
* "Add column" 按钮可选择在表格中显示的列。 如果 Experiment 的最终结果是 dict,则可以在表格中查看其它键。
![](../img/webui-img/addColumn.png)
* 可使用 "Copy as python" 按钮来拷贝 Trial 的参数。
![](../img/webui-img/copyParameter.png)
* 如果在 OpenPAI 或 Kubeflow 平台上运行,还可以看到 hdfsLog。 * 如果在 OpenPAI 或 Kubeflow 平台上运行,还可以看到 hdfsLog。
![](../img/webui-img/detail-pai.png) ![](../img/webui-img/detail-pai.png)
......
#################
自动机器学习的经验分享
#################
.. toctree::
:maxdepth: 2
神经网络架构搜索的对比<CommunitySharings/AutomlPracticeSharing/NasComparison>
**Run Neural Network Architecture Search in NNI**
===
Now we have an NAS example [NNI-NAS-Example](https://github.com/Crysple/NNI-NAS-Example) run in NNI using NAS interface from our contributors.
Thanks our lovely contributors.
And welcome more and more people to join us!
...@@ -74,6 +74,8 @@ def load_data(train_path='./data/regression.train', test_path='./data/regression ...@@ -74,6 +74,8 @@ def load_data(train_path='./data/regression.train', test_path='./data/regression
def run(lgb_train, lgb_eval, params, X_test, y_test): def run(lgb_train, lgb_eval, params, X_test, y_test):
print('Start training...') print('Start training...')
params['num_leaves'] = int(params['num_leaves'])
# train # train
gbm = lgb.train(params, gbm = lgb.train(params,
lgb_train, lgb_train,
......
{ {
"num_leaves":{"_type":"choice","_value":[31, 28, 24, 20]}, "num_leaves":{"_type":"randint","_value":[20, 31]},
"learning_rate":{"_type":"choice","_value":[0.01, 0.05, 0.1, 0.2]}, "learning_rate":{"_type":"choice","_value":[0.01, 0.05, 0.1, 0.2]},
"bagging_fraction":{"_type":"uniform","_value":[0.7, 1.0]}, "bagging_fraction":{"_type":"uniform","_value":[0.7, 1.0]},
"bagging_freq":{"_type":"choice","_value":[1, 2, 4, 8, 10]} "bagging_freq":{"_type":"choice","_value":[1, 2, 4, 8, 10]}
......
authorName: default authorName: default
experimentName: example_pytorch_cifar10 experimentName: example_pytorch_cifar10
trialConcurrency: 1 trialConcurrency: 4
maxExecDuration: 100h maxExecDuration: 100h
maxTrialNum: 10 maxTrialNum: 10
#choice: local, remote, pai #choice: local, remote, pai
...@@ -19,3 +19,5 @@ trial: ...@@ -19,3 +19,5 @@ trial:
command: python3 main.py command: python3 main.py
codeDir: . codeDir: .
gpuNum: 1 gpuNum: 1
localConfig:
maxTrialNumPerGpu: 2
authorName: default authorName: default
experimentName: mnist-cascading-search-space experimentName: mnist-nested-search-space
trialConcurrency: 2 trialConcurrency: 2
maxExecDuration: 1h maxExecDuration: 1h
maxTrialNum: 100 maxTrialNum: 100
......
...@@ -14,7 +14,7 @@ from tensorflow.examples.tutorials.mnist import input_data ...@@ -14,7 +14,7 @@ from tensorflow.examples.tutorials.mnist import input_data
import nni import nni
logger = logging.getLogger('mnist_cascading_search_space') logger = logging.getLogger('mnist_nested_search_space')
FLAGS = None FLAGS = None
class MnistNetwork(object): class MnistNetwork(object):
......
...@@ -70,6 +70,18 @@ interface TrialJobInfo { ...@@ -70,6 +70,18 @@ interface TrialJobInfo {
stderrPath?: string; stderrPath?: string;
} }
interface HyperParameterFormat {
parameter_source: string;
parameters: Object;
parameter_id: number;
}
interface ExportedDataFormat {
parameter: Object;
value: Object;
id: string;
}
abstract class DataStore { abstract class DataStore {
public abstract init(): Promise<void>; public abstract init(): Promise<void>;
public abstract close(): Promise<void>; public abstract close(): Promise<void>;
...@@ -82,6 +94,8 @@ abstract class DataStore { ...@@ -82,6 +94,8 @@ abstract class DataStore {
public abstract getTrialJob(trialJobId: string): Promise<TrialJobInfo>; public abstract getTrialJob(trialJobId: string): Promise<TrialJobInfo>;
public abstract storeMetricData(trialJobId: string, data: string): Promise<void>; public abstract storeMetricData(trialJobId: string, data: string): Promise<void>;
public abstract getMetricData(trialJobId?: string, metricType?: MetricType): Promise<MetricDataRecord[]>; public abstract getMetricData(trialJobId?: string, metricType?: MetricType): Promise<MetricDataRecord[]>;
public abstract exportTrialHpConfigs(): Promise<string>;
public abstract getImportedData(): Promise<string[]>;
} }
abstract class Database { abstract class Database {
...@@ -99,5 +113,5 @@ abstract class Database { ...@@ -99,5 +113,5 @@ abstract class Database {
export { export {
DataStore, Database, TrialJobEvent, MetricType, MetricData, TrialJobInfo, DataStore, Database, TrialJobEvent, MetricType, MetricData, TrialJobInfo,
ExperimentProfileRecord, TrialJobEventRecord, MetricDataRecord ExperimentProfileRecord, TrialJobEventRecord, MetricDataRecord, HyperParameterFormat, ExportedDataFormat
}; };
...@@ -100,6 +100,7 @@ abstract class Manager { ...@@ -100,6 +100,7 @@ abstract class Manager {
public abstract getExperimentProfile(): Promise<ExperimentProfile>; public abstract getExperimentProfile(): Promise<ExperimentProfile>;
public abstract updateExperimentProfile(experimentProfile: ExperimentProfile, updateType: ProfileUpdateType): Promise<void>; public abstract updateExperimentProfile(experimentProfile: ExperimentProfile, updateType: ProfileUpdateType): Promise<void>;
public abstract importData(data: string): Promise<void>; public abstract importData(data: string): Promise<void>;
public abstract exportData(): Promise<string>;
public abstract addCustomizedTrialJob(hyperParams: string): Promise<void>; public abstract addCustomizedTrialJob(hyperParams: string): Promise<void>;
public abstract cancelTrialJobByUser(trialJobId: string): Promise<void>; public abstract cancelTrialJobByUser(trialJobId: string): Promise<void>;
......
...@@ -24,7 +24,8 @@ import { Deferred } from 'ts-deferred'; ...@@ -24,7 +24,8 @@ import { Deferred } from 'ts-deferred';
import * as component from '../common/component'; import * as component from '../common/component';
import { Database, DataStore, MetricData, MetricDataRecord, MetricType, import { Database, DataStore, MetricData, MetricDataRecord, MetricType,
TrialJobEvent, TrialJobEventRecord, TrialJobInfo } from '../common/datastore'; TrialJobEvent, TrialJobEventRecord, TrialJobInfo, HyperParameterFormat,
ExportedDataFormat } from '../common/datastore';
import { NNIError } from '../common/errors'; import { NNIError } from '../common/errors';
import { getExperimentId, isNewExperiment } from '../common/experimentStartupInfo'; import { getExperimentId, isNewExperiment } from '../common/experimentStartupInfo';
import { getLogger, Logger } from '../common/log'; import { getLogger, Logger } from '../common/log';
...@@ -171,6 +172,61 @@ class NNIDataStore implements DataStore { ...@@ -171,6 +172,61 @@ class NNIDataStore implements DataStore {
return this.db.queryMetricData(trialJobId, metricType); return this.db.queryMetricData(trialJobId, metricType);
} }
public async exportTrialHpConfigs(): Promise<string> {
const jobs: TrialJobInfo[] = await this.listTrialJobs();
let exportedData: ExportedDataFormat[] = [];
for (const job of jobs) {
if (job.hyperParameters && job.finalMetricData) {
if (job.hyperParameters.length === 1 && job.finalMetricData.length === 1) {
// optimization for non-multi-phase case
const parameters: HyperParameterFormat = <HyperParameterFormat>JSON.parse(job.hyperParameters[0]);
const oneEntry: ExportedDataFormat = {
parameter: parameters.parameters,
value: JSON.parse(job.finalMetricData[0].data),
id: job.id
};
exportedData.push(oneEntry);
} else {
let paraMap: Map<number, Object> = new Map();
let metricMap: Map<number, Object> = new Map();
for (const eachPara of job.hyperParameters) {
const parameters: HyperParameterFormat = <HyperParameterFormat>JSON.parse(eachPara);
paraMap.set(parameters.parameter_id, parameters.parameters);
}
for (const eachMetric of job.finalMetricData) {
const value: Object = JSON.parse(eachMetric.data);
metricMap.set(Number(eachMetric.parameterId), value);
}
paraMap.forEach((value: Object, key: number) => {
const metricValue: Object | undefined = metricMap.get(key);
if (metricValue) {
const oneEntry: ExportedDataFormat = {
parameter: value,
value: metricValue,
id: job.id
};
exportedData.push(oneEntry);
}
});
}
}
}
return JSON.stringify(exportedData);
}
public async getImportedData(): Promise<string[]> {
let importedData: string[] = [];
const importDataEvents: TrialJobEventRecord[] = await this.db.queryTrialJobEvent(undefined, 'IMPORT_DATA');
for (const event of importDataEvents) {
if (event.data) {
importedData.push(event.data);
}
}
return importedData;
}
private async queryTrialJobs(status?: TrialJobStatus, trialJobId?: string): Promise<TrialJobInfo[]> { private async queryTrialJobs(status?: TrialJobStatus, trialJobId?: string): Promise<TrialJobInfo[]> {
const result: TrialJobInfo[] = []; const result: TrialJobInfo[] = [];
const trialJobEvents: TrialJobEventRecord[] = await this.db.queryTrialJobEvent(trialJobId); const trialJobEvents: TrialJobEventRecord[] = await this.db.queryTrialJobEvent(trialJobId);
......
...@@ -58,6 +58,8 @@ class NNIManager implements Manager { ...@@ -58,6 +58,8 @@ class NNIManager implements Manager {
private status: NNIManagerStatus; private status: NNIManagerStatus;
private waitingTrials: string[]; private waitingTrials: string[];
private trialJobs: Map<string, TrialJobDetail>; private trialJobs: Map<string, TrialJobDetail>;
private trialDataForTuner: string;
private trialJobMetricListener: (metric: TrialJobMetric) => void; private trialJobMetricListener: (metric: TrialJobMetric) => void;
constructor() { constructor() {
...@@ -69,6 +71,7 @@ class NNIManager implements Manager { ...@@ -69,6 +71,7 @@ class NNIManager implements Manager {
this.dispatcherPid = 0; this.dispatcherPid = 0;
this.waitingTrials = []; this.waitingTrials = [];
this.trialJobs = new Map<string, TrialJobDetail>(); this.trialJobs = new Map<string, TrialJobDetail>();
this.trialDataForTuner = '';
this.log = getLogger(); this.log = getLogger();
this.dataStore = component.get(DataStore); this.dataStore = component.get(DataStore);
...@@ -116,6 +119,10 @@ class NNIManager implements Manager { ...@@ -116,6 +119,10 @@ class NNIManager implements Manager {
return this.dataStore.storeTrialJobEvent('IMPORT_DATA', '', data); return this.dataStore.storeTrialJobEvent('IMPORT_DATA', '', data);
} }
public async exportData(): Promise<string> {
return this.dataStore.exportTrialHpConfigs();
}
public addCustomizedTrialJob(hyperParams: string): Promise<void> { public addCustomizedTrialJob(hyperParams: string): Promise<void> {
if (this.currSubmittedTrialNum >= this.experimentProfile.params.maxTrialNum) { if (this.currSubmittedTrialNum >= this.experimentProfile.params.maxTrialNum) {
return Promise.reject( return Promise.reject(
...@@ -212,6 +219,16 @@ class NNIManager implements Manager { ...@@ -212,6 +219,16 @@ class NNIManager implements Manager {
.filter((job: TrialJobInfo) => job.status === 'WAITING' || job.status === 'RUNNING') .filter((job: TrialJobInfo) => job.status === 'WAITING' || job.status === 'RUNNING')
.map((job: TrialJobInfo) => this.dataStore.storeTrialJobEvent('FAILED', job.id))); .map((job: TrialJobInfo) => this.dataStore.storeTrialJobEvent('FAILED', job.id)));
// Collect generated trials and imported trials
const finishedTrialData: string = await this.exportData();
const importedData: string[] = await this.dataStore.getImportedData();
let trialData: Object[] = JSON.parse(finishedTrialData);
for (const oneImportedData of importedData) {
// do not deduplicate
trialData = trialData.concat(<Object[]>JSON.parse(oneImportedData));
}
this.trialDataForTuner = JSON.stringify(trialData);
if (this.experimentProfile.execDuration < this.experimentProfile.params.maxExecDuration && if (this.experimentProfile.execDuration < this.experimentProfile.params.maxExecDuration &&
this.currSubmittedTrialNum < this.experimentProfile.params.maxTrialNum && this.currSubmittedTrialNum < this.experimentProfile.params.maxTrialNum &&
this.experimentProfile.endTime) { this.experimentProfile.endTime) {
...@@ -647,6 +664,12 @@ class NNIManager implements Manager { ...@@ -647,6 +664,12 @@ class NNIManager implements Manager {
switch (commandType) { switch (commandType) {
case INITIALIZED: case INITIALIZED:
// Tuner is intialized, search space is set, request tuner to generate hyper parameters // Tuner is intialized, search space is set, request tuner to generate hyper parameters
if (this.trialDataForTuner.length > 0) {
if (this.dispatcher === undefined) {
throw new Error('Dispatcher error: tuner has not been setup');
}
this.dispatcher.sendCommand(IMPORT_DATA, this.trialDataForTuner);
}
this.requestTrialJobs(this.experimentProfile.params.trialConcurrency); this.requestTrialJobs(this.experimentProfile.params.trialConcurrency);
break; break;
case NEW_TRIAL_JOB: case NEW_TRIAL_JOB:
......
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