未验证 提交 64ffd96e 编写于 作者: 走神的阿圆's avatar 走神的阿圆 提交者: GitHub

Merge branch 'release/v1.8' into Modify_serving_docs3

## `v1.8.0`
* 预训练模型丰富,一键完成更多
*[文本生成](https://www.paddlepaddle.org.cn/hublist?filter=en_category&value=TextGeneration)』新增基于ERNIE-tiny和ERNIE-gen的对联和写诗生成模型,支持一键自动写诗和对对联。
*[词法分析](https://www.paddlepaddle.org.cn/hublist?filter=en_category&value=LexicalAnalysis)』新增jieba的paddle模式切词模型,可一键完成中文分词、关键词抽取等功能。
*[语义表示](https://www.paddlepaddle.org.cn/hublist?filter=en_category&value=SemanticModel)』新增基于网页、小说、新闻三类大规模文本数据的LDA主题模型及其语义相似度计算接口。
* Fine-tune API升级,提升灵活性并支持更多任务
* 新增Tokenizer API,支持更加灵活的切词、切字模式和自定义切词工具拓展。
* 新增[文本生成](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.8/demo/text_generation)任务,支持Seq2Seq任务的Fine-tuning。
* 新增文本匹配任务,支持[Pointwise](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.8/demo/pointwise_text_matching)[Pairwise](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.8/demo/pairwise_text_matching)两种文本匹配训练模式,更便捷完成语义匹配任务。
## `v1.7.0`
* 丰富预训练模型,提升应用性
* 新增VENUS系列视觉预训练模型[yolov3_darknet53_venus](https://www.paddlepaddle.org.cn/hubdetail?name=yolov3_darknet53_venus&en_category=ObjectDetection)[faster_rcnn_resnet50_fpn_venus](https://www.paddlepaddle.org.cn/hubdetail?name=faster_rcnn_resnet50_fpn_venus&en_category=ObjectDetection),可大幅度提升图像分类和目标检测任务的Fine-tune效果
* 新增工业级短视频分类模型[videotag_tsn_lstm](https://paddlepaddle.org.cn/hubdetail?name=videotag_tsn_lstm&en_category=VideoClassification),支持3000类中文标签识别
* 新增轻量级中文OCR模型[chinese_ocr_db_rcnn](https://www.paddlepaddle.org.cn/hubdetail?name=chinese_ocr_db_rcnn&en_category=TextRecognition)[chinese_text_detection_db](https://www.paddlepaddle.org.cn/hubdetail?name=chinese_text_detection_db&en_category=TextRecognition),支持一键快速OCR识别
* 新增轻量级中文[OCR模型](https://www.paddlepaddle.org.cn/hublist?filter=en_category&value=TextRecognition),支持一键快速OCR识别
* 新增行人检测、车辆检测、动物识别、Object等工业级模型
* Fine-tune API升级
......@@ -52,7 +64,7 @@
# `v1.5.0`
* 升级PaddleHub Serving,提升性能和易用性
* 新增文本Embedding服务[Bert Service](./tutorial/bert_service.md), 轻松获取文本embedding;
* 新增文本Embedding服务[Bert Service](./docs/tutorial/bert_service.md), 轻松获取文本embedding;
* 代码精短,易于使用。服务端/客户端一行命令即可获取文本embedding;
* 更高性能,更高效率。通过Paddle AnalysisPredictor API优化计算图,提升速度减小显存占用
* 随"机"应变,灵活扩展。根据机器资源和实际需求可灵活增加服务端数量,支持多显卡多模型计算任务
......
......@@ -8,7 +8,7 @@ This page implements the [DELTA](https://arxiv.org/abs/1901.09229) algorithm in
## Preparation of Data and Pre-trained Model
- Download transfer learning target datasets, like [Caltech-256](http://www.vision.caltech.edu/Image_Datasets/Caltech256/), [CUB_200_2011](http://www.vision.caltech.edu/visipedia/CUB-200-2011.html) or others. Arrange the dataset in this way:
- Download transfer learning target datasets, like [Caltech-256](https://www.kaggle.com/jessicali9530/caltech256), [CUB_200_2011](http://www.vision.caltech.edu/visipedia/CUB-200-2011.html) or others. Arrange the dataset in this way:
```
root/train/dog/xxy.jpg
root/train/dog/xxz.jpg
......
......@@ -33,14 +33,14 @@
* [阅读理解](./reading_comprehension)
该样例展示了PaddleHub如何将BERT作为预训练模型在SQAD数据集上完成阅读理解的FineTune和预测。
* [检索式问答任务](./qa_classfication)
* [检索式问答任务](./qa_classification)
该样例展示了PaddleHub如何将ERNIE和BERT作为预训练模型在NLPCC-DBQA等数据集上完成检索式问答任务的FineTune和预测。
* [句子语义相似度计算](./sentence_similarity)
该样例展示了PaddleHub如何将word2vec_skipgram用于计算两个文本语义相似度。
* [超参优化AutoDL Finetuner使用](./autofinetune)
该样例展示了PaddleHub超参优化AutoDL Finetuner如何使用,给出了自动搜素图像分类/文本分类任务的较佳超参数示例。
* 超参优化AutoDL Finetuner使用
该样例展示了PaddleHub超参优化AutoDL Finetuner如何使用,给出了自动搜[图像分类](./autofinetune_image_classification)/[文本分类](./autofinetune_text_classification)任务的较佳超参数示例。
* [服务化部署Hub Serving使用](./serving)
该样例文件夹下展示了服务化部署Hub Serving如何使用,将PaddleHub支持的可预测Module如何服务化部署。
......@@ -50,7 +50,7 @@
请确认转化时,使用的PaddleHub为1.6.0以上版本。
**NOTE:**
以上任务示例均是利用PaddleHub提供的数据集,若您想在自定义数据集上完成相应任务,请查看[PaddleHub适配自定义数据完成Fine-tune](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub%E9%80%82%E9%85%8D%E8%87%AA%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%88%90FineTune)
以上任务示例均是利用PaddleHub提供的数据集,若您想在自定义数据集上完成相应任务,请查看[PaddleHub适配自定义数据完成Fine-tune](../docs/tutorial/how_to_load_data.md)
## 在线体验
......
......@@ -4,7 +4,7 @@
## 如何开始Fine-tune
在完成安装PaddlePaddle与PaddleHub后,通过执行脚本`sh run_classifier.sh`即可开始使用ResNet对[Flowers](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-Dataset#class-hubdatasetflowersdataset)等数据集进行Fine-tune。
在完成安装PaddlePaddle与PaddleHub后,通过执行脚本`sh run_classifier.sh`即可开始使用ResNet对[Flowers](../../docs/reference/dataset.md#class-hubdatasetflowers)等数据集进行Fine-tune。
其中脚本参数说明如下:
......@@ -66,7 +66,7 @@ data_reader = hub.reader.ImageClassificationReader(
#### 自定义数据集
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub%E9%80%82%E9%85%8D%E8%87%AA%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%88%90FineTune)
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](../../docs/tutorial/how_to_load_data.md)
### Step3:选择优化策略和运行配置
......@@ -81,7 +81,7 @@ config = hub.RunConfig(use_cuda=True, use_data_parallel=True, num_epoch=3, batch
#### 优化策略
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-Strategy)
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](../../docs/reference/strategy.md)
其中`DefaultFinetuneStrategy`:
......@@ -125,7 +125,7 @@ task.finetune_and_eval()
#### 自定义迁移任务
如果想改变迁移任务组网,详细参见[自定义迁移任务](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub:-%E8%87%AA%E5%AE%9A%E4%B9%89Task)
如果想改变迁移任务组网,详细参见[自定义迁移任务](../../docs/tutorial/how_to_define_task.md)
## 可视化
......
......@@ -6,7 +6,7 @@
如下图所示:
<p align="center">
<img src="https://github.com/PaddlePaddle/PaddleHub/blob/release/v1.4/docs/imgs/multi-label-cls.png" hspace='10'/> <br />
<img src="../../docs/imgs/multi-label-cls.png" hspace='10'/> <br />
</p>
*图片来源于https://mc.ai/building-a-multi-label-text-classifier-using-bert-and-tensorflow/*
......@@ -39,6 +39,8 @@
### Step1: 加载预训练模型
```python
import paddlehub as hub
module = hub.Module(name="ernie_v2_eng_base")
inputs, outputs, program = module.context(trainable=True, max_seq_len=128)
```
......@@ -66,32 +68,44 @@ RoBERTa-wwm-ext-large, Chinese | `hub.Module(name='roberta_wwm_ext_chinese_L
更多模型请参考[PaddleHub官网](https://www.paddlepaddle.org.cn/hub)
### Step2: 准备数据集并使用MultiLabelClassifyReader读取数据
### Step2: 准备数据集并使用tokenizer预处理数据
```python
dataset = hub.dataset.Toxic()
reader = hub.reader.MultiLabelClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=128)
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.Toxic(
tokenizer=tokenizer, max_seq_len=128)
```
如果是使用ernie_tiny预训练模型,请使用ErnieTinyTokenizer。
```
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
```
ErnieTinyTokenizer和BertTokenizer的区别在于它将按词粒度进行切分,详情请参考[文章](https://www.jiqizhixin.com/articles/2019-11-06-9)
其中数据集的准备代码可以参考[toxic.py](https://github.com/PaddlePaddle/PaddleHub/blob/release/v1.2/paddlehub/dataset/toxic.py)
数据集的准备代码可以参考[toxic.py](../../paddlehub/dataset/toxic.py)
`hub.dataset.Toxic()` 会自动从网络下载数据集并解压到用户目录下`$HOME/.paddlehub/dataset`目录;
`module.get_vocab_path()` 会返回预训练模型对应的词表;
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致;
MultiLabelClassifyReader中的`data_generator`会自动按照模型对应词表对数据进行tokenize,以迭代器的方式返回BERT所需要的Tensor格式,包括`input_ids``position_ids``segment_id`与序列对应的mask `input_mask`
`module.sp_model_path` 若module为ernie_tiny则返回对应的子词切分模型路径,否则返回None;
**NOTE**: Reader返回tensor的顺序是固定的,默认按照input_ids, position_ids, segment_id, input_mask这一顺序返回。
`module.word_dict_path` 若module为ernie_tiny则返回对应的词语切分模型路径,否则返回None;
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致;
dataset将调用传入的tokenizer提供的encode接口对全量数据进行预处理,您可以通过以下方式观察数据的处理流程:
```
single_result = tokenizer.encode(text="hello", text_pair="world", max_seq_len=10) # BertTokenizer
# {'input_ids': [3, 1, 5, 39825, 5, 0, 0, 0, 0, 0], 'segment_ids': [0, 0, 0, 1, 1, 0, 0, 0, 0, 0], 'seq_len': 5, 'input_mask': [1, 1, 1, 1, 1, 0, 0, 0, 0, 0], 'position_ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
dataset_result = dataset.get_dev_records() # set dataset max_seq_len = 10
# {'input_ids': [101, 2233, 2289, 1006, 11396, 1007, 2003, 3746, 1024, 102], 'segment_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'seq_len': 10, 'input_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'position_ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'label': [0, 0, 0, 0, 0, 0]}
```
#### 自定义数据集
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub%E9%80%82%E9%85%8D%E8%87%AA%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%88%90\)
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](../../docs/tutorial/how_to_load_data.md)
### Step3:选择优化策略和运行配置
......@@ -114,7 +128,7 @@ config = hub.RunConfig(use_cuda=True, use_data_parallel=True, use_pyreader=True,
* `warmup_proportion`: 如果warmup_proportion>0, 例如0.1, 则学习率会在前10%的steps中线性增长至最高值learning_rate;
* `lr_scheduler`: 有两种策略可选(1) `linear_decay`策略学习率会在最高点后以线性方式衰减; `noam_decay`策略学习率会在最高点以多项式形式衰减;
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-Strategy)
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](../../docs/reference/strategy.md)
#### 运行配置
`RunConfig` 主要控制Fine-tune的训练,包含以下可控制的参数:
......@@ -135,31 +149,21 @@ PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy`、`ULMFiTStr
```python
pooled_output = outputs["pooled_output"]
# feed_list的Tensor顺序不可以调整
feed_list = [
inputs["input_ids"].name,
inputs["position_ids"].name,
inputs["segment_ids"].name,
inputs["input_mask"].name,
]
cls_task = hub.MultiLabelClassifierTask(
data_reader=reader,
multi_label_cls_task = hub.MultiLabelClassifierTask(
dataset=dataset,
feature=pooled_output,
feed_list=feed_list,
num_classes=dataset.num_labels,
config=config)
cls_task.finetune_and_eval()
multi_label_cls_task.finetune_and_eval()
```
**NOTE:**
1. `outputs["pooled_output"]`返回了ERNIE/BERT模型对应的[CLS]向量,可以用于句子或句对的特征表达。
2. `feed_list`中的inputs参数指名了ERNIE/BERT中的输入tensor的顺序,与MultiLabelClassifierTask返回的结果一致。
3. `hub.MultiLabelClassifierTask`通过输入特征,label与迁移的类别数,可以生成适用于多标签分类的迁移任务`MultiLabelClassifierTask`
2. `hub.MultiLabelClassifierTask`通过输入特征,label与迁移的类别数,可以生成适用于多标签分类的迁移任务`MultiLabelClassifierTask`
#### 自定义迁移任务
如果想改变迁移任务组网,详细参见[自定义迁移任务](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub:-%E8%87%AA%E5%AE%9A%E4%B9%89Task)
如果想改变迁移任务组网,详细参见[自定义迁移任务](../../docs/tutorial/how_to_define_task.md)
## 可视化
......
......@@ -39,18 +39,17 @@ if __name__ == '__main__':
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Download dataset and use MultiLabelReader to read dataset
dataset = hub.dataset.Toxic()
reader = hub.reader.MultiLabelClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=args.max_seq_len)
# Setup feed list for data feeder
feed_list = [
inputs["input_ids"].name, inputs["position_ids"].name,
inputs["segment_ids"].name, inputs["input_mask"].name
]
# Use the appropriate tokenizer to preprocess the data set
# For ernie_tiny, it will do word segmentation to get subword. More details: https://www.jiqizhixin.com/articles/2019-11-06-9
if module.name == "ernie_tiny":
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
else:
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.Toxic(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use "pooled_output" for classification tasks on an entire sentence.
......@@ -72,9 +71,8 @@ if __name__ == '__main__':
# Define a classfication fine-tune task by PaddleHub's API
multi_label_cls_task = hub.MultiLabelClassifierTask(
data_reader=reader,
dataset=dataset,
feature=pooled_output,
feed_list=feed_list,
num_classes=dataset.num_labels,
config=config)
......
......@@ -45,20 +45,11 @@ if __name__ == '__main__':
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Download dataset and use MultiLabelReader to read dataset
# Download dataset and get its label list and label num
# If you just want labels information, you can omit its tokenizer parameter to avoid preprocessing the train set.
dataset = hub.dataset.Toxic()
reader = hub.reader.MultiLabelClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=args.max_seq_len)
# Setup feed list for data feeder
feed_list = [
inputs["input_ids"].name,
inputs["position_ids"].name,
inputs["segment_ids"].name,
inputs["input_mask"].name,
]
num_classes = dataset.num_labels
label_list = dataset.get_labels()
# Construct transfer learning network
# Use "pooled_output" for classification tasks on an entire sentence.
......@@ -75,20 +66,29 @@ if __name__ == '__main__':
# Define a classfication fine-tune task by PaddleHub's API
multi_label_cls_task = hub.MultiLabelClassifierTask(
data_reader=reader,
dataset=dataset,
feature=pooled_output,
feed_list=feed_list,
num_classes=dataset.num_labels,
config=config)
# Data to be predicted
data = [
[
"Yes you did. And you admitted to doing it. See the Warren Kinsella talk page."
],
[
"I asked you a question. We both know you have my page on your watch list, so are why are you playing games and making me formally ping you? Makin'Bacon"
],
"Yes you did. And you admitted to doing it. See the Warren Kinsella talk page.",
"I asked you a question. We both know you have my page on your watch list, so are why are you playing games and making me formally ping you? Makin'Bacon",
]
# Use the appropriate tokenizer to preprocess the data
# For ernie_tiny, it will do word segmentation to get subword. More details: https://www.jiqizhixin.com/articles/2019-11-06-9
if module.name == "ernie_tiny":
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
else:
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
print(multi_label_cls_task.predict(data=data, return_result=True))
encoded_data = [
tokenizer.encode(text=text, max_seq_len=args.max_seq_len)
for text in data
]
print(
multi_label_cls_task.predict(data=encoded_data, label_list=label_list))
#coding:utf-8
import argparse
import os
import ast
import paddle.fluid as fluid
import paddlehub as hub
import numpy as np
from paddlehub.reader.cv_reader import ObjectDetectionReader
from paddlehub.dataset.base_cv_dataset import ObjectDetectionDataset
from paddlehub.contrib.ppdet.utils.coco_eval import bbox2out
from paddlehub.common.detection_config import get_model_type, get_feed_list, get_mid_feature
from paddlehub.common import detection_config as dconf
# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--use_gpu", type=ast.literal_eval, default=False, help="Whether use GPU for predict.")
parser.add_argument("--checkpoint_dir", type=str, default="paddlehub_finetune_ckpt", help="Path to save log data.")
parser.add_argument("--batch_size", type=int, default=2, help="Total examples' number in batch for training.")
parser.add_argument("--module", type=str, default="ssd", help="Module used as a feature extractor.")
parser.add_argument("--dataset", type=str, default="coco10", help="Dataset to finetune.")
# yapf: enable.
module_map = {
"yolov3": "yolov3_darknet53_coco2017",
"ssd": "ssd_vgg16_512_coco2017",
"faster_rcnn": "faster_rcnn_resnet50_coco2017",
}
def predict(args):
module_name = args.module # 'yolov3_darknet53_coco2017'
model_type = get_model_type(module_name) # 'yolo'
# define data
ds = hub.dataset.Coco10(model_type)
print("ds.num_labels", ds.num_labels)
data_reader = ObjectDetectionReader(dataset=ds, model_type=model_type)
# define model(program)
module = hub.Module(name=module_name)
if model_type == 'rcnn':
input_dict, output_dict, program = module.context(
trainable=True, phase='train')
input_dict_pred, output_dict_pred, program_pred = module.context(
trainable=False)
else:
input_dict, output_dict, program = module.context(trainable=True)
input_dict_pred = output_dict_pred = None
feed_list, pred_feed_list = get_feed_list(module_name, input_dict,
input_dict_pred)
feature, pred_feature = get_mid_feature(module_name, output_dict,
output_dict_pred)
config = hub.RunConfig(
use_data_parallel=False,
use_pyreader=True,
use_cuda=args.use_gpu,
batch_size=args.batch_size,
enable_memory_optim=False,
checkpoint_dir=args.checkpoint_dir,
strategy=hub.finetune.strategy.DefaultFinetuneStrategy())
task = hub.DetectionTask(
data_reader=data_reader,
num_classes=ds.num_labels,
feed_list=feed_list,
feature=feature,
predict_feed_list=pred_feed_list,
predict_feature=pred_feature,
model_type=model_type,
config=config)
data = [
"./test/test_img_bird.jpg",
"./test/test_img_cat.jpg",
]
label_map = ds.label_dict()
run_states = task.predict(data=data, accelerate_mode=False)
results = [run_state.run_results for run_state in run_states]
for outs in results:
keys = ['im_shape', 'im_id', 'bbox']
res = {
k: (np.array(v), v.recursive_sequence_lengths())
for k, v in zip(keys, outs)
}
print("im_id", res['im_id'])
is_bbox_normalized = dconf.conf[model_type]['is_bbox_normalized']
clsid2catid = {}
for k in label_map:
clsid2catid[k] = k
bbox_results = bbox2out([res], clsid2catid, is_bbox_normalized)
print(bbox_results)
if __name__ == "__main__":
args = parser.parse_args()
if not args.module in module_map:
hub.logger.error("module should in %s" % module_map.keys())
exit(1)
args.module = module_map[args.module]
predict(args)
# -*- coding:utf8 -*-
import argparse
import os
import ast
import paddle.fluid as fluid
import paddlehub as hub
from paddlehub.reader.cv_reader import ObjectDetectionReader
from paddlehub.dataset.base_cv_dataset import ObjectDetectionDataset
import numpy as np
from paddlehub.common.detection_config import get_model_type, get_feed_list, get_mid_feature
# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--num_epoch", type=int, default=50, help="Number of epoches for fine-tuning.")
parser.add_argument("--use_gpu", type=ast.literal_eval, default=False, help="Whether use GPU for fine-tuning.")
parser.add_argument("--checkpoint_dir", type=str, default="paddlehub_finetune_ckpt", help="Path to save log data.")
parser.add_argument("--batch_size", type=int, default=8, help="Total examples' number in batch for training.")
parser.add_argument("--module", type=str, default="ssd", help="Module used as feature extractor.")
parser.add_argument("--dataset", type=str, default="coco_10", help="Dataset to finetune.")
parser.add_argument("--use_data_parallel", type=ast.literal_eval, default=False, help="Whether use data parallel.")
# yapf: enable.
module_map = {
"yolov3": "yolov3_darknet53_coco2017",
"ssd": "ssd_vgg16_512_coco2017",
"faster_rcnn": "faster_rcnn_resnet50_coco2017",
}
def finetune(args):
module_name = args.module # 'yolov3_darknet53_coco2017'
model_type = get_model_type(module_name) # 'yolo'
# define dataset
ds = hub.dataset.Coco10(model_type)
# base_path = '/home/local3/zhaopenghao/data/detect/paddle-job-84942-0'
# train_dir = 'train_data/images'
# train_list = 'train_data/coco/instances_coco.json'
# val_dir = 'eval_data/images'
# val_list = 'eval_data/coco/instances_coco.json'
# ds = ObjectDetectionDataset(base_path, train_dir, train_list, val_dir, val_list, val_dir, val_list, model_type=model_type)
# print(ds.label_dict())
print("ds.num_labels", ds.num_labels)
# define batch reader
data_reader = ObjectDetectionReader(dataset=ds, model_type=model_type)
# define model(program)
module = hub.Module(name=module_name)
if model_type == 'rcnn':
input_dict, output_dict, program = module.context(
trainable=True, phase='train')
input_dict_pred, output_dict_pred, program_pred = module.context(
trainable=False)
else:
input_dict, output_dict, program = module.context(trainable=True)
input_dict_pred = output_dict_pred = None
print("input_dict keys", input_dict.keys())
print("output_dict keys", output_dict.keys())
feed_list, pred_feed_list = get_feed_list(module_name, input_dict,
input_dict_pred)
print("output_dict length:", len(output_dict))
print(output_dict.keys())
if output_dict_pred is not None:
print(output_dict_pred.keys())
feature, pred_feature = get_mid_feature(module_name, output_dict,
output_dict_pred)
config = hub.RunConfig(
log_interval=10,
eval_interval=100,
use_data_parallel=args.use_data_parallel,
use_pyreader=True,
use_cuda=args.use_gpu,
num_epoch=args.num_epoch,
batch_size=args.batch_size,
enable_memory_optim=False,
checkpoint_dir=args.checkpoint_dir,
strategy=hub.finetune.strategy.DefaultFinetuneStrategy(
learning_rate=0.00025, optimizer_name="adam"))
task = hub.DetectionTask(
data_reader=data_reader,
num_classes=ds.num_labels,
feed_list=feed_list,
feature=feature,
predict_feed_list=pred_feed_list,
predict_feature=pred_feature,
model_type=model_type,
config=config)
task.finetune_and_eval()
if __name__ == "__main__":
args = parser.parse_args()
if not args.module in module_map:
hub.logger.error("module should in %s" % module_map.keys())
exit(1)
args.module = module_map[args.module]
finetune(args)
# PaddleHub Pairwise 文本匹配
本示例将展示如何使用PaddleHub Fine-tune API以及预训练模型(word2vec_skipgram、simnet_bow、ERNIE等)完成pairwise文本匹配任务。
**PaddleHub 1.8.0以上版本支持在预训练模型之后拼接预置网络(bow, cnn, gru, lstm)完成文本匹配任务**
## 目录结构
```
pairwise_text_matching
├── embedding_pairwise_matching_predict.py # 词向量预训练模型拼接预置网络的预测脚本
├── embedding_pairwise_matching.py # 词向量预训练模型拼接预置网络的训练脚本
├── ernie_pairwise_matching_predict.py # ERNIE预训练模型的预测脚本
├── ernie_pairtwise_matching.py # ERNIE预训练模型的训练脚本
├── run_embedding_pairwise_matching_predict.sh # 词向量预训练模型拼接预置网络的预测启动脚本
├── run_embedding_pairwise_matching.sh # 词向量预训练模型拼接预置网络的训练启动脚本
├── run_ernie_pairwise_matching_predict.sh # ERNIE预训练模型的预测启动脚本
└── run_ernie_pairwise_matching.sh # ERNIE预训练模型的训练启动脚本
```
## 如何开始Fine-tune
以下例子以最简单的匹配网络(词向量拼接预置网络)完成文本分类任务,说明PaddleHub如何完成pairwise文本匹配迁移学习。
<p align="center">
<img src="../../docs/imgs/pairwise.png" width='70%' align="middle"
</p>
在完成安装PaddlePaddle与PaddleHub后,通过执行脚本`sh run_embedding_pairtwise_matching.sh`即可开始使用预训练embedding对DuEL数据集进行Fine-tune。
其中脚本参数说明如下:
```bash
--batch_size: 批处理大小,请结合显存情况进行调整,若出现显存不足,请适当调低这一参数;
--learning_rate: Fine-tune的最大学习率;
--weight_decay: 控制正则项力度的参数,用于防止过拟合,默认为0.01;
--warmup_proportion: 学习率warmup策略的比例,如果0.1,则学习率会在前10%训练step的过程中从0慢慢增长到learning_rate, 而后再缓慢衰减,默认为0;
--num_epoch: Fine-tune迭代的轮数;
--max_seq_len: 模型使用的最大序列长度,最大不能超过512, 若出现显存不足,请适当调低这一参数;
--checkpoint_dir: 模型保存路径,PaddleHub会自动保存验证集上表现最好的模型;
--network: 预置网络,可选bow、cnn、gru、lstm
```
## 代码步骤
使用PaddleHub Fine-tune API进行Fine-tune可以分为4个步骤。
### Step1: 加载预训练模型
```python
module = hub.Module(name="tencent_ailab_chinese_embedding_small")
inputs, outputs, program = module.context(trainable=True, max_seq_len=128, num_slots=3)
```
其中最大序列长度`max_seq_len`是可以调整的参数,建议值128,根据任务文本长度不同可以调整该值。
`num_slots`: 文本匹配任务输入文本的数据量。pairtwise文本匹配任务num_slots应为3,如上图中的query、left和right。pointwise文本匹配任务num_slots应为2。
PaddleHub还提供等其他词向量预训练模型可供选择, 模型对应的加载示例如下:
模型名 | PaddleHub Module
---------------------------------- | :------:
tencent_ailab_chinese_embedding_small | `hub.Module(name='tencent_ailab_chinese_embedding_small')`
word2vec_skipgram | `hub.Module(name='word2vec_skipgram')`
simnet_bow | `hub.Module(name='simnet_bow')`
更多模型请参考[PaddleHub官网](https://www.paddlepaddle.org.cn/hublist)
如果想尝试word2vec_skipgram模型,只需要更换Module中的`name`参数即可.
```python
# 更换name参数即可无缝切换word2vec_skipgram模型, 代码示例如下
module = hub.Module(name="word2vec_skipgram")
```
### Step2: 选择Tokenizer读取数据
```python
tokenizer = hub.CustomTokenizer(
vocab_file=module.get_vocab_path(),
tokenize_chinese_chars=True,
)
```
`module.get_vocab_path()` 会返回预训练模型对应的词表;
`tokenize_chinese_chars` 是否切分中文文本
**NOTE:**
1. 如果使用Transformer类模型(如ERNIE、BERT、RoBerta等),则应该选择`hub.BertTokenizer`.
2. 如果使用非Transformer类模型(如word2vec_skipgram、tencent_ailab_chinese_embedding_small等),则应该选择`hub.CustomTokenizer`
### Step3: 准备数据集
```python
dataset = hub.dataset.DuEL(tokenizer=tokenizer, max_seq_len=128)
```
`hub.dataset.DuEL()` 会自动从网络下载数据集并解压到用户目录下`$HOME/.paddlehub/dataset`目录;
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致;
更多数据集信息参考[Dataset](../../docs/reference/dataset.md)
#### 自定义数据集
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](../../docs/tutorial/how_to_load_data.md)
### Step4:选择优化策略和运行配置
```python
strategy = hub.DefaultStrategy(optimizer_name="sgd", learning_rate=5e-2)
config = hub.RunConfig(use_cuda=False, num_epoch=300, batch_size=128, strategy=strategy)
```
#### 优化策略
**NOTE:**
1. 如果使用非Transformer类模型(如word2vec_skipgram、tencent_ailab_chinese_embedding_small等,推荐使用SGD优化器在CPU下运行。
2. 如果使用Transformer类模型(如ERNIE、BERT、RoBerta等),推荐使用Adam优化器在GPU下运行。
针对ERNIE与BERT类任务,PaddleHub封装了适合这一任务的迁移学习优化策略`AdamWeightDecayStrategy`
```python
strategy = hub.AdamWeightDecayStrategy(
warmup_proportion=0.1,
weight_decay=0.01,
learning_rate=5e-5,
lr_scheduler="linear_decay")
```
* `learning_rate`: Fine-tune过程中的最大学习率;
* `weight_decay`: 模型的正则项参数,默认0.01,如果模型有过拟合倾向,可适当调高这一参数;
* `warmup_proportion`: 如果warmup_proportion>0, 例如0.1, 则学习率会在前10%的steps中线性增长至最高值learning_rate;
* `lr_scheduler`: 有两种策略可选(1) `linear_decay`策略学习率会在最高点后以线性方式衰减; `noam_decay`策略学习率会在最高点以多项式形式衰减;
#### 运行配置
`RunConfig` 主要控制Fine-tune的训练,包含以下可控制的参数:
* `use_cuda`: 是否使用GPU训练,默认为False;
* `checkpoint_dir`: 模型checkpoint保存路径, 若用户没有指定,程序会自动生成;
* `num_epoch`: Fine-tune的轮数;
* `batch_size`: 训练的批大小,如果使用GPU,请根据实际情况调整batch_size;
* `strategy`: Fine-tune优化策略;
### Step5: 构建网络并创建pairwise文本匹配迁移任务进行Fine-tune
```python
query = outputs["emb"]
left = outputs['emb_2']
right = outputs['emb_3']
matching_task = hub.PairwiseTextMatchingTask(
dataset=dataset,
left_feature=left,
right_feature=right,
tokenizer=tokenizer,
network='bow',
config=config)
matching_task.finetune_and_eval()
```
**NOTE:**
1. `outputs["emb"]`返回了预训练模型输入title对应的词向量向量,`outputs["emb_2"]`返回了预训练模型输入left对应的词向量。`outputs["emb_3"]`返回了预训练模型输入right对应的词向量。
该词向量是token-levle特征,shape应为[-1, max_seq_len, emb_size]。
2. `hub.PairwiseTextMatchingTask`通过输入特征,label与预置网络,可以生成适用于pairwise文本匹配的迁移任务`PairwiseTextMatchingTask`
3. 使用预置网络,可以通过`hub.PairwiseTextMatchingTask`参数network进行指定不同的网络结构。如下代码表示选择bilstm网络拼接在预训练模型之后。
PaddleHub 文本匹配分类任务预置网络支持BOW,CNN,GRU,LSTM。指定network应是其中之一。
#### 自定义迁移任务
如果想改变迁移任务组网,详细参见[自定义迁移任务](../../docs/tutorial/how_to_define_task.md)
## 可视化
Fine-tune API训练过程中会自动对关键训练指标进行打点,启动程序后执行下面命令:
```bash
$ visualdl --logdir $CKPT_DIR/visualization --host ${HOST_IP} --port ${PORT_NUM}
```
其中${HOST_IP}为本机IP地址,${PORT_NUM}为可用端口号,如本机IP地址为192.168.0.1,端口号8040,用浏览器打开192.168.0.1:8040,即可看到训练过程中指标的变化情况。
## 模型预测
通过Fine-tune完成模型训练后,在对应的ckpt目录下,会自动保存验证集上效果最好的模型。
配置脚本参数
```
CKPT_DIR="ckpt_embedding_pairwise_matching/"
python embedding_pairwise_matching_predict.py --checkpoint_dir $CKPT_DIR --max_seq_len 128
```
其中CKPT_DIR为Fine-tune API保存最佳模型的路径, max_seq_len是模型的最大序列长度,*请与训练时配置的参数保持一致*
参数配置正确后,请执行脚本`sh run_embedding_pairwise_matching_predict.sh`,即可看到文本匹配预测结果。
如:
```shell
data: ["小品《战狼故事》中,吴京突破重重障碍解救爱人,深情告白太感人;爱人", "外文名:愛人;摘要:爱人,意思是:情人。;义项描述:日本语词汇;语言:日文;中文名:爱人;标签:文化;"], predict_label: 1
data: ["儿子祝融被杀害,西天王大发雷霆,立即下令捉拿天庭三公主;儿子", "摘要:《儿子》是曹国昌1983年创作的木雕,收藏于中国美术馆。;材质::木雕;作者::曹国昌;中文名:儿子;创作年代::1983年;义项描述:曹国昌木雕;标签:文化;"], predict_label: 1
```
data字段表述预测的文本对数据,predict_label为0表示语义不相关,predict_label为1表示语义相关。
我们在AI Studio上提供了IPython NoteBook形式的demo,点击[PaddleHub教程合集](https://aistudio.baidu.com/aistudio/projectdetail/231146),可使用AI Studio平台提供的GPU算力进行快速尝试。
## 超参优化AutoDL Finetuner
PaddleHub还提供了超参优化(Hyperparameter Tuning)功能, 自动搜索最优模型超参得到更好的模型效果。详细信息参见[AutoDL Finetuner超参优化功能教程](../../docs/tutorial/autofinetune.md)
## Fine-tune之后保存的模型转化为PaddleHub Module
Fine-tune之后保存的模型转化为PaddleHub Module[教程](../../docs/tutorial/finetuned_model_to_module.md)
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Fine-tuning on pairwise text matching task """
import argparse
import ast
import paddlehub as hub
# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--num_epoch", type=int, default=3, help="Number of epoches for fine-tuning.")
parser.add_argument("--learning_rate", type=float, default=5e-5, help="Learning rate used to train with warmup.")
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
parser.add_argument("--max_seq_len", type=int, default=512, help="Number of words of the longest seqence.")
parser.add_argument("--batch_size", type=int, default=32, help="Total examples' number in batch for training.")
parser.add_argument("--network", type=str, default=None, help="Pre-defined network which was connected after module.")
args = parser.parse_args()
# yapf: enable.
jieba_paddle = hub.Module(name='jieba_paddle')
def cut(text):
res = jieba_paddle.cut(text, use_paddle=False)
return res
if __name__ == '__main__':
# Load Paddlehub word embedding pretrained model
module = hub.Module(name="word2vec_skipgram")
# module = hub.Module(name="simnet_bow")
# module = hub.Module(name="tencent_ailab_chinese_embedding_small")
# Pairwise task needs: query, title_left, right_title (3 slots)
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len, num_slots=3)
# Tokenizer tokenizes the text data and encodes the data as model needed.
# If you use transformer modules (ernie, bert, roberta and so on), tokenizer should be hub.BertTokenizer.
# Otherwise, tokenizer should be hub.CustomTokenizer.
# If you choose CustomTokenizer, you can also change the chinese word segmentation tool, for example jieba.
tokenizer = hub.CustomTokenizer(
vocab_file=module.get_vocab_path(),
tokenize_chinese_chars=True,
cut_function=cut, # jieba.cut as cut function
)
dataset = hub.dataset.DuEL(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use token-level output.
query = outputs["emb"]
left = outputs['emb_2']
right = outputs['emb_3']
# Select fine-tune strategy
strategy = hub.DefaultStrategy(
optimizer_name="sgd", learning_rate=args.learning_rate)
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
eval_interval=300,
use_data_parallel=False,
use_cuda=False,
num_epoch=args.num_epoch,
batch_size=args.batch_size,
checkpoint_dir=args.checkpoint_dir,
strategy=strategy)
# Define a text matching task by PaddleHub's API
# network choice: bow, cnn, gru, lstm (PaddleHub pre-defined network)
matching_task = hub.PairwiseTextMatchingTask(
dataset=dataset,
query_feature=query,
left_feature=left,
right_feature=right,
tokenizer=tokenizer,
network=args.network,
config=config)
# Fine-tune and evaluate by PaddleHub's API
# will finish training, evaluation, testing, save model automatically
matching_task.finetune_and_eval()
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Fine-tuning on pairwise text matching task """
import argparse
import ast
import paddlehub as hub
# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--num_epoch", type=int, default=3, help="Number of epoches for fine-tuning.")
parser.add_argument("--learning_rate", type=float, default=5e-5, help="Learning rate used to train with warmup.")
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
parser.add_argument("--max_seq_len", type=int, default=512, help="Number of words of the longest seqence.")
parser.add_argument("--batch_size", type=int, default=32, help="Total examples' number in batch for training.")
parser.add_argument("--network", type=str, default=None, help="Pre-defined network which was connected after module.")
args = parser.parse_args()
# yapf: enable.
jieba_paddle = hub.Module(name='jieba_paddle')
def cut(text):
res = jieba_paddle.cut(text, use_paddle=False)
return res
if __name__ == '__main__':
# Load Paddlehub word embedding pretrained model
module = hub.Module(name="word2vec_skipgram")
# module = hub.Module(name="tencent_ailab_chinese_embedding_small")
# module = hub.Module(name="simnet_bow")
# Pairwise task needs: query, title_left, right_title (3 slots)
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len, num_slots=3)
# Tokenizer tokenizes the text data and encodes the data as model needed.
# If you use transformer modules (ernie, bert, roberta and so on), tokenizer should be hub.BertTokenizer.
# Otherwise, tokenizer should be hub.CustomTokenizer.
# If you choose CustomTokenizer, you can also change the chinese word segmentation tool, for example jieba.
tokenizer = hub.CustomTokenizer(
vocab_file=module.get_vocab_path(),
tokenize_chinese_chars=True,
cut_function=cut, # jieba.cut as cut function
)
dataset = hub.dataset.DuEL(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use token-level output.
query = outputs["emb"]
left = outputs['emb_2']
right = outputs['emb_3']
# Select fine-tune strategy
strategy = hub.DefaultStrategy(optimizer_name="sgd")
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
use_data_parallel=False,
use_cuda=False,
batch_size=args.batch_size,
checkpoint_dir=args.checkpoint_dir,
strategy=strategy)
# Define a text matching task by PaddleHub's API
# network choice: bow, cnn, gru, lstm (PaddleHub pre-defined network)
pairwise_matching_task = hub.PairwiseTextMatchingTask(
dataset=dataset,
query_feature=query,
left_feature=left,
right_feature=right,
tokenizer=tokenizer,
network=args.network,
config=config)
# Prediction data sample.
text_pairs = [
[
"小品《战狼故事》中,吴京突破重重障碍解救爱人,深情告白太感人;爱人", # query
"外文名:愛人;摘要:爱人,意思是:情人。;义项描述:日本语词汇;语言:日文;中文名:爱人;标签:文化;", # title
],
[
"儿子祝融被杀害,西天王大发雷霆,立即下令捉拿天庭三公主;儿子", # query
"摘要:《儿子》是曹国昌1983年创作的木雕,收藏于中国美术馆。;材质::木雕;作者::曹国昌;中文名:儿子;创作年代::1983年;义项描述:曹国昌木雕;标签:文化;", # title
]
]
# Predict by PaddleHub's API
results = pairwise_matching_task.predict(
data=text_pairs,
max_seq_len=args.max_seq_len,
label_list=dataset.get_labels(),
return_result=True,
accelerate_mode=True)
for index, text in enumerate(text_pairs):
print("data: %s, prediction_label: %s" % (text, results[index]))
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Fine-tuning on pairwise text matching task """
import argparse
import ast
import paddlehub as hub
# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--num_epoch", type=int, default=3, help="Number of epoches for fine-tuning.")
parser.add_argument("--use_gpu", type=ast.literal_eval, default=True, help="Whether to use GPU for fine-tuning or not.")
parser.add_argument("--learning_rate", type=float, default=5e-5, help="Learning rate used to train with warmup.")
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
parser.add_argument("--max_seq_len", type=int, default=512, help="Number of words of the longest seqence.")
parser.add_argument("--batch_size", type=int, default=32, help="Total examples' number in batch for training.")
parser.add_argument("--use_data_parallel", type=ast.literal_eval, default=False, help="Whether to use data parallel or not.")
parser.add_argument("--weight_decay", type=float, default=0.01, help="Weight decay rate for L2 regularizer.")
parser.add_argument("--warmup_proportion", type=float, default=0.1, help="Warmup proportion params for warmup strategy")
args = parser.parse_args()
# yapf: enable.
if __name__ == '__main__':
# Load Paddlehub ERNIE pretrained model
module = hub.Module(name="ernie")
# Pairwise task needs: query, title_left, right_title (3 slots)
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len, num_slots=3)
# Tokenizer tokenizes the text data and encodes the data as model needed.
# If you use transformer modules (ernie, bert, roberta and so on), tokenizer should be hub.BertTokenizer.
# Otherwise, tokenizer should be hub.CustomTokenizer.
tokenizer = hub.BertTokenizer(
vocab_file=module.get_vocab_path(), tokenize_chinese_chars=True)
# Load dataset
dataset = hub.dataset.DuEL(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use sequence-level output.
query = outputs["pooled_output"]
left = outputs['pooled_output_2']
right = outputs['pooled_output_3']
# Select fine-tune strategy
strategy = hub.AdamWeightDecayStrategy(
warmup_proportion=args.warmup_proportion,
weight_decay=args.weight_decay,
learning_rate=args.learning_rate)
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
eval_interval=300,
use_data_parallel=args.use_data_parallel,
use_cuda=args.use_gpu,
num_epoch=args.num_epoch,
batch_size=args.batch_size,
checkpoint_dir=args.checkpoint_dir,
strategy=strategy)
# Define a pairwise text matching task by PaddleHub's API
pairwise_matching_task = hub.PairwiseTextMatchingTask(
query_feature=query,
left_feature=left,
right_feature=right,
tokenizer=tokenizer,
dataset=dataset,
config=config)
# Fine-tune and evaluate by PaddleHub's API
# will finish training, evaluation, testing, save model automatically
pairwise_matching_task.finetune_and_eval()
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Fine-tuning on pairwise text matching task """
import argparse
import ast
import paddlehub as hub
# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--num_epoch", type=int, default=3, help="Number of epoches for fine-tuning.")
parser.add_argument("--use_gpu", type=ast.literal_eval, default=True, help="Whether to use GPU for fine-tuning or not.")
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
parser.add_argument("--max_seq_len", type=int, default=512, help="Number of words of the longest seqence.")
parser.add_argument("--batch_size", type=int, default=32, help="Total examples' number in batch for training.")
args = parser.parse_args()
# yapf: enable.
if __name__ == '__main__':
# Load Paddlehub ERNIE pretrained model.
module = hub.Module(name="ernie")
# Pairwise task needs: query, title_left, right_title (3 slots).
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len, num_slots=3)
# Tokenizer tokenizes the text data and encodes the data as model needed.
# If you use transformer modules (ernie, bert, roberta and so on), tokenizer should be hub.BertTokenizer.
# Otherwise, tokenizer should be hub.CustomTokenizer.
tokenizer = hub.BertTokenizer(
vocab_file=module.get_vocab_path(), tokenize_chinese_chars=True)
# Load dataset
dataset = hub.dataset.DuEL(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network.
# Use sequence-level output.
query = outputs["pooled_output"]
left = outputs['pooled_output_2']
right = outputs['pooled_output_3']
# Select fine-tune strategy.
strategy = hub.AdamWeightDecayStrategy()
# Setup RunConfig for PaddleHub Fine-tune API.
config = hub.RunConfig(
use_data_parallel=False,
use_cuda=args.use_gpu,
num_epoch=args.num_epoch,
batch_size=args.batch_size,
checkpoint_dir=args.checkpoint_dir,
strategy=strategy)
# Define a pairwise text matching task by PaddleHub's API.
pairwise_matching_task = hub.PairwiseTextMatchingTask(
query_feature=query,
left_feature=left,
right_feature=right,
tokenizer=tokenizer,
dataset=dataset,
config=config)
# Prediction data sample.
text_pairs = [
[
"小品《战狼故事》中,吴京突破重重障碍解救爱人,深情告白太感人;爱人", # query
"外文名:愛人;摘要:爱人,意思是:情人。;义项描述:日本语词汇;语言:日文;中文名:爱人;标签:文化;", # title
],
[
"儿子祝融被杀害,西天王大发雷霆,立即下令捉拿天庭三公主;儿子", # query
"摘要:《儿子》是曹国昌1983年创作的木雕,收藏于中国美术馆。;材质::木雕;作者::曹国昌;中文名:儿子;创作年代::1983年;义项描述:曹国昌木雕;标签:文化;", # title
]
]
# Predict by PaddleHub's API
results = pairwise_matching_task.predict(
data=text_pairs,
max_seq_len=args.max_seq_len,
label_list=dataset.get_labels(),
return_result=True,
accelerate_mode=False)
for index, text in enumerate(text_pairs):
print("data: %s, prediction_label: %s" % (text, results[index]))
CKPT_DIR="./ckpt_embedding_pairwise_matching"
python -u embedding_pairwise_matching.py \
--batch_size=128 \
--checkpoint_dir=${CKPT_DIR} \
--learning_rate=5e-2 \
--max_seq_len=128 \
--num_epoch=300 \
--network=bow
CKPT_DIR="./ckpt_embedding_pairwise_matching"
python -u embedding_pairwise_matching_predict.py \
--batch_size=1 \
--checkpoint_dir=${CKPT_DIR} \
--max_seq_len=128 \
--network=lstm
export CUDA_VISIBLE_DEVICES=0
CKPT_DIR="./ckpt_ernie_pairwise_matching"
python -u ernie_pairwise_matching.py \
--batch_size=32 \
--use_gpu=True \
--checkpoint_dir=${CKPT_DIR} \
--learning_rate=5e-5 \
--max_seq_len=128 \
--num_epoch=3 \
--warmup_proportion=0.1 \
--weight_decay=0.01 \
--use_data_parallel=True
CKPT_DIR="./ckpt_ernie_pairwise_matching"
python -u ernie_pairwise_matching_predict.py \
--batch_size=1 \
--use_gpu=False \
--checkpoint_dir=${CKPT_DIR} \
--max_seq_len=128
# PaddleHub Pointwise 文本匹配
本示例将展示如何使用PaddleHub Fine-tune API以及预训练模型(word2vec_skipgram、simnet_bow、ERNIE等)完成pointwise文本匹配任务。
**PaddleHub 1.8.0以上版本支持在预训练模型之后拼接预置网络(bow, cnn, gru, lstm)完成文本匹配任务**
## 目录结构
```
pointwise_text_matching
├── embedding_pointwise_matching_predict.py # 词向量预训练模型拼接预置网络的预测脚本
├── embedding_pointwise_matching.py # 词向量预训练模型拼接预置网络的训练脚本
├── ernie_pointwise_matching_predict.py # ERNIE预训练模型的预测脚本
├── ernie_pointwise_matching.py # ERNIE预训练模型的训练脚本
├── run_embedding_pointwise_matching_predict.sh # 词向量预训练模型拼接预置网络的预测启动脚本
├── run_embedding_pointwise_matching.sh # 词向量预训练模型拼接预置网络的训练启动脚本
├── run_ernie_pointwise_matching_predict.sh # ERNIE预训练模型的预测启动脚本
└── run_ernie_pointwise_matching.sh # ERNIE预训练模型的训练启动脚本
```
## 如何开始Fine-tune
以下例子以最简单的匹配网络(词向量拼接预置网络)完成文本分类任务,说明PaddleHub如何完成pointwise文本匹配迁移学习。
<p align="center">
<img src="../../docs/imgs/pointwise.png" width='60%' align="middle"
</p>
在完成安装PaddlePaddle与PaddleHub后,通过执行脚本`sh run_embedding_pointwise_matching.sh`即可开始使用预训练embedding对LCQMC数据集进行Fine-tune。
其中脚本参数说明如下:
```bash
--batch_size: 批处理大小,请结合显存情况进行调整,若出现显存不足,请适当调低这一参数;
--learning_rate: Fine-tune的最大学习率;
--num_epoch: Fine-tune迭代的轮数;
--max_seq_len: 模型使用的最大序列长度,最大不能超过512, 若出现显存不足,请适当调低这一参数;
--checkpoint_dir: 模型保存路径,PaddleHub会自动保存验证集上表现最好的模型;
--network: 预置网络,可选bow、cnn、gru、lstm
```
## 代码步骤
使用PaddleHub Fine-tune API进行Fine-tune可以分为4个步骤。
### Step1: 加载预训练模型
```python
module = hub.Module(name="tencent_ailab_chinese_embedding_small")
inputs, outputs, program = module.context(trainable=True, max_seq_len=128, num_slots=2)
```
其中最大序列长度`max_seq_len`是可以调整的参数,建议值128,根据任务文本长度不同可以调整该值。
`num_slots`: 文本匹配任务输入文本的数据量。pointwise文本匹配任务num_slots应为2,如上图中的query和title。 pairtwise文本匹配任务num_slots应为3。
PaddleHub还提供等其他词向量预训练模型可供选择, 模型对应的加载示例如下:
模型名 | PaddleHub Module
---------------------------------- | :------:
tencent_ailab_chinese_embedding_small | `hub.Module(name='tencent_ailab_chinese_embedding_small')`
word2vec_skipgram | `hub.Module(name='word2vec_skipgram')`
simnet_bow | `hub.Module(name='simnet_bow')`
更多模型请参考[PaddleHub官网](https://www.paddlepaddle.org.cn/hublist)
如果想尝试word2vec_skipgram模型,只需要更换Module中的`name`参数即可.
```python
# 更换name参数即可无缝切换word2vec_skipgram模型, 代码示例如下
module = hub.Module(name="word2vec_skipgram")
```
### Step2: 选择Tokenizer读取数据
```python
tokenizer = hub.CustomTokenizer(
vocab_file=module.get_vocab_path(),
tokenize_chinese_chars=True,
)
```
`module.get_vocab_path()` 会返回预训练模型对应的词表;
`tokenize_chinese_chars` 是否切分中文文本
**NOTE:**
1. 如果使用Transformer类模型(如ERNIE、BERT、RoBerta等),则应该选择`hub.BertTokenizer`.
2. 如果使用非Transformer类模型(如word2vec_skipgram、tencent_ailab_chinese_embedding_small等),则应该选择`hub.CustomTokenizer`
### Step3: 准备数据集
```python
dataset = hub.dataset.LCQMC(tokenizer=tokenizer, max_seq_len=128)
```
`hub.dataset.LCQMC()` 会自动从网络下载数据集并解压到用户目录下`$HOME/.paddlehub/dataset`目录;
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致;
更多数据集信息参考[Dataset](../../docs/reference/dataset.md)
#### 自定义数据集
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](../../docs/tutorial/how_to_load_data.md)
### Step4:选择优化策略和运行配置
```python
strategy = hub.DefaultStrategy(optimizer_name="sgd", learning_rate=5e-2)
config = hub.RunConfig(use_cuda=False, num_epoch=300, batch_size=128, strategy=strategy)
```
#### 优化策略
**NOTE:**
1. 如果使用非Transformer类模型(如word2vec_skipgram、tencent_ailab_chinese_embedding_small等,推荐使用SGD优化器在CPU下运行。
2. 如果使用Transformer类模型(如ERNIE、BERT、RoBerta等),推荐使用Adam优化器在GPU下运行。
针对ERNIE与BERT类任务,PaddleHub封装了适合这一任务的迁移学习优化策略`AdamWeightDecayStrategy`
```python
strategy = hub.AdamWeightDecayStrategy(
warmup_proportion=0.1,
weight_decay=0.01,
learning_rate=5e-5,
lr_scheduler="linear_decay")
```
* `learning_rate`: Fine-tune过程中的最大学习率;
* `weight_decay`: 模型的正则项参数,默认0.01,如果模型有过拟合倾向,可适当调高这一参数;
* `warmup_proportion`: 如果warmup_proportion>0, 例如0.1, 则学习率会在前10%的steps中线性增长至最高值learning_rate;
* `lr_scheduler`: 有两种策略可选(1) `linear_decay`策略学习率会在最高点后以线性方式衰减; `noam_decay`策略学习率会在最高点以多项式形式衰减;
#### 运行配置
`RunConfig` 主要控制Fine-tune的训练,包含以下可控制的参数:
* `use_cuda`: 是否使用GPU训练,默认为False;
* `checkpoint_dir`: 模型checkpoint保存路径, 若用户没有指定,程序会自动生成;
* `num_epoch`: Fine-tune的轮数;
* `batch_size`: 训练的批大小,如果使用GPU,请根据实际情况调整batch_size;
* `strategy`: Fine-tune优化策略;
### Step5: 构建网络并创建pointwise文本匹配迁移任务进行Fine-tune
```python
query = outputs["emb"]
title = outputs['emb_2']
matching_task = hub.PointwiseTextMatchingTask(
dataset=dataset,
query_feature=query,
title_feature=title,
tokenizer=tokenizer,
network='bow',
config=config)
matching_task.finetune_and_eval()
```
**NOTE:**
1. `outputs["emb"]`返回了预训练模型输入title对应的词向量向量,`outputs["emb_2"]`返回了预训练模型输入query对应的词向量。该词向量是token-levle特征,shape应为[-1, max_seq_len, emb_size]。
2. `hub.PointwiseTextMatchingTask`通过输入特征,label与预置网络,可以生成适用于pointwise文本匹配的迁移任务`PointwiseTextMatchingTask`
3. 使用预置网络,可以通过`hub.PointwiseTextMatchingTask`参数network进行指定不同的网络结构。如下代码表示选择bilstm网络拼接在预训练模型之后。
PaddleHub 文本匹配分类任务预置网络支持BOW,CNN,GRU,LSTM。指定network应是其中之一。
#### 自定义迁移任务
如果想改变迁移任务组网,详细参见[自定义迁移任务](../../docs/tutorial/how_to_define_task.md)
## 可视化
Fine-tune API训练过程中会自动对关键训练指标进行打点,启动程序后执行下面命令:
```bash
$ visualdl --logdir $CKPT_DIR/visualization --host ${HOST_IP} --port ${PORT_NUM}
```
其中${HOST_IP}为本机IP地址,${PORT_NUM}为可用端口号,如本机IP地址为192.168.0.1,端口号8040,用浏览器打开192.168.0.1:8040,即可看到训练过程中指标的变化情况。
## 模型预测
通过Fine-tune完成模型训练后,在对应的ckpt目录下,会自动保存验证集上效果最好的模型。
配置脚本参数
```
CKPT_DIR="ckpt_embedding_pointwise_matching/"
python embedding_pointwise_matching_predict.py --checkpoint_dir $CKPT_DIR --max_seq_len 128
```
其中CKPT_DIR为Fine-tune API保存最佳模型的路径, max_seq_len是模型的最大序列长度,*请与训练时配置的参数保持一致*
参数配置正确后,请执行脚本`sh run_embedding_pointwise_matching_predict.sh`,即可看到文本匹配预测结果。
如:
```shell
data: ["请问不是您的账户吗?", "您好,请问您使用的邮箱类型是?"], predict_label: 0
data: ["推荐个手机游戏", "手机游戏推荐"], predict_label: 1
```
data字段表述预测的文本对数据,predict_label为0表示语义不相关,predict_label为1表示语义相关。
我们在AI Studio上提供了IPython NoteBook形式的demo,点击[PaddleHub教程合集](https://aistudio.baidu.com/aistudio/projectdetail/231146),可使用AI Studio平台提供的GPU算力进行快速尝试。
## 超参优化AutoDL Finetuner
PaddleHub还提供了超参优化(Hyperparameter Tuning)功能, 自动搜索最优模型超参得到更好的模型效果。详细信息参见[AutoDL Finetuner超参优化功能教程](../../docs/tutorial/autofinetune.md)
## Fine-tune之后保存的模型转化为PaddleHub Module
Fine-tune之后保存的模型转化为PaddleHub Module[教程](../../docs/tutorial/finetuned_model_to_module.md)
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Fine-tuning on pointwise text matching task """
import argparse
import ast
import paddlehub as hub
# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--num_epoch", type=int, default=3, help="Number of epoches for fine-tuning.")
parser.add_argument("--learning_rate", type=float, default=5e-5, help="Learning rate used to train with warmup.")
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
parser.add_argument("--max_seq_len", type=int, default=512, help="Number of words of the longest seqence.")
parser.add_argument("--batch_size", type=int, default=32, help="Total examples' number in batch for training.")
parser.add_argument("--network", type=str, default=None, help="Pre-defined network which was connected after module.")
args = parser.parse_args()
# yapf: enable.
jieba_paddle = hub.Module(name='jieba_paddle')
def cut(text):
res = jieba_paddle.cut(text, use_paddle=False)
return res
if __name__ == '__main__':
# Load Paddlehub word embedding pretrained model
module = hub.Module(name="word2vec_skipgram")
# module = hub.Module(name="simnet_bow")
# module = hub.Module(name="tencent_ailab_chinese_embedding_small")
# Pointwise task needs: query, title (2 slots)
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len, num_slots=2)
# Tokenizer tokenizes the text data and encodes the data as model needed.
# If you use transformer modules (ernie, bert, roberta and so on), tokenizer should be hub.BertTokenizer.
# Otherwise, tokenizer should be hub.CustomTokenizer.
# If you choose CustomTokenizer, you can also change the chinese word segmentation tool, for example jieba.
tokenizer = hub.CustomTokenizer(
vocab_file=module.get_vocab_path(),
tokenize_chinese_chars=True,
cut_function=cut, # jieba.cut as cut function
)
dataset = hub.dataset.LCQMC(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use token-level output.
query = outputs["emb"]
title = outputs['emb_2']
# Select fine-tune strategy
strategy = hub.DefaultStrategy(
optimizer_name="sgd", learning_rate=args.learning_rate)
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
eval_interval=300,
use_data_parallel=False,
use_cuda=False,
num_epoch=args.num_epoch,
batch_size=args.batch_size,
checkpoint_dir=args.checkpoint_dir,
strategy=strategy)
# Define a text matching task by PaddleHub's API
# network choice: bow, cnn, gru, lstm (PaddleHub pre-defined network)
matching_task = hub.PointwiseTextMatchingTask(
dataset=dataset,
query_feature=query,
title_feature=title,
tokenizer=tokenizer,
network=args.network,
config=config)
# Fine-tune and evaluate by PaddleHub's API
# will finish training, evaluation, testing, save model automatically
matching_task.finetune_and_eval()
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Fine-tuning on pointwise text matching task """
import argparse
import ast
import paddlehub as hub
# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--num_epoch", type=int, default=3, help="Number of epoches for fine-tuning.")
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
parser.add_argument("--max_seq_len", type=int, default=512, help="Number of words of the longest seqence.")
parser.add_argument("--batch_size", type=int, default=32, help="Total examples' number in batch for training.")
parser.add_argument("--network", type=str, default=None, help="Pre-defined network which was connected after module.")
args = parser.parse_args()
# yapf: enable.
jieba_paddle = hub.Module(name='jieba_paddle')
def cut(text):
res = jieba_paddle.cut(text, use_paddle=False)
return res
if __name__ == '__main__':
# Load Paddlehub word embedding pretrained model
module = hub.Module(name="word2vec_skipgram")
# module = hub.Module(name="simnet_bow")
# module = hub.Module(name="tencent_ailab_chinese_embedding_small")
# Pointwise task needs: query, title (2 slots)
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len, num_slots=2)
# Tokenizer tokenizes the text data and encodes the data as model needed.
# If you use transformer modules (ernie, bert, roberta and so on), tokenizer should be hub.BertTokenizer.
# Otherwise, tokenizer should be hub.CustomTokenizer.
# If you choose CustomTokenizer, you can also change the chinese word segmentation tool, for example jieba.
tokenizer = hub.CustomTokenizer(
vocab_file=module.get_vocab_path(),
tokenize_chinese_chars=True,
cut_function=cut, # jieba.cut as cut function
)
dataset = hub.dataset.LCQMC(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use token-level output.
query = outputs["emb"]
title = outputs['emb_2']
# Select fine-tune strategy
strategy = hub.DefaultStrategy(optimizer_name="sgd")
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
use_data_parallel=False,
use_cuda=False,
batch_size=args.batch_size,
checkpoint_dir=args.checkpoint_dir,
strategy=strategy)
# Define a text matching task by PaddleHub's API
# network choice: bow, cnn, gru, lstm (PaddleHub pre-defined network)
pointwise_matching_task = hub.PointwiseTextMatchingTask(
dataset=dataset,
query_feature=query,
title_feature=title,
tokenizer=tokenizer,
network=args.network,
config=config)
# Prediction data sample.
text_pairs = [
[
"请问不是您的账户吗?", # query
"您好,请问您使用的邮箱类型是?" # title
],
[
"推荐个手机游戏", # query
"手机游戏推荐" # title
]
]
# Predict by PaddleHub's API
results = pointwise_matching_task.predict(
data=text_pairs,
max_seq_len=args.max_seq_len,
label_list=dataset.get_labels(),
return_result=True,
accelerate_mode=True)
for index, text in enumerate(text_pairs):
print("data: %s, prediction_label: %s" % (text, results[index]))
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Fine-tuning on ponitwise text matching task """
import argparse
import ast
import paddlehub as hub
# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--num_epoch", type=int, default=3, help="Number of epoches for fine-tuning.")
parser.add_argument("--use_gpu", type=ast.literal_eval, default=True, help="Whether use GPU for fine-tuning, input should be True or False")
parser.add_argument("--learning_rate", type=float, default=5e-5, help="Learning rate used to train with warmup.")
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
parser.add_argument("--max_seq_len", type=int, default=512, help="Number of words of the longest seqence.")
parser.add_argument("--batch_size", type=int, default=32, help="Total examples' number in batch for training.")
parser.add_argument("--use_data_parallel", type=ast.literal_eval, default=False, help="Whether use data parallel.")
parser.add_argument("--weight_decay", type=float, default=0.01, help="Weight decay rate for L2 regularizer.")
parser.add_argument("--warmup_proportion", type=float, default=0.1, help="Warmup proportion params for warmup strategy")
args = parser.parse_args()
# yapf: enable.
if __name__ == '__main__':
# Load Paddlehub ERNIE pretrained model
module = hub.Module(name="ernie")
# Pointwise task needs: query, title_left (2 slots)
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len, num_slots=2)
# Tokenizer tokenizes the text data and encodes the data as model needed.
# If you use transformer modules (ernie, bert, roberta and so on), tokenizer should be hub.BertTokenizer.
# else tokenizer should be hub.CustomTokenizer.
tokenizer = hub.BertTokenizer(
vocab_file=module.get_vocab_path(), tokenize_chinese_chars=True)
# Load dataset
dataset = hub.dataset.LCQMC(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use token-level output.
query = outputs["pooled_output"]
left = outputs['pooled_output_2']
# Select fine-tune strategy
strategy = hub.AdamWeightDecayStrategy(
warmup_proportion=args.warmup_proportion,
weight_decay=args.weight_decay,
learning_rate=args.learning_rate)
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
eval_interval=300,
use_data_parallel=args.use_data_parallel,
use_cuda=args.use_gpu,
num_epoch=args.num_epoch,
batch_size=args.batch_size,
checkpoint_dir=args.checkpoint_dir,
strategy=strategy)
# Define a pointwise text matching task by PaddleHub's API
# network choice: bow, cnn, gru, lstm (PaddleHub pre-defined network)
pointwise_matching_task = hub.PointwiseTextMatchingTask(
dataset=dataset,
query_feature=query,
title_feature=left,
tokenizer=tokenizer,
config=config)
# Fine-tune and evaluate by PaddleHub's API
# will finish training, evaluation, testing, save model automatically
pointwise_matching_task.finetune_and_eval()
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Fine-tuning on ponitwise text matching task """
import argparse
import ast
import paddlehub as hub
# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--num_epoch", type=int, default=3, help="Number of epoches for fine-tuning.")
parser.add_argument("--use_gpu", type=ast.literal_eval, default=True, help="Whether use GPU for fine-tuning, input should be True or False")
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
parser.add_argument("--max_seq_len", type=int, default=512, help="Number of words of the longest seqence.")
parser.add_argument("--batch_size", type=int, default=32, help="Total examples' number in batch for training.")
args = parser.parse_args()
# yapf: enable.
if __name__ == '__main__':
# Load Paddlehub ERNIE pretrained model
module = hub.Module(name="ernie")
# Pointwise task needs: query, title_left (2 slots)
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len, num_slots=2)
# Tokenizer tokenizes the text data and encodes the data as model needed.
# If you use transformer modules (ernie, bert, roberta and so on), tokenizer should be hub.BertTokenizer.
# else tokenizer should be hub.CustomTokenizer.
tokenizer = hub.BertTokenizer(
vocab_file=module.get_vocab_path(), tokenize_chinese_chars=True)
# Load dataset
dataset = hub.dataset.LCQMC(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use token-level output.
query = outputs["pooled_output"]
left = outputs['pooled_output_2']
# Select fine-tune strategy
strategy = hub.AdamWeightDecayStrategy()
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
use_data_parallel=False,
use_cuda=args.use_gpu,
batch_size=args.batch_size,
checkpoint_dir=args.checkpoint_dir,
strategy=strategy)
# Define a pointwise text matching task by PaddleHub's API
pointwise_matching_task = hub.PointwiseTextMatchingTask(
dataset=dataset,
query_feature=query,
title_feature=left,
tokenizer=tokenizer,
config=config)
# Prediction data sample.
text_pairs = [
[
"小品《战狼故事》中,吴京突破重重障碍解救爱人,深情告白太感人;爱人", # query
"外文名:愛人;摘要:爱人,意思是:情人。;义项描述:日本语词汇;语言:日文;中文名:爱人;标签:文化;", # title
],
[
"儿子祝融被杀害,西天王大发雷霆,立即下令捉拿天庭三公主;儿子", # query
"摘要:《儿子》是曹国昌1983年创作的木雕,收藏于中国美术馆。;材质::木雕;作者::曹国昌;中文名:儿子;创作年代::1983年;义项描述:曹国昌木雕;标签:文化;", # title
]
]
# Predict by PaddleHub's API
results = pointwise_matching_task.predict(
data=text_pairs,
max_seq_len=args.max_seq_len,
label_list=dataset.get_labels(),
return_result=True,
accelerate_mode=False)
for index, text in enumerate(text_pairs):
print("data: %s, prediction_label: %s" % (text, results[index]))
CKPT_DIR="./ckpt_embedding_pointwise_matching"
python -u embedding_pointwise_matching.py \
--batch_size=128 \
--checkpoint_dir=${CKPT_DIR} \
--learning_rate=5e-3 \
--max_seq_len=128 \
--num_epoch=300 \
--network=bow
CKPT_DIR="./ckpt_embedding_pointwise_matching"
python -u embedding_pointwise_matching_predict.py \
--batch_size=1 \
--checkpoint_dir=${CKPT_DIR} \
--max_seq_len=128 \
--network=bow
export CUDA_VISIBLE_DEVICES=0
CKPT_DIR="./ckpt_ernie_pointwise_matching"
python -u ernie_pointwise_matching.py \
--batch_size=32 \
--use_gpu=True \
--checkpoint_dir=${CKPT_DIR} \
--learning_rate=5e-5 \
--max_seq_len=128 \
--num_epoch=3 \
--warmup_proportion=0.1 \
--weight_decay=0.01 \
--use_data_parallel=True \
CKPT_DIR="./ckpt_ernie_pointwise_matching"
python -u ernie_pointwise_matching_predict.py \
--batch_size=1 \
--use_gpu=False \
--checkpoint_dir=${CKPT_DIR} \
--max_seq_len=128
......@@ -28,6 +28,8 @@
### Step1: 加载预训练模型
```python
import paddlehub as hub
module = hub.Module(name="ernie")
inputs, outputs, program = module.context(trainable=True, max_seq_len=128)
```
......@@ -60,31 +62,44 @@ RoBERTa-wwm-ext-large, Chinese | `hub.Module(name='roberta_wwm_ext_chinese_L
module = hub.Module(name="bert_chinese_L-12_H-768_A-12")
```
### Step2: 准备数据集并使用ClassifyReader读取数据
### Step2: 准备数据集并使用tokenizer预处理数据
```python
dataset = hub.dataset.NLPCC_DBQA()
reader = hub.reader.ClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=128)
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.NLPCC_DBQA(
tokenizer=tokenizer, max_seq_len=128)
```
如果是使用ernie_tiny预训练模型,请使用ErnieTinyTokenizer。
```
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
```
ErnieTinyTokenizer和BertTokenizer的区别在于它将按词粒度进行切分,详情请参考[文章](https://www.jiqizhixin.com/articles/2019-11-06-9)
其中数据集的准备代码可以参考[nlpcc_dbqa.py](https://github.com/PaddlePaddle/PaddleHub/blob/release/v1.2/paddlehub/dataset/nlpcc_dbqa.py)
数据集的准备代码可以参考[nlpcc_dbqa.py](../../paddlehub/dataset/nlpcc_dbqa.py)
`hub.dataset.NLPCC_DBQA())` 会自动从网络下载数据集并解压到用户目录下`$HOME/.paddlehub/dataset`目录;
`module.get_vocab_path()` 会返回预训练模型对应的词表;
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致
`module.sp_model_path` 若module为ernie_tiny则返回对应的子词切分模型路径,否则返回None
ClassifyReader中的`data_generator`会自动按照模型对应词表对数据进行切词,以迭代器的方式返回ERNIE/BERT所需要的Tensor格式,包括`input_ids``position_ids``segment_id`与序列对应的mask `input_mask`
`module.word_dict_path` 若module为ernie_tiny则返回对应的词语切分模型路径,否则返回None
**NOTE**: Reader返回tensor的顺序是固定的,默认按照input_ids, position_ids, segment_id, input_mask这一顺序返回。
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致;
dataset将调用传入的tokenizer提供的encode接口对全量数据进行预处理,您可以通过以下方式观察数据的处理流程:
```
single_result = tokenizer.encode(text="hello", text_pair="world", max_seq_len=10) # BertTokenizer
# {'input_ids': [3, 1, 5, 39825, 5, 0, 0, 0, 0, 0], 'segment_ids': [0, 0, 0, 1, 1, 0, 0, 0, 0, 0], 'seq_len': 5, 'input_mask': [1, 1, 1, 1, 1, 0, 0, 0, 0, 0], 'position_ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
dataset_result = dataset.get_dev_records() # set dataset max_seq_len = 10
# {'input_ids': [3, 1637, 964, 1676, 613, 5, 1637, 964, 1676, 5], 'segment_ids': [0, 0, 0, 0, 0, 0, 1, 1, 1, 1], 'seq_len': 10, 'input_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'position_ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'label': 0}
```
#### 自定义数据集
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub%E9%80%82%E9%85%8D%E8%87%AA%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%88%90FineTune)
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](../../docs/tutorial/how_to_load_data.md)
### Step3:选择优化策略和运行配置
......@@ -101,7 +116,7 @@ config = hub.RunConfig(use_cuda=True, use_data_parallel=True, use_pyreader=True,
#### 优化策略
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-Strategy)
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](../../docs/reference/strategy.md)
针对ERNIE与BERT类任务,PaddleHub封装了适合这一任务的迁移学习优化策略`AdamWeightDecayStrategy`
......@@ -129,18 +144,9 @@ PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy`、`ULMFiTStr
```python
pooled_output = outputs["pooled_output"]
# feed_list的Tensor顺序不可以调整
feed_list = [
inputs["input_ids"].name,
inputs["position_ids"].name,
inputs["segment_ids"].name,
inputs["input_mask"].name,
]
cls_task = hub.TextClassifierTask(
data_reader=reader,
dataset=dataset,
feature=pooled_output,
feed_list=feed_list,
num_classes=dataset.num_labels,
config=config)
......@@ -148,12 +154,11 @@ cls_task.finetune_and_eval()
```
**NOTE:**
1. `outputs["pooled_output"]`返回了ERNIE/BERT模型对应的[CLS]向量,可以用于句子或句对的特征表达。
2. `feed_list`中的inputs参数指名了ERNIE/BERT中的输入tensor的顺序,与ClassifyReader返回的结果一致。
3. `hub.TextClassifierTask`通过输入特征,label与迁移的类别数,可以生成适用于文本分类的迁移任务`TextClassifierTask`
2. `hub.TextClassifierTask`通过输入特征,label与迁移的类别数,可以生成适用于文本分类的迁移任务`TextClassifierTask`
#### 自定义迁移任务
如果想改变迁移任务组网,详细参见[自定义迁移任务](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub:-%E8%87%AA%E5%AE%9A%E4%B9%89Task)
如果想改变迁移任务组网,详细参见[自定义迁移任务](../../docs/tutorial/how_to_define_task.md)
## 可视化
......
......@@ -36,31 +36,28 @@ args = parser.parse_args()
if __name__ == '__main__':
# Load Paddlehub ERNIE pretrained model
module = hub.Module(name="ernie")
module = hub.Module(name="ernie_tiny")
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.NLPCC_DBQA()
reader = hub.reader.ClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=args.max_seq_len)
# Use the appropriate tokenizer to preprocess the data set
# For ernie_tiny, it will do word segmentation to get subword. More details: https://www.jiqizhixin.com/articles/2019-11-06-9
if module.name == "ernie_tiny":
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
else:
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.NLPCC_DBQA(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use "pooled_output" for classification tasks on an entire sentence.
# Use "sequence_output" for token-level output.
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 fine-tune strategy, setup config and fine-tune
strategy = hub.AdamWeightDecayStrategy(
warmup_proportion=args.warmup_proportion,
......@@ -78,9 +75,8 @@ if __name__ == '__main__':
# Define a classfication fine-tune task by PaddleHub's API
cls_task = hub.TextClassifierTask(
data_reader=reader,
dataset=dataset,
feature=pooled_output,
feed_list=feed_list,
num_classes=dataset.num_labels,
config=config)
......
......@@ -39,30 +39,20 @@ args = parser.parse_args()
if __name__ == '__main__':
# loading Paddlehub ERNIE pretrained model
module = hub.Module(name="ernie")
module = hub.Module(name="ernie_tiny")
inputs, outputs, program = module.context(max_seq_len=args.max_seq_len)
# Sentence classification dataset reader
# Download dataset and get its label list and label num
# If you just want labels information, you can omit its tokenizer parameter to avoid preprocessing the train set.
dataset = hub.dataset.NLPCC_DBQA()
reader = hub.reader.ClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=args.max_seq_len)
num_classes = dataset.num_labels
label_list = dataset.get_labels()
# Construct transfer learning network
# Use "pooled_output" for classification tasks on an entire sentence.
# Use "sequence_output" for token-level output.
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,
]
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
use_data_parallel=False,
......@@ -73,9 +63,8 @@ if __name__ == '__main__':
# Define a classfication fine-tune task by PaddleHub's API
cls_task = hub.TextClassifierTask(
data_reader=reader,
dataset=dataset,
feature=pooled_output,
feed_list=feed_list,
num_classes=dataset.num_labels,
config=config)
......@@ -83,5 +72,18 @@ if __name__ == '__main__':
data = [["北京奥运博物馆的场景效果负责人是谁?", "主要承担奥运文物征集、保管、研究和爱国主义教育基地建设相关工作。"],
["北京奥运博物馆的场景效果负责人是谁", "于海勃,美国加利福尼亚大学教授 场景效果负责人 总设计师"],
["北京奥运博物馆的场景效果负责人是谁?", "洪麦恩,清华大学美术学院教授 内容及主展线负责人 总设计师"]]
print(cls_task.predict(data=data, return_result=True))
# Use the appropriate tokenizer to preprocess the data
# For ernie_tiny, it will do word segmentation to get subword. More details: https://www.jiqizhixin.com/articles/2019-11-06-9
if module.name == "ernie_tiny":
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
else:
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
encoded_data = [
tokenizer.encode(
text=text, text_pair=text_pair, max_seq_len=args.max_seq_len)
for text, text_pair in data
]
print(cls_task.predict(data=encoded_data, label_list=label_list))
......@@ -29,6 +29,8 @@
### Step1: 加载预训练模型
```python
import paddlehub as hub
module = hub.Module(name="bert_uncased_L-12_H-768_A-12")
inputs, outputs, program = module.context(trainable=True, max_seq_len=384)
```
......@@ -61,27 +63,42 @@ RoBERTa-wwm-ext-large, Chinese | `hub.Module(name='roberta_wwm_ext_chinese_L
module = hub.Module(name="bert_chinese_L-12_H-768_A-12")
```
### Step2: 准备数据集并使用ReadingComprehensionReader读取数据
### Step2: 准备数据集并使用tokenizer预处理数据
```python
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.SQUAD(
version_2_with_negative=False)
reader = hub.reader.ReadingComprehensionReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_length=384)
version_2_with_negative=False,
tokenizer=tokenizer,
max_seq_len=128)
```
如果是使用ernie_tiny预训练模型,请使用ErnieTinyTokenizer。
```
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
```
ErnieTinyTokenizer和BertTokenizer的区别在于它将按词粒度进行切分,详情请参考[文章](https://www.jiqizhixin.com/articles/2019-11-06-9)
其中数据集的准备代码可以参考 [squad.py](https://github.com/PaddlePaddle/PaddleHub/blob/release/v1.2/paddlehub/dataset/squad.py)
数据集的准备代码可以参考 [squad.py](../../paddlehub/dataset/squad.py)
`hub.dataset.SQUAD(version_2_with_negative=False)` 会自动从网络下载数据集SQuAD v1.1并解压到用户目录下`$HOME/.paddlehub/dataset`目录;如果想选择数据集SQuAD v2.0,则只需version_2_with_negative=True;
`module.get_vocab_path()` 会返回预训练模型对应的词表;
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致;
`module.sp_model_path` 若module为ernie_tiny则返回对应的子词切分模型路径,否则返回None;
`module.word_dict_path` 若module为ernie_tiny则返回对应的词语切分模型路径,否则返回None;
ReadingComprehensionReader中的`data_generator`会自动按照模型对应词表对数据进行切词,以迭代器的方式返回BERT所需要的Tensor格式,包括`input_ids``position_ids``segment_id`与序列对应的mask `input_mask`
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致
**NOTE**: Reader返回tensor的顺序是固定的,默认按照input_ids, position_ids, segment_id, input_mask这一顺序返回。
dataset将调用传入的tokenizer提供的encode接口对全量数据进行预处理,您可以通过以下方式观察数据的处理流程:
```
single_result = tokenizer.encode(text="hello", text_pair="world", max_seq_len=10) # BertTokenizer
# {'input_ids': [3, 1, 5, 39825, 5, 0, 0, 0, 0, 0], 'segment_ids': [0, 0, 0, 1, 1, 0, 0, 0, 0, 0], 'seq_len': 5, 'input_mask': [1, 1, 1, 1, 1, 0, 0, 0, 0, 0], 'position_ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
dataset_result = dataset.get_dev_records() # set dataset max_seq_len = 50
# {'input_ids': [101, 2029, 5088, 2136, 3421, 1996, 10511, 2012, 3565, 4605, 2753, 1029, 102, 3565, 4605, 2753, 2001, 2019, 2137, 2374, 2208, 2000, 5646, 1996, 3410, 1997, 1996, 2120, 2374, 2223, 1006, 5088, 1007, 2005, 1996, 2325, 2161, 1012, 1996, 2137, 2374, 3034, 1006, 10511, 1007, 3410, 7573, 14169, 3249, 102], 'segment_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'seq_len': 50, 'input_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'position_ids': [0, 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, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49], 'start_position': 46, 'end_position': 47, 'unique_id': 1000000000}
```
PaddleHub还提供了其他的阅读理解数据集,具体信息如下表:
......@@ -92,11 +109,11 @@ SQuAD v2.0 | hub.dataset.SQUAD(version_2_with_negative=True)
DRCD | hub.dataset.DRCD() |roberta_wwm_ext_chinese_L-24_H-1024_A-16|
CMRC 2018 | hub.dataset.CMRC2018() |roberta_wwm_ext_chinese_L-24_H-1024_A-16|
更多数据集信息参考[Dataset](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-Dataset)
更多数据集信息参考[Dataset](../../docs/reference/dataset.md)
#### 自定义数据集
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub%E9%80%82%E9%85%8D%E8%87%AA%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%88%90FineTune)
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](../../docs/tutorial/how_to_load_data.md)
### Step3:选择优化策略和运行配置
......@@ -121,7 +138,7 @@ config = hub.RunConfig(use_cuda=True, num_epoch=2, batch_size=12, strategy=strat
`lr_scheduler`: 有两种策略可选(1)`linear_decay`策略学习率会在最高点后以线性方式衰减; (2)`noam_decay`策略学习率会在最高点以多项式形式衰减;
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-Strategy)
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](../../docs/reference/strategy.md)
#### 运行配置
`RunConfig` 主要控制Fine-tune的训练,包含以下可控制的参数:
......@@ -140,33 +157,24 @@ PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy`、`ULMFiTStr
```python
seq_output = outputs["sequence_output"]
# feed_list的Tensor顺序不可以调整
feed_list = [
inputs["input_ids"].name,
inputs["position_ids"].name,
inputs["segment_ids"].name,
inputs["input_mask"].name,
]
reading_comprehension_task = hub.ReadingComprehensionTask(
data_reader=reader,
feature=seq_output,
feed_list=feed_list,
dataset=dataset,
feature=outputs["sequence_output"],
config=config,
sub_task="squad")
sub_task="squad",
)
reading_comprehension_task.finetune_and_eval()
```
**NOTE:**
1. `outputs["sequence_output"]`返回了ERNIE/BERT模型输入单词的对应输出,可以用于单词的特征表达。
2. `feed_list`中的inputs参数指名了BERT中的输入tensor的顺序,与ReadingComprehensionReader返回的结果一致。
3. `sub_task`指明阅读理解数据集名称,可选{squad, squad2.0, cmrc2018, drcd}, 用于适配各个数据集的模型训练过程中的评估方法。
4. `hub.ReadingComprehensionTask`通过输入特征、段落背景、问题和答案,可以生成适用于阅读理解迁移任务ReadingComprehensionTask。
2. `sub_task`指明阅读理解数据集名称,可选{squad, squad2.0, cmrc2018, drcd}, 用于适配各个数据集的模型训练过程中的评估方法。
3. `hub.ReadingComprehensionTask`通过输入特征、段落背景、问题和答案,可以生成适用于阅读理解迁移任务ReadingComprehensionTask。
#### 自定义迁移任务
如果想改变迁移任务组网,详细参见[自定义迁移任务](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub:-%E8%87%AA%E5%AE%9A%E4%B9%89Task)
如果想改变迁移任务组网,详细参见[自定义迁移任务](../../docs/tutorial/how_to_define_task.md)
## 可视化
......
......@@ -17,7 +17,6 @@
import argparse
import ast
import paddle.fluid as fluid
import paddlehub as hub
hub.common.logger.logger.setLevel("INFO")
......@@ -42,28 +41,23 @@ if __name__ == '__main__':
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Download dataset and use ReadingComprehensionReader to read dataset
# Use the appropriate tokenizer to preprocess the data set
# For ernie_tiny, it will do word segmentation to get subword. More details: https://www.jiqizhixin.com/articles/2019-11-06-9
if module.name == "ernie_tiny":
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
else:
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
# If you wanna load SQuAD 2.0 dataset, just set version_2_with_negative as True
dataset = hub.dataset.SQUAD(version_2_with_negative=False)
dataset = hub.dataset.SQUAD(
version_2_with_negative=False,
tokenizer=tokenizer,
max_seq_len=args.max_seq_len)
# dataset = hub.dataset.SQUAD(version_2_with_negative=True)
reader = hub.reader.ReadingComprehensionReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=args.max_seq_len,
doc_stride=128,
max_query_length=64)
seq_output = outputs["sequence_output"]
# Setup feed list for data feeder
feed_list = [
inputs["input_ids"].name,
inputs["position_ids"].name,
inputs["segment_ids"].name,
inputs["input_mask"].name,
]
# Select fine-tune strategy, setup config and fine-tune
strategy = hub.AdamWeightDecayStrategy(
weight_decay=args.weight_decay,
......@@ -72,7 +66,7 @@ if __name__ == '__main__':
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
eval_interval=300,
eval_interval=100,
use_data_parallel=args.use_data_parallel,
use_cuda=args.use_gpu,
num_epoch=args.num_epoch,
......@@ -82,9 +76,8 @@ if __name__ == '__main__':
# Define a reading comprehension fine-tune task by PaddleHub's API
reading_comprehension_task = hub.ReadingComprehensionTask(
data_reader=reader,
feature=seq_output,
feed_list=feed_list,
dataset=dataset,
feature=outputs["sequence_output"],
config=config,
sub_task="squad",
)
......
......@@ -30,6 +30,8 @@
### Step1: 加载预训练模型
```python
import paddlehub as hub
module = hub.Module(name="ernie_v2_eng_base")
inputs, outputs, program = module.context(trainable=True, max_seq_len=128)
```
......@@ -62,30 +64,44 @@ RoBERTa-wwm-ext-large, Chinese | `hub.Module(name='roberta_wwm_ext_chinese_L
module = hub.Module(name="bert_cased_L-12_H-768_A-12")
```
### Step2: 准备数据集并使用RegressionReader读取数据
### Step2: 准备数据集并使用tokenizer预处理数据
```python
dataset = hub.dataset.GLUE("STS-B")
reader = hub.reader.RegressionReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=args.max_seq_len)
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.GLUE(
"STS-B", tokenizer=tokenizer, max_seq_len=128)
```
如果是使用ernie_tiny预训练模型,请使用ErnieTinyTokenizer。
```
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
```
ErnieTinyTokenizer和BertTokenizer的区别在于它将按词粒度进行切分,详情请参考[文章](https://www.jiqizhixin.com/articles/2019-11-06-9)
其中数据集的准备代码可以参考[glue.py](https://github.com/PaddlePaddle/PaddleHub/blob/release/v1.2/paddlehub/dataset/glue.py)
数据集的准备代码可以参考[glue.py](../../paddlehub/dataset/glue.py)
`hub.dataset.GLUE("STS-B")` 会自动从网络下载数据集并解压到用户目录下`$HOME/.paddlehub/dataset`目录;
`module.get_vocab_path()` 会返回预训练模型对应的词表;
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致
`module.sp_model_path` 若module为ernie_tiny则返回对应的子词切分模型路径,否则返回None
RegressionReader中的`data_generator`会自动按照模型对应词表对数据进行切词,以迭代器的方式返回ERNIE/BERT所需要的Tensor格式,包括`input_ids``position_ids``segment_id`与序列对应的mask `input_mask`
`module.word_dict_path` 若module为ernie_tiny则返回对应的词语切分模型路径,否则返回None
**NOTE**: Reader返回tensor的顺序是固定的,默认按照input_ids, position_ids, segment_id, input_mask这一顺序返回。
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致;
dataset将调用传入的tokenizer提供的encode接口对全量数据进行预处理,您可以通过以下方式观察数据的处理流程:
```
single_result = tokenizer.encode(text="hello", text_pair="world", max_seq_len=10) # BertTokenizer
# {'input_ids': [3, 1, 5, 39825, 5, 0, 0, 0, 0, 0], 'segment_ids': [0, 0, 0, 1, 1, 0, 0, 0, 0, 0], 'seq_len': 5, 'input_mask': [1, 1, 1, 1, 1, 0, 0, 0, 0, 0], 'position_ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
dataset_result = dataset.get_dev_records() # set dataset max_seq_len = 10
# {'input_ids': [101, 1037, 2158, 2007, 1037, 102, 1037, 2158, 4147, 102], 'segment_ids': [0, 0, 0, 0, 0, 0, 1, 1, 1, 1], 'seq_len': 10, 'input_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'position_ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'label': 5.0}
```
#### 自定义数据集
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub%E9%80%82%E9%85%8D%E8%87%AA%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%88%90FineTune)
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](../../docs/tutorial/how_to_load_data.md)
### Step3:选择优化策略和运行配置
......@@ -102,7 +118,7 @@ config = hub.RunConfig(use_cuda=True, num_epoch=3, batch_size=32, strategy=strat
#### 优化策略
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-Strategy)
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](../../docs/reference/strategy.md)
针对ERNIE与BERT类任务,PaddleHub封装了适合这一任务的迁移学习优化策略`AdamWeightDecayStrategy`
......@@ -128,29 +144,17 @@ PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy`、`ULMFiTStr
```python
pooled_output = outputs["pooled_output"]
# feed_list的Tensor顺序不可以调整
feed_list = [
inputs["input_ids"].name,
inputs["position_ids"].name,
inputs["segment_ids"].name,
inputs["input_mask"].name,
]
reg_task = hub.RegressionTask(
data_reader=reader,
feature=pooled_output,
feed_list=feed_list,
config=config)
dataset=dataset, feature=pooled_output, config=config)
reg_task.finetune_and_eval()
```
**NOTE:**
1. `outputs["pooled_output"]`返回了ERNIE/BERT模型对应的[CLS]向量,可以用于句子或句对的特征表达。
2. `feed_list`中的inputs参数指名了BERT中的输入tensor的顺序,与RegressionReader返回的结果一致。
#### 自定义迁移任务
如果想改变迁移任务组网,详细参见[自定义迁移任务](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub:-%E8%87%AA%E5%AE%9A%E4%B9%89Task)
如果想改变迁移任务组网,详细参见[自定义迁移任务](../../docs/tutorial/how_to_define_task.md)
## 可视化
......
......@@ -20,12 +20,6 @@ from __future__ import print_function
import argparse
import ast
import numpy as np
import os
import time
import paddle
import paddle.fluid as fluid
import paddlehub as hub
# yapf: disable
......@@ -43,27 +37,11 @@ if __name__ == '__main__':
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Download dataset and use RegressionReader to read dataset
dataset = hub.dataset.GLUE("STS-B")
reader = hub.reader.RegressionReader(
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.
# Use "sequence_output" for token-level output.
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,
]
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
use_data_parallel=False,
......@@ -74,13 +52,22 @@ if __name__ == '__main__':
# Define a regression fine-tune task by PaddleHub's API
reg_task = hub.RegressionTask(
data_reader=reader,
feature=pooled_output,
feed_list=feed_list,
config=config,
)
# Data to be prdicted
data = [[d.text_a, d.text_b] for d in dataset.get_predict_examples()[:10]]
print(reg_task.predict(data=data, return_result=True))
# STS-B has provided the predict data, and the dataset has process it. If you want to process customized data,
# see the predict.py in text_classification demo
# Use the appropriate tokenizer to preprocess the data
# For ernie_tiny, it will do word segmentation to get subword. More details: https://www.jiqizhixin.com/articles/2019-11-06-9
if module.name == "ernie_tiny":
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
else:
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.GLUE(
"STS-B", tokenizer=tokenizer, max_seq_len=args.max_seq_len)
encoded_data = dataset.get_predict_records()[:10]
print(reg_task.predict(data=encoded_data))
......@@ -17,7 +17,6 @@
import argparse
import ast
import paddle.fluid as fluid
import paddlehub as hub
# yapf: disable
......@@ -41,27 +40,24 @@ if __name__ == '__main__':
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Download dataset and use RegressionReader to read dataset
dataset = hub.dataset.GLUE("STS-B")
reader = hub.reader.RegressionReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=args.max_seq_len)
# Use the appropriate tokenizer to preprocess the data set
# For ernie_tiny, it will do word segmentation to get subword. More details: https://www.jiqizhixin.com/articles/2019-11-06-9
if module.name == "ernie_tiny":
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
else:
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.GLUE(
"STS-B", tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use "pooled_output" for classification tasks on an entire sentence.
# Use "sequence_output" for token-level output.
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 fine-tune strategy, setup config and fine-tune
strategy = hub.AdamWeightDecayStrategy(
warmup_proportion=args.warmup_proportion,
......@@ -70,7 +66,6 @@ if __name__ == '__main__':
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
eval_interval=300,
use_data_parallel=args.use_data_parallel,
use_cuda=args.use_gpu,
num_epoch=args.num_epoch,
......@@ -80,10 +75,7 @@ if __name__ == '__main__':
# Define a regression fine-tune task by PaddleHub's API
reg_task = hub.RegressionTask(
data_reader=reader,
feature=pooled_output,
feed_list=feed_list,
config=config)
dataset=dataset, feature=pooled_output, config=config)
# Fine-tune and evaluate by PaddleHub's API
# will finish training, evaluation, testing, save model automatically
......
......@@ -41,6 +41,7 @@ $ sh run_finetune.sh
--batch_size: 批处理大小,请结合显存情况进行调整,若出现显存不足,请适当调低这一参数;
--checkpoint_dir: 模型保存路径,PaddleHub会自动保存验证集上表现最好的模型;
--num_epoch: Fine-tune迭代的轮数;
--max_seq_len: 模型使用的最大序列长度, 若出现显存不足,请适当调低这一参数;
--use_gpu: 是否使用GPU进行训练,如果机器支持GPU且安装了GPU版本的PaddlePaddle,我们建议您打开这个开关;
```
......@@ -50,9 +51,11 @@ $ sh run_finetune.sh
```python
module = hub.Module(name="senta_bilstm")
inputs, outputs, program = module.context(trainable=True)
inputs, outputs, program = module.context(trainable=True, max_seq_len=96)
```
其中最大序列长度`max_seq_len`是可以调整的参数,根据任务文本长度不同可以调整该值。
PaddleHub提供Senta一列模型可供选择, 模型对应的加载示例如下:
模型名 | PaddleHub Module
......@@ -71,25 +74,36 @@ senta_cnn | `hub.Module(name='senta_cnn')`
module = hub.Module(name="senta_gru")
```
### Step2: 准备数据集并使用LACClassifyReader读取数据
### Step2: 选择Tokenizer读取数据
```python
dataset = hub.dataset.ChnSentiCorp()
reader = hub.reader.LACClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path())
tokenizer = hub.CustomTokenizer(
vocab_file=module.get_vocab_path(),
tokenize_chinese_chars=True,
)
```
`hub.dataset.ChnSentiCorp()` 会自动从网络下载数据集并解压到用户目录下`$HOME/.paddlehub/dataset`目录;
`module.get_vocab_path()` 会返回预训练模型对应的词表;
`tokenize_chinese_chars` 是否切分中文文本
**NOTE:**
1. 如果使用Transformer类模型(如ERNIE、BERT、RoBerta等),则应该选择`hub.BertTokenizer`.
2. 如果使用非Transformer类模型(如senta、word2vec_skipgram、tencent_ailab_chinese_embedding_small等),则应该选择`hub.CustomTokenizer`
### Step3: 准备数据集
```python
dataset = hub.dataset.LCQMC(tokenizer=tokenizer, max_seq_len=128)
```
LACClassifyReader中的`data_generator`会自动按照模型对应词表对数据进行切词,以迭代器的方式返回Senta所需要的word id
`hub.dataset.LCQMC()` 会自动从网络下载数据集并解压到用户目录下`$HOME/.paddlehub/dataset`目录
更多数据集信息参考[Dataset](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-Dataset)
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致;
更多数据集信息参考[Dataset](../../docs/reference/dataset.md)
#### 自定义数据集
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub%E9%80%82%E9%85%8D%E8%87%AA%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%88%90FineTune)
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](../../docs/tutorial/how_to_load_data.md)
### Step3:选择优化策略和运行配置
......@@ -106,7 +120,7 @@ config = hub.RunConfig(use_cuda=True, num_epoch=3, batch_size=32, strategy=strat
#### 优化策略
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-Strategy)
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](../../docs/reference/strategy.md)
其中`AdamWeightDecayStrategy`
......@@ -131,18 +145,20 @@ sent_feature = outputs["sentence_feature"]
feed_list = [inputs["words"].name]
cls_task = hub.TextClassifierTask(
data_reader=reader,
dataset=dataset,
feature=sent_feature,
feed_list=feed_list,
num_classes=dataset.num_labels,
num_classes=2,
config=config)
cls_task.finetune_and_eval()
```
**NOTE:**
1. `outputs["sentence_feature"]`返回了senta模型对应的句子特征,可以用于句子的特征表达;
2. `feed_list`中的inputs参数指名了senta中的输入tensor的顺序,与LACClassifyReader返回的结果一致;
3. `hub.TextClassifierTask`通过输入特征,label与迁移的类别数,可以生成适用于文本分类的迁移任务`TextClassifierTask`
2. `hub.TextClassifierTask`通过输入特征,label与迁移的类别数,可以生成适用于文本分类的迁移任务`TextClassifierTask`
#### 自定义迁移任务
如果想改变迁移任务组网,详细参见[自定义迁移任务](../../docs/tutorial/how_to_define_task.md)
## 可视化
......@@ -165,24 +181,7 @@ python predict.py --checkpoint_dir $CKPT_DIR
参数配置正确后,请执行脚本`sh run_predict.sh`,即可看到以下文本分类预测结果, 以及最终准确率。
如需了解更多预测步骤,请参考`predict.py`
我们在AI Studio上提供了IPython NoteBook形式的demo,您可以直接在平台上在线体验,链接如下:
|预训练模型|任务类型|数据集|AIStudio链接|备注|
|-|-|-|-|-|
|ResNet|图像分类|猫狗数据集DogCat|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/147010)||
|ERNIE|文本分类|中文情感分类数据集ChnSentiCorp|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/147006)||
|ERNIE|文本分类|中文新闻分类数据集THUNEWS|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/221999)|本教程讲述了如何将自定义数据集加载,并利用Fine-tune API完成文本分类迁移学习。|
|ERNIE|序列标注|中文序列标注数据集MSRA_NER|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/147009)||
|ERNIE|序列标注|中文快递单数据集Express|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/184200)|本教程讲述了如何将自定义数据集加载,并利用Fine-tune API完成序列标注迁移学习。|
|ERNIE Tiny|文本分类|中文情感分类数据集ChnSentiCorp|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/221971)||
|Senta|文本分类|中文情感分类数据集ChnSentiCorp|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/216846)|本教程讲述了任何利用Senta和Fine-tune API完成情感分类迁移学习。|
|Senta|情感分析预测|N/A|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/215814)||
|LAC|词法分析|N/A|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/215711)||
|Ultra-Light-Fast-Generic-Face-Detector-1MB|人脸检测|N/A|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/215962)||
我们在AI Studio上提供了IPython NoteBook形式的demo,点击[PaddleHub教程合集](https://aistudio.baidu.com/aistudio/projectdetail/231146),可使用AI Studio平台提供的GPU算力进行快速尝试。
## 超参优化AutoDL Finetuner
......
#coding:utf-8
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
# coding:utf-8
import argparse
import ast
import numpy as np
import os
import time
import paddle
import paddle.fluid as fluid
import paddlehub as hub
# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
parser.add_argument("--use_gpu", type=ast.literal_eval, default=True, help="Whether use GPU for fine-tuning, input should be True or False")
parser.add_argument("--batch_size", type=int, default=1, help="Total examples' number in batch when the program predicts.")
parser.add_argument("--num_epoch", type=int, default=3, help="Number of epoches for fine-tuning.")
parser.add_argument("--use_gpu", type=ast.literal_eval, default=True, help="Whether use GPU for fine-tuning, input should be True or False")
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
parser.add_argument("--max_seq_len", type=int, default=128, help="Number of words of the longest seqence.")
parser.add_argument("--batch_size", type=int, default=32, help="Total examples' number in batch for training.")
args = parser.parse_args()
# yapf: enable.
jieba_paddle = hub.Module(name='jieba_paddle')
def cut(text):
res = jieba_paddle.cut(text, use_paddle=False)
return res
if __name__ == '__main__':
# loading Paddlehub senta pretrained model
module = hub.Module(name="senta_bilstm")
inputs, outputs, program = module.context(trainable=True)
# Load Paddlehub senta pretrained model
module = hub.Module(name="senta_bow", version='1.2.0')
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Download dataset and use LACClassifyReader to read dataset
dataset = hub.dataset.ChnSentiCorp()
reader = hub.reader.LACClassifyReader(
dataset=dataset, vocab_path=module.get_vocab_path())
# Tokenizer tokenizes the text data and encodes the data as model needed.
# If you use transformer modules (ernie, bert, roberta and so on), tokenizer should be hub.BertTokenizer.
# Otherwise, tokenizer should be hub.CustomTokenizer.
# If you choose CustomTokenizer, you can also change the chinese word segmentation tool, for example jieba.
tokenizer = hub.CustomTokenizer(
vocab_file=module.get_vocab_path(),
tokenize_chinese_chars=True,
cut_function=cut, # jieba.cut as cut function
)
sent_feature = outputs["sentence_feature"]
dataset = hub.dataset.ChnSentiCorp(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
num_classes = dataset.num_labels
label_list = dataset.get_labels()
# Setup feed list for data feeder
# Must feed all the tensor of senta's module need
feed_list = [inputs["words"].name]
# Construct transfer learning network
# Use sentence-level output.
sent_feature = outputs["sentence_feature"]
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
use_data_parallel=False,
use_cuda=args.use_gpu,
num_epoch=args.num_epoch,
batch_size=args.batch_size,
checkpoint_dir=args.checkpoint_dir,
strategy=hub.AdamWeightDecayStrategy())
# Define a classfication fine-tune task by PaddleHub's API
cls_task = hub.TextClassifierTask(
data_reader=reader,
feature=sent_feature,
feed_list=feed_list,
num_classes=dataset.num_labels,
config=config)
feature=sent_feature, num_classes=num_classes, config=config)
# Data to be predicted
data = ["这家餐厅很好吃", "这部电影真的很差劲"]
print(cls_task.predict(data=data, return_result=True))
encoded_data = [
tokenizer.encode(text=text, max_seq_len=args.max_seq_len)
for text in data
]
print(cls_task.predict(data=encoded_data, label_list=label_list))
export FLAGS_eager_delete_tensor_gb=0.0
export CUDA_VISIBLE_DEVICES=0
CKPT_DIR="./ckpt_chnsenticorp"
python -u senta_finetune.py \
--batch_size=24 \
--max_seq_len=96 \
--use_gpu=True \
--checkpoint_dir=${CKPT_DIR} \
--num_epoch=3
export FLAGS_eager_delete_tensor_gb=0.0
export CUDA_VISIBLE_DEVICES=0
CKPT_DIR="./ckpt_chnsenticorp "
python -u predict.py --checkpoint_dir $CKPT_DIR --use_gpu True
python -u predict.py --checkpoint_dir $CKPT_DIR --use_gpu True --max_seq_len=96
#coding:utf-8
# coding:utf-8
import argparse
import ast
......@@ -10,31 +10,45 @@ parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--num_epoch", type=int, default=3, help="Number of epoches for fine-tuning.")
parser.add_argument("--use_gpu", type=ast.literal_eval, default=True, help="Whether use GPU for fine-tuning, input should be True or False")
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
parser.add_argument("--max_seq_len", type=int, default=128, help="Number of words of the longest seqence.")
parser.add_argument("--batch_size", type=int, default=32, help="Total examples' number in batch for training.")
args = parser.parse_args()
# yapf: enable.
if __name__ == '__main__':
# Load Paddlehub senta pretrained model
module = hub.Module(name="senta_bilstm")
inputs, outputs, program = module.context(trainable=True)
jieba_paddle = hub.Module(name='jieba_paddle')
# Download dataset and use LACClassifyReader to read dataset
dataset = hub.dataset.ChnSentiCorp()
reader = hub.reader.LACClassifyReader(
dataset=dataset, vocab_path=module.get_vocab_path())
sent_feature = outputs["sentence_feature"]
def cut(text):
res = jieba_paddle.cut(text, use_paddle=False)
return res
# Setup feed list for data feeder
# Must feed all the tensor of senta's module need
feed_list = [inputs["words"].name]
if __name__ == '__main__':
# Load Paddlehub senta pretrained model
module = hub.Module(name="senta_bow", version='1.2.0')
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Tokenizer tokenizes the text data and encodes the data as model needed.
# If you use transformer modules (ernie, bert, roberta and so on), tokenizer should be hub.BertTokenizer.
# Otherwise, tokenizer should be hub.CustomTokenizer.
# If you choose CustomTokenizer, you can also change the chinese word segmentation tool, for example jieba.
tokenizer = hub.CustomTokenizer(
vocab_file=module.get_vocab_path(),
tokenize_chinese_chars=True,
cut_function=cut, # jieba.cut as cut function
)
dataset = hub.dataset.ChnSentiCorp(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use sentence-level output.
sent_feature = outputs["sentence_feature"]
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
use_cuda=args.use_gpu,
use_pyreader=False,
use_data_parallel=False,
num_epoch=args.num_epoch,
batch_size=args.batch_size,
checkpoint_dir=args.checkpoint_dir,
......@@ -42,12 +56,8 @@ if __name__ == '__main__':
# Define a classfication fine-tune task by PaddleHub's API
cls_task = hub.TextClassifierTask(
data_reader=reader,
dataset=dataset,
feature=sent_feature,
feed_list=feed_list,
num_classes=dataset.num_labels,
config=config)
# Fine-tune and evaluate by PaddleHub's API
# will finish training, evaluation, testing, save model automatically
cls_task.finetune_and_eval()
......@@ -28,6 +28,8 @@
### Step1: 加载预训练模型
```python
import paddlehub as hub
module = hub.Module(name="ernie")
inputs, outputs, program = module.context(trainable=True, max_seq_len=128)
```
......@@ -60,18 +62,16 @@ RoBERTa-wwm-ext-large, Chinese | `hub.Module(name='roberta_wwm_ext_chinese_L
module = hub.Module(name="bert_chinese_L-12_H-768_A-12")
```
### Step2: 准备数据集并使用SequenceLabelReader读取数据
### Step2: 准备数据集并使用tokenizer预处理数据
```python
dataset = hub.dataset.MSRA_NER()
reader = hub.reader.SequenceLabelReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=128,
sp_model_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.MSRA_NER(
tokenizer=tokenizer, max_seq_len=128)
```
**NOTE**:
* 即使是使用ernie_tiny预训练模型,也请使用BertTokenizer,而不要使用ErnieTinyTokenizer。因为序列标注任务需要为每一个字进行标注。
其中数据集的准备代码可以参考[msra_ner.py](https://github.com/PaddlePaddle/PaddleHub/blob/release/v1.2/paddlehub/dataset/msra_ner.py)
数据集的准备代码可以参考[msra_ner.py](../../paddlehub/dataset/msra_ner.py)
`hub.dataset.MSRA_NER()` 会自动从网络下载数据集并解压到用户目录下`$HOME/.paddlehub/dataset`目录;
......@@ -79,17 +79,17 @@ reader = hub.reader.SequenceLabelReader(
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致;
`module.sp_model_path``module.word_dict_path` 用于 ERNIE Tiny 中文sub-word中文切词技术;
SequenceLabelReader中的`data_generator`会自动按照模型对应词表对数据进行切词,以迭代器的方式返回ERNIE/BERT所需要的Tensor格式,包括`input_ids``position_ids``segment_id`与序列对应的mask `input_mask`
**NOTE**:
* Reader返回tensor的顺序是固定的,默认按照input_ids, position_ids, segment_id, input_mask这一顺序返回。
* 如果选择的预训练模型不是ERNIE Tiny,则无需设定sp_model_path和word_dict_path参数。
dataset将调用传入的tokenizer提供的encode接口对全量数据进行预处理,您可以通过以下方式观察数据的处理流程:
```
single_result = tokenizer.encode(text="hello", text_pair="world", max_seq_len=10) # BertTokenizer
# {'input_ids': [3, 1, 5, 39825, 5, 0, 0, 0, 0, 0], 'segment_ids': [0, 0, 0, 1, 1, 0, 0, 0, 0, 0], 'seq_len': 5, 'input_mask': [1, 1, 1, 1, 1, 0, 0, 0, 0, 0], 'position_ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
dataset_result = dataset.get_dev_records() # set dataset max_seq_len = 10
# {'input_ids': [101, 100, 100, 1962, 100, 100, 1744, 100, 1749, 102], 'segment_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'seq_len': 10, 'input_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'position_ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'label': [6, 6, 6, 6, 6, 6, 6, 6, 6, 6]}
```
#### 自定义数据集
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub%E9%80%82%E9%85%8D%E8%87%AA%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%88%90FineTune)
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](../../docs/tutorial/how_to_load_data.md)
### Step3:选择优化策略和运行配置
......@@ -105,7 +105,7 @@ config = hub.RunConfig(use_cuda=True, num_epoch=3, batch_size=32, strategy=strat
```
#### 优化策略
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-Strategy)
PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy``ULMFiTStrategy``DefaultFinetuneStrategy`等,详细信息参见[策略](../../docs/reference/strategy.md)
针对ERNIE与BERT类任务,PaddleHub封装了适合这一任务的迁移学习优化策略`AdamWeightDecayStrategy`
......@@ -132,17 +132,10 @@ PaddleHub提供了许多优化策略,如`AdamWeightDecayStrategy`、`ULMFiTStr
sequence_output = outputs["sequence_output"]
# feed_list的Tensor顺序不可以调整
feed_list = [
inputs["input_ids"].name, inputs["position_ids"].name,
inputs["segment_ids"].name, inputs["input_mask"].name
]
seq_label_task = hub.SequenceLabelTask(
data_reader=reader,
dataset=dataset,
feature=sequence_output,
feed_list=feed_list,
max_seq_len=args.max_seq_len,
max_seq_len=128,
num_classes=dataset.num_labels,
config=config,
add_crf=False)
......@@ -152,13 +145,12 @@ seq_label_task.finetune_and_eval()
**NOTE:**
1. `outputs["sequence_output"]`返回了ERNIE/BERT模型输入单词的对应输出,可以用于单词的特征表达;
2. `feed_list`中的inputs参数指名了ERNIE/BERT中的输入tensor的顺序,与SequenceLabelReader返回的结果一致;
3. `hub.SequenceLabelTask`通过输入特征,迁移的类别数,可以生成适用于序列标注的迁移任务`SequenceLabelTask`
4. `hub.SequenceLabelTask`通过add_crf, 选择是否加入crf作为decoder。如果add_crf=True, 则在预训练模型计算图加入fc+crf层,否则只在在预训练模型计算图加入fc层;
2. `hub.SequenceLabelTask`通过输入特征,迁移的类别数,可以生成适用于序列标注的迁移任务`SequenceLabelTask`
3. `hub.SequenceLabelTask`通过add_crf, 选择是否加入crf作为decoder。如果add_crf=True, 则在预训练模型计算图加入fc+crf层,否则只在在预训练模型计算图加入fc层;
#### 自定义迁移任务
如果想改变迁移任务组网,详细参见[自定义迁移任务](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub:-%E8%87%AA%E5%AE%9A%E4%B9%89Task)
如果想改变迁移任务组网,详细参见[自定义迁移任务](../../docs/tutorial/how_to_define_task.md)
## 可视化
......
......@@ -42,30 +42,16 @@ if __name__ == '__main__':
module = hub.Module(name="ernie_tiny")
inputs, outputs, program = module.context(max_seq_len=args.max_seq_len)
# Sentence labeling dataset reader
# Download dataset and get its label list and label num
# If you just want labels information, you can omit its tokenizer parameter to avoid preprocessing the train set.
dataset = hub.dataset.MSRA_NER()
reader = hub.reader.SequenceLabelReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=args.max_seq_len,
sp_model_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
inv_label_map = {val: key for key, val in reader.label_map.items()}
num_classes = dataset.num_labels
label_list = dataset.get_labels()
# Construct transfer learning network
# Use "sequence_output" for token-level output.
sequence_output = outputs["sequence_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,
]
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
use_data_parallel=False,
......@@ -77,33 +63,31 @@ if __name__ == '__main__':
# Define a sequence labeling fine-tune task by PaddleHub's API
# if add crf, the network use crf as decoder
seq_label_task = hub.SequenceLabelTask(
data_reader=reader,
feature=sequence_output,
feed_list=feed_list,
max_seq_len=args.max_seq_len,
num_classes=dataset.num_labels,
num_classes=num_classes,
config=config,
add_crf=False)
# Data to be predicted
# If using python 2, prefix "u" is necessary
data = [
[u"我们变而以书会友,以书结缘,把欧美、港台流行的食品类图谱、画册、工具书汇集一堂。"],
[u"为了跟踪国际最新食品工艺、流行趋势,大量搜集海外专业书刊资料是提高技艺的捷径。"],
[u"其中线装古籍逾千册;民国出版物几百种;珍本四册、稀见本四百余册,出版时间跨越三百余年。"],
[u"有的古木交柯,春机荣欣,从诗人句中得之,而入画中,观之令人心驰。"],
[u"不过重在晋趣,略增明人气息,妙在集古有道、不露痕迹罢了。"],
text_a = [
"我们变而以书会友,以书结缘,把欧美、港台流行的食品类图谱、画册、工具书汇集一堂。",
"为了跟踪国际最新食品工艺、流行趋势,大量搜集海外专业书刊资料是提高技艺的捷径。",
"其中线装古籍逾千册;民国出版物几百种;珍本四册、稀见本四百余册,出版时间跨越三百余年。",
"有的古木交柯,春机荣欣,从诗人句中得之,而入画中,观之令人心驰。",
"不过重在晋趣,略增明人气息,妙在集古有道、不露痕迹罢了。",
]
# Add 0x02 between characters to match the format of training data,
# otherwise the length of prediction results will not match the input string
# if the input string contains non-Chinese characters.
tmp_data = []
for example in data:
formatted = []
for sentence in example:
formatted.append('\x02'.join(list(sentence)))
tmp_data.append(formatted)
data = tmp_data
formatted_text_a = list(map("\002".join, text_a))
print(seq_label_task.predict(data=data, return_result=True))
# Use the appropriate tokenizer to preprocess the data
# For ernie_tiny, it use BertTokenizer too.
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
encoded_data = [
tokenizer.encode(text=text, max_seq_len=args.max_seq_len)
for text in formatted_text_a
]
print(seq_label_task.predict(data=encoded_data, label_list=label_list))
......@@ -40,26 +40,16 @@ if __name__ == '__main__':
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Download dataset and use SequenceLabelReader to read dataset
dataset = hub.dataset.MSRA_NER()
reader = hub.reader.SequenceLabelReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=args.max_seq_len,
sp_model_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
# Use the appropriate tokenizer to preprocess the data set
# For ernie_tiny, it use BertTokenizer too.
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.MSRA_NER(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use "sequence_output" for token-level output.
sequence_output = outputs["sequence_output"]
# Setup feed list for data feeder
# Must feed all the tensor of module need
feed_list = [
inputs["input_ids"].name, inputs["position_ids"].name,
inputs["segment_ids"].name, inputs["input_mask"].name
]
# Select a fine-tune strategy
strategy = hub.AdamWeightDecayStrategy(
warmup_proportion=args.warmup_proportion,
......@@ -78,9 +68,8 @@ if __name__ == '__main__':
# Define a sequence labeling fine-tune task by PaddleHub's API
# If add crf, the network use crf as decoder
seq_label_task = hub.SequenceLabelTask(
data_reader=reader,
dataset=dataset,
feature=sequence_output,
feed_list=feed_list,
max_seq_len=args.max_seq_len,
num_classes=dataset.num_labels,
config=config,
......
......@@ -21,9 +21,9 @@ parser.add_argument("--max_seq_len", type=int, default=512,
# yapf: enable.
class TransformerSequenceLabelLayer(fluid.dygraph.Layer):
class TransformerSeqLabeling(fluid.dygraph.Layer):
def __init__(self, num_classes, transformer):
super(TransformerSequenceLabelLayer, self).__init__()
super(TransformerSeqLabeling, self).__init__()
self.num_classes = num_classes
self.transformer = transformer
self.fc = Linear(input_dim=768, output_dim=num_classes)
......@@ -39,11 +39,15 @@ class TransformerSequenceLabelLayer(fluid.dygraph.Layer):
def finetune(args):
ernie = hub.Module(name="ernie", max_seq_len=args.max_seq_len)
module = hub.Module(name="ernie", max_seq_len=args.max_seq_len)
# Use the appropriate tokenizer to preprocess the data set
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.MSRA_NER(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
with fluid.dygraph.guard():
dataset = hub.dataset.MSRA_NER()
ts = TransformerSequenceLabelLayer(
num_classes=dataset.num_labels, transformer=ernie)
ts = TransformerSeqLabeling(
num_classes=dataset.num_labels, transformer=module)
adam = AdamOptimizer(learning_rate=1e-5, parameter_list=ts.parameters())
state_dict_path = os.path.join(args.checkpoint_dir,
'dygraph_state_dict')
......@@ -51,34 +55,32 @@ def finetune(args):
state_dict, _ = fluid.load_dygraph(state_dict_path)
ts.load_dict(state_dict)
reader = hub.reader.SequenceLabelReader(
dataset=dataset,
vocab_path=ernie.get_vocab_path(),
max_seq_len=args.max_seq_len,
sp_model_path=ernie.get_spm_path(),
word_dict_path=ernie.get_word_dict_path())
train_reader = reader.data_generator(
batch_size=args.batch_size, phase='train')
loss_sum = total_infer = total_label = total_correct = cnt = 0
# 执行epoch_num次训练
for epoch in range(args.num_epoch):
# 读取训练数据进行训练
for batch_id, data in enumerate(train_reader()):
input_ids = np.array(data[0][0]).astype(np.int64)
position_ids = np.array(data[0][1]).astype(np.int64)
segment_ids = np.array(data[0][2]).astype(np.int64)
input_mask = np.array(data[0][3]).astype(np.float32)
labels = np.array(data[0][4]).astype(np.int64).reshape(-1, 1)
seq_len = np.squeeze(
np.array(data[0][5]).astype(np.int64), axis=1)
for batch_id, data in enumerate(
dataset.batch_records_generator(
phase="train",
batch_size=args.batch_size,
shuffle=True,
pad_to_batch_max_seq_len=False)):
batch_size = len(data["input_ids"])
input_ids = np.array(data["input_ids"]).astype(
np.int64).reshape([batch_size, -1, 1])
position_ids = np.array(data["position_ids"]).astype(
np.int64).reshape([batch_size, -1, 1])
segment_ids = np.array(data["segment_ids"]).astype(
np.int64).reshape([batch_size, -1, 1])
input_mask = np.array(data["input_mask"]).astype(
np.float32).reshape([batch_size, -1, 1])
labels = np.array(data["label"]).astype(np.int64).reshape(-1, 1)
seq_len = np.array(data["seq_len"]).astype(np.int64).reshape(
-1, 1)
pred, ret_infers = ts(input_ids, position_ids, segment_ids,
input_mask)
loss = fluid.layers.cross_entropy(pred, to_variable(labels))
avg_loss = fluid.layers.mean(loss)
avg_loss.backward()
# 参数更新
adam.minimize(avg_loss)
loss_sum += avg_loss.numpy() * labels.shape[0]
......
......@@ -75,7 +75,7 @@ Loading lac successful.
```
这样我们就完成了对词法分析的预测服务化部署和测试。
完整的测试代码见[lac_serving_demo.py](lac_serving_demo.py)
完整的测试代码见[lac_serving_demo.py](./lac_serving_demo.py)
### 使用自定义词典
`LAC`模型在预测时还可以使用自定义词典干预默认分词结果,这种情况只需要将自定义词典以文件的形式附加到request请求即可,数据格式如下:
......@@ -91,7 +91,7 @@ Loading lac successful.
>>> r = requests.post(url=url, files=file, data=text)
```
完整的测试代码见[lac_with_dict_serving_demo.py](lac_with_dict_serving_demo.py)
完整的测试代码见[lac_with_dict_serving_demo.py](./lac_with_dict_serving_demo.py)
### 客户端请求新版模型的方式
对某些新版模型,客户端请求方式有所变化,更接近本地预测的请求方式,以降低学习成本。
......@@ -146,4 +146,4 @@ if __name__ == "__main__":
]
}
```
此Demo的具体信息和代码请参见[LAC Serving_2.1.0](lac_2.1.0_serving_demo.py)
此Demo的具体信息和代码请参见[LAC Serving_2.1.0](./lac_2.1.0_serving_demo.py)
......@@ -24,7 +24,7 @@ text_classification
## 如何开始Fine-tune
以下例子已不使用预置网络完成文本分类任务,说明PaddleHub如何完成迁移学习。使用预置网络完成文本分类任务,步骤类似。
以下以不使用预置网络的文本分类任务为例,说明PaddleHub如何完成迁移学习。使用预置网络完成文本分类任务,步骤类似。
在完成安装PaddlePaddle与PaddleHub后,通过执行脚本`sh run_cls.sh`即可开始使用ERNIE对ChnSentiCorp数据集进行Fine-tune。
......@@ -48,6 +48,8 @@ text_classification
### Step1: 加载预训练模型
```python
import paddlehub as hub
module = hub.Module(name="ernie")
inputs, outputs, program = module.context(trainable=True, max_seq_len=128)
```
......@@ -80,31 +82,40 @@ RoBERTa-wwm-ext-large, Chinese | `hub.Module(name='roberta_wwm_ext_chinese_L
module = hub.Module(name="bert_chinese_L-12_H-768_A-12")
```
### Step2: 准备数据集并使用ClassifyReader读取数据
### Step2: 准备数据集并使用tokenizer预处理数据
```python
dataset = hub.dataset.ChnSentiCorp()
reader = hub.reader.ClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=128,
sp_model_path=module.get_spm_path(),
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.ChnSentiCorp(
tokenizer=tokenizer, max_seq_len=128)
```
如果是使用ernie_tiny预训练模型,请使用ErnieTinyTokenizer。
```
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
metrics_choices = ["acc"]
```
ErnieTinyTokenizer和BertTokenizer的区别在于它将按词粒度进行切分,详情请参考[文章](https://www.jiqizhixin.com/articles/2019-11-06-9)
数据集的准备代码可以参考 [chnsenticorp.py](../../paddlehub/dataset/chnsenticorp.py)
`hub.dataset.ChnSentiCorp()` 会自动从网络下载数据集并解压到用户目录下`$HOME/.paddlehub/dataset`目录;
`module.get_vocab_path()` 会返回预训练模型对应的词表;
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致;
`module.sp_model_path` 若module为ernie_tiny则返回对应的子词切分模型,否则返回None;
`module.sp_model_path` 若module为ernie_tiny则返回对应的子词切分模型路径,否则返回None;
`module.word_dict_path` 若module为ernie_tiny则返回对应的词语切分模型,否则返回None;
`module.word_dict_path` 若module为ernie_tiny则返回对应的词语切分模型路径,否则返回None;
ClassifyReader中的`data_generator`会自动按照模型对应词表对数据进行切词,以迭代器的方式返回ERNIE/BERT所需要的Tensor格式,包括`input_ids``position_ids``segment_id`与序列对应的mask `input_mask`
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致
**NOTE**: Reader返回tensor的顺序是固定的,默认按照input_ids, position_ids, segment_id, input_mask这一顺序返回。
dataset将调用传入的tokenizer提供的encode接口对全量数据进行预处理,您可以通过以下方式观察数据的处理流程:
```
single_result = tokenizer.encode(text="hello", text_pair="world", max_seq_len=10) # BertTokenizer
# {'input_ids': [3, 1, 5, 39825, 5, 0, 0, 0, 0, 0], 'segment_ids': [0, 0, 0, 1, 1, 0, 0, 0, 0, 0], 'seq_len': 5, 'input_mask': [1, 1, 1, 1, 1, 0, 0, 0, 0, 0], 'position_ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
dataset_result = dataset.get_dev_records() # set dataset max_seq_len = 10
# {'input_ids': [101, 100, 1969, 100, 100, 100, 100, 1796, 100, 102], 'segment_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'seq_len': 10, 'input_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'position_ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'label': 1}
```
PaddleHub还提供了其他的文本分类数据集,分两类(单句分类和句对分类),具体信息如下表
......@@ -128,11 +139,11 @@ ChineseGLUE-INEWS | hub.dataset.INews() | 句对 | roberta_wwm_ext_
ChineseGLUE-TNEWS | hub.dataset.TNews() | 句对 | roberta_wwm_ext_chinese_L-24_H-1024_A-16 | accuracy |
ChinesGLUE-BQ | hub.dataset.BQ() | 句对 | roberta_wwm_ext_chinese_L-24_H-1024_A-16 | accuracy |
更多数据集信息参考[Dataset](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-Dataset)
更多数据集信息参考[Dataset](../../docs/reference/dataset.md)
#### 自定义数据集
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub%E9%80%82%E9%85%8D%E8%87%AA%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%88%90FineTune)
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](../../docs/tutorial/how_to_load_data.md)
### Step3:选择优化策略和运行配置
......@@ -168,39 +179,29 @@ config = hub.RunConfig(use_cuda=True, num_epoch=3, batch_size=32, strategy=strat
```python
pooled_output = outputs["pooled_output"]
# feed_list的Tensor顺序不可以调整
feed_list = [
inputs["input_ids"].name,
inputs["position_ids"].name,
inputs["segment_ids"].name,
inputs["input_mask"].name,
]
cls_task = hub.TextClassifierTask(
data_reader=reader,
dataset=dataset,
feature=pooled_output,
feed_list=feed_list,
num_classes=dataset.num_labels,
config=config)
config=config,
metrics_choices=["acc"])
cls_task.finetune_and_eval()
```
**NOTE:**
1. `outputs["pooled_output"]`返回了Transformer类预训练模型对应的[CLS]向量,可以用于句子或句对的特征表达。
2. `feed_list`中的inputs参数指名了Transformer类预训练模型中的输入tensor的顺序,与ClassifyReader返回的结果一致。
3. `hub.TextClassifierTask`通过输入特征,label与迁移的类别数,可以生成适用于文本分类的迁移任务`TextClassifierTask`
4. 使用预置网络与否,传入`hub.TextClassifierTask`的特征不相同。`hub.TextClassifierTask`通过参数`feature``token_feature`区分。
2. `hub.TextClassifierTask`通过输入特征,label与迁移的类别数,可以生成适用于文本分类的迁移任务`TextClassifierTask`
3. 使用预置网络与否,传入`hub.TextClassifierTask`的特征不相同。`hub.TextClassifierTask`通过参数`feature``token_feature`区分。
`feature`应是sentence-level特征,shape应为[-1, emb_size];`token_feature`是token-levle特征,shape应为[-1, max_seq_len, emb_size]。
如果使用预置网络,则应取Transformer类预训练模型的sequence_output特征(`outputs["sequence_output"]`)。并且`hub.TextClassifierTask(token_feature=outputs["sequence_output"])`
如果不使用预置网络,直接通过fc网络进行分类,则应取Transformer类预训练模型的pooled_output特征(`outputs["pooled_output"]`)。并且`hub.TextClassifierTask(feature=outputs["pooled_output"])`
5. 使用预置网络,可以通过`hub.TextClassifierTask`参数network进行指定不同的网络结构。如下代码表示选择bilstm网络拼接在Transformer类预训练模型之后。
4. 使用预置网络,可以通过`hub.TextClassifierTask`参数network进行指定不同的网络结构。如下代码表示选择bilstm网络拼接在Transformer类预训练模型之后。
PaddleHub文本分类任务预置网络支持BOW,Bi-LSTM,CNN,DPCNN,GRU,LSTM。指定network应是其中之一。
其中DPCNN网络实现为[ACL2017-Deep Pyramid Convolutional Neural Networks for Text Categorization](https://www.aclweb.org/anthology/P17-1052.pdf)
```python
cls_task = hub.TextClassifierTask(
data_reader=reader,
dataset=dataset,
token_feature=outputs["sequence_output"],
feed_list=feed_list,
network='bilstm',
num_classes=dataset.num_labels,
config=config,
......@@ -210,7 +211,7 @@ cls_task = hub.TextClassifierTask(
#### 自定义迁移任务
如果想改变迁移任务组网,详细参见[自定义迁移任务](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub:-%E8%87%AA%E5%AE%9A%E4%B9%89Task)
如果想改变迁移任务组网,详细参见[自定义迁移任务](../../docs/tutorial/how_to_define_task.md)
## 可视化
......
......@@ -20,11 +20,7 @@ from __future__ import print_function
import argparse
import ast
import numpy as np
import os
import time
import paddle
import paddle.fluid as fluid
import paddlehub as hub
# yapf: disable
......@@ -43,32 +39,11 @@ if __name__ == '__main__':
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Download dataset and use accuracy as metrics
# Choose dataset: GLUE/XNLI/ChinesesGLUE/NLPCC-DBQA/LCQMC
# Download dataset and get its label list and label num
# If you just want labels information, you can omit its tokenizer parameter to avoid preprocessing the train set.
dataset = hub.dataset.ChnSentiCorp()
# For ernie_tiny, it use sub-word to tokenize chinese sentence
# If not ernie tiny, sp_model_path and word_dict_path should be set None
reader = hub.reader.ClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=args.max_seq_len,
sp_model_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
# Construct transfer learning network
# Use "pooled_output" for classification tasks on an entire sentence.
# Use "sequence_output" for token-level output.
pooled_output = outputs["pooled_output"]
# Setup feed list for data feeder
# Must feed all the tensor of module need
feed_list = [
inputs["input_ids"].name,
inputs["position_ids"].name,
inputs["segment_ids"].name,
inputs["input_mask"].name,
]
num_classes = dataset.num_labels
label_list = dataset.get_labels()
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
......@@ -80,14 +55,26 @@ if __name__ == '__main__':
# Define a classfication fine-tune task by PaddleHub's API
cls_task = hub.TextClassifierTask(
data_reader=reader,
feature=pooled_output,
feed_list=feed_list,
num_classes=dataset.num_labels,
feature=outputs["pooled_output"],
num_classes=num_classes,
config=config)
# Data to be prdicted
data = [["这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般"], ["交通方便;环境很好;服务态度很好 房间较小"],
["19天硬盘就罢工了~~~算上运来的一周都没用上15天~~~可就是不能换了~~~唉~~~~你说这算什么事呀~~~"]]
print(cls_task.predict(data=data, return_result=True))
text_a = [
"这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般", "交通方便;环境很好;服务态度很好 房间较小",
"19天硬盘就罢工了~~~算上运来的一周都没用上15天~~~可就是不能换了~~~唉~~~~你说这算什么事呀~~~"
]
# Use the appropriate tokenizer to preprocess the data
# For ernie_tiny, it will do word segmentation to get subword. More details: https://www.jiqizhixin.com/articles/2019-11-06-9
if module.name == "ernie_tiny":
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
else:
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
encoded_data = [
tokenizer.encode(text=text, max_seq_len=args.max_seq_len)
for text in text_a
]
print(cls_task.predict(data=encoded_data, label_list=label_list))
......@@ -20,11 +20,7 @@ from __future__ import print_function
import argparse
import ast
import numpy as np
import os
import time
import paddle
import paddle.fluid as fluid
import paddlehub as hub
# yapf: disable
......@@ -44,33 +40,17 @@ if __name__ == '__main__':
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Download dataset and use accuracy as metrics
# Choose dataset: GLUE/XNLI/ChinesesGLUE/NLPCC-DBQA/LCQMC
# Download dataset and get its label list and label num
# If you just want labels information, you can omit its tokenizer parameter to avoid preprocessing the train set.
dataset = hub.dataset.ChnSentiCorp()
# For ernie_tiny, it use sub-word to tokenize chinese sentence
# If not ernie tiny, sp_model_path and word_dict_path should be set None
reader = hub.reader.ClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=args.max_seq_len,
sp_model_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
num_classes = dataset.num_labels
label_list = dataset.get_labels()
# Construct transfer learning network
# Use "pooled_output" for classification tasks on an entire sentence.
# Use "sequence_output" for token-level output.
token_feature = outputs["sequence_output"]
# Setup feed list for data feeder
# Must feed all the tensor of module need
feed_list = [
inputs["input_ids"].name,
inputs["position_ids"].name,
inputs["segment_ids"].name,
inputs["input_mask"].name,
]
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
use_data_parallel=args.use_data_parallel,
......@@ -85,15 +65,27 @@ if __name__ == '__main__':
# you must use the outputs["sequence_output"] as the token_feature of TextClassifierTask,
# rather than outputs["pooled_output"], and feature is None
cls_task = hub.TextClassifierTask(
data_reader=reader,
token_feature=token_feature,
feed_list=feed_list,
network=args.network,
num_classes=dataset.num_labels,
config=config)
# Data to be prdicted
data = [["这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般"], ["交通方便;环境很好;服务态度很好 房间较小"],
["19天硬盘就罢工了~~~算上运来的一周都没用上15天~~~可就是不能换了~~~唉~~~~你说这算什么事呀~~~"]]
print(cls_task.predict(data=data, return_result=True))
text_a = [
"这个宾馆比较陈旧了,特价的房间也很一般。总体来说一般", "交通方便;环境很好;服务态度很好 房间较小",
"19天硬盘就罢工了~~~算上运来的一周都没用上15天~~~可就是不能换了~~~唉~~~~你说这算什么事呀~~~"
]
# Use the appropriate tokenizer to preprocess the data
# For ernie_tiny, it will do word segmentation to get subword. More details: https://www.jiqizhixin.com/articles/2019-11-06-9
if module.name == "ernie_tiny":
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
else:
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
encoded_data = [
tokenizer.encode(text=text, max_seq_len=args.max_seq_len)
for text in text_a
]
print(cls_task.predict(data=encoded_data, label_list=label_list))
......@@ -7,4 +7,4 @@ python -u predict.py \
--checkpoint_dir=$CKPT_DIR \
--max_seq_len=128 \
--use_gpu=True \
--batch_size=24
--batch_size=1
......@@ -7,5 +7,5 @@ python -u predict_predefine_net.py \
--checkpoint_dir=$CKPT_DIR \
--max_seq_len=128 \
--use_gpu=True \
--batch_size=24 \
--batch_size=1 \
--network=bilstm
......@@ -40,11 +40,23 @@ class TransformerClassifier(fluid.dygraph.Layer):
def finetune(args):
ernie = hub.Module(name="ernie", max_seq_len=args.max_seq_len)
module = hub.Module(name="ernie", max_seq_len=args.max_seq_len)
# Use the appropriate tokenizer to preprocess the data set
# For ernie_tiny, it will do word segmentation to get subword. More details: https://www.jiqizhixin.com/articles/2019-11-06-9
if module.name == "ernie_tiny":
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path(),
)
else:
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.ChnSentiCorp(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
with fluid.dygraph.guard():
dataset = hub.dataset.ChnSentiCorp()
tc = TransformerClassifier(
num_classes=dataset.num_labels, transformer=ernie)
num_classes=dataset.num_labels, transformer=module)
adam = AdamOptimizer(learning_rate=1e-5, parameter_list=tc.parameters())
state_dict_path = os.path.join(args.checkpoint_dir,
'dygraph_state_dict')
......@@ -52,32 +64,31 @@ def finetune(args):
state_dict, _ = fluid.load_dygraph(state_dict_path)
tc.load_dict(state_dict)
reader = hub.reader.ClassifyReader(
dataset=dataset,
vocab_path=ernie.get_vocab_path(),
max_seq_len=args.max_seq_len,
sp_model_path=ernie.get_spm_path(),
word_dict_path=ernie.get_word_dict_path())
train_reader = reader.data_generator(
batch_size=args.batch_size, phase='train')
loss_sum = acc_sum = cnt = 0
# 执行epoch_num次训练
for epoch in range(args.num_epoch):
# 读取训练数据进行训练
for batch_id, data in enumerate(train_reader()):
input_ids = np.array(data[0][0]).astype(np.int64)
position_ids = np.array(data[0][1]).astype(np.int64)
segment_ids = np.array(data[0][2]).astype(np.int64)
input_mask = np.array(data[0][3]).astype(np.float32)
labels = np.array(data[0][4]).astype(np.int64)
for batch_id, data in enumerate(
dataset.batch_records_generator(
phase="train",
batch_size=args.batch_size,
shuffle=True,
pad_to_batch_max_seq_len=False)):
batch_size = len(data["input_ids"])
input_ids = np.array(data["input_ids"]).astype(
np.int64).reshape([batch_size, -1, 1])
position_ids = np.array(data["position_ids"]).astype(
np.int64).reshape([batch_size, -1, 1])
segment_ids = np.array(data["segment_ids"]).astype(
np.int64).reshape([batch_size, -1, 1])
input_mask = np.array(data["input_mask"]).astype(
np.float32).reshape([batch_size, -1, 1])
labels = np.array(data["label"]).astype(np.int64).reshape(
[batch_size, 1])
pred = tc(input_ids, position_ids, segment_ids, input_mask)
acc = fluid.layers.accuracy(pred, to_variable(labels))
loss = fluid.layers.cross_entropy(pred, to_variable(labels))
avg_loss = fluid.layers.mean(loss)
avg_loss.backward()
# 参数更新
adam.minimize(avg_loss)
loss_sum += avg_loss.numpy() * labels.shape[0]
......
......@@ -16,6 +16,7 @@
import argparse
import ast
import paddlehub as hub
# yapf: disable
......@@ -39,35 +40,24 @@ if __name__ == '__main__':
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Download dataset and use accuracy as metrics
# Choose dataset: GLUE/XNLI/ChinesesGLUE/NLPCC-DBQA/LCQMC
# metric should be acc, f1 or matthews
dataset = hub.dataset.ChnSentiCorp()
metrics_choices = ["acc"]
# Use the appropriate tokenizer to preprocess the data set
# For ernie_tiny, it will do word segmentation to get subword. More details: https://www.jiqizhixin.com/articles/2019-11-06-9
if module.name == "ernie_tiny":
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
else:
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
# For ernie_tiny, it use sub-word to tokenize chinese sentence
# If not ernie tiny, sp_model_path and word_dict_path should be set None
reader = hub.reader.ClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=args.max_seq_len,
sp_model_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
dataset = hub.dataset.ChnSentiCorp(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use "pooled_output" for classification tasks on an entire sentence.
# Use "sequence_output" for token-level output.
pooled_output = outputs["pooled_output"]
# Setup feed list for data feeder
# Must feed all the tensor of module need
feed_list = [
inputs["input_ids"].name,
inputs["position_ids"].name,
inputs["segment_ids"].name,
inputs["input_mask"].name,
]
# Select fine-tune strategy, setup config and fine-tune
strategy = hub.AdamWeightDecayStrategy(
warmup_proportion=args.warmup_proportion,
......@@ -85,12 +75,11 @@ if __name__ == '__main__':
# Define a classfication fine-tune task by PaddleHub's API
cls_task = hub.TextClassifierTask(
data_reader=reader,
dataset=dataset,
feature=pooled_output,
feed_list=feed_list,
num_classes=dataset.num_labels,
config=config,
metrics_choices=metrics_choices)
metrics_choices=["acc"])
# Fine-tune and evaluate by PaddleHub's API
# will finish training, evaluation, testing, save model automatically
......
......@@ -40,35 +40,24 @@ if __name__ == '__main__':
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Download dataset and use accuracy as metrics
# Choose dataset: GLUE/XNLI/ChinesesGLUE/NLPCC-DBQA/LCQMC
# metric should be acc, f1 or matthews
dataset = hub.dataset.ChnSentiCorp()
metrics_choices = ["acc"]
# Use the appropriate tokenizer to preprocess the data set
# For ernie_tiny, it will do word segmentation to get subword. More details: https://www.jiqizhixin.com/articles/2019-11-06-9
if module.name == "ernie_tiny":
tokenizer = hub.ErnieTinyTokenizer(
vocab_file=module.get_vocab_path(),
spm_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
else:
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
# For ernie_tiny, it use sub-word to tokenize chinese sentence
# If not ernie tiny, sp_model_path and word_dict_path should be set None
reader = hub.reader.ClassifyReader(
dataset=dataset,
vocab_path=module.get_vocab_path(),
max_seq_len=args.max_seq_len,
sp_model_path=module.get_spm_path(),
word_dict_path=module.get_word_dict_path())
dataset = hub.dataset.ChnSentiCorp(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use "pooled_output" for classification tasks on an entire sentence.
# Use "sequence_output" for token-level output.
token_feature = outputs["sequence_output"]
# Setup feed list for data feeder
# Must feed all the tensor of module need
feed_list = [
inputs["input_ids"].name,
inputs["position_ids"].name,
inputs["segment_ids"].name,
inputs["input_mask"].name,
]
# Select fine-tune strategy, setup config and fine-tune
strategy = hub.AdamWeightDecayStrategy(
warmup_proportion=args.warmup_proportion,
......@@ -90,13 +79,12 @@ if __name__ == '__main__':
# you must use the outputs["sequence_output"] as the token_feature of TextClassifierTask,
# rather than outputs["pooled_output"], and feature is None
cls_task = hub.TextClassifierTask(
data_reader=reader,
dataset=dataset,
token_feature=token_feature,
feed_list=feed_list,
network=args.network,
num_classes=dataset.num_labels,
config=config,
metrics_choices=metrics_choices)
metrics_choices=["acc"])
# Fine-tune and evaluate by PaddleHub's API
# will finish training, evaluation, testing, save model automatically
......
# PaddleHub 文本生成
本示例将展示如何使用PaddleHub Fine-tune API以及Transformer类预训练模型(ERNIE/BERT/RoBERTa)完成生成任务。
## 目录结构
```
text_generation
├── predict.py # 预测脚本
├── README.md # 文本生成迁移学习文档说明
├── run_text_gen.sh # 训练启动脚本
├── run_predict.sh # # 预测启动脚本
├── text_gen.py # 训练脚本
```
## 如何开始Fine-tune
在完成安装PaddlePaddle与PaddleHub后,通过执行脚本`sh run_text_gen.sh`即可开始使用ERNIE对Couplet数据集进行Fine-tune。
其中脚本参数说明如下:
```bash
--batch_size: 批处理大小,请结合显存情况进行调整,若出现显存不足,请适当调低这一参数;
--learning_rate: Fine-tune的最大学习率;
--cut_fraction: Slanted triangular策略中学习率上升阶段的比例,如果0.1,则学习率会在前10%训练step的过程中从0慢慢增长到learning_rate, 而后再缓慢衰减,默认为learning_rate/32;
--num_epoch: Fine-tune迭代的轮数;
--max_seq_len: ERNIE/BERT模型使用的最大序列长度,最大不能超过512, 若出现显存不足,请适当调低这一参数;
--use_data_parallel: 是否使用并行计算,默认True。打开该功能依赖nccl库;
--checkpoint_dir: 模型保存路径,PaddleHub会自动保存验证集上表现最好的模型;
```
## 代码步骤
使用PaddleHub Fine-tune API进行Fine-tune可以分为4个步骤。
### Step1: 加载预训练模型
```python
import paddlehub as hub
module = hub.Module(name="ernie")
inputs, outputs, program = module.context(trainable=True, max_seq_len=128)
```
其中最大序列长度`max_seq_len`是可以调整的参数,建议值128,根据任务文本长度不同可以调整该值,但最大不超过512。
PaddleHub还提供BERT等模型可供选择, 模型对应的加载示例如下:
模型名 | PaddleHub Module
---------------------------------- | :------:
ERNIE, Chinese | `hub.Module(name='ernie')`
ERNIE tiny, Chinese | `hub.Module(name='ernie_tiny')`
ERNIE 2.0 Base, English | `hub.Module(name='ernie_v2_eng_base')`
ERNIE 2.0 Large, English | `hub.Module(name='ernie_v2_eng_large')`
BERT-Base, Uncased | `hub.Module(name='bert_uncased_L-12_H-768_A-12')`
BERT-Large, Uncased | `hub.Module(name='bert_uncased_L-24_H-1024_A-16')`
BERT-Base, Cased | `hub.Module(name='bert_cased_L-12_H-768_A-12')`
BERT-Large, Cased | `hub.Module(name='bert_cased_L-24_H-1024_A-16')`
BERT-Base, Multilingual Cased | `hub.Module(nane='bert_multi_cased_L-12_H-768_A-12')`
BERT-Base, Chinese | `hub.Module(name='bert_chinese_L-12_H-768_A-12')`
BERT-wwm, Chinese | `hub.Module(name='bert_wwm_chinese_L-12_H-768_A-12')`
BERT-wwm-ext, Chinese | `hub.Module(name='bert_wwm_ext_chinese_L-12_H-768_A-12')`
RoBERTa-wwm-ext, Chinese | `hub.Module(name='roberta_wwm_ext_chinese_L-12_H-768_A-12')`
RoBERTa-wwm-ext-large, Chinese | `hub.Module(name='roberta_wwm_ext_chinese_L-24_H-1024_A-16')`
更多模型请参考[PaddleHub官网](https://www.paddlepaddle.org.cn/hub?filter=hot&value=1)
如果想尝试BERT模型,只需要更换Module中的`name`参数即可.
```python
# 更换name参数即可无缝切换BERT中文模型, 代码示例如下
module = hub.Module(name="bert_chinese_L-12_H-768_A-12")
```
### Step2: 准备数据集并使用tokenizer预处理数据
```python
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.Couplet(
tokenizer=tokenizer, max_seq_len=128)
```
**NOTE**:
* 即使是使用ernie_tiny预训练模型,也请使用BertTokenizer,而不要使用ErnieTinyTokenizer。因为对联数据集中上联是按字切分并以特殊字符"\002"作为分隔符的。
数据集的准备代码可以参考 [couplet.py](../../paddlehub/dataset/couplet.py)
`hub.dataset.Couplet()` 会自动从网络下载数据集并解压到用户目录下`$HOME/.paddlehub/dataset`目录;
`module.get_vocab_path()` 会返回预训练模型对应的词表;
`max_seq_len` 需要与Step1中context接口传入的序列长度保持一致;
dataset将调用传入的tokenizer提供的encode接口对全量数据进行预处理,您可以通过以下方式观察数据的处理流程:
```
single_result = tokenizer.encode(text="hello", text_pair="world", max_seq_len=10) # BertTokenizer
# {'input_ids': [3, 1, 5, 39825, 5, 0, 0, 0, 0, 0], 'segment_ids': [0, 0, 0, 1, 1, 0, 0, 0, 0, 0], 'seq_len': 5, 'input_mask': [1, 1, 1, 1, 1, 0, 0, 0, 0, 0], 'position_ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
dataset_result = dataset.get_dev_records() # set dataset max_seq_len = 10
# {'input_ids': [101, 1745, 1751, 100, 100, 100, 100, 100, 100, 102], 'segment_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'seq_len': 10, 'input_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 'position_ids': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'label': [144, 643, 7057, 12130, 8375, 296, 525, 1884, 702, 702], 'dec_input': [134, 144, 643, 7057, 12130, 8375, 296, 525, 1884, 702]}
```
#### 自定义数据集
如果想加载自定义数据集完成迁移学习,详细参见[自定义数据集](../../docs/tutorial/how_to_load_data.md)
### Step3:选择优化策略和运行配置
```python
strategy = hub.ULMFiTStrategy(
learning_rate=5e-3,
optimizer_name="adam",
cut_fraction=0.1,
dis_params_layer=module.get_params_layer(),
frz_params_layer=module.get_params_layer())
config = hub.RunConfig(use_cuda=True, num_epoch=3, batch_size=32, strategy=strategy)
```
#### 优化策略
由于文本生成任务不是简单拼接全连接层即可完成,它引入了RNN seq2seq结构,带来了较多的新参数。得益于PaddleHub的ULMFiT策略,我们可以用较大的学习率更新Decoder中的参数,同时减缓Encoder中的参数更新速度。实验表明,在本任务中使用ULMFiTStrategy可以取得比AdamWeightDecayStrategy更好的效果。
* `learning_rate`: Fine-tune过程中的最大学习率;
* `optimizer_name`: 优化器类别。
* `cut_fraction`: Slanted triangular策略中学习率上升阶段的比例,如果0.1,则学习率会在前10%训练step的过程中从0慢慢增长到learning_rate, 而后再缓慢衰减,默认为learning_rate/32;
* `dis_params_layer`: 分层学习率策略需要的参数层次信息,如果设置为module.get_params_layer(),预训练模型中各层神经网络的更新速度将逐层衰减,默认每一层的学习率是上一层学习率的1/2.6;
* `frz_params_layer`: 逐层解冻策略需要的参数层次信息,如果设置为module.get_params_layer(),预训练模型中各层神经网络将在训练过程中随着epoch的增大而参与更新,例如epoch=1时只有最上层参数会更新,epoch=2时最上2层参数都会参与更新;
关于ULMFiT策略的详细说明,请参考[论文](https://arxiv.org/pdf/1801.06146.pdf)。如果您希望将ULMFiT策略与AdamWeightDecay策略进行组合实验,请参考[CombinedStrategy](../../docs/reference/strategy.md)
#### 运行配置
`RunConfig` 主要控制Fine-tune的训练,包含以下可控制的参数:
* `use_cuda`: 是否使用GPU训练,默认为False;
* `checkpoint_dir`: 模型checkpoint保存路径, 若用户没有指定,程序会自动生成;
* `num_epoch`: Fine-tune的轮数;
* `batch_size`: 训练的批大小,如果使用GPU,请根据实际情况调整batch_size;
* `strategy`: Fine-tune优化策略;
### Step4: 构建网络并创建生成迁移任务进行Fine-tune
```python
pooled_output = outputs["pooled_output"]
sequence_output = outputs["sequence_output"]
gen_task = hub.TextGenerationTask(
dataset=dataset,
feature=pooled_output,
token_feature=sequence_output,
max_seq_len=128,
num_classes=dataset.num_labels,
config=config,
metrics_choices=["bleu"])
gen_task.finetune_and_eval()
```
**NOTE:**
1. `outputs["pooled_output"]`返回了Transformer类预训练模型对应的[CLS]向量,可以用于句子或句对的特征表达。这一特征将用于TextGenerationTask Decoder状态初始化。
2. `outputs["sequence_output"]`返回了ERNIE/BERT模型输入单词的对应输出,可以用于单词的特征表达;这一特征将用于TextGenerationTask Decoder解码。
3. 当前TextGenerationTask采用如下图所示的seq2seq结构:
<p align="center">
<img src="https://d2l.ai/_images/encoder-decoder.svg" width='60%' align="middle"
</p>
其中Encoder为hub.Module指定的预训练模型,Decoder为通用的LSTM+Attention结构.
#### 自定义迁移任务
如果想改变迁移任务组网,详细参见[自定义迁移任务](../../docs/tutorial/how_to_define_task.md)
## 可视化
Fine-tune API训练过程中会自动对关键训练指标进行打点,启动程序后执行下面命令:
```bash
$ visualdl --logdir $CKPT_DIR/visualization --host ${HOST_IP} --port ${PORT_NUM}
```
其中${HOST_IP}为本机IP地址,${PORT_NUM}为可用端口号,如本机IP地址为192.168.0.1,端口号8040,用浏览器打开192.168.0.1:8040,即可看到训练过程中指标的变化情况。
## 模型预测
通过Fine-tune完成模型训练后,在对应的ckpt目录下,会自动保存验证集上效果最好的模型。
配置脚本参数
```
CKPT_DIR="ckpt_generation/"
python predict.py --checkpoint_dir $CKPT_DIR --max_seq_len 128
```
其中CKPT_DIR为Fine-tune API保存最佳模型的路径, max_seq_len是ERNIE模型的最大序列长度,*请与训练时配置的参数保持一致*
参数配置正确后,请执行脚本`sh run_predict.sh`,即可看到文本生成预测结果。
我们在AI Studio上提供了IPython NoteBook形式的demo,点击[PaddleHub教程合集](https://aistudio.baidu.com/aistudio/projectdetail/231146),可使用AI Studio平台提供的GPU算力进行快速尝试。
## 超参优化AutoDL Finetuner
PaddleHub还提供了超参优化(Hyperparameter Tuning)功能, 自动搜索最优模型超参得到更好的模型效果。详细信息参见[AutoDL Finetuner超参优化功能教程](../../docs/tutorial/autofinetune.md)
## Fine-tune之后保存的模型转化为PaddleHub Module
Fine-tune之后保存的模型转化为PaddleHub Module[教程](../../docs/tutorial/finetuned_model_to_module.md)
#coding:utf-8
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Fine-tuning on classification task """
import argparse
import ast
import paddlehub as hub
# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
parser.add_argument("--batch_size", type=int, default=1, help="Total examples' number in batch for training.")
parser.add_argument("--max_seq_len", type=int, default=512, help="Number of words of the longest seqence.")
parser.add_argument("--use_gpu", type=ast.literal_eval, default=False, help="Whether use GPU for fine-tuning, input should be True or False")
parser.add_argument("--use_data_parallel", type=ast.literal_eval, default=False, help="Whether use data parallel.")
args = parser.parse_args()
# yapf: enable.
if __name__ == '__main__':
# Load Paddlehub ERNIE Tiny pretrained model
module = hub.Module(name="ernie_tiny")
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Download dataset and get its label list and label num
# If you just want labels information, you can omit its tokenizer parameter to avoid preprocessing the train set.
dataset = hub.dataset.Couplet()
num_classes = dataset.num_labels
label_list = dataset.get_labels()
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
use_data_parallel=args.use_data_parallel,
use_cuda=args.use_gpu,
batch_size=args.batch_size,
checkpoint_dir=args.checkpoint_dir,
strategy=hub.AdamWeightDecayStrategy())
# Construct transfer learning network
# Use "pooled_output" for classification tasks on an entire sentence.
# Use "sequence_output" for token-level output.
pooled_output = outputs["pooled_output"]
sequence_output = outputs["sequence_output"]
# Define a classfication fine-tune task by PaddleHub's API
gen_task = hub.TextGenerationTask(
feature=pooled_output,
token_feature=sequence_output,
max_seq_len=args.max_seq_len,
num_classes=dataset.num_labels,
config=config,
metrics_choices=["bleu"])
# Data to be predicted
text_a = ["人增福寿年增岁", "风吹云乱天垂泪", "若有经心风过耳"]
# Add 0x02 between characters to match the format of training data,
# otherwise the length of prediction results will not match the input string
# if the input string contains non-Chinese characters.
formatted_text_a = list(map("\002".join, text_a))
# Use the appropriate tokenizer to preprocess the data
# For ernie_tiny, it use BertTokenizer too.
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
encoded_data = [
tokenizer.encode(text=text, max_seq_len=args.max_seq_len)
for text in formatted_text_a
]
print(
gen_task.predict(
data=encoded_data, label_list=label_list, accelerate_mode=False))
export FLAGS_eager_delete_tensor_gb=0.0
export CUDA_VISIBLE_DEVICES=0
CKPT_DIR="./ckpt_generation"
python -u predict.py \
--checkpoint_dir=$CKPT_DIR \
--max_seq_len=128 \
--use_gpu=True \
--batch_size=1
export FLAGS_eager_delete_tensor_gb=0.0
export CUDA_VISIBLE_DEVICES=0
CKPT_DIR="./ckpt_generation"
python -u text_gen.py \
--batch_size 16 \
--num_epoch 30 \
--checkpoint_dir $CKPT_DIR \
--max_seq_len 50 \
--learning_rate 5e-3 \
--cut_fraction 0.1 \
--use_data_parallel True \
--use_gpu True
#coding:utf-8
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Fine-tuning on classification task """
import argparse
import ast
import paddlehub as hub
# yapf: disable
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--num_epoch", type=int, default=3, help="Number of epoches for fine-tuning.")
parser.add_argument("--use_gpu", type=ast.literal_eval, default=True, help="Whether use GPU for fine-tuning, input should be True or False")
parser.add_argument("--learning_rate", type=float, default=5e-5, help="Learning rate used to train with warmup.")
parser.add_argument("--cut_fraction", type=float, default=0.1, help="Warmup proportion params for warmup strategy")
parser.add_argument("--checkpoint_dir", type=str, default=None, help="Directory to model checkpoint")
parser.add_argument("--max_seq_len", type=int, default=512, help="Number of words of the longest seqence.")
parser.add_argument("--batch_size", type=int, default=32, help="Total examples' number in batch for training.")
parser.add_argument("--use_data_parallel", type=ast.literal_eval, default=False, help="Whether use data parallel.")
args = parser.parse_args()
# yapf: enable.
if __name__ == '__main__':
# Load Paddlehub ERNIE Tiny pretrained model
module = hub.Module(name="ernie_tiny")
inputs, outputs, program = module.context(
trainable=True, max_seq_len=args.max_seq_len)
# Use the appropriate tokenizer to preprocess the data set
# For ernie_tiny, it use BertTokenizer too.
tokenizer = hub.BertTokenizer(vocab_file=module.get_vocab_path())
dataset = hub.dataset.Couplet(
tokenizer=tokenizer, max_seq_len=args.max_seq_len)
# Construct transfer learning network
# Use "pooled_output" for classification tasks on an entire sentence.
# Use "sequence_output" for token-level output.
pooled_output = outputs["pooled_output"]
sequence_output = outputs["sequence_output"]
# Select fine-tune strategy, setup config and fine-tune
strategy = hub.ULMFiTStrategy(
learning_rate=args.learning_rate,
optimizer_name="adam",
cut_fraction=args.cut_fraction,
dis_params_layer=module.get_params_layer(),
frz_params_layer=module.get_params_layer())
# Setup RunConfig for PaddleHub Fine-tune API
config = hub.RunConfig(
use_data_parallel=args.use_data_parallel,
use_cuda=args.use_gpu,
num_epoch=args.num_epoch,
batch_size=args.batch_size,
checkpoint_dir=args.checkpoint_dir,
strategy=strategy)
# Define a classfication fine-tune task by PaddleHub's API
gen_task = hub.TextGenerationTask(
dataset=dataset,
feature=pooled_output,
token_feature=sequence_output,
max_seq_len=args.max_seq_len,
num_classes=dataset.num_labels,
config=config,
metrics_choices=["bleu"])
# Fine-tune and evaluate by PaddleHub's API
# will finish training, evaluation, testing, save model automatically
gen_task.finetune_and_eval()
......@@ -30,11 +30,17 @@ release = "v1.5"
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.napoleon',
'sphinx.ext.coverage',
'sphinx.ext.viewcode',
'sphinx.ext.mathjax',
'sphinx_markdown_tables',
'sphinx.ext.githubpages',
'sphinx.ext.napoleon',
'recommonmark',
'sphinx_markdown_tables',
]
# Add any paths that contain templates here, relative to this directory.
......
......@@ -107,7 +107,7 @@ upstream
## 文档
文档使用了 [sphinx](http://sphinx-doc.org/) 来生成,支持 [Markdown](https://guides.github.com/features/mastering-markdown/)[reStructuredText](http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) 格式。 所有文档都在 [docs/](../docs) 目录下。
文档使用了 [sphinx](http://sphinx-doc.org/) 来生成,支持 [Markdown](https://guides.github.com/features/mastering-markdown/)[reStructuredText](http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) 格式。 所有文档都在 [docs/](../../) 目录下。
* 在提交文档改动前,请先**在本地生成文档**`cd docs/ && make clean && make html`,然后,可以在 `docs/_build/html` 目录下找到所有生成的网页。 请认真分析生成日志中的**每个 WARNING**,这非常有可能是或**空连接**或其它问题。
......
# 常见问题
## 使用pip install paddlehub时提示`Could not find a version that satisfies the requirement paddlehub (from versions: )`
## 使用pip install paddlehub时提示
`Could not find a version that satisfies the requirement paddlehub (from versions: )`
这可能是因为pip指向了一个pypi的镜像源,该镜像源没有及时同步paddlehub版本导致。
......@@ -10,7 +11,8 @@
$ pip install -i https://pypi.org/simple/ paddlehub
```
## 使用paddlehub时,提示`ModuleNotFoundError: No module named 'paddle'`
## 使用paddlehub时,提示
`ModuleNotFoundError: No module named 'paddle'`
这是因为PaddleHub依赖于PaddlePaddle,用户需要自行安装合适的PaddlePaddle版本。
如果机器不支持GPU,那么使用如下命令来安装PaddlePaddle的CPU版本:
......@@ -23,7 +25,8 @@ $ pip install paddlepaddle
$ pip install paddlepaddle-gpu
```
## 利用PaddleHub ernie/bert进行Finetune时,提示`paddle.fluid.core_avx.EnforceNotMet: Input ShapeTensor cannot be found in Op reshape2`等信息
## 利用PaddleHub ernie/bert进行Finetune时,提示
`paddle.fluid.core_avx.EnforceNotMet: Input ShapeTensor cannot be found in Op reshape2`等信息
这是因为ernie/bert module的创建时和此时运行环境中PaddlePaddle版本不对应。
首先将PaddleHub升级至最新版本,同时将ernie卸载。
......@@ -54,7 +57,7 @@ results = lac.lexical_analysis(data=inputs, use_gpu=True, batch_size=10)
## 如何获取输入句子经过ERNIE编码后的句子表示Embedding?
具体参考[BERT Services]()使用说明
具体参考[BERT Services](./tutorial/bert_service.md)使用说明
## 在虚拟机Python2环境中使用hub命令报错“Illegal instruction”
......
# 安装
## 环境依赖
* Python==2.7 or Python>=3.5 for Linux or Mac
**Python>=3.6 for Windows**
* Python>=3.6
* PaddlePaddle>=1.7.0
* 操作系统:Windows/Mac/Linux
* PaddlePaddle>=1.5
## 安装命令
在安装PaddleHub之前,请先安装PaddlePaddle深度学习框架,更多安装说明请查阅[飞桨快速安装](https://github.com/PaddlePaddle/PaddleHub)
```shell
pip install paddlehub
```
除上述依赖外,PaddleHub的预训练模型和预置数据集需要连接服务端进行下载,请确保机器可以正常访问网络。若本地已存在相关的数据集和预训练模型,则可以离线运行PaddleHub。
......
欢迎使用**PaddleHub**
# 概述
PaddleHub是飞桨预训练模型管理和迁移学习工具,通过PaddleHub开发者可以使用高质量的预训练模型结合Fine-tune API快速完成迁移学习到应用部署的全流程工作。其提供了飞桨生态下的高质量预训练模型,涵盖了图像分类、目标检测、词法分析、语义模型、情感分析、视频分类、图像生成、图像分割、文本审核、关键点检测等主流模型。更多模型详情请查看官网:https://www.paddlepaddle.org.cn/hub
## PaddleHub是什么
基于预训练模型,PaddleHub支持以下功能:
PaddleHub是飞桨生态的预训练模型应用工具,开发者可以便捷地使用高质量的预训练模型结合Fine-tune API快速完成模型迁移到部署的全流程工作。PaddleHub提供的预训练模型涵盖了图像分类、目标检测、词法分析、语义模型、情感分析、视频分类、图像生成、图像分割、文本审核、关键点检测等主流模型。更多详情可查看[PaddleHub官网](https://www.paddlepaddle.org.cn/hub)
* **[命令行工具](#命令行工具)**,通过Python API或命令行方便快捷地完成模型的搜索、下载、安装、升级、预测等功能
PaddleHub以预训练模型应用为核心具备以下特点:
* **[迁移学习](#迁移学习)**,用户通过Fine-tune API,只需要少量代码即可完成自然语言处理和计算机视觉场景的深度迁移学习
* **模型即软件:** 通过Python API或命令行实现模型调用,可快速体验或集成飞桨特色预训练模型
* **[服务化部署](#服务化部署paddlehub-serving)**,简单一行命令即可搭建属于自己的模型的API服务
* **易用的迁移学习:** 通过Fine-tune API,内置多种优化策略,只需少量代码即可完成预训练模型的Fine-tuning
* **[超参优化](#超参优化autodl-finetuner)**,自动搜索最优超参,得到更好的模型效果
* **一键模型转服务:** 简单一行命令即可搭建属于自己的深度学习模型API服务完成部署
![PaddleHub](./docs/imgs/paddlehub_figure.jpg)
* **自动超参优化:** 内置AutoDL Finetuner能力,一键启动自动化超参搜索。
# PaddleHub 特性
![](./imgs/paddlehub_figure.jpg)
## 命令行工具
<p align='center'>
PaddleHub全景图
</p>
借鉴了Anaconda和PIP等软件包管理的理念,开发了PaddleHub命令行工具。可以方便快捷的完成模型的搜索、下载、安装、升级、预测等功能。
更加详细的使用说明可以参考
[PaddleHub命令行工具](tutorial/cmdintro.md)
目前的预训练模型覆盖了图像分类、语义模型、视频分类、图像生成、图像分割、文本审核、关键点检测等业界主流模型,更多PaddleHub已经发布的模型,请前往 https://www.paddlepaddle.org.cn/hub 查看。[快速体验](quickstart.md)通过命令行即可调用预训练模型进行预测。
## PaddleHub特性
## 迁移学习
### 一、模型即软件
迁移学习(Transfer Learning)通俗来讲,就是运用已有的知识来学习新的知识,核心是找到已有知识和新知识之间的相似性。PaddleHub提供了Fine-tune API,只需要少量代码即可完成深度学习模型在自然语言处理和计算机视觉场景下的迁移学习,可以在更短的时间完成模型的训练,同时模型具备更好的泛化能力
PaddleHub采用模型即软件的设计理念,所有的预训练模型与Python软件包类似,具备版本的概念,通过`hub install/uninstall` 可以便捷完成模型的升级和卸载。此外,还可以通过Python的API或命令行实现快速预测的软件集成,更方便地应用和管理深度学习模型,完成模型的搜索、下载、安装、升级、预测等功能。更加详细的使用说明可以参考[PaddleHub命令行工具](./tutorial/cmdintro.md)
![PaddleHub-Finetune](./docs/imgs/paddlehub_finetune.jpg)
目前的预训练模型覆盖了图像分类、语义模型、视频分类、图像生成、图像分割、文本审核、关键点检测等业界主流模型,更多PaddleHub已经发布的模型,请前往 [PaddleHub官网](https://www.paddlepaddle.org.cn/hub) 查看。也可以使用[快速体验](./quickstart.md)通过命令行即可调用预训练模型进行预测。
<p align="center">
<img src="./imgs/paddlehub_finetune.gif" align="middle"
</p>
### 二、易用的迁移学习
迁移学习(Transfer Learning)通俗来讲,就是运用已有的知识来学习新的知识,核心是找到已有知识和新知识之间的相似性。PaddleHub提供了Fine-tune API,只需要少量代码即可完成深度学习模型在自然语言处理和计算机视觉场景下的迁移学习,可以在更短的时间完成模型的训练,同时模型具备更好的泛化能力。下图所示是基于PaddleHub,通过数十行代码完成ERNIE工业级文本分类示例:
![](./imgs/paddlehub_finetune.gif)
<p align='center'>
十行代码完成ERNIE工业级文本分类
</p>
PaddleHub提供了使用Finetune-API和预训练模型完成[文本分类](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.4/demo/text_classification)[序列标注](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.4/demo/sequence_labeling)[多标签分类](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.4/demo/multi_label_classification)[图像分类](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.4/demo/image_classification)[检索式问答任务](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.4/demo/qa_classification)[回归任务](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.4/demo/regression)[句子语义相似度计算](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.4/demo/sentence_similarity)[阅读理解任务](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.4/demo/reading_comprehension)等迁移任务的使用示例,详细参见[demo](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.4/demo)
PaddleHub提供了使用Finetune-API和预训练模型完成[文本分类](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.7/demo/text_classification)[序列标注](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.7/demo/sequence_labeling)[多标签分类](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.7/demo/multi_label_classification)[图像分类](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.7/demo/image_classification)[检索式问答任务](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.7/demo/qa_classification)[回归任务](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.7/demo/regression)[句子语义相似度计算](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.7/demo/sentence_similarity)[阅读理解任务](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.7/demo/reading_comprehension)等迁移任务的使用示例,详细参见[demo](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.7/demo)。下图是PaddleHub的迁移学习全景图:
![](./imgs/paddlehub_finetune.jpg)
* 场景化使用
......@@ -46,6 +51,12 @@ PaddleHub是飞桨预训练模型管理和迁移学习工具,通过PaddleHub
|预训练模型|任务类型|数据集|AIStudio链接|备注|
|-|-|-|-|-|
|chinese_text_detection_db_mobile|ocr中文识别|icdar2015数据集|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/507159)|超轻量9M模型,火爆|
|video_tag_lstm|短视频分类|百度自建数据集|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/509776)|大规模,多标签|
|resnet50_vd_animals|动物识别|百度自建动物数据集|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/437648)|八千种动物识别|
|face_landmark_localization|AI川剧变脸|AFW/AFLW|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/402892)||
|face_landmark_localization|人像美颜|AFW/AFLW|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/389512)||
|DeepLabv3+|人像抠图|百度自建数据集|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/341116)||
|ResNet|图像分类|猫狗数据集DogCat|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/147010)||
|ERNIE|文本分类|中文情感分类数据集ChnSentiCorp|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/147006)||
|ERNIE|文本分类|中文新闻分类数据集THUNEWS|[点击体验](https://aistudio.baidu.com/aistudio/projectdetail/221999)|本教程讲述了如何将自定义数据集加载,并利用Fine-tune API完成文本分类迁移学习。|
......@@ -61,7 +72,7 @@ PaddleHub是飞桨预训练模型管理和迁移学习工具,通过PaddleHub
关于PaddleHub快捷完成迁移学习,更多信息参考:
[API](reference)
[API](./reference/ref_index.md)
[自定义数据集如何Fine-tune](tutorial/how_to_load_data.md)
......@@ -69,14 +80,14 @@ PaddleHub是飞桨预训练模型管理和迁移学习工具,通过PaddleHub
[ULMFiT优化策略](tutorial/strategy_exp.md)
## 服务化部署PaddleHub Serving
### 三、一键模型转服务
PaddleHub Serving是基于PaddleHub的一键模型服务部署工具,能够通过简单的Hub命令行工具轻松启动一个模型预测在线服务。
其主要包括利用Bert Service实现embedding服务化,以及利用预测模型实现预训练模型预测服务化两大功能。未来还将支持开发者使用PaddleHub Fine-tune API的模型服务化。
关于服务化部署详细信息参见[PaddleHub Serving一键服务部署](tutorial/serving.md)
## 超参优化AutoDL Finetuner
### 四、自动超参优化
深度学习模型往往包含许多的超参数,而这些超参数的取值对模型性能起着至关重要的作用。因为模型参数空间大,目前超参调整都是通过手动,依赖人工经验或者不断尝试,且不同模型、样本数据和场景下不尽相同,所以需要大量尝试,时间成本和资源成本非常浪费。PaddleHub AutoDL Finetuner可以实现自动调整超参数,使得模型性能达到最优水平。它通过多种调优的算法来搜索最优超参。
......
PaddlePaddle 提供了丰富的模型,使得用户可以采用模块化的方法解决各种学习问题。本文,我们将整体介绍PaddleHub中已经准备好的丰富的预训练模型。
* 如果是想了解具体预训练模型的使用可以继续学习本课程,也可以参考 [PaddleHub预训练模型库]( https://www.paddlepaddle.org.cn/hublist )
* 如果是想了解具体预训练模型的使用可以继续学习本课程,也可以参考 [PaddleHub预训练模型库](https://www.paddlepaddle.org.cn/hublist)
* 如果想了解更多模型组网网络结构源代码请参考 [飞桨模型库](https://github.com/PaddlePaddle/models )
* 如果想了解更多模型组网网络结构源代码请参考 [飞桨模型库](https://github.com/PaddlePaddle/models)
## PaddleHub预训练模型库
* [飞桨优势特色模型](#飞桨优势特色模型)
* [飞桨优势特色模型](#百度飞桨独有优势特色模型)
* [图像](#图像)
* [图像分类](#图像分类)
* [目标检测](#目标检测)
......
# 快速体验
安装PaddleHub成功后,执行命令[hub run](tutorial/cmdintro.md),可以快速体验PaddleHub无需代码、一键预测的命令行功能,如下个示例:
安装PaddleHub成功后,执行命令[hub run](tutorial/cmdintro.md),可以快速体验PaddleHub无需代码、一键预测的命令行功能,如下个示例:
使用[词法分析](http://www.paddlepaddle.org.cn/hub?filter=category&value=LexicalAnalysis)模型LAC进行分词
```shell
$ hub run lac --input_text "今天是个好日子"
[{'word': ['今天', '是', '个', '好日子'], 'tag': ['TIME', 'v', 'q', 'n']}]
```
使用[情感分析](http://www.paddlepaddle.org.cn/hub?filter=category&value=SentimentAnalysis)模型Senta对句子进行情感预测
## 视觉领域场景体验
* 使用[文字识别](https://www.paddlepaddle.org.cn/hublist?filter=en_category&value=TextRecognition)轻量级中文OCR模型chinese_ocr_db_crnn_mobile即可一键快速识别图片中的文字。
```shell
$ hub run senta_bilstm --input_text "今天天气真好"
{'text': '今天天气真好', 'sentiment_label': 1, 'sentiment_key': 'positive', 'positive_probs': 0.9798, 'negative_probs': 0.0202}]
$ wget https://paddlehub.bj.bcebos.com/model/image/ocr/test_ocr.jpg
$ hub run chinese_ocr_db_crnn_mobile --input_path test_ocr.jpg --visualization=True
```
使用[目标检测](http://www.paddlepaddle.org.cn/hub?filter=category&value=ObjectDetection)模型Ultra-Light-Fast-Generic-Face-Detector-1MB对图片进行人脸识别
预测结果图片保存在当前运行路径下ocr_result文件夹中,如下图所示。
![](./imgs/ocr_res.jpg)
* 使用[目标检测](https://www.paddlepaddle.org.cn/hublist?filter=en_category&value=ObjectDetection)模型Ultra-Light-Fast-Generic-Face-Detector-1MB对图片进行人脸识别
```shell
$ wget https://paddlehub.bj.bcebos.com/resources/test_image.jpg
$ hub run ultra_light_fast_generic_face_detector_1mb_640 --input_path test_image.jpg
```
![人脸识别结果](./imgs/face_detection_result.jpeg)
![](./imgs/face_detection_result.jpeg)
* 使用[目标检测](https://www.paddlepaddle.org.cn/hublist?filter=en_category&value=ObjectDetection)模型pyramidbox_lite_mobile_mask对图片进行口罩检测
```shell
$ wget https://paddlehub.bj.bcebos.com/resources/test_mask_detection.jpg
$ hub run pyramidbox_lite_mobile_mask --input_path test_mask_detection.jpg
```
![](./imgs/test_mask_detection_result.jpg)
* 使用[图像分割](https://www.paddlepaddle.org.cn/hublist?filter=en_category&value=ImageSegmentation)模型进行人像扣图和人体部件识别
使用[图像分割](https://www.paddlepaddle.org.cn/hub?filter=en_category&value=ImageSegmentation)模型ace2p对图片进行tu
```shell
$ wget https://paddlehub.bj.bcebos.com/resources/test_image.jpg
$ hub run ace2p --input_path test_image.jpg
$ hub run deeplabv3p_xception65_humanseg --input_path test_image.jpg
```
![](./imgs/img_seg_result.jpeg) ![](./imgs/humanseg_test_res.png)
<p align='center'>
&#8194;&#8194;&#8194&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;ACE2P人体部件分割&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;&#8194;
HumanSeg人像分割&#8194;&#8194;&#8194;
</p>
## NLP领域场景体验
* 使用[词法分析](https://www.paddlepaddle.org.cn/hublist?filter=en_category&value=LexicalAnalysis)模型LAC进行分词
```shell
$ hub run lac --input_text "现在,慕尼黑再保险公司不仅是此类行动的倡议者,更是将其大量气候数据整合进保险产品中,并与公众共享大量天气信息,参与到新能源领域的保障中。"
[{
'word': ['现在', ',', '慕尼黑再保险公司', '不仅', '是', '此类', '行动', '的', '倡议者', ',', '更是', '将', '其', '大量', '气候', '数据', '整合', '进', '保险', '产品', '中', ',', '并', '与', '公众', '共享', '大量', '天气', '信息', ',', '参与', '到', '新能源', '领域', '的', '保障', '中', '。'],
'tag': ['TIME', 'w', 'ORG', 'c', 'v', 'r', 'n', 'u', 'n', 'w', 'd', 'p', 'r', 'a', 'n', 'n', 'v', 'v', 'n', 'n', 'f', 'w', 'c', 'p', 'n', 'v', 'a', 'n', 'n', 'w', 'v', 'v', 'n', 'n', 'u', 'vn', 'f', 'w']
}]
```
* 使用[情感分析](https://www.paddlepaddle.org.cn/hublist?filter=en_category&value=SentimentAnalysis)模型Senta对句子进行情感预测
```shell
$ hub run senta_bilstm --input_text "今天天气真好"
{'text': '今天天气真好', 'sentiment_label': 1, 'sentiment_key': 'positive', 'positive_probs': 0.9798, 'negative_probs': 0.0202}]
```
![图像分割结果](./imgs/img_seg_result.jpeg)
除了上述三类模型外,PaddleHub还发布了图像分类、语义模型、视频分类、图像生成、图像分割、文本审核、关键点检测等业界主流模型,更多PaddleHub已经发布的模型,请前往 https://www.paddlepaddle.org.cn/hub 查看
除了上述几类模型外,PaddleHub还发布了图像分类、语义模型、视频分类、图像生成、图像分割、文本审核、关键点检测等业界主流模型,更多PaddleHub已经发布的模型,请前往 [PaddleHub官网](https://www.paddlepaddle.org.cn/hub) 查看
......@@ -346,6 +346,25 @@ dataset = hub.dataset.THUCNEWS()
每个字段以“\_!\_”键分隔,第1列表示类别ID,第2列表示类别,第3列表示文本数据。
### Class `hub.dataset.Couplet`
Couplet是一个开源对联数据集,来源于https://github.com/v-zich/couplet-clean-dataset。该数据集包含74万条对联数据,已利用敏感词词库过滤、删除了低俗或敏感内容。
**示例**
```python
import paddlehub as hub
dataset = hub.dataset.Couplet()
```
数据集样例:
```text
亲情似日堪融雪 孝意如春可著花
```
上下联中每个字以不可见字符“\002”分隔,上下联之间以tab键分隔。
### Class `hub.dataset.DogCatDataset`
DOGCAT是由Kaggle提供的数据集,用于图像二分类,其目标是判断一张图片是猫或是狗。
......
sphinx==2.1.2
sphinx-argparse==0.2.5
sphinx-markdown-tables==0.0.12
sphinx-rtd-theme==0.4.3
sphinx==3.1.2
sphinx-markdown-tables==0.0.15
sphinx-rtd-theme==0.5.0
recommonmark==0.6.0
sphinx-serve==1.0.1
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==1.0.3
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.4
......@@ -82,9 +82,9 @@ train.py用于接受PaddleHub搜索到的超参进行一次优化过程,将优
### 示例
[PaddleHub AutoDL Finetuner超参优化--NLP情感分类任务](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.5/demo/autofinetune_text_classification)
[PaddleHub AutoDL Finetuner超参优化--NLP情感分类任务](../../demo/autofinetune_text_classification)
[PaddleHub AutoDL Finetuner超参优化--CV图像分类任务](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.5/demo/autofinetune_image_classification)
[PaddleHub AutoDL Finetuner超参优化--CV图像分类任务](../../demo/autofinetune_image_classification)
## 三、启动方式
......@@ -99,7 +99,7 @@ $ hub autofinetune train.py --param_file=hparam.yaml --gpu=0,1 --popsize=5 --rou
其中,选项
* `--param_file`: 必填,待优化的超参数信息yaml文件,即上述[hparam.yaml](#1-hparamyaml)
* `--param_file`: 必填,待优化的超参数信息yaml文件,即上述**hparam.yaml**
* `--gpu`: 必填,设置运行程序的可用GPU卡号,中间以逗号隔开,不能有空格;
......@@ -167,7 +167,7 @@ $ visualdl --logdir ${OUTPUT}/visualization --host ${HOST_IP} --port ${PORT_NUM}
## 六、args参数传递
PaddleHub AutoDL Finetuner 支持将train.py中的args其余不需要搜索的参数通过autofinetune remainder方式传入。这个不需要搜索的选项参数名称应该和通过hub autofinetune的传入选项参数名称保持一致。如[PaddleHub AutoDL Finetuner超参优化--NLP情感分类任务](../demo/autofinetune_text_classification)示例中的max_seq_len选项,可以参照以下方式传入。
PaddleHub AutoDL Finetuner 支持将train.py中的args其余不需要搜索的参数通过autofinetune remainder方式传入。这个不需要搜索的选项参数名称应该和通过hub autofinetune的传入选项参数名称保持一致。如[PaddleHub AutoDL Finetuner超参优化--NLP情感分类任务](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.7/demo/autofinetune_text_classification)示例中的max_seq_len选项,可以参照以下方式传入。
```shell
$ OUTPUT=result/
......
......@@ -7,11 +7,8 @@
整体流程图如下:
<div align="center">
![](../imgs/bs.png)
<img src="../imgs/bs.png" aligh="center" width="100%" alt="BS流程图" />
</div>
### Bert Service的特点
使用`Bert Service`能够帮助开发者在限制性较强的环境中有效获取embedding,常用于以下情景:
......@@ -101,14 +98,7 @@ $ hub serving start bert_service -m ernie_tiny -p 8866 --use_gpu --gpu 0
```shell
Server[baidu::paddle_serving::predictor::bert_service::BertServiceImpl] is serving on port=8866.
```
整个启动过程如下图:
<div align="center">
&emsp;&emsp;<img src="https://github.com/ShenYuhan/ml-python/blob/master/short_start_fast.gif" aligh="center" width="70%" alt="启动BS" />
</div>
[整个启动过程动态图](https://github.com/ShenYuhan/ml-python/blob/master/short_start_fast.gif)
其中各参数说明如下表:
......@@ -203,19 +193,14 @@ result = bc.get_result(input_text=input_text)
```python
[[0.9993321895599361, 0.9994612336158751, 0.9999646544456481, 0.732795298099517, -0.34387934207916204, ... ]]
```
客户端代码demo文件见[示例](../../demo/serving/bert_service/bert_service_client.py)
客户端代码demo文件见[示例](https://github.com/PaddlePaddle/PaddleHub/blob/release/v1.7/demo/serving/bert_service/bert_service_client.py)
运行命令如下:
```shell
$ python bert_service_client.py
```
运行过程如下图:
<div align="center">
&emsp;&emsp;<img src="https://github.com/ShenYuhan/ml-python/blob/master/short_client_fast.gif" aligh="center" width="70%" alt="启动BS" />
[运行过程动态图](https://github.com/ShenYuhan/ml-python/blob/master/short_client_fast.gif)
</div>
### Step4:关闭Bert Service服务端
如要停止`Bert Service`服务端程序,可在其启动命令行页面使用Ctrl+C方式关闭,关闭成功会打印如下日志:
......@@ -237,7 +222,7 @@ $ hub serving start bert_service -m bert_chinese_L-12_H-768_A-12 -p 8867
Q : 启动时显示"Check out http://yq01-gpu-255-129-12-00.epc.baidu.com:8887 in web
browser.",这个页面有什么作用。
A : 这是`BRPC`的内置服务,主要用于查看请求数、资源占用等信息,可对server端性能有大致了解,具体信息可查看[BRPC内置服务](https://github.com/apache/incubator-brpc/blob/master/docs/cn/builtin_service.md)
A : 这是`BRPC`的内置服务,主要用于查看请求数、资源占用等信息,可对server端性能有大致了解,具体信息可查看[BRPC内置服务](https://github.com/apache/incubator-brpc/tree/master/docs/cn)
Q : 为什么输入文本的格式为[["文本1"], ["文本2"], ],而不是["文本1", "文本2", ]?
A : 因为Bert模型可以对一轮对话生成向量表示,例如[["问题1","回答1"],["问题2","回答2"]],为了防止使用时混乱,每个样本使用一个list表示,一个样本list内部可以是1条string或2条string,如下面的文本:
......
......@@ -8,19 +8,19 @@ PaddleHub支持修改预训练模型存放路径:
目前命令行支持以下12个命令:
## `install`
## `hub install`
用于将Module安装到本地,默认安装在`${HUB_HOME}/.paddlehub/modules`目录下,当一个Module安装到本地后,用户可以通过其他命令操作该Module(例如,使用该Module进行预测),也可以使用PaddleHub提供的python API,将Module应用到自己的任务中,实现迁移学习
## `uninstall`
## `hub uninstall`
用于卸载本地Module
## `show`
## `hub show`
用于查看本地已安装Module的属性或者指定目录下确定的Module的属性,包括其名字、版本、描述、作者等信息
## `download`
## `hub download`
用于下载百度提供的Module
......@@ -31,36 +31,36 @@ PaddleHub支持修改预训练模型存放路径:
* `--type`:指定下载的资源类型,当指定Model时,download只会下载Model的资源。默认为All,此时会优先搜索Module资源,如果没有相关的Module资源,则搜索Model
## `search`
## `hub search`
通过关键字在服务端检索匹配的Module,当想要查找某个特定模型的Module时,使用search命令可以快速得到结果,例如`hub search ssd`命令,会查找所有包含了ssd字样的Module,命令支持正则表达式,例如`hub search ^s.*`搜索所有以s开头的资源。
`注意`
如果想要搜索全部的Module,使用`hub search *`并不生效,这是因为shell会自行进行通配符展开,将*替换为当前目录下的文件名。为了进行全局搜索,用户可以直接键入`hub search`
## `list`
## `hub list`
列出本地已经安装的Module
## `run`
## `hub run`
用于执行Module的预测,需要注意的是,并不是所有的模型都支持预测(同样,也不是所有的模型都支持迁移学习),更多关于run命令的细节,请查看下方的`关于预测`
## `help`
## `hub help`
显示帮助信息
## `version`
## `hub version`
显示PaddleHub版本信息
## `clear`
## `hub clear`
PaddleHub在使用过程中会产生一些缓存数据,这部分数据默认存放在${HUB_HOME}/.paddlehub/cache目录下,用户可以通过clear命令来清空缓存
## `autofinetune`
## `hub autofinetune`
用于自动调整Fine-tune任务的超参数,具体使用详情参考[PaddleHub AutoDL Finetuner使用教程](https://github.com/PaddlePaddle/PaddleHub/blob/release/v1.4/tutorial/autofinetune.md)
用于自动调整Fine-tune任务的超参数,具体使用详情参考[PaddleHub AutoDL Finetuner使用教程](./autofinetune.md)
`选项`
* `--param_file`: 需要搜索的超参数信息yaml文件
......@@ -78,7 +78,7 @@ PaddleHub在使用过程中会产生一些缓存数据,这部分数据默认
* `--strategy`: 设置自动搜索超参算法,可选hazero和pshe2,默认为hazero
## `config`
## `hub config`
用于查看和设置paddlehub相关设置,包括对server地址、日志级别的设置:
`示例`
......@@ -90,13 +90,13 @@ PaddleHub在使用过程中会产生一些缓存数据,这部分数据默认
* `hub config log==[level]`: 设置当前日志级别为[level], 可选值为critical, error, warning, info, debug, nolog, 从左到右优先级从高到低,nolog表示不显示日志信息
## `serving`
## `hub serving`
用于一键部署Module预测服务,详细用法见[PaddleHub Serving一键服务部署](serving.md)
**NOTE:**
1. 在PaddleHub中,Module表示一个`可执行的神经网络模型`,一个Module可以支持直接命令行预测,也可以配合PaddleHub Finetune API,通过少量代码实现迁移学习。不是所有的Module都支持命令行预测 (例如BERT/ERNIE Transformer类模型,一般需要搭配任务进行finetune),也不是所有的Module都可用于finetune(例如LAC词法分析模型,我们不建议用户用于finetune)。
在PaddleHub中,Module表示一个`可执行的神经网络模型`,一个Module可以支持直接命令行预测,也可以配合PaddleHub Finetune API,通过少量代码实现迁移学习。不是所有的Module都支持命令行预测 (例如BERT/ERNIE Transformer类模型,一般需要搭配任务进行finetune),也不是所有的Module都可用于finetune(例如LAC词法分析模型,我们不建议用户用于finetune)。
PaddleHub尽量简化了用户在使用命令行预测时的理解成本,一般来讲,我们将预测分为NLP和CV两大类
......@@ -126,11 +126,11 @@ $ hub run lac --input_file test.txt
```shell
# 单张照片预测
$ hub run ssd_mobilenet_v1_pascal --input_path test.jpg
$ hub run resnet_v2_50_imagenet --input_path test.jpg
```
```shell
# 多张照片预测
$ hub run ssd_mobilenet_v1_pascal --input_file test.txt
$ hub run resnet_v2_50_imagenet --input_file test.txt
```
其中test.txt的格式为
```
......
......@@ -12,7 +12,7 @@ author_email:
type: nlp/semantic_model
```
**本示例代码可以参考[finetuned_model_to_module](../../demo/text_classification/finetuned_model_to_module/)**
**本示例代码可以参考[finetuned_model_to_module](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.7/demo/text_classification/finetuned_model_to_module)**
Module存在一个接口predict,用于接收带预测,并给出文本的情感倾向(正面/负面),支持python接口调用和命令行调用。
```python
......@@ -192,7 +192,7 @@ def predict(self, data, return_result=False, accelerate_mode=True):
prediction = []
for batch_result in results:
# get predict index
batch_result = np.argmax(batch_result, axis=2)[0]
batch_result = np.argmax(batch_result[0], axis=1)
batch_result = batch_result.tolist()
prediction += batch_result
return prediction
......@@ -200,9 +200,9 @@ def predict(self, data, return_result=False, accelerate_mode=True):
### 完整代码
* [module.py](../../demo/text_classification/finetuned_model_to_module/module.py)
* [module.py](https://github.com/PaddlePaddle/PaddleHub/blob/release/v1.7/demo/text_classification/finetuned_model_to_module/module.py)
* [__init__.py](../../demo/text_classification/finetuned_model_to_module/__init__.py)
* [__init__.py](https://github.com/PaddlePaddle/PaddleHub/blob/release/v1.7/demo/text_classification/finetuned_model_to_module/__init__.py)
**NOTE:** `__init__.py`是空文件
......@@ -305,4 +305,4 @@ r = requests.post(url=url, headers=headers, data=json.dumps(data))
print(json.dumps(r.json(), indent=4, ensure_ascii=False))
```
关与PaddleHub Serving更多信息参见[Hub Serving教程](../../docs/tutorial/serving.md)以及[Demo](../../demo/serving)
关与PaddleHub Serving更多信息参见[Hub Serving教程](./serving.md)以及[Demo](https://github.com/PaddlePaddle/PaddleHub/tree/release/v1.7/demo/serving)
......@@ -3,20 +3,19 @@
了解如何修改Task内置方法,我们首先了解下Task中的事件。
Task定义了[组网事件]()和[运行事件]()。其中运行事件的工作流程如下图。
Task定义了[组网事件](./how_to_define_task.md)[运行事件](./how_to_define_task.md)。其中运行事件的工作流程如下图。
<p align="center">
<img src="https://github.com/PaddlePaddle/PaddleHub/blob/develop/docs/imgs/task_event_workflow.png" hspace='10'/> <br />
</p>
![](../imgs/task_event_workflow.png)
**NOTE:**
* 图中提到的运行设置config参见[RunConfig说明]()
* "finetune_start_event","finetune_end_event","predict_start_event","predict_end_event","eval_start_event","eval_end_event"等事件是用于打印相应阶段的日志信息。"save_ckpt_interval_event"事件用于保存当前训练的模型参数。"log_interval_event"事件用于计算模型评价指标以及可视化这些指标。
* 图中提到的运行设置config参见[RunConfig说明](../reference/config.md)
* "finetune_start_event","finetune_end_event","predict_start_event","predict_end_event",
"eval_start_event","eval_end_event"等事件是用于打印相应阶段的日志信息。"save_ckpt_interval_event"事件用于保存当前训练的模型参数。"log_interval_event"事件用于计算模型评价指标以及可视化这些指标。
如果您需要对图中提到的事件的具体实现进行修改,可以通过Task提供的事件回调hook机制进行改写。
如你想要改变任务评价指标,如下示例中将PaddleHub默认的accuracy评价指标改为F1评价指标。同时还想用自定义的可视化工具可视化模型训练过程,如下示例将可视化工具改写为tb-paddle。则你需要改写评估方法[log_interval_event]()。这时候你可以用Hook实现。具体使用方法如下:
如你想要改变任务评价指标,如下示例中将PaddleHub默认的accuracy评价指标改为F1评价指标。同时还想用自定义的可视化工具可视化模型训练过程,如下示例将可视化工具改写为tb-paddle。则你需要改写评估方法log_interval_event。这时候你可以用Hook实现。具体使用方法如下:
```python
import numpy as np
......@@ -113,10 +112,12 @@ task.hook_info()
```
**NOTE:**
* 关于上述提到的run_states参见[RunEnv说明]()
* tb-paddle详细信息参见[官方文档](https://github.com/linshuliang/tb-paddle)
* 关于上述提到的run_states参见[RunEnv说明](../reference/task/runenv.md)
* tb-paddle详细信息参见[官方文档](https://github.com/ShenYuhan/tb-paddle)
* 改写的事件方法,参数列表务必与PaddleHub内置的相应方法保持一致。
* 只支持改写/删除以下事件hook类型:
"build_env_start_event","build_env_end_event","finetune_start_event","finetune_end_event","predict_start_event","predict_end_event","eval_start_event","eval_end_event","log_interval_event","save_ckpt_interval_event","eval_interval_event","run_step_event"。
* 如果想要改写组网事件,Hook不支持。改写组网事件参见[自定义Task]()。
* 如何创建Task,参见[PaddleHub迁移学习示例](https://github.com/PaddlePaddle/PaddleHub/tree/develop/demo)
"build_env_start_event","build_env_end_event","finetune_start_event","finetune_end_event",
"predict_start_event","predict_end_event","eval_start_event","eval_end_event",
"log_interval_event","save_ckpt_interval_event","eval_interval_event","run_step_event"。
* 如果想要改写组网事件,Hook不支持。改写组网事件参见[自定义Task](./how_to_define_task.md)
* 如何创建Task,参见[PaddleHub迁移学习示例](../../demo)
# 自定义Task
本节内容讲述如何实现自定义Task。在了解本节内容之前,您需要先了解以下内容:
* 任务基类[BasicTask]()
* 运行状态[RunState]()
* 运行环境[RunEnv]()
* 任务基类[BasicTask](../reference/task/base_task.md)
* 运行状态[RunState](../reference/task/runstate.md)
* 运行环境[RunEnv](../reference/task/runenv.md)
当自定义一个Task时,我们并不需要重新实现eval、finetune等通用接口。一般来讲,新的Task与其他Task的区别在于
* 网络结构
......@@ -139,7 +139,7 @@ def _predict_end_event(self):
```
### `_log_interval_event`
调用*finetune* 或者 *finetune_and_eval*接口时,每当命中用户设置的日志打印周期时([RunConfig.log_interval](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-RunConfig#log_interval))。通过继承实现该函数,用户可以在finetune过程中定期打印所需数据,例如计算运行速度、loss、准确率等
调用*finetune* 或者 *finetune_and_eval*接口时,每当命中用户设置的日志打印周期时([RunConfig.log_interval](../reference/config.md))。通过继承实现该函数,用户可以在finetune过程中定期打印所需数据,例如计算运行速度、loss、准确率等
```python
# 代码示例
......@@ -153,7 +153,7 @@ def _log_interval_event(self, run_states):
* `run_states`: 一个list对象,list中的每一个元素都是RunState对象,该list包含了整个从上一次该事件被触发到本次被触发的状态数据
### `_save_ckpt_interval_event`
调用*finetune* 或者 *finetune_and_eval*接口时,每当命中用户设置的保存周期时([RunConfig.save_ckpt_interval](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-RunConfig#save_ckpt_interval)),该事件被触发。通过继承实现该函数,用户可以在定期保存checkpoint
调用*finetune* 或者 *finetune_and_eval*接口时,每当命中用户设置的保存周期时([RunConfig.save_ckpt_interval](../reference/config.md)),该事件被触发。通过继承实现该函数,用户可以在定期保存checkpoint
```python
# 代码示例
......@@ -162,7 +162,7 @@ def _save_ckpt_interval_event(self):
```
### `_eval_interval_event`
调用*finetune_and_eval*接口时,每当命中用户设置的评估周期时([RunConfig.eval_interval](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub-API:-RunConfig#eval_interval)),该事件被触发。通过继承实现该函数,用户可以实现自定义的评估指标计算
调用*finetune_and_eval*接口时,每当命中用户设置的评估周期时([RunConfig.eval_interval](../reference/config.md)),该事件被触发。通过继承实现该函数,用户可以实现自定义的评估指标计算
```python
# 代码示例
......
......@@ -95,7 +95,7 @@ label_list.txt的格式如下
```
示例:
[DogCat数据集](../reference/dataset.md#class-hubdatasetdogcatdataset)为示例,train_list.txt/test_list.txt/validate_list.txt内容如下示例
[DogCat数据集](../reference/dataset.md)为示例,train_list.txt/test_list.txt/validate_list.txt内容如下示例
```
cat/3270.jpg 0
cat/646.jpg 0
......
......@@ -31,7 +31,9 @@ $ hub serving start --modules [Module1==Version1, Module2==Version2, ...] \
|--use_gpu|使用GPU进行预测,必须安装paddlepaddle-gpu|
|--use_multiprocess|是否启用并发方式,默认为单进程方式,推荐多核CPU机器使用此方式<br>*`Windows操作系统只支持单进程方式`*|
|--workers|在并发方式下指定的并发任务数,默认为`2*cpu_count-1`,其中`cpu_count`为CPU核数|
**NOTE:** --use_gpu不可与--use_multiprocess共用。
#### 配置文件启动
启动命令
```shell
......@@ -92,11 +94,8 @@ $ hub serving start --config config.json
### Step3:利用PaddleHub Serving进行个性化开发
使用PaddleHub Serving进行模型服务部署后,可以利用得到的接口进行开发,如对外提供web服务,或接入到应用程序中,以降低客户端预测压力,提高性能,下面展示了一个web页面demo:
<p align="center">
<img src="../imgs/web_demo.png" width="60%" />
![](../imgs/web_demo.png)
</p>
### Step4:关闭serving
使用关闭命令即可关闭启动的serving,
......@@ -143,11 +142,7 @@ $ hub serving start -c serving_config.json
```
启动成功界面如图:
<p align="center">
<img src="../imgs/start_serving_lac.png" width="100%" />
</p>
![](../imgs/start_serving_lac.png)
这样我们就在8866端口成功部署了lac的在线分词服务。
*此处warning为Flask提示,不影响使用*
......
## 概述
Deep Voice 3是百度研究院2017年发布的端到端的TTS模型(论文录用于ICLR 2018)。它是一个基于卷积神经网络和注意力机制的seq2seq模型,由于不包含循环神经网络,它可以并行训练,远快于基于循环神经网络的模型。Deep Voice 3可以学习到多个说话人的特征,也支持搭配多种声码器使用。deepvoice3_ljspeech是基于ljspeech英文语音数据集预训练得到的英文TTS模型,仅支持预测。
<p align="center">
<img src="https://github.com/PaddlePaddle/Parakeet/blob/develop/examples/deepvoice3/images/model_architecture.png" hspace='10'/> <br />
</p>
更多详情参考论文[Deep Voice 3: Scaling Text-to-Speech with Convolutional Sequence Learning](https://arxiv.org/abs/1710.07654)
## 命令行预测
```shell
$ hub run deepvoice3_ljspeech --input_text='Simple as this proposition is, it is necessary to be stated' --use_gpu True --vocoder griffin-lim
```
## API
```python
def synthesize(texts, use_gpu=False, vocoder="griffin-lim"):
```
预测API,由输入文本合成对应音频波形。
**参数**
* texts (list\[str\]): 待预测文本;
* use\_gpu (bool): 是否使用 GPU;**若使用GPU,请先设置CUDA\_VISIBLE\_DEVICES环境变量**
* vocoder: 指定声码器,可选 "griffin-lim"或"waveflow"
**返回**
* wavs (list): 语音合成结果列表,列表中每一个元素为对应输入文本的音频波形,可使用`soundfile.write`进一步处理或保存。
* sample\_rate (int): 合成音频的采样率。
**代码示例**
```python
import paddlehub as hub
import soundfile as sf
# Load deepvoice3_ljspeech module.
module = hub.Module(name="deepvoice3_ljspeech")
# Predict sentiment label
test_texts = ['Simple as this proposition is, it is necessary to be stated',
'Parakeet stands for Paddle PARAllel text-to-speech toolkit']
wavs, sample_rate = module.synthesize(texts=test_texts)
for index, wav in enumerate(wavs):
sf.write(f"{index}.wav", wav, sample_rate)
```
## 服务部署
PaddleHub Serving 可以部署在线服务。
### 第一步:启动PaddleHub Serving
运行启动命令:
```shell
$ hub serving start -m deepvoice3_ljspeech
```
这样就完成了一个服务化API的部署,默认端口号为8866。
**NOTE:** 如使用GPU预测,则需要在启动服务之前,请设置CUDA\_VISIBLE\_DEVICES环境变量,否则不用设置。
### 第二步:发送预测请求
配置好服务端,以下数行代码即可实现发送预测请求,获取预测结果
```python
import requests
import json
import soundfile as sf
# 发送HTTP请求
data = {'texts':['Simple as this proposition is, it is necessary to be stated',
'Parakeet stands for Paddle PARAllel text-to-speech toolkit'],
'use_gpu':False}
headers = {"Content-type": "application/json"}
url = "http://127.0.0.1:8866/predict/deepvoice3_ljspeech"
r = requests.post(url=url, headers=headers, data=json.dumps(data))
# 保存结果
result = r.json()["results"]
wavs = result["wavs"]
sample_rate = result["sample_rate"]
for index, wav in enumerate(wavs):
sf.write(f"{index}.wav", wav, sample_rate)
```
## 查看代码
https://github.com/PaddlePaddle/Parakeet
### 依赖
paddlepaddle >= 1.8.2
paddlehub >= 1.7.0
**NOTE:** 除了python依赖外还必须安装libsndfile库
对于Ubuntu用户,请执行:
```
sudo apt-get install libsndfile1
```
对于Centos用户,请执行:
```
sudo yum install libsndfile
```
## 更新历史
* 1.0.0
初始发布
## 概述
FastSpeech是基于Transformer的前馈神经网络,作者从encoder-decoder结构的teacher model中提取attention对角线来做发音持续时间预测,即使用长度调节器对文本序列进行扩展来匹配目标梅尔频谱的长度,以便并行生成梅尔频谱。该模型基本上消除了复杂情况下的跳词和重复的问题,并且可以平滑地调整语音速度,更重要的是,该模型大幅度提升了梅尔频谱的生成速度。fastspeech_ljspeech是基于ljspeech英文语音数据集预训练得到的英文TTS模型,仅支持预测。
<p align="center">
<img src="https://github.com/PaddlePaddle/Parakeet/blob/develop/examples/fastspeech/images/model_architecture.png" hspace='10'/> <br />
</p>
更多详情参考论文[FastSpeech: Fast, Robust and Controllable Text to Speech](https://arxiv.org/abs/1905.09263)
## 命令行预测
```shell
$ hub run fastspeech_ljspeech --input_text='Simple as this proposition is, it is necessary to be stated' --use_gpu True --vocoder griffin-lim
```
## API
```python
def synthesize(texts, use_gpu=False, speed=1.0, vocoder="griffin-lim"):
```
预测API,由输入文本合成对应音频波形。
**参数**
* texts (list\[str\]): 待预测文本;
* use\_gpu (bool): 是否使用 GPU;**若使用GPU,请先设置CUDA\_VISIBLE\_DEVICES环境变量**
* speed(float): 音频速度,1.0表示以原速输出。
* vocoder: 指定声码器,可选 "griffin-lim"或"waveflow"
**返回**
* wavs (list): 语音合成结果列表,列表中每一个元素为对应输入文本的音频波形,可使用`soundfile.write`进一步处理或保存。
* sample\_rate (int): 合成音频的采样率。
**代码示例**
```python
import paddlehub as hub
import soundfile as sf
# Load fastspeech_ljspeech module.
module = hub.Module(name="fastspeech_ljspeech")
# Predict sentiment label
test_texts = ['Simple as this proposition is, it is necessary to be stated',
'Parakeet stands for Paddle PARAllel text-to-speech toolkit']
wavs, sample_rate = module.synthesize(texts=test_texts)
for index, wav in enumerate(wavs):
sf.write(f"{index}.wav", wav, sample_rate)
```
## 服务部署
PaddleHub Serving 可以部署在线服务。
### 第一步:启动PaddleHub Serving
运行启动命令:
```shell
$ hub serving start -m fastspeech_ljspeech
```
这样就完成了一个服务化API的部署,默认端口号为8866。
**NOTE:** 如使用GPU预测,则需要在启动服务之前,请设置CUDA\_VISIBLE\_DEVICES环境变量,否则不用设置。
### 第二步:发送预测请求
配置好服务端,以下数行代码即可实现发送预测请求,获取预测结果
```python
import requests
import json
import soundfile as sf
# 发送HTTP请求
data = {'texts':['Simple as this proposition is, it is necessary to be stated',
'Parakeet stands for Paddle PARAllel text-to-speech toolkit'],
'use_gpu':False}
headers = {"Content-type": "application/json"}
url = "http://127.0.0.1:8866/predict/fastspeech_ljspeech"
r = requests.post(url=url, headers=headers, data=json.dumps(data))
# 保存结果
result = r.json()["results"]
wavs = result["wavs"]
sample_rate = result["sample_rate"]
for index, wav in enumerate(wavs):
sf.write(f"{index}.wav", wav, sample_rate)
```
## 查看代码
https://github.com/PaddlePaddle/Parakeet
### 依赖
paddlepaddle >= 1.8.2
paddlehub >= 1.7.0
**NOTE:** 除了python依赖外还必须安装libsndfile库
对于Ubuntu用户,请执行:
```
sudo apt-get install libsndfile1
```
对于Centos用户,请执行:
```
sudo yum install libsndfile
```
## 更新历史
* 1.0.0
初始发布
## 概述
TansformerTTS 是使用了 Transformer 结构的端到端语音合成模型,对 Transformer 和 Tacotron2 进行了融合,取得了令人满意的效果。因为删除了 RNN 的循环连接,可并行的提供 decoder 的输入,进行并行训练,大大提升了模型的训练速度。transformer_tts_ljspeech是基于ljspeech英文语音数据集预训练得到的英文TTS模型,仅支持预测。
<p align="center">
<img src="https://github.com/PaddlePaddle/Parakeet/blob/develop/examples/transformer_tts/images/model_architecture.jpg" hspace='10'/> <br />
</p>
更多详情参考论文[Neural Speech Synthesis with Transformer Network](https://arxiv.org/abs/1809.08895)
## 命令行预测
```shell
$ hub run transformer_tts_ljspeech --input_text="Life was like a box of chocolates, you never know what you're gonna get." --use_gpu True --vocoder griffin-lim
```
## API
```python
def synthesize(texts, use_gpu=False, vocoder="griffin-lim"):
```
预测API,由输入文本合成对应音频波形。
**参数**
* texts (list\[str\]): 待预测文本;
* use\_gpu (bool): 是否使用 GPU;**若使用GPU,请先设置CUDA\_VISIBLE\_DEVICES环境变量**
* vocoder: 指定声码器,可选 "griffin-lim"或"waveflow"
**返回**
* wavs (list): 语音合成结果列表,列表中每一个元素为对应输入文本的音频波形,可使用`soundfile.write`进一步处理或保存。
* sample\_rate (int): 合成音频的采样率。
**代码示例**
```python
import paddlehub as hub
import soundfile as sf
# Load transformer_tts_ljspeech module.
module = hub.Module(name="transformer_tts_ljspeech")
# Predict sentiment label
test_texts = ["Life was like a box of chocolates, you never know what you're gonna get."]
wavs, sample_rate = module.synthesize(texts=test_texts, use_gpu=True, vocoder="waveflow")
for index, wav in enumerate(wavs):
sf.write(f"{index}.wav", wav, sample_rate)
```
## 服务部署
PaddleHub Serving 可以部署在线服务。
### 第一步:启动PaddleHub Serving
运行启动命令:
```shell
$ hub serving start -m transformer_tts_ljspeech
```
这样就完成了一个服务化API的部署,默认端口号为8866。
**NOTE:** 如使用GPU预测,则需要在启动服务之前,请设置CUDA\_VISIBLE\_DEVICES环境变量,否则不用设置。
### 第二步:发送预测请求
配置好服务端,以下数行代码即可实现发送预测请求,获取预测结果
```python
import requests
import json
import soundfile as sf
# 发送HTTP请求
data = {'texts':['Simple as this proposition is, it is necessary to be stated',
'Parakeet stands for Paddle PARAllel text-to-speech toolkit'],
'use_gpu':False}
headers = {"Content-type": "application/json"}
url = "http://127.0.0.1:8866/predict/transformer_tts_ljspeech"
r = requests.post(url=url, headers=headers, data=json.dumps(data))
# 保存结果
result = r.json()["results"]
wavs = result["wavs"]
sample_rate = result["sample_rate"]
for index, wav in enumerate(wavs):
sf.write(f"{index}.wav", wav, sample_rate)
```
## 查看代码
https://github.com/PaddlePaddle/Parakeet
### 依赖
paddlepaddle >= 1.8.2
paddlehub >= 1.7.0
**NOTE:** 除了python依赖外还必须安装libsndfile库
对于Ubuntu用户,请执行:
```
sudo apt-get install libsndfile1
```
对于Centos用户,请执行:
```
sudo yum install libsndfile
```
## 更新历史
* 1.0.0
初始发布
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册