autofinetune.md 10.0 KB
Newer Older
S
Steffy-zxf 已提交
1
# PaddleHub 超参优化(Auto Fine-tune)
Z
zhangxuefei 已提交
2 3 4 5 6

## 一、简介

机器学习训练模型的过程中自然少不了调参。模型的参数可分成两类:参数与超参数,前者是模型通过自身的训练学习得到的参数数据;后者则需要通过人工经验设置(如学习率、dropout_rate、batch_size等),以提高模型训练的效果。当前模型往往参数空间大,手动调参十分耗时,尝试成本高。PaddleHub  Auto Fine-tune可以实现自动调整超参数。

S
Steffy-zxf 已提交
7
PaddleHub Auto Fine-tune提供两种超参优化策略:
Z
zhangxuefei 已提交
8 9 10 11 12 13

* HAZero: 核心思想是通过对正态分布中协方差矩阵的调整来处理变量之间的依赖关系和scaling。算法基本可以分成以下三步: 采样产生新解;计算目标函数值;更新正态分布参数。调整参数的基本思路为,调整参数使得产生好解的概率逐渐增大

* PSHE2: 采用粒子群算法,最优超参数组合就是所求问题的解。现在想求得最优解就是要找到更新超参数组合,即如何更新超参数,才能让算法更快更好的收敛到最优解。PSE2算法根据超参数本身历史的最优,在一定随机扰动的情况下决定下一步的更新方向。


Z
zhangxuefei 已提交
14 15 16 17




Z
zhangxuefei 已提交
18 19 20 21
PaddleHub Auto Fine-tune提供两种超参评估策略:

* FullTrail: 给定一组超参,利用这组超参从头开始Finetune一个新模型,之后在数据集dev部分评估这个模型

S
Steffy-zxf 已提交
22
* ModelBased: 给定一组超参,若这组超参来自第一轮优化的超参,则从头开始Finetune一个新模型;若这组超参数不是来自第一轮优化的超参数,则程序会加载前几轮已经Fine-tune完毕后保存的较好模型,基于这个模型,在当前的超参数组合下继续Finetune。这个Fine-tune完毕后保存的较好模型,评估方式是这个模型在数据集dev部分的效果。
Z
zhangxuefei 已提交
23 24 25

## 二、准备工作

S
Steffy-zxf 已提交
26
使用PaddleHub Auto Fine-tune必须准备两个文件,并且这两个文件需要按照指定的格式书写。这两个文件分别是需要Fine-tune的python脚本finetuee.py和需要优化的超参数信息yaml文件hparam.yaml。
Z
zhangxuefei 已提交
27

S
Steffy-zxf 已提交
28
以Fine-tune中文情感分类任务为例,我们展示如何利用PaddleHub Auto Finetune进行超参优化。
Z
zhangxuefei 已提交
29

S
Steffy-zxf 已提交
30
以下是待优化超参数的yaml文件hparam.yaml,包含需要搜素的超参名字、类型、范围等信息。其中类型只支持float和int类型
Z
zhangxuefei 已提交
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
```
param_list:
- name : learning_rate
  init_value : 0.001
  type : float
  lower_than : 0.05
  greater_than : 0.000005
- name : weight_decay
  init_value : 0.1
  type : float
  lower_than : 1
  greater_than : 0.0
- name : batch_size
  init_value : 32
  type : int
  lower_than : 40
  greater_than : 30
- name : warmup_prop
  init_value : 0.1
  type : float
  lower_than : 0.2
  greater_than : 0.0
```

**NOTE:** 该yaml文件的最外层级的key必须是param_list


以下是中文情感分类的finetunee.py

