写点什么

基于 Ernie-3.0 CAIL2019 法研杯要素识别多标签分类任务

作者:汀丶
  • 2022-10-24
    浙江
  • 本文字数:6765 字

    阅读完需:约 22 分钟

相关项目:


本项目链接:基于Ernie-3.0 CAIL2019法研杯要素识别多标签分类任务本项目将介绍如何基于 PaddleNLP 对 ERNIE 3.0 预训练模型微调完成法律文本多标签分类预测。本项目主要包括“什么是多标签文本分类预测”、“ERNIE 3.0 模型”、“如何使用 ERNIE 3.0 中文预训练模型进行法律文本多标签分类预测”等三个部分。

1. 什么是多标签文本分类预测

文本多标签分类是自然语言处理(NLP)中常见的文本分类任务,文本多标签分类在各种现实场景中具有广泛的适用性,例如商品分类、网页标签、新闻标注、蛋白质功能分类、电影分类、语义场景分类等。多标签数据集中样本用来自 n_classes 个可能类别的m个标签类别标记,其中m的取值在 0 到n_classes之间,这些类别具有不相互排斥的属性。通常,我们将每个样本的标签用 One-hot 的形式表示,正类用 1 表示,负类用 0 表示。例如,数据集中样本可能标签是 A、B 和 C 的多标签分类问题,标签为[1,0,1]代表存在标签 A 和 C 而标签 B 不存在的样本。


近年来,随着司法改革的全面推进,“以公开为原则,不公开为例外”的政策逐步确立,大量包含了案件事实及其适用法律条文信息的裁判文书逐渐在互联网上公开,海量的数据使自然语言处理技术的应用成为可能。法律条文的组织呈树形层次结构,现实中的案情错综复杂,同一案件可能适用多项法律条文,涉及数罪并罚,需要多标签模型充分学习标签之间的关联性,对文本进行分类预测。

2. ERNIE 3.0 模型

ERNIE 3.0 首次在百亿级预训练模型中引入大规模知识图谱,提出了海量无监督文本与大规模知识图谱的平行预训练方法(Universal Knowledge-Text Prediction),通过将知识图谱挖掘算法得到五千万知识图谱三元组与 4TB 大规模语料同时输入到预训练模型中进行联合掩码训练,促进了结构化知识和无结构文本之间的信息共享,大幅提升了模型对于知识的记忆和推理能力。


ERNIE 3.0 框架分为两层。第一层是通用语义表示网络,该网络学习数据中的基础和通用的知识。第二层是任务语义表示网络,该网络基于通用语义表示,学习任务相关的知识。在学习过程中,任务语义表示网络只学习对应类别的预训练任务,而通用语义表示网络会学习所有的预训练任务。



<font size=2><center>ERNIE 3.0 模型框架</center></font>

3. ERNIE 3.0 中文预训练模型进行法律文本多标签分类预测

3.1 环境准备

AI Studio 平台默认安装了 Paddle 和 PaddleNLP,并定期更新版本。 如需手动更新 Paddle,可参考飞桨安装说明,安装相应环境下最新版飞桨框架。使用如下命令确保安装最新版 PaddleNLP:

3.2 加载法律文本多标签数据

本数据集(2019年法研杯要素识别任务)来自于“中国裁判文书网”公开的法律文书,每条训练数据由一份法律文书的案情描述片段构成,其中每个句子都被标记了对应的类别标签,数据集一共包含 20 个标签,标签代表含义如下:


DV1    0    婚后有子女DV2    1    限制行为能力子女抚养DV3    2    有夫妻共同财产DV4    3    支付抚养费DV5    4    不动产分割DV6    5    婚后分居DV7    6    二次起诉离婚DV8    7    按月给付抚养费DV9    8    准予离婚DV10    9    有夫妻共同债务DV11    10    婚前个人财产DV12    11    法定离婚DV13    12    不履行家庭义务DV14    13    存在非婚生子DV15    14    适当帮助DV16    15    不履行离婚协议DV17    16    损害赔偿DV18    17    感情不和分居满二年DV19    18    子女随非抚养权人生活DV20    19    婚后个人财产
复制代码


数据集示例:


