越学越有趣:『手把手带你学NLP』系列项目03 ——快递单信息抽取的那些事儿...

点击左上方蓝字关注我们

课程简介

“手把手带你学NLP”是基于飞桨PaddleNLP的系列实战项目。本系列由百度多位资深工程师精心打造,提供了从词向量、预训练语言模型,到信息抽取、情感分析、文本问答、结构化数据问答、文本翻译、机器同传、对话系统等实践项目的全流程讲解,旨在帮助开发者更全面清晰地掌握百度飞桨框架在NLP领域的用法,并能够举一反三、灵活使用飞桨框架和PaddleNLP进行NLP深度学习实践。

从6月7日起,百度飞桨 & 自然语言处理部携手推出了12节NLP精品课,课程中会介绍到这里的实践项目。

课程报名请戳:

https://aistudio.baidu.com/aistudio/course/introduce/24177

欢迎来课程QQ群(群号:758287592)交流吧~~


1. 背景介绍

命名实体识别是NLP中一项非常基础的任务,是信息提取、问答系统、句法分析、机器翻译等众多NLP任务的重要基础工具。命名实体识别的准确度,决定了下游任务的效果,是NLP中的一个基础问题。在NER任务提供了两种解决方案,一类LSTM/GRU + CRF,通过RNN类的模型来抽取底层文本的信息,而CRF(条件随机场)模型来学习底层Token之间的联系;另外一类是通过预训练模型,例如ERNIE,BERT模型,直接来预测Token的标签信息。

本项目将演示如何使用PaddleNLP语义预训练模型ERNIE完成从快递单中抽取姓名、电话、省、市、区、详细地址等内容,形成结构化信息。辅助物流行业从业者进行有效信息的提取,从而降低客户填单的成本。

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

图1 RNN示意图

近年来随着深度学习的发展,模型参数的数量飞速增长。为了训练这些参数,需要更大的数据集来避免过拟合。然而,对于大部分NLP任务来说,构建大规模的标注数据集非常困难(成本过高),特别是对于句法和语义相关的任务。相比之下,大规模的未标注语料库的构建则相对容易。为了利用这些数据,我们可以先从其中学习到一个好的表示,再将这些表示应用到其他任务中。最近的研究表明,基于大规模未标注语料库的预训练模型(Pretrained Models, PTM) 在NLP任务上取得了很好的表现。

近年来,大量的研究表明基于大型语料库的预训练模型(Pretrained Models, PTM)可以学习通用的语言表示,有利于下游NLP任务,同时能够避免从零开始训练模型。随着计算能力的不断提高,深度模型的出现(即 Transformer)和训练技巧的增强使得 PTM 不断发展,由浅变深。

本示例展示了以ERNIE(Enhanced Representation through Knowledge Integration)为代表的预训练模型如何Finetune完成序列标注任务。

记得给PaddleNLP点个小小的Star

开源不易,希望大家多多支持~

GitHub地址:

https://github.com/PaddlePaddle/PaddleNLP 

AI Studio平台后续会默认安装PaddleNLP最新版,在此之前可使用如下命令更新安装。

!pip install --upgrade paddlenlp -i https://pypi.org/simple
# 下载并解压数据集
from paddle.utils.download import get_path_from_url
URL = "https://paddlenlp.bj.bcebos.com/paddlenlp/datasets/waybill.tar.gz"
get_path_from_url(URL, "./")
from functools import partial
import paddlefrom paddlenlp.datasets import MapDatasetfrom paddlenlp.data import Stack, Tuple, Padfrom paddlenlp.transformers import ErnieTokenizer, ErnieForTokenClassificationfrom paddlenlp.metrics import ChunkEvaluatorfrom utils import convert_example, evaluate, predict, load_dict


2. 快速实践


2.1 加载自定义数据集

推荐使用MapDataset()自定义数据集。