```python
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import argparse
import ast

import paddle.fluid as fluid
import paddlehub as hub
import os
from paddlehub.common.logger import logger

# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--epochs", type=int, default=3, help="epochs.")
parser.add_argument("--batch_size", type=int, default=32, help="batch_size.")
parser.add_argument("--learning_rate", type=float, default=5e-5, help="learning_rate.")
parser.add_argument("--warmup_prop", type=float, default=0.1, help="warmup_prop.")
parser.add_argument("--weight_decay", type=float, default=0.01, help="weight_decay.")
parser.add_argument("--max_seq_len", type=int, default=128, help="Number of words of the longest seqence.")
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
S
Steffy-zxf 已提交
82
parser.add_argument("--saved_params_dir", type=str, default="", help="Directory for saving model during ")
Z
zhangxuefei 已提交
83 84 85 86 87
parser.add_argument("--model_path", type=str, default="", help="load model path")
args = parser.parse_args()
# yapf: enable.


S
Steffy-zxf 已提交
88 89 90 91 92 93 94 95 96
def is_path_valid(path):
    if path == "":
        return False
    path = os.path.abspath(path)
    dirname = os.path.dirname(path)
    if not os.path.exists(dirname):
        os.mkdir(dirname)
    return True

Z
zhangxuefei 已提交
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 142 143 144 145 146 147 148 149
if __name__ == '__main__':
    # Load Paddlehub ERNIE pretrained model
    module = hub.Module(name="ernie")
    inputs, outputs, program = module.context(
        trainable=True, max_seq_len=args.max_seq_len)

    # Download dataset and use ClassifyReader to read dataset
    dataset = hub.dataset.ChnSentiCorp()
    metrics_choices = ["acc"]

    reader = hub.reader.ClassifyReader(
        dataset=dataset,
        vocab_path=module.get_vocab_path(),
        max_seq_len=args.max_seq_len)

    # Construct transfer learning network
    # Use "pooled_output" for classification tasks on an entire sentence.
    pooled_output = outputs["pooled_output"]

    # Setup feed list for data feeder
    # Must feed all the tensor of ERNIE's module need
    feed_list = [
        inputs["input_ids"].name,
        inputs["position_ids"].name,
        inputs["segment_ids"].name,
        inputs["input_mask"].name,
    ]

    # Select finetune strategy, setup config and finetune
    strategy = hub.AdamWeightDecayStrategy(
        warmup_proportion=args.warmup_prop,
        learning_rate=args.learning_rate,
        weight_decay=args.weight_decay,
        lr_scheduler="linear_decay")

    # Setup runing config for PaddleHub Finetune API
    config = hub.RunConfig(
        checkpoint_dir=args.checkpoint_dir,
        use_cuda=True,
        num_epoch=args.epochs,
        batch_size=args.batch_size,
        enable_memory_optim=True,
        strategy=strategy)

    # Define a classfication finetune task by PaddleHub's API
    cls_task = hub.TextClassifierTask(
        data_reader=reader,
        feature=pooled_output,
        feed_list=feed_list,
        num_classes=dataset.num_labels,
        config=config,
        metrics_choices=metrics_choices)

S
Steffy-zxf 已提交
150
    # Load model from the defined model path or not
Z
zhangxuefei 已提交
151 152 153 154 155 156
    if args.model_path != "":
        with cls_task.phase_guard(phase="train"):
            cls_task.init_if_necessary()
            cls_task.load_parameters(args.model_path)
            logger.info("PaddleHub has loaded model from %s" % args.model_path)

S
Steffy-zxf 已提交
157
    cls_task.finetune()
Z
zhangxuefei 已提交
158 159
    run_states = cls_task.eval()
    eval_avg_score, eval_avg_loss, eval_run_speed = cls_task._calculate_metrics(run_states)
Z
zhangxuefei 已提交
160

S
Steffy-zxf 已提交
161 162 163 164
    # Move ckpt/best_model to the defined saved parameters directory
    if is_path_valid(args.saved_params_dir) and os.path.exists(config.checkpoint_dir+"/best_model/"):
        shutil.copytree(config.checkpoint_dir+"/best_model/", args.saved_params_dir)
        shutil.rmtree(config.checkpoint_dir)
Z
zhangxuefei 已提交
165

166
    print("AutoFinetuneEval"+"\t"+str(float(eval_avg_score["acc"])))
Z
zhangxuefei 已提交
167 168
```
**Note**:以上是finetunee.py的写法。
S
Steffy-zxf 已提交
169
> finetunee.py必须可以接收待优化超参数选项参数, 并且待搜素超参数选项名字和yaml文件中的超参数名字保持一致。
Z
zhangxuefei 已提交
170

S
Steffy-zxf 已提交
171
> finetunee.py必须有saved_params_dir这个选项。
Z
zhangxuefei 已提交
172 173 174

> PaddleHub Auto Fine-tune超参评估策略选择为ModelBased,finetunee.py必须有model_path选项。

S
Steffy-zxf 已提交
175
> PaddleHub Auto Fine-tune优化超参策略选择hazero时,必须提供两个以上的待优化超参。
Z
zhangxuefei 已提交
176