text    labels所以起诉至法院请求变更两个孩子均由原告抚养,被告承担一个孩子抚养费每月600元。  0,7,3,12014年8月原、被告因感情不和分居,2014年10月16日被告文某某向务川自治县人民法院提起离婚诉讼,被法院依法驳回了离婚诉讼请求。  6,5女儿由原告抚养,被告每月支付小孩抚养费500元;  0,7,3,1
复制代码


使用本地文件创建数据集,自定义read_custom_data()函数读取数据文件,传入load_dataset()创建数据集,返回数据类型为 MapDataset。更多数据集自定方法详见如何自定义数据集


# 自定义数据集import re
from paddlenlp.datasets import load_dataset
def clean_text(text): text = text.replace("\r", "").replace("\n", "") text = re.sub(r"\\n\n", ".", text) return text
# 定义读取数据集函数def read_custom_data(is_test=False, is_one_hot=True):
file_num = 6 if is_test else 48 #文件个数 filepath = 'raw_data/test/' if is_test else 'raw_data/train/'
for i in range(file_num): f = open('{}labeled_{}.txt'.format(filepath, i)) while True: line = f.readline() if not line: break data = line.strip().split('\t') # 标签用One-hot表示 if is_one_hot: labels = [float(1) if str(i) in data[1].split(',') else float(0) for i in range(20)] else: labels = [int(d) for d in data[1].split(',')] yield {"text": clean_text(data[0]), "labels": labels} f.close()
label_vocab = { 0: "婚后有子女", 1: "限制行为能力子女抚养", 2: "有夫妻共同财产", 3: "支付抚养费", 4: "不动产分割", 5: "婚后分居", 6: "二次起诉离婚", 7: "按月给付抚养费", 8: "准予离婚", 9: "有夫妻共同债务", 10: "婚前个人财产", 11: "法定离婚", 12: "不履行家庭义务", 13: "存在非婚生子", 14: "适当帮助", 15: "不履行离婚协议", 16: "损害赔偿", 17: "感情不和分居满二年", 18: "子女随非抚养权人生活", 19: "婚后个人财产"}
复制代码


# load_dataset()创建数据集train_ds = load_dataset(read_custom_data, is_test=False, lazy=False) test_ds = load_dataset(read_custom_data, is_test=True, lazy=False)
# lazy=False,数据集返回为MapDataset类型print("数据类型:", type(train_ds))
# labels为One-hot标签print("训练集样例:", train_ds[0])print("测试集样例:", test_ds[0])
复制代码


数据类型: <class 'paddlenlp.datasets.dataset.MapDataset'>训练集样例: {'text': '2013年11月28日原、被告离婚时自愿达成协议,婚生子张某乙由被告李某某抚养,本院以(2013)宝渭法民初字第01848号民事调解书对该协议内容予以了确认,该协议具有法律效力,对原、被告双方均有约束力。', 'labels': [1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]}测试集样例: {'text': '综上,原告现要求变更女儿李乙抚养关系的请求,本院应予支持。', 'labels': [1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]}
复制代码

3.3 加载中文 ERNIE 3.0 预训练模型和分词器

PaddleNLP 中 Auto 模块(包括 AutoModel, AutoTokenizer 及各种下游任务类)提供了方便易用的接口,无需指定模型类别,即可调用不同网络结构的预训练模型。PaddleNLP 的预训练模型可以很容易地通过 from_pretrained()方法加载,Transformer预训练模型汇总包含了 40 多个主流预训练模型,500 多个模型权重。


AutoModelForSequenceClassification 可用于多标签分类,通过预训练模型获取输入文本的表示,之后将文本表示进行分类。PaddleNLP 已经实现了 ERNIE 3.0 预训练模型,可以通过一行代码实现 ERNIE 3.0 预训练模型和分词器的加载。


# 加载中文ERNIE 3.0预训练模型和分词器from paddlenlp.transformers import AutoModelForSequenceClassification, AutoTokenizer
model_name = "ernie-3.0-base-zh"num_classes = 20model = AutoModelForSequenceClassification.from_pretrained(model_name, num_classes=num_classes)tokenizer = AutoTokenizer.from_pretrained(model_name)
复制代码

3.4 基于预训练模型的数据处理

Dataset中通常为原始数据,需要经过一定的数据处理并进行采样组 batch。


  • 通过Datasetmap函数,使用分词器将数据集从原始文本处理成模型的输入。

  • 定义paddle.io.BatchSamplercollate_fn构建 paddle.io.DataLoader


实际训练中,根据显存大小调整批大小batch_size和文本最大长度max_seq_length


