Paddlenlp之UIE分类模型以情感倾向分析新闻分类为例含智能标注方案)
Posted 汀、
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Paddlenlp之UIE分类模型以情感倾向分析新闻分类为例含智能标注方案)相关的知识,希望对你有一定的参考价值。
相关文章:
Paddlenlp之UIE模型实战实体抽取任务【打车数据、快递单】
项目连接:百度AIstudio直接fork我的项目就可以复现
Paddlenlp之UIE分类模型【以情感倾向分析新闻分类为例】含智能标注方案)
0 前言
首先回顾上一个项目:
Paddlenlp之UIE模型实战实体抽取任务【打车数据、快递单】
会存在以下问题:
-
自己样本数据该如何标注
-
如果样本量大有什么好方法进行智能标注
-
可视化工具详细介绍
本次项目将会先把,数据标注、智能标注、数据可视化方法
进行详细讲解。
0.1 如何对数据进行标注—doccano
强烈推荐:数据标注平台doccano----简介、安装、使用、踩坑记录
详细步骤可以参考博客
官方文档:
https://github.com/doccano/doccano
记的进虚拟环境!!!!!
Step 1. 本地安装doccano(请勿在AI Studio内部运行,本地测试环境python=3.8)
$ pip install doccano
Step 2. 初始化数据库和账户(用户名和密码可替换为自定义值)
# 初始化,设置用户名= admin,密码=pass
doccano init
doccano createuser --username admin --password pass
-------------------------个人设置---------------------------
$ doccano init
$ doccano createuser --username my_admin_name --password my_password
Step 3. 启动doccano
在一个窗口启动doccano的WebServer,保持窗口
$ doccano webserver --port 8000
在另一个窗口启动doccano的任务队列
$ doccano task
打开浏览器(推荐Chrome),在地址栏中输入http://127.0.0.1:8000/后回车即得以下界面。
具体如何进行标注请参考博客或者官网文档
0.2 智能标注
当你数据样本很大的时候,一条条标注会很费时,效率很低
这里推荐去hugging face加载一些预训练模型进行一次标注再进行人工复核。
基于 hugging face 预训练模型的实体识别智能标注方案:生成doccano要求json格式
根据doccano标注平台格式要求
json格式导入数据格式要求: 实体;包含关系样式展示
"text": "Google was founded on September 4, 1998, by Larry Page and Sergey Brin.",
"entities": [
"id": 0,
"start_offset": 0,
"end_offset": 6,
"label": "ORG"
,
"id": 1,
"start_offset": 22,
"end_offset": 39,
"label": "DATE"
,
"id": 2,
"start_offset": 44,
"end_offset": 54,
"label": "PERSON"
,
"id": 3,
"start_offset": 59,
"end_offset": 70,
"label": "PERSON"
],
"relations": [
"from_id": 0,
"to_id": 1,
"type": "foundedAt"
,
"from_id": 0,
"to_id": 2,
"type": "foundedBy"
,
"from_id": 0,
"to_id": 3,
"type": "foundedBy"
]
0.3 实体智能标注+格式转换
0.3.1 长文本(一个txt长篇)code
注释部分包含预训练模型识别实体;以及精灵标注助手格式要求
ps:提示一下下面这段程序是在torch下用的,因为直接拿的huggingface预训练模型,降低我们工作量。用paddle的话求快推荐UIE直接小样本搞一个简单预模型协助标注!
from transformers import pipeline
import os
from tqdm import tqdm
import pandas as pd
from time import time
import json
def return_single_entity(name, start, end):
return [int(start), int(end), name]
# def return_single_entity(name, word, start, end, id, attributes=[]):
# entity =
# entity['type'] = 'T'
# entity['name'] = name
# entity['value'] = word
# entity['start'] = int(start)
# entity['end'] = int(end)
# entity['attributes'] = attributes
# entity['id'] = int(id)
# return entity
# input_dir = 'E:/datasets/myUIE/inputs'
input_dir = 'C:/Users/admin/Desktop//test_input.txt'
output_dir = 'C:/Users/admin/Desktop//outputs'
tagger = pipeline(task='ner', model='xlm-roberta-large-finetuned-conll03-english',
aggregation_strategy='simple')
keywords = 'PER': '人', 'ORG': '机构' # loc 地理位置 misc 其他类型实体
# for filename in tqdm(input_dir):
# # 读取数据并自动打标
# json_list = []
with open(input_dir, 'r', encoding='utf8') as f:
text = f.readlines()
json_list = [0 for i in range(len(text))]
for t in text:
i = t.strip("\\n").strip("'").strip('"')
named_ents = tagger(i) # 预训练模型
# named_ents = tagger(text)
df = pd.DataFrame(named_ents)
""" 标注结果:entity_group score word start end
0 ORG 0.999997 National Science Board 18 40
1 ORG 0.999997 NSB 42 45
2 ORG 0.999997 NSF 71 74"""
# 放在循环里面,那每次开始新的循环就会重新定义一次,上一次定义的内容就丢了
# json_list = [0 for i in range(len(text))]
entity_list=[]
# entity_list2=[]
for index, elem in df.iterrows():
if not elem.entity_group in keywords:
continue
if elem.end - elem.start <= 1:
continue
entity = return_single_entity(
keywords[elem.entity_group], elem.start, elem.end)
entity_list.append(entity)
# entity_list2.append(entity_list)
json_obj = "text": text[index], "label": entity_list
json_list[index] = json.dumps(json_obj)
# entity_list.append(entity)
# data = json.dumps(json_list)
# json_list.append(data)
with open(f'output_dir/data_2.json', 'w', encoding='utf8') as f:
for line in json_list:
f.write(line+"\\n")
# f.write('\\n'.join(data))
# f.write(str(data))
print('done!')
# 转化为精灵标注助手导入格式(但是精灵标注助手的nlp标注模块有编码的问题,部分utf8字符不能正常显示,会影响标注结果)
# id = 1
# entity_list = ['']
# for index, elem in df.iterrows():
# if not elem.entity_group in keywords:
# continue
# entity = return_single_entity(keywords[elem.entity_group], elem.word, elem.start, elem.end, id)
# id += 1
# entity_list.append(entity)
# python_obj = 'path': f'input_dir/filename',
# 'outputs': 'annotation': 'T': entity_list, "E": [""], "R": [""], "A": [""],
# 'time_labeled': int(1000 * time()), 'labeled': True, 'content': text
# data = json.dumps(python_obj)
# with open(f'output_dir/filename.rstrip(".txt").json', 'w', encoding='utf8') as f:
# f.write(data)
输出结果:
"text": "The company was founded in 1852 by Jacob Estey\\n", "label": [[35, 46, "\\u4eba"]]
"text": "The company was founded in 1852 by Jacob Estey, who bought out another Brattleboro manufacturing business.", "label": [[35, 46, "\\u4eba"], [71, 82, "\\u673a\\u6784"]]
可以看到label标签是乱码的,不用在意导入到doccano平台后会显示正常
0.3.2 提高标注质量
- 人工复核
不多说就是一条一条检查过去,智能标注后已经省事很多了
对已标注数据进行
- 删除无效标注
import json
dir_path = r'C:/Users/admin/Desktop/光合项目/自动标注' # 这里改文件地址
with open(f'dir_path/pre_data.jsonl', 'r',encoding='utf8')as f: # 文件命名
text = f.readlines()
content = [json.loads(elem.strip('\\n')) for elem in text]
content = [json.dumps(cont) for cont in content if cont['entities'] != []]
with open(f'dir_path/remove_empty_data.jsonl', 'w',encoding='utf8')as f: # 文件命名
f.write('\\n'.join(content))
print("输出数据")
- 上述处理在英文版本数据集上有不错体现,当然在中文版本可以在上述基础上,用paddle UIE等模型,先人工小批量标注,然后生成个base模型,通过模型对输入结果进行预标注,再人工复核
当然你可能会问还有更简单的方法吗,当然是有的!!!
闪亮登场!!!!
EasyData 为百度大脑推出的一站式数据处理和服务平台,主要围绕AI模型开发过程中所需的数据采集、数据质检、数据智能处理、数据标注等环节提供完整的数据服务。目前EasyData已支持图片、文本、音频、视频、表格五类基础数据的处理。
同时EasyData已与EasyDL、BML平台数据管理模块打通,EasyData处理的数据可直接应用于EasyDL、BML平台进行模型训练。
功能齐全很强大,小伙伴们可以试一试哦!
我就不做更多推广了,,信百度没问题,哈哈。
0.4 visualDL工具使用,可视化利器。
VisualDL 是一个面向深度学习任务设计的可视化工具。VisualDL 利用了丰富的图表来展示数据,用户可以更直观、清晰地查看数据的特征与变化趋势,有助于分析数据、及时发现错误,进而改进神经网络模型的设计。
目前,VisualDL 支持 scalar, image, audio, graph, histogram, prcurve, high dimensional 七个组件.
不进行过多介绍,可以参考我的项目或者博客。有详细讲解!!!
from visualdl import LogWriter
if __name__ == '__main__':
value = [i/1000.0 for i in range(1000)]
# 步骤一:创建父文件夹:log与子文件夹:scalar_test
with LogWriter(logdir="./log/scalar_test") as writer:
for step in range(1000):
# 步骤二:向记录器添加一个tag为`train/acc`的数据
writer.add_scalar(tag="train/acc", step=step, value=value[step])
# 步骤二:向记录器添加一个tag为`train/loss`的数据
writer.add_scalar(tag="train/loss", step=step, value=1/(value[step] + 1))
# 步骤一:创建第二个子文件夹scalar_test2
value = [i/500.0 for i in range(1000)]
with LogWriter(logdir="./log/scalar_test2") as writer:
for step in range(1000):
# 步骤二:在同样名为`train/acc`下添加scalar_test2的accuracy的数据
writer.add_scalar(tag="train/acc", step=step, value=value[step])
# 步骤二:在同样名为`train/loss`下添加scalar_test2的loss的数据
writer.add_scalar(tag="train/loss", step=step, value=1/(value[step] + 1))
1. 背景介绍
文本分类任务是自然语言处理中最常见的任务,文本分类任务简单来说就是对给定的一个句子或一段文本使用文本分类器进行分类。文本分类任务广泛应用于长短文本分类、情感分析、新闻分类、事件类别分类、政务数据分类、商品信息分类、商品类目预测、文章分类、论文类别分类、专利分类、案件描述分类、罪名分类、意图分类、论文专利分类、邮件自动标签、评论正负识别、药物反应分类、对话分类、税种识别、来电信息自动分类、投诉分类、广告检测、敏感违法内容检测、内容安全检测、舆情分析、话题标记等各类日常或专业领域中。
文本分类任务可以根据标签类型分为多分类(multi class)、多标签(multi label)、层次分类(hierarchical)等三类任务。
进入正片,本项目将演示多分类任务如何通过小样本样本进行模型微调.
数据集情况:
数据概览: 7000 多条酒店评论数据,5000 多条正向评论,2000 多条负向评论
推荐实验: 情感/观点/评论 倾向性分析
数据来源:携程网
原数据集: ChnSentiCorp_htl,由谭松波老师整理的一份数据集
cla.jsonl是数据集demo:
"id":1286,"text":"這間酒店環境和服務態度亦算不錯,但房間空間太小~~不宣容納太大件行李~~且房間格調還可以~~ 中餐廳的廣東點心不太好吃~~要改善之~~~~但算價錢平宜~~可接受~~ 西餐廳格調都很好~~但吃的味道一般且令人等得太耐了~~要改善之~~\\t","label":["正向"]
"id":1287,"text":"<荐书> 推荐所有喜欢<红楼>的红迷们一定要收藏这本书,要知道当年我听说这本书的时候花很长时间去图书馆找和借都没能如愿,所以这次一看到当当有,马上买了,红迷们也要记得备货哦!\\t","label":["正向"]
"id":1288,"text":"商品的不足暂时还没发现,京东的订单处理速度实在.......周二就打包完成,周五才发货...\\t","label":["负向"]
"id":1289,"text":"2001年来福州就住在这里,这次感觉房间就了点,温泉水还是有的.总的来说很满意.早餐简单了些.\\t","label":["正向"]
"id":1290,"text":"不错的上网本,外形很漂亮,操作系统应该是个很大的 卖点,电池还可以。整体上讲,作为一个上网本的定位,还是不错的。\\t","label":["正向"]
"id":1291,"text":"房间地毯太脏,临近火车站十分吵闹,还好是双层玻璃。服务一般,酒店门口的TAXI讲是酒店的长期合作关系,每月要交费给酒店。从酒店到机场讲得是打表147元,到了后非要200元,可能被小宰30-40元。\\t","label":["负向"]
"id":1292,"text":"本来想没事的时候翻翻,可惜看不下去,还是和张没法比,他的书能畅销大部分还是受张的影响,对这个男人实在是没好感,不知道怎么买的,后悔\\t","label":["负向"]
1.1结果展示预览
输入:
酒店环境和服务都还不错,地理位置也不错,尤其是酒店北面的川北凉粉确实好吃。
设施老化,紧靠马路噪音太大。晚上楼上卫生间的水流声和空调噪音非常大,无法入眠。
很不错的一个酒店,床很大,很舒服.酒店员工的服务态度很亲切。
非常糟糕!我们通过其商务中心包了一辆车游西湖,该车拉我们去不正规景点买茶叶。
总的来说,酒店还不错。比较安静,地理位置比较好,服务也不错,包括入住和结账。
输出:
['text': '酒店环境和服务都还不错,地理位置也不错,尤其是酒店北面的川北凉粉确实好吃。\\n', 'label': 'positive', 'score': 0.8420582413673401,
'text': '设施老化,紧靠马路噪音太大。晚上楼上卫生间的水流声和空调噪音非常大,无法入眠。\\n', 'label': 'negative', 'score': 0.9905866980552673,
'text': '很不错的一个酒店,床很大,很舒服.酒店员工的服务态度很亲切。\\n', 'label': 'positive', 'score': 0.9800688028335571,
'text': '非常糟糕!我们通过其商务中心包了一辆车游西湖,该车拉我们去不正规景点买茶叶。\\n', 'label': 'negative', 'score': 0.9315289258956909,
'text': '总的来说,酒店还不错。比较安静,地理位置比较好,服务也不错,包括入住和结账。', 'label': 'positive', 'score': 0.90092933177948]
1.2 数据集加载
!python doccano.py \\
--doccano_file ./data/cla.jsonl \\
--task_type 'cls' \\
--save_dir ./data \\
--splits 0.8 0.1 0.1 \\
--negative_ratio 5 \\
--prompt_prefix "情感倾向" \\
--options "正向" "负向"
2022-07-18 11:28:41,687] [ INFO] - Converting doccano data...
0%| | 0/8 [00:00<?, ?it/s]
[2022-07-18 11:28:41,689] [ INFO] - Converting doccano data...
0%| | 0/1 [00:00<?, ?it/s]
[2022-07-18 11:28:41,690] [ INFO] - Converting doccano data...
0%| | 0/2 [00:00<?, ?it/s]
[2022-07-18 11:28:41,691] [ INFO] - Save 8 examples to ./data/train.txt.
[2022-07-18 11:28:41,691] [ INFO] - Save 1 examples to ./data/dev.txt.
[2022-07-18 11:28:41,691] [ INFO] - Save 2 examples to ./data/test.txt.
[2022-07-18 11:28:41,691] [ INFO] - Finished! It takes 0.00 seconds
doccano_file: 从doccano导出的数据标注文件。
save_dir: 训练数据的保存目录,默认存储在data目录下。
negative_ratio: 最大负例比例,该参数只对抽取类型任务有效,适当构造负例可提升模型效果。负例数量和实际的标签数量有关,最大负例数量 = negative_ratio * 正例数量。该参数只对训练集有效,默认为5。为了保证评估指标的准确性,验证集和测试集默认构造全负例。
splits: 划分数据集时训练集、验证集所占的比例。默认为[0.8, 0.1, 0.1]表示按照8:1:1的比例将数据划分为训练集、验证集和测试集。choices=[‘ext’, ‘cls’]
task_type: 选择任务类型,可选有抽取和分类两种类型的任务。
options: 指定分类任务的类别标签,该参数只对分类类型任务有效。默认为[“正向”, “负向”]。
prompt_prefix: 声明分类任务的prompt前缀信息,该参数只对分类类型任务有效。默认为"情感倾向"。
在数据转换阶段,我们会自动构造用于模型训练的prompt信息。例如句子级情感分类中,prompt为情感倾向[正向,负向],可以通过prompt_prefix和options参数进行声明。
is_shuffle: 是否对数据集进行随机打散,默认为True。
seed: 随机种子,默认为1000.
*separator: 实体类别/评价维度与分类标签的分隔符,该参数只对实体/评价维度级分类任务有效。默认为"##"。
输出部分展示:
"content": "商品的不足暂时还没发现,京东的订单处理速度实在.......周二就打包完成,周五才发货...\\t", "result_list": ["text": "负向", "start": -4, "end": -2], "prompt": "情感倾向[正向,负向]"
"content": "本来想没事的时候翻翻,可惜看不下去,还是和张没法比,他的书能畅销大部分还是受张的影响,对这个男人实在是没好感,不知道怎么买的,后悔\\t", "result_list": ["text": "负向", "start": -7, "end": -5], "prompt": "情感倾向[负向,正向]"
"content": "全键盘带数字键的 显卡足够强大.N卡相对A卡,个人偏向N卡 GHOST XP很容易.除了指纹识别外.所有驱动都能装齐全了,指纹识别,非要在XP下使用的朋友,可以用替代驱动. (华硕官方地址,放心下吧)\\t", "result_list": ["text": "正向", "start": -4, "end": -2], "prompt": "情感倾向[负向,正向]"
"content": "房间地毯太脏,临近火车站十分吵闹,还好是双层玻璃。服务一般,酒店门口的TAXI讲是酒店的长期合作关系,每月要交费给酒店。从酒店到机场讲得是打表147元,到了后非要200元,可能被小宰30-40元。\\t", "result_list": ["text": "负向", "start": -7, "end": -5], "prompt": "情感倾向[负向,正向]"
"content": "<荐书> 推荐所有喜欢<红楼>的红迷们一定要收藏这本书,要知道当年我听说这本书的时候花很长时间去图书馆找和借都没能如愿,所以这次一看到当当有,马上买了,红迷们也要记得备货哦!\\t", "result_list": ["text": "正向", "start": -7, "end": -5], "prompt": "情感倾向[正向,负向]"
2.模型训练
!python finetune.py \\
--train_path "./data/train.txt" \\
--dev_path "./data/dev.txt" \\
--save_dir "./checkpoint" \\
--learning_rate 1e-5 \\
--batch_size 16 \\
--max_seq_len 512 \\
--num_epochs 100 \\
--model "uie-base" \\
--seed 1000 \\
--logging_steps 10 \\
--valid_steps 50 \\
--device "gpu"
部分训练效果展示:具体输出已折叠
(由于训练样本比较少,且比较简单所有很容易就达到F1=100%)
[2022-07-17 11:33:46,088] [ INFO] - global step 10, epoch: 10, loss: 0.00021, speed: 1.50 step/s
[2022-07-17 11:33:52,276] [ INFO] - global step 20, epoch: 20, loss: 0.00011, speed: 1.62 step/s
[2022-07-17 11:33:58,431] [ INFO] - global step 30, epoch: 30, loss: 0.00007, speed: 1.62 step/s
[2022-07-17 11:34:04,630] [ INFO] - global step 40, epoch: 40, loss: 0.00006, speed: 1.61 step/s
[2022-07-17 11:34:10,816] [ INFO] - global step 50, epoch: 50, loss: 0.00005, speed: 1.62 step/s
[2022-07-17 11:34:10,863] [ INFO] - Evaluation precision: 1.00000, recall: 1.00000, F1: 1.00000
[2022-07-17 11:34:10,863] [ INFO] - best F1 performence has been updated: 0.00000 --> 1.00000
[2022-07-17 11:34:11,996] [ INFO] - tokenizer config file saved in ./checkpoint/model_best/tokenizer_config.json
[2022-07-17 11:34:11,997] [ INFO] - Special tokens file saved in ./checkpoint/model_best/special_tokens_map.json
[2022-07-17 11:34:18,202] [ INFO] - global step 60, epoch: 60, loss: 0.00004, speed: 1.61 step/s
[2022-07-17 11:34:24,355] [ INFO] - global step 70, epoch: 70, loss: 0.00003, speed: 1.63 step/s
[2022-07-17 11:34:30,515] [ INFO] - global step 80, epoch: 80, loss: 0.00003, speed: 1.62 step/s
[2022-07-17 11:34:36,700] [ INFO] - global step 90, epoch: 90, loss: 0.00003, speed: 1.62 step/s
[2022-07-17 11:34:42,851] [ INFO] - global step 100, epoch: 100, loss: 0.00002, speed: 1.63 step/s
[2022-07-17 11:34:42,897] [ INFO] - Evaluation precision: 1.00000, recall: 1.00000, F1: 1.00000
推荐使用GPU环境,否则可能会内存溢出。CPU环境下,可以修改model为uie-tiny,适当调下batch_size。
增加准确率的话:–num_epochs 设置大点多训练训练
可配置参数说明:
train_path: 训练集文件路径。
dev_path: 验证集文件路径。
save_dir: 模型存储路径,默认为./checkpoint。
learning_rate: 学习率,默认为1e-5。
batch_size: 批处理大小,请结合显存情况进行调整,若出现显存不足,请适当调低这一参数,默认为16。
max_seq_len: 文本最大切分长度,输入超过最大长度时会对输入文本进行自动切分,默认为512。
num_epochs: 训练轮数,默认为100。
model 选择模型,程序会基于选择的模型进行模型微调,可选有uie-base和uie-tiny,默认为uie-base。
seed: 随机种子,默认为1000.
logging_steps: 日志打印的间隔steps数,默认10。
valid_steps: evaluate的间隔steps数,默认100。
device: 选用什么设备进行训练,可选cpu或gpu。
3.模型评估
!python evaluate.py \\
--model_path ./checkpoint/model_best \\
--test_path ./data/test.txt \\
--batch_size 16 \\
--max_seq_len 512
[2022-07-18 11:37:05,934] [ INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load './checkpoint/model_best'.
W0718 11:37:05.965226 2210 gpu_context.cc:278] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.2, Runtime API Version: 10.1
W0718 11:37:05.969079 2210 gpu_context.cc:306] device: 0, cuDNN Version: 7.6.
[2022-07-18 11:37:11,584] [ INFO] - ------以上是关于Paddlenlp之UIE分类模型以情感倾向分析新闻分类为例含智能标注方案)的主要内容,如果未能解决你的问题,请参考以下文章
PaddleNLP基于ERNIR3.0文本分类:WOS数据集为例(层次分类)
PaddleNLP基于ERNIR3.0文本分类以CAIL2018-SMALL数据集罪名预测任务为例多标签
PaddleNLP基于ERNIR3.0文本分类以中医疗搜索检索词意图分类(KUAKE-QIC)为例多分类(单标签)
应用实践:Paddle分类模型大集成者[PaddleHubFinetuneprompt]