S
Steffy-zxf 已提交
177
> finetunee.py必须输出模型在数据集dev上的评价效果,同时以“AutoFinetuneEval"开始,和评价效果之间以“\t”分开,如print("AutoFinetuneEval"+"\t"+str(float(eval_avg_score["acc"])))。
Z
zhangxuefei 已提交
178 179 180 181 182 183 184 185 186 187 188



## 三、启动方式

**确认安装PaddleHub版本在1.2.0以上, 同时PaddleHub Auto Fine-tune功能要求至少有一张GPU显卡可用。**

通过以下命令方式:
```shell
$ OUTPUT=result/
$ hub autofinetune finetunee.py --param_file=hparam.yaml --cuda=['1','2'] --popsize=5 --round=10
S
Steffy-zxf 已提交
189
 --output_dir=${OUTPUT} --evaluate_choice=fulltrail --tuning_strategy=pshe2
Z
zhangxuefei 已提交
190 191 192 193
```

其中,选项

S
Steffy-zxf 已提交
194
> `--param_file`: 需要优化的超参数信息yaml文件
Z
zhangxuefei 已提交
195 196 197 198 199 200 201 202 203

> `--cuda`: 设置运行程序的可用GPU卡号,list类型,中间以逗号隔开,不能有空格,默认为[‘0’]

> `--popsize`: 设置程序运行每轮产生的超参组合数,默认为5

> `--round`: 设置程序运行的轮数,默认是10

> `--output_dir`: 设置程序运行输出结果存放目录,可选,不指定该选项参数时,在当前运行路径下生成存放程序运行输出信息的文件夹

S
Steffy-zxf 已提交
204
> `--evaluate_choice`: 设置自动优化超参的评价效果方式,可选fulltrail和modelbased, 默认为fulltrail
Z
zhangxuefei 已提交
205

S
Steffy-zxf 已提交
206
> `--tuning_strategy`: 设置自动优化超参策略,可选hazero和pshe2,默认为hazero
Z
zhangxuefei 已提交
207

S
Steffy-zxf 已提交
208
**NOTE:** Auto Fine-tune功能会根据popsize和cuda自动实现排队使用GPU,如popsize=5,cuda=['0','1','2','3'],则每搜索一轮,Auto Fine-tune自动起四个进程训练,所以第5组超参组合需要排队一次。为了提高GPU利用率以及超参优化效率,此时建议可以设置为3张可用的卡,cuda=['0','1','2']。
Z
zhangxuefei 已提交
209 210 211 212


## 四、可视化

S
Steffy-zxf 已提交
213
Auto Finetune API在优化超参过程中会自动对关键训练指标进行打点,启动程序后执行下面命令
Z
zhangxuefei 已提交
214 215 216 217 218 219 220

```shell
$ tensorboard --logdir $OUTPUT/tb_paddle --host ${HOST_IP} --port ${PORT_NUM}
```

其中${HOST_IP}为本机IP地址,${PORT_NUM}为可用端口号,如本机IP地址为192.168.0.1,端口号8040,
用浏览器打开192.168.0.1:8040,即可看到搜素过程中各超参以及指标的变化情况
S
Steffy-zxf 已提交
221 222 223

## 五、其他

S
Steffy-zxf 已提交
224
1. 如在使用Auto Fine-tune功能时,输出信息中包含如下字样:
S
Steffy-zxf 已提交
225

S
Steffy-zxf 已提交
226
**WARNING:Program which was ran with hyperparameters as ... was crashed!**
S
Steffy-zxf 已提交
227

S
Steffy-zxf 已提交
228
首先根据终端上的输出信息,确定这个输出信息是在第几个round(如round 3),之后查看${OUTPUT}/round3/下的日志文件信息log.info, 查看具体出错原因。
S
Steffy-zxf 已提交
229 230 231 232 233 234

2. PaddleHub AutoFinetune 命令行支持从启动命令hub autofinetune传入finetunee.py中不需要搜索的选项参数,如上述示例中的max_seq_len选项,可以参照以下方式传入。

```shell
$ OUTPUT=result/
$ hub autofinetune finetunee.py --param_file=hparam.yaml --cuda=['1','2'] --popsize=5 --round=10
S
Steffy-zxf 已提交
235
 --output_dir=${OUTPUT} --evaluate_choice=fulltrail --tuning_strategy=pshe2 max_seq_len 128
S
Steffy-zxf 已提交
236
```