From 713efd452d87d22a46656204a8a7ac2b171f95d1 Mon Sep 17 00:00:00 2001 From: KP <109694228@qq.com> Date: Tue, 5 Jan 2021 14:25:22 +0800 Subject: [PATCH] Custom dataset tutorial (#1165) * Add tutorial about nlp dataset customization * Update doc and tokenizer usage in seq-cls demo * Updata tutorial --- demo/sequence_labeling/README.md | 1 + demo/text_classification/README.md | 29 ++++- demo/text_classification/train.py | 44 +++++-- docs/docs_ch/tutorial/how_to_load_data.md | 140 ++++++++++++++++++++++ 4 files changed, 200 insertions(+), 14 deletions(-) diff --git a/demo/sequence_labeling/README.md b/demo/sequence_labeling/README.md index 04c3450a..c18c0fca 100644 --- a/demo/sequence_labeling/README.md +++ b/demo/sequence_labeling/README.md @@ -50,6 +50,7 @@ model = hub.Module(name='ernie_tiny', version='2.0.1', task='token-cls', label_m * `name`:模型名称,可以选择`ernie`,`ernie_tiny`,`bert-base-cased`, `bert-base-chinese`, `roberta-wwm-ext`,`roberta-wwm-ext-large`等。 * `version`:module版本号 * `task`:fine-tune任务。此处为`token-cls`,表示序列标注任务。 +* `label_map`:数据集中的标签信息,实体识别任务中需要根据不同标签种类对模型性能进行评价。 PaddleHub还提供BERT等模型可供选择, 当前支持序列标注任务的模型对应的加载示例如下: diff --git a/demo/text_classification/README.md b/demo/text_classification/README.md index d624cd49..70113ccd 100644 --- a/demo/text_classification/README.md +++ b/demo/text_classification/README.md @@ -31,7 +31,7 @@ python train.py ```python import paddlehub as hub -model = hub.Module(name='ernie_tiny', version='2.0.1', task='seq-cls') +model = hub.Module(name='ernie_tiny', version='2.0.1', task='seq-cls', num_classes=2) ``` 其中,参数: @@ -39,6 +39,29 @@ model = hub.Module(name='ernie_tiny', version='2.0.1', task='seq-cls') * `name`:模型名称,可以选择`ernie`,`ernie_tiny`,`bert-base-cased`, `bert-base-chinese`, `roberta-wwm-ext`,`roberta-wwm-ext-large`等。 * `version`:module版本号 * `task`:fine-tune任务。此处为`seq-cls`,表示文本分类任务。 +* `num_classes`:表示当前文本分类任务的类别数,根据具体使用的数据集确定,默认为2。 + +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, Cased | `hub.Module(name='bert-base-cased')` +BERT-Base, Uncased | `hub.Module(name='bert-base-uncased')` +BERT-Large, Cased | `hub.Module(name='bert-large-cased')` +BERT-Large, Uncased | `hub.Module(name='bert-large-uncased')` +BERT-Base, Multilingual Cased | `hub.Module(nane='bert-base-multilingual-cased')` +BERT-Base, Multilingual Uncased | `hub.Module(nane='bert-base-multilingual-uncased')` +BERT-Base, Chinese | `hub.Module(name='bert-base-chinese')` +BERT-wwm, Chinese | `hub.Module(name='chinese-bert-wwm')` +BERT-wwm-ext, Chinese | `hub.Module(name='chinese-bert-wwm-ext')` +RoBERTa-wwm-ext, Chinese | `hub.Module(name='roberta-wwm-ext')` +RoBERTa-wwm-ext-large, Chinese | `hub.Module(name='roberta-wwm-ext-large')` +RBT3, Chinese | `hub.Module(name='rbt3')` +RBTL3, Chinese | `hub.Module(name='rbtl3')` 通过以上的一行代码,`model`初始化为一个适用于文本分类任务的模型,为ERNIE Tiny的预训练模型后拼接上一个全连接网络(Full Connected)。 ![](https://ai-studio-static-online.cdn.bcebos.com/f9e1bf9d56c6412d939960f2e3767c2f13b93eab30554d738b137ab2b98e328c) @@ -49,9 +72,9 @@ model = hub.Module(name='ernie_tiny', version='2.0.1', task='seq-cls') ```python train_dataset = hub.datasets.ChnSentiCorp( - tokenizer=model.get_tokenizer(tokenize_chinese_chars=True), max_seq_len=128, mode='train') + tokenizer=model.get_tokenizer(), max_seq_len=128, mode='train') dev_dataset = hub.datasets.ChnSentiCorp( - tokenizer=model.get_tokenizer(tokenize_chinese_chars=True), max_seq_len=128, mode='dev') + tokenizer=model.get_tokenizer(), max_seq_len=128, mode='dev') ``` * `tokenizer`:表示该module所需用到的tokenizer,其将对输入文本完成切词,并转化成module运行所需模型输入格式。 diff --git a/demo/text_classification/train.py b/demo/text_classification/train.py index 3f1ec858..ad4075c3 100644 --- a/demo/text_classification/train.py +++ b/demo/text_classification/train.py @@ -11,21 +11,43 @@ # 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. + import paddle import paddlehub as hub +from paddlehub.datasets import ChnSentiCorp + +import ast +import argparse + +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("--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.") +parser.add_argument("--checkpoint_dir", type=str, default='./checkpoint', help="Directory to model checkpoint") +parser.add_argument("--save_interval", type=int, default=1, help="Save checkpoint every n epoch.") + +args = parser.parse_args() if __name__ == '__main__': model = hub.Module(name='ernie_tiny', version='2.0.1', task='seq-cls') - train_dataset = hub.datasets.ChnSentiCorp( - tokenizer=model.get_tokenizer(tokenize_chinese_chars=True), max_seq_len=128, mode='train') - dev_dataset = hub.datasets.ChnSentiCorp( - tokenizer=model.get_tokenizer(tokenize_chinese_chars=True), max_seq_len=128, mode='dev') - test_dataset = hub.datasets.ChnSentiCorp( - tokenizer=model.get_tokenizer(tokenize_chinese_chars=True), max_seq_len=128, mode='test') - - optimizer = paddle.optimizer.AdamW(learning_rate=5e-5, parameters=model.parameters()) - trainer = hub.Trainer(model, optimizer, checkpoint_dir='test_ernie_text_cls', use_gpu=True) + train_dataset = ChnSentiCorp( + tokenizer=model.get_tokenizer(), max_seq_len=args.max_seq_len, mode='train') + dev_dataset = ChnSentiCorp( + tokenizer=model.get_tokenizer(), max_seq_len=args.max_seq_len, mode='dev') + test_dataset = ChnSentiCorp( + tokenizer=model.get_tokenizer(), max_seq_len=args.max_seq_len, mode='test') - trainer.train(train_dataset, epochs=3, batch_size=32, eval_dataset=dev_dataset, save_interval=1) - trainer.evaluate(test_dataset, batch_size=32) + optimizer = paddle.optimizer.AdamW( + learning_rate=args.learning_rate, parameters=model.parameters()) + trainer = hub.Trainer(model, optimizer, checkpoint_dir=args.checkpoint_dir, use_gpu=args.use_gpu) + trainer.train( + train_dataset, + epochs=args.num_epoch, + batch_size=args.batch_size, + eval_dataset=dev_dataset, + save_interval=args.save_interval, + ) + trainer.evaluate(test_dataset, batch_size=args.batch_size) diff --git a/docs/docs_ch/tutorial/how_to_load_data.md b/docs/docs_ch/tutorial/how_to_load_data.md index 7bb10669..423825c5 100644 --- a/docs/docs_ch/tutorial/how_to_load_data.md +++ b/docs/docs_ch/tutorial/how_to_load_data.md @@ -175,3 +175,143 @@ ccolor_set = MiniCOCO(transforms, mode='train') ``` * `transforms`: 数据预处理方式。 * `mode`: 选择数据模式,可选项有 `train`, `test`, 默认为`train`。 + +## 四、文本分类数据集 + +利用PaddleHub进行文本分类任务使用自定义数据时,需要切分数据集,将数据集切分为训练集和测试集。 + +### 数据准备 + +#### 1. 设置数据集目录 + +用户需要将数据集目录设定为如下格式: +```shell +├──data: 数据目录 + ├── train.txt: 训练集数据 + ├── dev.txt: 验证集数据 + └── test.txt: 测试集数据 +``` + +#### 2. 设置文件格式和内容 + +训练/验证/测试集的数据文件的编码格式建议为utf8格式。内容的第一列是文本类别标签,第二列为文本内容,列与列之间以Tab键分隔。建议在数据集文件第一行填写列说明"label"和"text_a",中间以Tab键分隔,示例如下: +```shell +label text_a +房产 昌平京基鹭府10月29日推别墅1200万套起享97折 +教育 贵州2011高考录取分数线发布理科一本448分 +社会 众多白领因集体户口面临结婚难题 +... +``` + +### 数据集加载 + +加载文本分类的自定义数据集,用户仅需要继承基类TextClassificationDataset,修改数据集存放地址以及类别即可,具体可以参考如下代码: + +```python +from paddlehub.datasets.base_nlp_dataset import TextClassificationDataset + +class MyDataset(TextClassificationDataset): + # 数据集存放目录 + base_path = '/path/to/dataset' + # 数据集的标签列表 + label_list=['体育', '科技', '社会', '娱乐', '股票', '房产', '教育', '时政', '财经', '星座', '游戏', '家居', '彩票', '时尚'] + + def __init__(self, tokenizer, max_seq_len: int = 128, mode: str = 'train'): + if mode == 'train': + data_file = 'train.txt' + elif mode == 'test': + data_file = 'test.txt' + else: + data_file = 'dev.txt' + super().__init__( + base_path=self.base_path, + tokenizer=tokenizer, + max_seq_len=max_seq_len, + mode=mode, + data_file=data_file, + label_list=self.label_list, + is_file_with_header=True) + + +# 选择所需要的模型,获取对应的tokenizer +import paddlehub as hub +model = hub.Module(name='ernie_tiny', task='seq-cls', num_classes=len(MyDataset.label_list)) +tokenizer = model.get_tokenizer() + +# 实例化训练集 +train_dataset = MyDataset(tokenizer) +``` + +至此用户可以通过MyDataset实例化获取对应的数据集,可以通过hub.Trainer对预训练模型`model`完成文本分类任务,详情可参考[PaddleHub文本分类demo](https://github.com/PaddlePaddle/PaddleHub/tree/release/v2.0.0-beta/demo/text_classification)。 + +## 五、序列标注数据集 + +利用PaddleHub进行序列标注任务使用自定义数据时,需要切分数据集,将数据集切分为训练集和测试集。 + +### 数据准备 + +#### 1. 设置数据集目录 + +用户需要将数据集目录设定为如下格式: +```shell +├──data: 数据目录 + ├── train.txt: 训练集数据 + ├── dev.txt: 验证集数据 + └── test.txt: 测试集数据 +``` + +#### 2. 设置文件格式和内容 + +训练/验证/测试集的数据文件的编码格式建议为utf8格式。内容的第一列是文本内容, 第二列为文本中每个token对应的标签。需要注意的是,在文本和标签中,都需使用分隔符(该例子中使用的是斜杠`/`)隔开不同的token。 +列与列之间以Tab键分隔。建议在数据集文件第一行填写列说明"label"和"text_a",中间以Tab键分隔,示例如下: +```shell +text_a label +5/月/1/2/日/,/北/京/市/怀/柔/县/民/政/局/、/畜/牧/局/领/导/来/到/驻/守/在/偏/远/山/区/的/武/警/北/京/一/总/队/十/支/队/十/四/中/队/。 O/O/O/O/O/O/B-LOC/I-LOC/I-LOC/B-ORG/I-ORG/I-ORG/I-ORG/I-ORG/I-ORG/O/B-ORG/I-ORG/I-ORG/O/O/O/O/O/O/O/O/O/O/O/O/B-ORG/I-ORG/I-ORG/I-ORG/I-ORG/I-ORG/I-ORG/I-ORG/I-ORG/I-ORG/I-ORG/I-ORG/I-ORG/I-ORG/O +他/每/年/还/为/河/北/农/业/大/学/扶/助/多/名/贫/困/学/生/。 O/O/O/O/O/B-ORG/I-ORG/I-ORG/I-ORG/I-ORG/I-ORG/O/O/O/O/O/O/O/O/O +... +``` + +### 数据准备 + +加载文本分类的自定义数据集,用户仅需要继承基类SeqLabelingDataset,修改数据集存放地址、类别信息和分隔符即可,具体可以参考如下代码: + +```python +from paddlehub.datasets.base_nlp_dataset import SeqLabelingDataset + +class MyDataset(SeqLabelingDataset): + # 数据集存放目录 + base_path = '/path/to/dataset' + # 数据集的标签列表 + label_list = ["B-PER", "I-PER", "B-ORG", "I-ORG", "B-LOC", "I-LOC", "O"] + label_map = {idx: label for idx, label in enumerate(label_list)} + # 数据文件使用的分隔符 + split_char = '/' + + def __init__(self, tokenizer, max_seq_len: int = 128, mode: str = 'train'): + if mode == 'train': + data_file = 'train.txt' + elif mode == 'test': + data_file = 'test.txt' + else: + data_file = 'dev.txt' + super().__init__( + base_path=self.base_path, + tokenizer=tokenizer, + max_seq_len=max_seq_len, + mode=mode, + data_file=data_file, + label_file=None, + label_list=self.label_list, + split_char=self.split_char, + is_file_with_header=True) + +# 选择所需要的模型,获取对应的tokenizer +import paddlehub as hub +model = hub.Module(name='ernie_tiny', task='token-cls', label_map=MyDataset.label_map) +tokenizer = model.get_tokenizer() + +# 实例化训练集 +train_dataset = MyDataset(tokenizer) +``` + +至此用户可以通过MyDataset实例化获取对应的数据集,可以通过hub.Trainer对预训练模型`model`完成系列标注任务,详情可参考[PaddleHub序列标注demo](https://github.com/PaddlePaddle/PaddleHub/tree/release/v2.0.0-beta/demo/sequence_labeling)。 \ No newline at end of file -- GitLab