【自然语言处理(NLP)】基于PaddleHub的文本审核


作者简介:在校大学生一枚,华为云享专家,阿里云专家博主,腾云先锋(TDP)成员,云曦智划项目总负责人,全国高等学校计算机教学与产业实践资源建设专家委员会(TIPCC)志愿者,以及编程爱好者,期待和大家一起学习,一起进步~ . 博客主页ぃ灵彧が的学习日志 . 本文专栏机器学习 . 专栏寄语:若你决定灿烂,山无遮,海无拦 . 在这里插入图片描述

(文章目录)


前言

(一)、任务描述

在2017年之前,工业界和学术界对NLP文本处理依赖于序列模型Recurrent Neural Network (RNN).

近年来随着深度学习的发展,模型参数数量飞速增长,为了训练这些参数,需要更大的数据集来避免过拟合。然而,对于大部分NLP任务来说,构建大规模的标注数据集成本过高,非常困难,特别是对于句法和语义相关的任务。相比之下,大规模的未标注语料库的构建则相对容易。最近的研究表明,基于大规模未标注语料库的预训练模型(Pretrained Models, PTM) 能够习得通用的语言表示,将预训练模型Fine-tune到下游任务,能够获得出色的表现。另外,预训练模型能够避免从零开始训练模型。

在这里插入图片描述


(二)、环境配置

本示例基于飞桨开源框架2.0版本。


一、数据准备

(一)、导入相关包

!pip install -U paddlehub -i https://pypi.tuna.tsinghua.edu.cn/simple
import paddlehub as hub
import paddle

(二)、代码步骤

使用PaddleHub Fine-tune API进行Fine-tune可以分为4个步骤。

  1. 选择模型
  2. 加载自定义数据集
  3. 选择优化策略和运行配置
  4. 执行fine-tune并评估模型

二、Fine-tune

(一)、选择模型

model = hub.Module(name="ernie", task='seq-cls', num_classes=3) # 在多分类任务中,num_classes需要显式地指定类别数,此处根据数据集设置为3

hub.Module的参数用法如下:

  • name:模型名称,可以选择ernieernie_tinybert-base-casedbert-base-chinese, roberta-wwm-extroberta-wwm-ext-large等。
  • task:fine-tune任务。此处为seq-cls,表示文本分类任务。
  • num_classes:表示当前文本分类任务的类别数,根据具体使用的数据集确定,默认为2。

NOTE: 文本多分类的任务中,num_classes需要用户指定,具体的类别数根据选用的数据集确定,本教程中为3。


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, English Cased hub.Module(name='bert-base-cased')
BERT-Base, English Uncased hub.Module(name='bert-base-uncased')
BERT-Large, English Cased hub.Module(name='bert-large-cased')
BERT-Large, English 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')
ELECTRA-Small, English hub.Module(name='electra-small')
ELECTRA-Base, English hub.Module(name='electra-base')
ELECTRA-Large, English hub.Module(name='electra-large')
ELECTRA-Base, Chinese hub.Module(name='chinese-electra-base')
ELECTRA-Small, Chinese hub.Module(name='chinese-electra-small')

通过以上的一行代码,model初始化为一个适用于文本分类任务的模型,为ERNIE的预训练模型后拼接上一个全连接网络(Full Connected)。

在这里插入图片描述 以上图片来自于:https://arxiv.org/pdf/1810.04805.pdf


(二)、加载自定义数据集

低俗文本较为敏感,这里用疫情期间网民情绪文本积极、中性、消极表示。

# 解压数据集
!cd data/data22724 && unzip -o test_dataset.zip
!cd data/data22724 && unzip -o "train_ dataset.zip"

# 转换编码
def re_encode(path):
    with open(path, 'r', encoding='GB2312', errors='ignore') as file:
        lines = file.readlines()
    with open(path, 'w', encoding='utf-8') as file:
        file.write(''.join(lines))
        
re_encode('data/data22724/nCov_10k_test.csv')
re_encode('data/data22724/nCoV_100k_train.labled.csv')

# 读取数据
import pandas as pd
train_labled = pd.read_csv('data/data22724/nCoV_100k_train.labled.csv', engine ='python')
test = pd.read_csv('data/data22724/nCov_10k_test.csv', engine ='python')

# 清除异常标签数据
train_labled = train_labled[train_labled['情感倾向'].isin(['-1','0','1'])]

# 划分验证集,保存格式  text[\t]label
from sklearn.model_selection import train_test_split

train_labled = train_labled[['微博中文内容', '情感倾向']]
train, valid = train_test_split(train_labled, test_size=0.2, random_state=2020)
train.to_csv('/home/aistudio/data/data22724/train.txt', index=False, header=False, sep='\t')
valid.to_csv('/home/aistudio/data/data22724/valid.txt', index=False, header=False, sep='\t')

输出结果如下图1所示:

