CustomizeTuner.md 4.23 KB
Newer Older
Chi Song's avatar
Chi Song committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 自定义 Tuner

## 自定义 Tuner

NNI 在内置的 Tuner 中提供了最新的调优算法。 NNI 同时也支持自定义 Tuner。

通过自定义 Tuner,可实现自己的调优算法。主要有三步:

1. 继承 Tuner 基类
2. 实现 receive_trial_result 和 generate_parameter 函数
3. 在 Experiment 的 YAML 文件中配置好自定义的 Tuner

样例如下:

**1. 继承 Tuner 基类**

```python
from nni.tuner import Tuner

class CustomizedTuner(Tuner):
    def __init__(self, ...):
        ...
```

**2. 实现 receive_trial_result 和 generate_parameter 函数**

```python
from nni.tuner import Tuner

class CustomizedTuner(Tuner):
    def __init__(self, ...):
        ...

Chi Song's avatar
Chi Song committed
34
    def receive_trial_result(self, parameter_id, parameters, value, **kwargs):
Chi Song's avatar
Chi Song committed
35
36
37
38
39
40
41
42
43
    '''
    接收 Trial 的最终结果。
    parameter_id: int
    parameters: 'generate_parameters()' 所创建的对象
    value: Trial 的最终指标结果
    '''
    # 实现代码
    ...

Chi Song's avatar
Chi Song committed
44
    def generate_parameters(self, parameter_id, **kwargs):
Chi Song's avatar
Chi Song committed
45
46
47
48
49
50
51
52
53
    '''
    返回 Trial 的超参组合的序列化对象
    parameter_id: int
    '''
    # 代码实现位置
    return your_parameters
    ...
```

Chi Song's avatar
Chi Song committed
54
`receive_trial_result` 从输入中会接收 `parameter_id, parameters, value` 参数。 Tuner 会收到 Trial 进程发送的完全一样的 `value` 值。 如果在 Experiment 配置文件里 `multiPhase``true`, 会有一个附加的 `trial_job_id``**kwargs` 参数中返回给 `receive_trial_result``generate_parameters`
Chi Song's avatar
Chi Song committed
55
56
57
58
59
60

`generate_parameters` 函数返回的 `your_parameters`,会被 NNI SDK 打包为 json。 然后 SDK 会将 json 对象解包给 Trial 进程。因此,Trial 进程会收到来自 Tuner 的完全相同的 `your_parameters`

例如: 如下实现了 `generate_parameters`

```python
Chi Song's avatar
Chi Song committed
61
def generate_parameters(self, parameter_id, **kwargs):
Chi Song's avatar
Chi Song committed
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
    '''
    返回 Trial 的超参组合的序列化对象
    parameter_id: int
    '''
    # 代码实现位置
    return {"dropout": 0.3, "learning_rate": 0.4}

```

这表示 Tuner 会一直生成超参组合 `{"dropout": 0.3, "learning_rate": 0.4}`。 而 Trial 进程也会在调用 API `nni.get_next_parameter()` 时得到 `{"dropout": 0.3, "learning_rate": 0.4}`。 Trial 结束后的返回值(通常是某个指标),通过调用 API `nni.report_final_result()` 返回给 Tuner。如: `nni.report_final_result(0.93)`。 而 Tuner 的 `receive_trial_result` 函数会收到如下结果:

```python
parameter_id = 82347
parameters = {"dropout": 0.3, "learning_rate": 0.4}
value = 0.93
```

**注意** 如果需要存取自定义的 Tuner 目录里的文件 (如, `data.txt`),不能使用 `open('data.txt', 'r')`。 要使用:

```python
_pwd = os.path.dirname(__file__)
_fd = open(os.path.join(_pwd, 'data.txt'), 'r')
```

这是因为自定义的 Tuner 不是在自己的目录里执行的。(即,`pwd` 返回的目录不是 Tuner 的目录)。

**3. 在 Experiment 的 YAML 文件中配置好自定义的 Tuner**

NNI 需要定位到自定义的 Tuner 类,并实例化它,因此需要指定自定义 Tuner 类的文件位置,并将参数值传给 \_\_init__ 构造函数。

```yaml
tuner:
  codeDir: /home/abc/mytuner
  classFileName: my_customized_tuner.py
  className: CustomizedTuner
  # 任何传入 __init__ 构造函数的参数
  # 都需要声明在 classArgs 字段中,如:
  classArgs:
    arg1: value1

```

更多样例,可参考:

> - [evolution-tuner](https://github.com/Microsoft/nni/tree/master/src/sdk/pynni/nni/evolution_tuner)
> - [hyperopt-tuner](https://github.com/Microsoft/nni/tree/master/src/sdk/pynni/nni/hyperopt_tuner)
> - [evolution-based-customized-tuner](https://github.com/Microsoft/nni/tree/master/examples/tuners/ga_customer_tuner)

### 实现更高级的自动机器学习算法

Chi Song's avatar
Chi Song committed
112
上述内容足够写出通用的 Tuner。 但有时可能需要更多的信息,例如,中间结果, Trial 的状态等等,从而能够实现更强大的自动机器学习算法。 因此,有另一个 `Advisor` 类,直接继承于 `MsgDispatcherBase`,它在 [`src/sdk/pynni/nni/msg_dispatcher_base.py`](https://github.com/Microsoft/nni/tree/master/src/sdk/pynni/nni/msg_dispatcher_base.py)。 参考[这里](CustomizeAdvisor.md)来了解如何实现自定义的 Advisor。