PaddlePaddle-PaddleHub

        飞桨(PaddlePaddle)以百度多年的深度学习技术研究和业务应用为基础,是中国首个自主研发、功能完备、 开源开放的产业级深度学习平台,集深度学习核心训练和推理框架、基础模型库、端到端开发套件和丰富的工具组件于一体。PaddleHub旨在为开发者提供丰富的、高质量的、直接可用的预训练模型

ERNIE

        ERNIE(Enhanced Representation through kNowledge IntEgration)是百度提出的知识增强的语义表示模型,通过对词、实体等语义单元的掩码,使得模型学习完整概念的语义表示。在语言推断语义相似度命名实体识别情感分析问答匹配等自然语言处理(NLP)各类中文任务上的验证显示,模型效果全面超越BERT

更多详情请参考ERNIE论文

         本文主要介绍如何微调ERNIE中文情感分析(文本二分类)。项目已上传github

一、环境安装

# CPU
pip install paddlepaddle
# GPU
pip install paddlepaddle-gpu
pip install paddlehub

        有gpu的,建议安装paddlepaddle-gpu版(训练速度会提升好几倍)。paddlepaddle-gpu默认安装的是cuda10.2,如果需要安装其他cuda版本,到官方网站查找命令。(注意,从1.8.0开始采用动态图,所以paddlepaddlepaddlehub版本最好从1.8.0开始使用。)

二、数据预处理

         这里使用的数据是二分类数据集weibo_senti_100k.csv,即情感倾向只有正向负向,下载地址:https://github.com/SophonPlus/ChineseNlpCorpus。由于PaddleHub用的是tsv格式的数据集,所以需要将csv格式转成tsv格式。

import pandas as pd

# 转成tsv格式
file_path = "data/weibo_senti_100k/weibo_senti_100k.csv"
text = pd.read_csv(file_path, sep=",")
text = text.sample(frac=1)  # 打乱数据集
print(len(text))

train = text[:int(len(text) * 0.8)]
dev = text[int(len(text) * 0.8):int(len(text) * 0.9)]
test = text[int(len(text) * 0.9):]

train.to_csv('data/weibo_senti_100k/train.tsv', sep='\t', header=None, index=False, columns=None, mode="w")
dev.to_csv('data/weibo_senti_100k/dev.tsv', sep='\t', header=None, index=False, columns=None, mode="w")
test.to_csv('data/weibo_senti_100k/test.tsv', sep='\t', header=None, index=False, columns=None, mode="w")

# 验证train,dev,test标签分布是否均匀
for file in ['train', 'dev', 'test']:
    file_path = f"data/weibo_senti_100k/{file}.tsv"
    text = pd.read_csv(file_path, sep="\t", header=None)
    prob = dict()
    total = len(text[0])
    for i in text[0]:
        if prob.get(i) is None:
            prob[i] = 1
        else:
            prob[i] += 1
    # 按标签排序
    prob = {i[0]: round(i[1] / total, 3) for i in sorted(prob.items(), key=lambda k: k[0])}
    print(file, prob, total)

         随机采样数据集,将训练集验证集测试集划分比为8:1:1,然后用\t作分隔符转成tsv格式,最后验证traindevtest标签分布是否均匀

三、微调

import paddle
import paddlehub as hub
import ast
import argparse
from paddlehub.datasets.base_nlp_dataset import TextClassificationDataset


class MyDataset(TextClassificationDataset):
    # 数据集存放目录
    base_path = 'data/weibo_senti_100k'
    # 数据集的标签列表,多分类标签格式为['0', '1', '2', '3',...]
    label_list = ['0', '1']

    def __init__(self, tokenizer, max_seq_len: int = 128, mode: str = 'train'):
        if mode == 'train':
            data_file = 'train.tsv'
        elif mode == 'test':
            data_file = 'test.tsv'
        else:
            data_file = 'dev.tsv'
        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)


if __name__ == '__main__':
    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='./ernie_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()
    
    # 选择模型、任务和类别数
    model = hub.Module(name='ernie_tiny', task='seq-cls', num_classes=len(MyDataset.label_list))

    train_dataset = MyDataset(tokenizer=model.get_tokenizer(), max_seq_len=args.max_seq_len, mode='train')
    dev_dataset = MyDataset(tokenizer=model.get_tokenizer(), max_seq_len=args.max_seq_len, mode='dev')
    test_dataset = MyDataset(tokenizer=model.get_tokenizer(), max_seq_len=args.max_seq_len, mode='test')

    optimizer = paddle.optimizer.Adam(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)

1.自定义数据集

# 数据集存放目录
base_path = 'data/weibo_senti_100k'
# 数据集的标签列表,多分类标签格式为['0', '1', '2', '3',...]
label_list = ['0', '1']

        首先需要自定义数据集MyDataset类,在base_path路径下存放train.tsvtest.tsvdev.tsv三个数据集,定义label_list标签类别。若是多分类,则label_list格式为['0', '1', '2', '3',...]

2.选择模型

model = hub.Module(name='ernie_tiny', task='seq-cls', num_classes=len(MyDataset.label_list))
  • name:模型名称,可以选择ernieernie_tinybert-base-cased, bert-base-chineseroberta-wwm-extroberta-wwm-ext-large等。
  • version:module版本号
  • task:fine-tune任务。seq-cls(文本分类任务)token-cls(序列标注任务)
  • num_classes:表示当前文本分类任务的类别数,根据具体使用的数据集确定,默认为2。

        PaddleHub还提供BERT等模型可供选择, 当前支持文本分类任务的模型对应的加载示例如下: 

