NNI 在内置的 Tuner 中提供了最新的调优算法。 NNI 同时也支持自定义 Tuner。
通过自定义 Tuner,可实现自己的调优算法。主要有三步:
- 继承 Tuner 基类
- 实现 receive_trial_result 和 generate_parameter 函数
- 在 Experiment 的 YAML 文件中配置好自定义的 Tuner
样例如下:
1. 继承 Tuner 基类
from nni.tuner import Tuner
class CustomizedTuner(Tuner):
def __init__(self, ...):
...
2. 实现 receive_trial_result 和 generate_parameter 函数
from nni.tuner import Tuner
class CustomizedTuner(Tuner):
def __init__(self, ...):
...
def receive_trial_result(self, parameter_id, parameters, value, **kwargs):
'''
接收 Trial 的最终结果。
parameter_id: int
parameters: 'generate_parameters()' 所创建的对象
value: Trial 的最终指标结果
'''
# 实现代码
...
def generate_parameters(self, parameter_id, **kwargs):
'''
返回 Trial 的超参组合的序列化对象
parameter_id: int
'''
# 代码实现位置
return your_parameters
...
receive_trial_result
从输入中会接收 parameter_id, parameters, value
参数。 Tuner 会收到 Trial 进程发送的完全一样的 value
值。 如果在 Experiment 配置文件里 multiPhase
为 true
, 会有一个附加的 trial_job_id
在 **kwargs
参数中返回给 receive_trial_result
和 generate_parameters
。
generate_parameters
函数返回的 your_parameters
,会被 NNI SDK 打包为 json。 然后 SDK 会将 json 对象解包给 Trial 进程。因此,Trial 进程会收到来自 Tuner 的完全相同的 your_parameters
。
例如: 如下实现了 generate_parameters
:
def generate_parameters(self, parameter_id, **kwargs):
'''
返回 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
函数会收到如下结果:
parameter_id = 82347
parameters = {"dropout": 0.3, "learning_rate": 0.4}
value = 0.93
注意 如果需要存取自定义的 Tuner 目录里的文件 (如, data.txt
),不能使用 open('data.txt', 'r')
。 要使用:
_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__ 构造函数。
tuner:
codeDir: /home/abc/mytuner
classFileName: my_customized_tuner.py
className: CustomizedTuner
# 任何传入 __init__ 构造函数的参数
# 都需要声明在 classArgs 字段中,如:
classArgs:
arg1: value1
更多样例,可参考:
上述内容足够写出通用的 Tuner。 但有时可能需要更多的信息,例如,中间结果, Trial 的状态等等,从而能够实现更强大的自动机器学习算法。 因此,有另一个 Advisor
类,直接继承于 MsgDispatcherBase
,它在 src/sdk/pynni/nni/msg_dispatcher_base.py
。 参考这里来了解如何实现自定义的 Advisor。