在这里插入图片描述

import os, io, csv
from paddlehub.datasets.base_nlp_dataset import InputExample, TextClassificationDataset

# 数据集存放位置
DATA_DIR="/home/aistudio/data/data22724"

输出结果如下图2所示: 在这里插入图片描述

class News(TextClassificationDataset):
    def __init__(self, tokenizer, mode='train', max_seq_len=128):
        if mode == 'train':
            data_file = 'train.txt'
        elif mode == 'test':
            data_file = 'valid.txt'
        else:
            data_file = 'valid.txt'
        super(News, self).__init__(
            base_path=DATA_DIR,
            data_file=data_file,
            tokenizer=tokenizer,
            max_seq_len=max_seq_len,
            mode=mode,
            is_file_with_header=True,
            label_list=["-1", "0", "1"])

    # 解析文本文件里的样本
    def _read_file(self, input_file, is_file_with_header: bool = False):
        if not os.path.exists(input_file):
            raise RuntimeError("The file {} is not found.".format(input_file))
        else:
            with io.open(input_file, "r", encoding="UTF-8") as f:
                reader = csv.reader(f, delimiter="\t", quotechar=None)
                examples = []
                seq_id = 0
                header = next(reader) if is_file_with_header else None
                for line in reader:
                    example = InputExample(guid=seq_id, text_a=line[0], label=line[1])
                    seq_id += 1
                    examples.append(example)
                return examples

train_dataset = News(model.get_tokenizer(), mode='train', max_seq_len=128)
dev_dataset = News(model.get_tokenizer(), mode='dev', max_seq_len=128)
test_dataset = News(model.get_tokenizer(), mode='test', max_seq_len=128)
for e in train_dataset.examples[:3]:
    print(e)

输出结果如下图3所示:

在这里插入图片描述

NOTE: 最大序列长度max_seq_len是可以调整的参数,建议值128,根据任务文本长度不同可以调整该值,但最大不超过512。


(三)、选择优化策略和运行配置

optimizer = paddle.optimizer.Adam(learning_rate=5e-5, parameters=model.parameters())  # 优化器的选择和参数配置
trainer = hub.Trainer(model, optimizer, checkpoint_dir='./ckpt', use_gpu=True)        # fine-tune任务的执行者
优化策略

Paddle2.0-rc提供了多种优化器选择,如SGD, Adam, Adamax等,详细参见策略

在本教程中选择了Adam优化器,其的参数用法:

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

运行配置

Trainer 主要控制Fine-tune任务的训练,是任务的发起者,包含以下可控制的参数:

  • model: 被优化模型;
  • optimizer: 优化器选择;
  • use_gpu: 是否使用gpu训练;
  • use_vdl: 是否使用vdl可视化训练过程;
  • checkpoint_dir: 保存模型参数的地址;
  • compare_metrics: 保存最优模型的衡量指标;

(四)、执行fine-tune并评估模型

trainer.train(train_dataset, epochs=3, batch_size=32, eval_dataset=dev_dataset, save_interval=1)   # 配置训练参数,启动训练,并指定验证集

部分输出结果如下图4所示: 在这里插入图片描述 trainer.train 主要控制具体的训练过程,包含以下可控制的参数:

  • train_dataset: 训练时所用的数据集;
  • epochs: 训练轮数;
  • batch_size: 训练的批大小,如果使用GPU,请根据实际情况调整batch_size;
  • num_workers: works的数量,默认为0;
  • eval_dataset: 验证集;
  • log_interval: 打印日志的间隔, 单位为执行批训练的次数。
  • save_interval: 保存模型的间隔频次,单位为执行训练的轮数。
result = trainer.evaluate(test_dataset, batch_size=32)    # 在测试集上评估当前训练模型

输出结果如下图5所示:

在这里插入图片描述


三、模型预测

当Finetune完成后,我们加载训练后保存的最佳模型来进行预测,完整预测代码如下:

# Data to be prdicted
data = [[""],[""],[""]]]

label_list=["-1", "0", "1"])
label_map = { 
    idx: label_text for idx, label_text in enumerate(label_list)
}

model = hub.Module(
    name='ernie',
    task='seq-cls',
    load_checkpoint='./ckpt/best_model/model.pdparams',
    label_map=label_map)
results = model.predict(data, max_seq_len=128, batch_size=1, use_gpu=True)
for idx, text in enumerate(data):
    print('Data: {} \t Lable: {}'.format(text[0], results[idx]))

总结

本系列文章内容为根据清华社出版的《自然语言处理实践》所作的相关笔记和感悟,其中代码均为基于百度飞桨开发,若有任何侵权和不妥之处,请私信于我,定积极配合处理,看到必回!!!

最后,引用本次活动的一句话,来作为文章的结语~( ̄▽ ̄~)~:

【**学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。**】

在这里插入图片描述