def load_dataset(datafiles):
    def read(data_path):
        with open(data_path, 'r', encoding='utf-8') as fp:
            next(fp)  # Skip header
            for line in fp.readlines():
                words, labels = line.strip('\n').split('\t')
                words = words.split('\002')
                labels = labels.split('\002')
                yield words, labels

    if isinstance(datafiles, str):
        return MapDataset(list(read(datafiles)))
    elif isinstance(datafiles, list) or isinstance(datafiles, tuple):
        return [MapDataset(list(read(datafile))) for datafile in datafiles]
# Create dataset, tokenizer and dataloader.
train_ds, dev_ds, test_ds = load_dataset(datafiles=(
        './data/train.txt', './data/dev.txt', './data/test.txt'))

每条数据包含一句文本和这个文本中每个汉字以及数字对应的label标签。

之后,还需要对输入句子进行数据处理,如切词,映射词表id等。


2.2 数据处理

预训练模型ERNIE对中文数据的处理是以字为单位。PaddleNLP对于各种预训练模型已经内置了相应的tokenizer。指定想要使用的模型名字即可加载对应的tokenizer。

tokenizer作用为将原始输入文本转化成模型model可以接受的输入数据形式。

图2 ERNIE模型示意图
label_vocab = load_dict('./data/tag.dic')
tokenizer = ErnieTokenizer.from_pretrained('ernie-1.0')

trans_func = partial(convert_example, tokenizer=tokenizer, label_vocab=label_vocab)
train_ds.map(trans_func)
dev_ds.map(trans_func)
test_ds.map(trans_func)

数据读入:使用paddle.io.DataLoader接口多线程异步加载数据。

ignore_label = -1
batchify_fn = lambda samples, fn=Tuple(
    Pad(axis=0, pad_val=tokenizer.pad_token_id),  # input_ids
    Pad(axis=0, pad_val=tokenizer.pad_token_type_id),  # token_type_ids
    Stack(),  # seq_len
    Pad(axis=0, pad_val=ignore_label)  # labels
): fn(samples)

train_loader = paddle.io.DataLoader(
    dataset=train_ds,
    batch_size=36,
    return_list=True,
    collate_fn=batchify_fn)
dev_loader = paddle.io.DataLoader(
    dataset=dev_ds,
    batch_size=36,
    return_list=True,
    collate_fn=batchify_fn)
test_loader = paddle.io.DataLoader(
    dataset=test_ds,
    batch_size=36,
    return_list=True,
    collate_fn=batchify_fn)


2.3 PaddleNLP一键加载预训练模型

快递单信息抽取本质是一个序列标注任务,PaddleNLP对于各种预训练模型已经内置了对于下游任务文本分类Fine-tune网络。以下教程以ERNIE为预训练模型完成序列标注任务。

paddlenlp.transformers.ErnieForTokenClassification()一行代码即可加载预训练模型ERNIE用于序列标注任务的fine-tune网络。其在ERNIE模型后拼接上一个全连接网络进行分类。

paddlenlp.transformers.ErnieForTokenClassification.from_pretrained()方法只需指定想要使用的模型名称和文本分类的类别数即可完成定义模型网络。

# Define the model netword and its loss
model = ErnieForTokenClassification.from_pretrained("ernie-1.0", num_classes=len(label_vocab))

PaddleNLP不仅支持ERNIE预训练模型,还支持BERT、RoBERTa、Electra等预训练模型。您可以使用PaddleNLP提供的模型,完成文本分类、序列标注、问答等任务。


2.4 设置Fine-Tune优化策略,模型配置

适用于ERNIE/BERT这类Transformer模型的迁移优化学习率策略为warmup的动态学习率。

图3 动态学习率示意图
metric = ChunkEvaluator(label_list=label_vocab.keys(), suffix=True)
loss_fn = paddle.nn.loss.CrossEntropyLoss(ignore_index=ignore_label)
optimizer = paddle.optimizer.AdamW(learning_rate=2e-5, parameters=model.parameters())


2.5 模型训练与评估

模型训练的过程通常有以下步骤:

  1. 从dataloader中取出一个batch data

  2. 将batch data喂给model,做前向计算

  3. 将前向计算结果传给损失函数,计算loss。将前向计算结果传给评价方法,计算评价指标。

  4. loss反向回传,更新梯度。重复以上步骤。