import functoolsimport numpy as np
from paddle.io import DataLoader, BatchSamplerfrom paddlenlp.data import DataCollatorWithPadding
# 数据预处理函数,利用分词器将文本转化为整数序列def preprocess_function(examples, tokenizer, max_seq_length): result = tokenizer(text=examples["text"], max_seq_len=max_seq_length) result["labels"] = examples["labels"] return result
trans_func = functools.partial(preprocess_function, tokenizer=tokenizer, max_seq_length=128)train_ds = train_ds.map(trans_func)test_ds = test_ds.map(trans_func)
# collate_fn函数构造,将不同长度序列充到批中数据的最大长度,再将数据堆叠collate_fn = DataCollatorWithPadding(tokenizer)
# 定义BatchSampler,选择批大小和是否随机乱序,进行DataLoadertrain_batch_sampler = BatchSampler(train_ds, batch_size=64, shuffle=True)test_batch_sampler = BatchSampler(test_ds, batch_size=64, shuffle=False)train_data_loader = DataLoader(dataset=train_ds, batch_sampler=train_batch_sampler, collate_fn=collate_fn)test_data_loader = DataLoader(dataset=test_ds, batch_sampler=test_batch_sampler, collate_fn=collate_fn)
复制代码

3.5 数据训练和评估

定义训练所需的优化器、损失函数、评价指标等,就可以开始进行预模型微调任务。


import timeimport paddle.nn.functional as F
from metric import MultiLabelReport #文件在根目录下
# Adam优化器、交叉熵损失函数、自定义MultiLabelReport评价指标optimizer = paddle.optimizer.AdamW(learning_rate=1e-4, parameters=model.parameters())criterion = paddle.nn.BCEWithLogitsLoss()metric = MultiLabelReport()
复制代码


from eval import evaluateepochs = 5 # 训练轮次ckpt_dir = "ernie_ckpt" #训练过程中保存模型参数的文件夹
global_step = 0 #迭代次数tic_train = time.time()best_f1_score = 0for epoch in range(1, epochs + 1): for step, batch in enumerate(train_data_loader, start=1): input_ids, token_type_ids, labels = batch['input_ids'], batch['token_type_ids'], batch['labels']
# 计算模型输出、损失函数值、分类概率值、准确率、f1分数 logits = model(input_ids, token_type_ids) loss = criterion(logits, labels) probs = F.sigmoid(logits) metric.update(probs, labels) auc, f1_score, _, _ = metric.accumulate() #auc, f1_score, precison, recall
# 每迭代10次,打印损失函数值、准确率、f1分数、计算速度 global_step += 1 if global_step % 10 == 0: print( "global step %d, epoch: %d, batch: %d, loss: %.5f, auc: %.5f, f1 score: %.5f, speed: %.2f step/s" % (global_step, epoch, step, loss, auc, f1_score, 10 / (time.time() - tic_train))) tic_train = time.time() # 反向梯度回传,更新参数 loss.backward() optimizer.step() optimizer.clear_grad()
# 每迭代40次,评估当前训练的模型、保存当前最佳模型参数和分词器的词表等 if global_step % 40 == 0: save_dir = ckpt_dir if not os.path.exists(save_dir): os.makedirs(save_dir) eval_f1_score = evaluate(model, criterion, metric, test_data_loader, label_vocab, if_return_results=False) if eval_f1_score > best_f1_score: best_f1_score = eval_f1_score model.save_pretrained(save_dir) tokenizer.save_pretrained(save_dir)
复制代码


模型训练过程中会输出如下日志:


