how_to_define_task.md 7.0 KB
Newer Older
1 2 3
# 自定义Task

本节内容讲述如何实现自定义Task。在了解本节内容之前,您需要先了解以下内容:
A
adaxiadaxi 已提交
4 5 6
* 任务基类[BasicTask](../reference/task/base_task.md)
* 运行状态[RunState](../reference/task/runstate.md)
* 运行环境[RunEnv](../reference/task/runenv.md)
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141

当自定义一个Task时,我们并不需要重新实现eval、finetune等通用接口。一般来讲,新的Task与其他Task的区别在于
* 网络结构
* 评估指标

这两者的差异可以通过重载BasicTask的组网事件和运行事件来实现

## 组网事件
BasicTask定义了一系列的组网事件,当需要构建对应的Fluid Program时,相应的事件会被调用。通过重载实现对应的组网函数,用户可以实现自定义网络

###  `_build_net`
进行前向网络组网的函数,用户需要自定义实现该函数,函数需要返回对应预测结果的Variable list

```python
# 代码示例
def _build_net(self):
    cls_feats = self.feature
    if self.hidden_units is not None:
        for n_hidden in self.hidden_units:
            cls_feats = fluid.layers.fc(
                input=cls_feats, size=n_hidden, act="relu")

    logits = fluid.layers.fc(
        input=cls_feats,
        size=self.num_classes,
        param_attr=fluid.ParamAttr(
            name="cls_out_w",
            initializer=fluid.initializer.TruncatedNormal(scale=0.02)),
        bias_attr=fluid.ParamAttr(
            name="cls_out_b", initializer=fluid.initializer.Constant(0.)),
        act="softmax")

    return [logits]
```
###  `_add_label`
添加label的函数,用户需要自定义实现该函数,函数需要返回对应输入label的Variable list

```python
# 代码示例
def _add_label(self):
    return [fluid.layers.data(name="label", dtype="int64", shape=[1])]
```

###  `_add_metrics`
添加度量指标的函数,用户需要自定义实现该函数,函数需要返回对应度量指标的Variable list

```python
# 代码示例
def _add_metrics(self):
    return [fluid.layers.accuracy(input=self.outputs[0], label=self.label)]
```

## 运行事件
BasicTask定义了一系列的运行时回调事件,在特定的时机时触发对应的事件,在自定的Task中,通过重载实现对应的回调函数,用户可以实现所需的功能

###  `_build_env_start_event`

当需要进行一个新的运行环境构建时,该事件被触发。通过重载实现该函数,用户可以在一个环境开始构建前进行对应操作,例如写日志

```python
# 代码示例
def _build_env_start_event(self):
    logger.info("Start to build env {}".format(self.phase))
```

###  `_build_env_end_event`
当一个新的运行环境构建完成时,该事件被触发。通过继承实现该函数,用户可以在一个环境构建结束后进行对应操作,例如写日志

```python
# 代码示例
def _build_env_end_event(self):
    logger.info("End of build env {}".format(self.phase))
```
###  `_finetune_start_event`
当开始一次finetune时,该事件被触发。通过继承实现该函数,用户可以在开始一次finetune操作前进行对应操作,例如写日志

```python
# 代码示例
def _finetune_start_event(self):
    logger.info("PaddleHub finetune start")
```

###  `_finetune_end_event`
当结束一次finetune时,该事件被触发。通过继承实现该函数,用户可以在结束一次finetune操作后进行对应操作,例如写日志

```python
# 代码示例
def _finetune_end_event(self):
    logger.info("PaddleHub finetune finished.")
```

###  `_eval_start_event`
当开始一次evaluate时,该事件被触发。通过继承实现该函数,用户可以在开始一次evaluate操作前进行对应操作,例如写日志

```python
# 代码示例
def _eval_start_event(self):
    logger.info("Evaluation on {} dataset start".format(self.phase))
```
###  `_eval_end_event`
当结束一次evaluate时,该事件被触发。通过继承实现该函数,用户可以在完成一次evaluate操作后进行对应操作,例如计算运行速度、评估指标等

```python
# 代码示例
def _eval_end_event(self, run_states):
    run_step = 0
    for run_state in run_states:
        run_step += run_state.run_step

    run_time_used = time.time() - run_states[0].run_time_begin
    run_speed = run_step / run_time_used
    logger.info("[%s dataset evaluation result] [step/sec: %.2f]" %
        (self.phase, run_speed))
```
* `run_states`: 一个list对象,list中的每一个元素都是RunState对象,该list包含了整个评估过程的状态数据。

###  `_predict_start_event`
当开始一次predict时,该事件被触发。通过继承实现该函数,用户可以在开始一次predict操作前进行对应操作,例如写日志

```python
# 代码示例
def _predict_start_event(self):
    logger.info("PaddleHub predict start")
```

###  `_predict_end_event`
当结束一次predict时,该事件被触发。通过继承实现该函数,用户可以在结束一次predict操作后进行对应操作,例如写日志

```python
# 代码示例
def _predict_end_event(self):
    logger.info("PaddleHub predict finished.")
```

###  `_log_interval_event`
A
adaxiadaxi 已提交
142
调用*finetune* 或者 *finetune_and_eval*接口时,每当命中用户设置的日志打印周期时([RunConfig.log_interval](../reference/config.md))。通过继承实现该函数,用户可以在finetune过程中定期打印所需数据,例如计算运行速度、loss、准确率等
143 144 145 146 147 148 149 150 151 152 153 154 155

```python
# 代码示例
def _log_interval_event(self, run_states):
    avg_loss, avg_acc, run_speed = self._calculate_metrics(run_states)
    self.env.loss_scalar.add_record(self.current_step, avg_loss)
    self.env.acc_scalar.add_record(self.current_step, avg_acc)
    logger.info("step %d: loss=%.5f acc=%.5f [step/sec: %.2f]" %
        (self.current_step, avg_loss, avg_acc, run_speed))
```
* `run_states`: 一个list对象,list中的每一个元素都是RunState对象,该list包含了整个从上一次该事件被触发到本次被触发的状态数据

###  `_save_ckpt_interval_event`
A
adaxiadaxi 已提交
156
调用*finetune* 或者 *finetune_and_eval*接口时,每当命中用户设置的保存周期时([RunConfig.save_ckpt_interval](../reference/config.md)),该事件被触发。通过继承实现该函数,用户可以在定期保存checkpoint
157 158 159 160 161 162 163 164

```python
# 代码示例
def _save_ckpt_interval_event(self):
    self.save_checkpoint(self.current_epoch, self.current_step)
```

###  `_eval_interval_event`
A
adaxiadaxi 已提交
165
调用*finetune_and_eval*接口时,每当命中用户设置的评估周期时([RunConfig.eval_interval](../reference/config.md)),该事件被触发。通过继承实现该函数,用户可以实现自定义的评估指标计算
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181

```python
# 代码示例
def _eval_interval_event(self):
    self.eval(phase="dev")
```

###  `_run_step_event`
调用*eval**predict**finetune_and_eval**finetune*等接口时,每执行一次计算,该事件被触发。通过继承实现该函数,用户可以实现所需操作

```python
# 代码示例
def _run_step_event(self, run_state):
    ...
```
* `run_state`: 一个RunState对象,指明了该step的运行状态