每训练一个epoch时,程序将会评估一次,评估当前模型训练的效果。

step = 0for epoch in range(10):
    for idx, (input_ids, token_type_ids, length, labels) in enumerate(train_loader):
        logits = model(input_ids, token_type_ids)
        loss = paddle.mean(loss_fn(logits, labels))
        loss.backward()
        optimizer.step()
        optimizer.clear_grad()
        step += 1
        print("epoch:%d - step:%d - loss: %f" % (epoch, step, loss))
    evaluate(model, metric, dev_loader)

    paddle.save(model.state_dict(),
                './ernie_result/model_%d.pdparams' % step)
epoch:0 - step:1 - loss: 2.906866
epoch:0 - step:2 - loss: 2.670606
········
epoch:9 - step:450 - loss: 0.001273
eval precision: 0.993283 - recall: 0.994954 - f1: 0.994118


2.6 模型预测

训练保存好的模型,即可用于预测。如以下示例代码自定义预测数据,调用predict()函数即可一键预测。

preds = predict(model, test_loader, test_ds, label_vocab)
file_path = "ernie_results.txt"with open(file_path, "w", encoding="utf8") as fout:
    fout.write("\n".join(preds))# Print some examples
print(
    "The results have been saved in the file: %s, some examples are shown below: "
    % file_path)
print("\n".join(preds[:3]))
The results have been saved in the file: ernie_results.txt, some examples are shown below: 
('黑龙江省', 'A1')('双鸭山市', 'A2')('尖山区', 'A3')('八马路与东平行路交叉口北40米', 'A4')('韦业涛', 'P')('18600009172', 'T')
('广西壮族自治区', 'A1')('桂林市', 'A2')('雁山区', 'A3')('雁山镇西龙村老年活动中心', 'A4')('17610348888', 'T')('羊卓卫', 'P')
('15652864561', 'T')('河南省', 'A1')('开封市', 'A2')('顺河回族区', 'A3')('顺河区公园路32号', 'A4')('赵本山', 'P')


3、进一步使用CRF

PaddleNLP提供了CRF Layer,它能够学习label之间的关系,能够帮助模型更好地学习、预测序列标注任务。

我们在PaddleNLP仓库中提供了示例,您可以参照示例代码使用Ernie-CRF结构完成快递单信息抽取任务。

示例代码链接:

https://github.com/PaddlePaddle/PaddleNLP/blob/develop/examples/information_extraction/waybill_ie/run_ernie_crf.py


加入交流群,一起学习吧

如果你在学习过程中遇到任何问题或疑问,欢迎加入PaddleNLP的QQ技术交流群!


动手试一试

是不是觉得很有趣呀。小编强烈建议初学者参考上面的代码亲手敲一遍,因为只有这样,才能加深你对代码的理解呦。

本次项目对应的代码:

https://aistudio.baidu.com/aistudio/projectdetail/1329361

除此之外, PaddleNLP提供了多种预训练模型,可一键调用,来更换一下预训练模型试试吧:

https://paddlenlp.readthedocs.io/zh/latest/model_zoo/transformers.html

更多PaddleNLP信息,欢迎访问GitHub点star收藏后体验:

https://github.com/PaddlePaddle/PaddleNLP


回顾往期

越学越有趣:『手把手带你学NLP』系列项目01 ——词向量应用的那些事儿

越学越有趣:『手把手带你学NLP』系列项目02 ——语义相似度计算的那些事儿

飞桨(PaddlePaddle)以百度多年的深度学习技术研究和业务应用为基础,集深度学习核心训练和推理框架、基础模型库、端到端开发套件和丰富的工具组件于一体,是中国首个自主研发、功能完备、开源开放的产业级深度学习平台。飞桨企业版针对企业级需求增强了相应特性,包含零门槛AI开发平台EasyDL和全功能AI开发平台BML。EasyDL主要面向中小企业,提供零门槛、预置丰富网络和模型、便捷高效的开发平台;BML是为大型企业提供的功能全面、可灵活定制和被深度集成的开发平台。

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值