global step 770, epoch: 4, batch: 95, loss: 0.04217, auc: 0.99446, f1 score: 0.92639, speed: 0.61 step/sglobal step 780, epoch: 4, batch: 105, loss: 0.03375, auc: 0.99591, f1 score: 0.92674, speed: 0.98 step/sglobal step 790, epoch: 4, batch: 115, loss: 0.04217, auc: 0.99530, f1 score: 0.92483, speed: 0.80 step/sglobal step 800, epoch: 4, batch: 125, loss: 0.05338, auc: 0.99534, f1 score: 0.92467, speed: 0.67 step/seval loss: 0.05298, auc: 0.99185, f1 score: 0.90312, precison: 0.90031, recall: 0.90596[2022-07-27 16:31:27,917] [    INFO] - tokenizer config file saved in ernie_ckpt/tokenizer_config.json[2022-07-27 16:31:27,920] [    INFO] - Special tokens file saved in ernie_ckpt/special_tokens_map.jsonglobal step 810, epoch: 4, batch: 135, loss: 0.04668, auc: 0.99509, f1 score: 0.91319, speed: 0.59 step/sglobal step 820, epoch: 4, batch: 145, loss: 0.04317, auc: 0.99478, f1 score: 0.91696, speed: 0.98 step/sglobal step 830, epoch: 4, batch: 155, loss: 0.04573, auc: 0.99488, f1 score: 0.91815, speed: 0.80 step/sglobal step 840, epoch: 4, batch: 165, loss: 0.05505, auc: 0.99465, f1 score: 0.91753, speed: 0.65 step/seval loss: 0.05352, auc: 0.99234, f1 score: 0.89713, precison: 0.88058, recall: 0.91432global step 850, epoch: 4, batch: 175, loss: 0.03971, auc: 0.99626, f1 score: 0.92391, speed: 0.76 step/sglobal step 860, epoch: 4, batch: 185, loss: 0.04622, auc: 0.99593, f1 score: 0.91806, speed: 0.97 step/sglobal step 870, epoch: 4, batch: 195, loss: 0.04128, auc: 0.99587, f1 score: 0.91959, speed: 0.77 step/sglobal step 880, epoch: 4, batch: 205, loss: 0.06053, auc: 0.99566, f1 score: 0.92041, speed: 0.63 step/seval loss: 0.05234, auc: 0.99220, f1 score: 0.90272, precison: 0.89108, recall: 0.91466...
复制代码

3.6 多标签分类预测结果预测

加载微调好的模型参数进行情感分析预测,并保存预测结果


from eval import evaluate
# 模型在测试集中表现model.set_dict(paddle.load('ernie_ckpt/model_state.pdparams'))
# 也可以选择加载预先训练好的模型参数结果查看模型训练结果# model.set_dict(paddle.load('ernie_ckpt_trained/model_state.pdparams'))
print("ERNIE 3.0 在法律文本多标签分类test集表现", end= " ")results = evaluate(model, criterion, metric, test_data_loader, label_vocab)
复制代码


ERNIE 3.0 在法律文本多标签分类test集表现 eval loss: 0.05298, auc: 0.99185, f1 score: 0.90312, precison: 0.90031, recall: 0.90596
复制代码


test_ds = load_dataset(read_custom_data, is_test=True, is_one_hot=False, lazy=False)res_dir = "./results"if not os.path.exists(res_dir):    os.makedirs(res_dir)with open(os.path.join(res_dir, "multi_label.tsv"), 'w', encoding="utf8") as f:    f.write("text\tprediction\n")    for i, pred in enumerate(results):        f.write(test_ds[i]['text']+"\t"+pred+"\n")
复制代码


法律多标签文本预测结果示例:


4.总结

相关项目:


Paddlenlp之UIE模型实战实体抽取任务【打车数据、快递单】


Paddlenlp之UIE分类模型【以情感倾向分析新闻分类为例】含智能标注方案)


应用实践:分类模型大集成者[PaddleHub、Finetune、prompt]


Paddlenlp之UIE关系抽取模型【高管关系抽取为例】


PaddleNLP基于ERNIR3.0文本分类以中医疗搜索检索词意图分类(KUAKE-QIC)为例【多分类(单标签)】


基于ERNIR3.0文本分类:CAIL2018-SMALL罪名预测为例(多标签)


本项目主要讲解了法律任务,和对性能指标的简单探讨,可以看到实际更多问题是关于多标签分类的。


China AI & Law Challenge (CAIL)中国法研杯司法人工智能挑战赛本项目数据集:https://github.com/china-ai-law-challenge/CAIL2019/tree/master/%E8%A6%81%E7%B4%A0%E8%AF%86%E5%88%AB


数据集自取:



欢迎大家关注我的主页:https://aistudio.baidu.com/aistudio/usercenter


以及博客:https://blog.csdn.net/sinat_39620217?type=blog

用户头像

汀丶

关注

还未添加个人签名 2022-01-06 加入

还未添加个人简介

评论

发布
暂无评论
基于Ernie-3.0 CAIL2019法研杯要素识别多标签分类任务_nlp_汀丶_InfoQ写作社区