模型名PaddleHub Module
ERNIE, Chinesehub.Module(name='ernie')
ERNIE tiny, Chinesehub.Module(name='ernie_tiny')
ERNIE 2.0 Base, Englishhub.Module(name='ernie_v2_eng_base')
ERNIE 2.0 Large, Englishhub.Module(name='ernie_v2_eng_large')
BERT-Base, English Casedhub.Module(name='bert-base-cased')
BERT-Base, English Uncasedhub.Module(name='bert-base-uncased')
BERT-Large, English Casedhub.Module(name='bert-large-cased')
BERT-Large, English Uncasedhub.Module(name='bert-large-uncased')
BERT-Base, Multilingual Casedhub.Module(nane='bert-base-multilingual-cased')
BERT-Base, Multilingual Uncasedhub.Module(nane='bert-base-multilingual-uncased')
BERT-Base, Chinesehub.Module(name='bert-base-chinese')
BERT-wwm, Chinesehub.Module(name='chinese-bert-wwm')
BERT-wwm-ext, Chinesehub.Module(name='chinese-bert-wwm-ext')
RoBERTa-wwm-ext, Chinesehub.Module(name='roberta-wwm-ext')
RoBERTa-wwm-ext-large, Chinesehub.Module(name='roberta-wwm-ext-large')
RBT3, Chinesehub.Module(name='rbt3')
RBTL3, Chinesehub.Module(name='rbtl3')
ELECTRA-Small, Englishhub.Module(name='electra-small')
ELECTRA-Base, Englishhub.Module(name='electra-base')
ELECTRA-Large, Englishhub.Module(name='electra-large')
ELECTRA-Base, Chinesehub.Module(name='chinese-electra-base')
ELECTRA-Small, Chinesehub.Module(name='chinese-electra-small')

3.加载数据集 

train_dataset = MyDataset(tokenizer=model.get_tokenizer(), max_seq_len=args.max_seq_len, mode='train')
dev_dataset = MyDataset(tokenizer=model.get_tokenizer(), max_seq_len=args.max_seq_len, mode='dev')
test_dataset = MyDataset(tokenizer=model.get_tokenizer(), max_seq_len=args.max_seq_len, mode='test')
  • tokenizer:表示该module所需用到的tokenizer,其将对输入文本完成切词,并转化成module运行所需模型输入格式。
  • mode:选择数据模式,可选项有 traintestval, 默认为train
  • max_seq_len:ERNIE/BERT模型使用的最大序列长度,若出现显存不足,请适当调低这一参数。

        预训练模型ERNIE对中文数据的处理是以为单位,tokenizer作用为将原始输入文本转化成模型model可以接受的输入数据形式。

4.选择优化策略和运行配置

optimizer = paddle.optimizer.Adam(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)

优化策略

        Paddle提供了多种优化器选择,如SGDAdamAdamax等,详细参见策略。其中Adam:

  • learning_rate:全局学习率。默认为1e-3;
  • parameters:待优化模型参数。

运行配置

        hub.Trainer主要控制Fine-tune的训练,包含以下可控制的参数: 

  • model:被优化模型;
  • optimizer:优化器选择;
  • checkpoint_dir:保存模型参数的地址;
  • use_gpu:是否使用gpu,默认为False。对于GPU用户,建议开启use_gpu。

        trainer.train主要控制具体的训练过程,包含以下可控制的参数:

  • train_dataset:训练时所用的数据集;
  • epochs:训练轮数;
  • batch_size:训练的批大小,如果使用GPU,请根据实际情况调整batch_size;
  • num_workers:works的数量,默认为0;
  • eval_dataset:验证集;
  • log_interval:打印日志的间隔, 单位为执行批训练的次数。
  • save_interval:保存模型的间隔频次,单位为执行训练的轮数。

四、模型预测

import paddlehub as hub
import pandas as pd

if __name__ == '__main__':
    file_path = "data/weibo_senti_100k/test.tsv"
    text = pd.read_csv(file_path, sep="\t", header=None)
    data = [[i] for i in text[1]]
    label_map = {0: 0, 1: 1}  # {0: 'negative', 1: 'positive'}

    model = hub.Module(name='ernie_tiny', task='seq-cls',
                       load_checkpoint='./ernie_checkpoint/best_model/model.pdparams', label_map=label_map)
    results = model.predict(data, max_seq_len=128, batch_size=1, use_gpu=True)

    # 输出测试集准确率
    count = 0
    for i, j in zip(text[0], results):
        # print(type(i), type(j))
        if int(i) == int(j):
            count += 1
    print("测试集准确率", count / len(results))

        完成Fine-tune后,Fine-tune过程在验证集上表现最优的模型会被保存在${CHECKPOINT_DIR}/best_model目录下,其中${CHECKPOINT_DIR}目录为Fine-tune时所选择的保存checkpoint的目录。

label_map = {0: 0, 1: 1}  # {0: 'negative', 1: 'positive'}
data = [[i] for i in text[1]]

        label_map预测标签的映射;data预测数据,格式为[[数据], [数据], [数据],...,[数据], [数据], [数据]]

五、结果

        训练集

        测试集

        在二分类数据集weibo_senti_100k.csv上,训练集准确率可以达到98%测试集准确率同样可以达到98%

参考链接

https://github.com/PaddlePaddle/PaddleHub/blob/release/v2.1/docs/docs_ch/finetune/customized_dataset.md

https://github.com/PaddlePaddle/PaddleHub/tree/release/v2.1/demo/text_classification

https://www.paddlepaddle.org.cn/hublist

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