Paddlenlp之UIE分类模型【以情感倾向分析、新闻分类为例】--------(含智能标注讲解方案、可视化等)

0 前言

首先回顾上一个项目:
Paddlenlp之UIE模型实战实体抽取任务【打车数据、快递单】

会存在以下问题:

  1. 自己样本数据该如何标注

  2. 如果样本量大有什么好方法进行智能标注

  3. 可视化工具详细介绍

本次项目将会先把,数据标注、智能标注、数据可视化方法
进行详细讲解。

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

注释部分包含预训练模型识别实体;以及精灵标注助手格式要求

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数据服务介绍

闪亮登场!!!!

EasyData 为百度大脑推出的一站式数据处理和服务平台,主要围绕AI模型开发过程中所需的数据采集、数据质检、数据智能处理、数据标注等环节提供完整的数据服务。目前EasyData已支持图片、文本、音频、视频、表格五类基础数据的处理。
同时EasyData已与EasyDL、BML平台数据管理模块打通,EasyData处理的数据可直接应用于EasyDL、BML平台进行模型训练。

在这里插入图片描述

在这里插入图片描述

功能齐全很强大,小伙伴们可以试一试哦!

我就不做更多推广了,,信百度没问题,哈哈。

0.4 visualDL工具使用,可视化利器。

VisualDL 是一个面向深度学习任务设计的可视化工具。VisualDL 利用了丰富的图表来展示数据,用户可以更直观、清晰地查看数据的特征与变化趋势,有助于分析数据、及时发现错误,进而改进神经网络模型的设计。

目前,VisualDL 支持 scalar, image, audio, graph, histogram, prcurve, high dimensional 七个组件.

在这里插入图片描述

不进行过多介绍,可以参考我的项目或者博客。有详细讲解!!!

paddle之visualDL工具使用,可视化利器。

VisualDL2.0可视化展示

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}]
! pip install --upgrade paddlenlp

1.2 数据集加载

! cp 数据集/cla.jsonl ./data/
!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 "正向" "负向"

[32m[2022-07-18 11:28:41,687] [    INFO][0m - Converting doccano data...[0m
  0%|                                                     | 0/8 [00:00<?, ?it/s]
[32m[2022-07-18 11:28:41,689] [    INFO][0m - Converting doccano data...[0m
  0%|                                                     | 0/1 [00:00<?, ?it/s]
[32m[2022-07-18 11:28:41,690] [    INFO][0m - Converting doccano data...[0m
  0%|                                                     | 0/2 [00:00<?, ?it/s]
[32m[2022-07-18 11:28:41,691] [    INFO][0m - Save 8 examples to ./data/train.txt.[0m
[32m[2022-07-18 11:28:41,691] [    INFO][0m - Save 1 examples to ./data/dev.txt.[0m
[32m[2022-07-18 11:28:41,691] [    INFO][0m - Save 2 examples to ./data/test.txt.[0m
[32m[2022-07-18 11:28:41,691] [    INFO][0m - Finished! It takes 0.00 seconds[0m
[0m

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"
[2022-07-18 11:36:15,820] [    INFO] - Downloading resource files...
[2022-07-18 11:36:15,822] [    INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load 'uie-base'.
W0718 11:36:15.858469  1767 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:36:15.863831  1767 gpu_context.cc:306] device: 0, cuDNN Version: 7.6.
[2022-07-18 11:36:24,342] [    INFO] - global step 10, epoch: 10, loss: 0.00021, speed: 3.57 step/s
[2022-07-18 11:36:27,000] [    INFO] - global step 20, epoch: 20, loss: 0.00011, speed: 3.76 step/s
[2022-07-18 11:36:29,652] [    INFO] - global step 30, epoch: 30, loss: 0.00007, speed: 3.77 step/s
[2022-07-18 11:36:32,325] [    INFO] - global step 40, epoch: 40, loss: 0.00006, speed: 3.74 step/s
[2022-07-18 11:36:34,991] [    INFO] - global step 50, epoch: 50, loss: 0.00005, speed: 3.75 step/s
[2022-07-18 11:36:36,053] [    INFO] - Evaluation precision: 1.00000, recall: 1.00000, F1: 1.00000
[2022-07-18 11:36:36,053] [    INFO] - best F1 performence has been updated: 0.00000 --> 1.00000
[2022-07-18 11:36:39,661] [    INFO] - global step 60, epoch: 60, loss: 0.00004, speed: 3.75 step/s
[2022-07-18 11:36:42,336] [    INFO] - global step 70, epoch: 70, loss: 0.00003, speed: 3.74 step/s
[2022-07-18 11:36:45,007] [    INFO] - global step 80, epoch: 80, loss: 0.00003, speed: 3.75 step/s
[2022-07-18 11:36:47,690] [    INFO] - global step 90, epoch: 90, loss: 0.00003, speed: 3.73 step/s
[2022-07-18 11:36:50,358] [    INFO] - global step 100, epoch: 100, loss: 0.00002, speed: 3.75 step/s
[2022-07-18 11:36:51,336] [    INFO] - Evaluation precision: 1.00000, recall: 1.00000, F1: 1.00000

部分训练效果展示:具体输出已折叠
(由于训练样本比较少,且比较简单所有很容易就达到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] - -----------------------------
[2022-07-18 11:37:11,584] [    INFO] - Class Name: all_classes
[2022-07-18 11:37:11,584] [    INFO] - Evaluation Precision: 1.00000 | Recall: 1.00000 | F1: 1.00000
!python evaluate.py \
    --model_path ./checkpoint/model_best \
    --test_path ./data/test.txt \
    --debug
[2022-07-18 11:37:16,422] [    INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load './checkpoint/model_best'.
W0718 11:37:16.453305  2233 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:16.457065  2233 gpu_context.cc:306] device: 0, cuDNN Version: 7.6.
[2022-07-18 11:37:22,474] [    INFO] - -----------------------------
[2022-07-18 11:37:22,475] [    INFO] - Class Name: 情感倾向[正向,负向]
[2022-07-18 11:37:22,475] [    INFO] - Evaluation Precision: 1.00000 | Recall: 1.00000 | F1: 1.00000

model_path: 进行评估的模型文件夹路径,路径下需包含模型权重文件model_state.pdparams及配置文件model_config.json。

test_path: 进行评估的测试集文件。

batch_size: 批处理大小,请结合机器情况进行调整,默认为16。

max_seq_len: 文本最大切分长度,输入超过最大长度时会对输入文本进行自动切分,默认为512。

model: 选择所使用的模型,可选有uie-base, uie-medium, uie-mini, uie-micro和uie-nano,默认为uie-base。

debug: 是否开启debug模式对每个正例类别分别进行评估,该模式仅用于模型调试,默认关闭。


4.结果预测

from pprint import pprint
import json
from paddlenlp import Taskflow

def openreadtxt(file_name):
    data = []
    file = open(file_name,'r',encoding='UTF-8')  #打开文件
    file_data = file.readlines() #读取所有行
    for row in file_data:
        data.append(row) #将每行数据插入data中     
    return data

data_input=openreadtxt('./input/nlp.txt')

schema = '情感倾向[正向,负向]'
few_ie = Taskflow('information_extraction', schema=schema, batch_size=32,task_path='./checkpoint/model_best')
# few_ie = Taskflow('sentiment_analysis', schema=schema, batch_size=32,task_path='./checkpoint/model_best')
results=few_ie(data_input)

with open("./output/result.txt", "w+",encoding='UTF-8') as f:    #a :   写入文件,若文件不存在则会先创建再写入,但不会覆盖原文件,而是追加在文件末尾
    for result in results:
        line = json.dumps(result, ensure_ascii=False)  #对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False
        f.write(line + "\n")

print("数据结果已导出")
print(results)

[2022-07-18 11:37:27,518] [    INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load './checkpoint/model_best'.
[2022-07-18 11:37:28,913] [    INFO] - Converting to the inference model cost a little time.
[2022-07-18 11:37:39,279] [    INFO] - The inference model save in the path:./checkpoint/model_best/static/inference


数据结果已导出
[{'情感倾向[正向,负向]': [{'text': '正向', 'probability': 0.9987503023847921}]}, {'情感倾向[正向,负向]': [{'text': '负向', 'probability': 0.9998301339458919}]}, {'情感倾向[正向,负向]': [{'text': '正向', 'probability': 0.9998291803635482}]}, {'情感倾向[正向,负向]': [{'text': '负向', 'probability': 0.9997725616039332}]}, {'情感倾向[正向,负向]': [{'text': '正向', 'probability': 0.9986501116594084}]}]

输入文件展示:

酒店环境和服务都还不错,地理位置也不错,尤其是酒店北面的川北凉粉确实好吃。
设施老化,紧靠马路噪音太大。晚上楼上卫生间的水流声和空调噪音非常大,无法入眠。
很不错的一个酒店,床很大,很舒服.酒店员工的服务态度很亲切。
非常糟糕!我们通过其商务中心包了一辆车游西湖,该车拉我们去不正规景点买茶叶。
总的来说,酒店还不错。比较安静,地理位置比较好,服务也不错,包括入住和结账。

输出展示:

[{'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}]

当然paddlenlp也专门提供了情感分析的两个模型,默认使用的是BiLSTM。
以及SKEP。

集成百度自研的情感知识增强预训练模型SKEP,利用情感知识构建预训练目标,在海量中文数据上进行预训练,为各类情感分析任务提供统一且强大的情感语义表示能力。情感预训练模型SKEP(Sentiment Knowledge Enhanced Pre-training for Sentiment Analysis)。SKEP利用情感知识增强预训练模型, 在14项中英情感分析典型任务上全面超越SOTA,此工作已经被ACL 2020录用。SKEP是百度研究团队提出的基于情感知识增强的情感预训练算法,此算法采用无监督方法自动挖掘情感知识,然后利用情感知识构建预训练目标,从而让机器学会理解情感语义。SKEP为各类情感分析任务提供统一且强大的情感语义表示。

SKEP: Sentiment Knowledge Enhanced Pre-training for Sentiment Analysis

from pprint import pprint
import json
from paddlenlp import Taskflow

def openreadtxt(file_name):
    data = []
    file = open(file_name,'r',encoding='UTF-8')  #打开文件
    file_data = file.readlines() #读取所有行
    for row in file_data:
        data.append(row) #将每行数据插入data中     
    return data

data_input=openreadtxt('./input/nlp.txt')

schema = '情感倾向[正向,负向]'
few_ie = Taskflow("sentiment_analysis", schema=schema,model="skep_ernie_1.0_large_ch", batch_size=16)

results=few_ie(data_input)

with open("./output/result.txt", "w+",encoding='UTF-8') as f:    #a :   写入文件,若文件不存在则会先创建再写入,但不会覆盖原文件,而是追加在文件末尾
    for result in results:
        line = json.dumps(result, ensure_ascii=False)  #对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False
        f.write(line + "\n")

print("数据结果已导出")
print(results)
[2022-07-17 22:06:01,067] [    INFO] - Already cached /home/aistudio/.paddlenlp/models/skep_ernie_1.0_large_ch/skep_ernie_1.0_large_ch.vocab.txt
[2022-07-17 22:06:01,079] [    INFO] - tokenizer config file saved in /home/aistudio/.paddlenlp/models/skep_ernie_1.0_large_ch/tokenizer_config.json
[2022-07-17 22:06:01,082] [    INFO] - Special tokens file saved in /home/aistudio/.paddlenlp/models/skep_ernie_1.0_large_ch/special_tokens_map.json


数据结果已导出
[{'text': '酒店环境和服务都还不错,地理位置也不错,尤其是酒店北面的川北凉粉确实好吃。\n', 'label': 'positive', 'score': 0.9441452622413635}, {'text': '设施老化,紧靠马路噪音太大。晚上楼上卫生间的水流声和空调噪音非常大,无法入眠。\n', 'label': 'negative', 'score': 0.991821825504303}, {'text': '很不错的一个酒店,床很大,很舒服.酒店员工的服务态度很亲切。\n', 'label': 'positive', 'score': 0.989535927772522}, {'text': '非常糟糕!我们通过其商务中心包了一辆车游西湖,该车拉我们去不正规景点买茶叶。\n', 'label': 'negative', 'score': 0.9811170697212219}, {'text': '总的来说,酒店还不错。比较安静,地理位置比较好,服务也不错,包括入住和结账。', 'label': 'positive', 'score': 0.8622702360153198}]

5.可视化显示visualDL

详细文档可以参考:
https://aistudio.baidu.com/aistudio/projectdetail/1739945?contributionType=1
有详细讲解,具体实现参考代码,

核心是:添加一个初始化记录器

下面是结果展示:

由于数据训练很少,评测指标可能显示的就不是很多了,如下:

在这里插入图片描述

6.活学活用(新闻文本分类demo)

获取相关数据集,然后经过处理,这里选取了农业、金融、房产的部分数据仅作为测试方案可行性

import json
import numpy as np
import pandas as pd
from paddlenlp.datasets import load_dataset

train_ds, dev_ds, test_ds = load_dataset('clue', 'tnews', splits=["train", "dev", "test"])

with open("./output/test2.txt", "w+",encoding='UTF-8') as f:    #a :   写入文件,若文件不存在则会先创建再写入,但不会覆盖原文件,而是追加在文件末尾
    for result in test_ds:
        line = json.dumps(result, ensure_ascii=False)  #对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False
        f.write(line + "\n")

with open("./output/test1.txt", "w+",encoding='UTF-8') as f:    #a :   写入文件,若文件不存在则会先创建再写入,但不会覆盖原文件,而是追加在文件末尾
    for result in dev_ds:
        line = json.dumps(result, ensure_ascii=False)  #对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False
        f.write(line + "\n")

print("数据已导出")


数据已导出
! cp 数据集/input.jsonl ./data/
! pip install --upgrade paddlenlp

!python doccano.py \
    --doccano_file ./data/input.jsonl \
    --task_type 'cls' \
    --save_dir ./data \
    --splits 0.85 0.15 0 \
    --negative_ratio 5 \
    --prompt_prefix "新闻分类" \
    --options "农业" "房产" "金融"
[2022-07-18 17:38:00,557] [    INFO] - Converting doccano data...
  0%|                                                    | 0/12 [00:00<?, ?it/s]
[2022-07-18 17:38:00,559] [    INFO] - Converting doccano data...
  0%|                                                     | 0/3 [00:00<?, ?it/s]
[2022-07-18 17:38:00,560] [    INFO] - Converting doccano data...
0it [00:00, ?it/s]
[2022-07-18 17:38:00,561] [    INFO] - Save 12 examples to ./data/train.txt.
[2022-07-18 17:38:00,561] [    INFO] - Save 3 examples to ./data/dev.txt.
[2022-07-18 17:38:00,561] [    INFO] - Save 0 examples to ./data/test.txt.
[2022-07-18 17:38:00,561] [    INFO] - Finished! It takes 0.00 seconds
!python finetune.py \
    --train_path "./data/train.txt" \
    --dev_path "./data/dev.txt" \
    --save_dir "./checkpoint2" \
    --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"
[2022-07-18 17:42:27,804] [    INFO] - Downloading resource files...
[2022-07-18 17:42:27,806] [    INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load 'uie-base'.
W0718 17:42:27.833335  8453 gpu_context.cc:278] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.2, Runtime API Version: 10.1
W0718 17:42:27.836701  8453 gpu_context.cc:306] device: 0, cuDNN Version: 7.6.
[2022-07-18 17:42:37,127] [    INFO] - global step 10, epoch: 10, loss: 0.00761, speed: 2.66 step/s
[2022-07-18 17:42:40,756] [    INFO] - global step 20, epoch: 20, loss: 0.00583, speed: 2.76 step/s
[2022-07-18 17:42:44,393] [    INFO] - global step 30, epoch: 30, loss: 0.00518, speed: 2.75 step/s
[2022-07-18 17:42:48,054] [    INFO] - global step 40, epoch: 40, loss: 0.00481, speed: 2.73 step/s
[2022-07-18 17:42:51,690] [    INFO] - global step 50, epoch: 50, loss: 0.00458, speed: 2.75 step/s
[2022-07-18 17:42:52,694] [    INFO] - Evaluation precision: 0.00000, recall: 0.00000, F1: 0.00000
[2022-07-18 17:42:56,333] [    INFO] - global step 60, epoch: 60, loss: 0.00441, speed: 2.75 step/s
[2022-07-18 17:42:59,983] [    INFO] - global step 70, epoch: 70, loss: 0.00426, speed: 2.74 step/s
[2022-07-18 17:43:03,648] [    INFO] - global step 80, epoch: 80, loss: 0.00409, speed: 2.73 step/s
[2022-07-18 17:43:07,303] [    INFO] - global step 90, epoch: 90, loss: 0.00389, speed: 2.74 step/s
[2022-07-18 17:43:10,960] [    INFO] - global step 100, epoch: 100, loss: 0.00366, speed: 2.74 step/s
[2022-07-18 17:43:11,928] [    INFO] - Evaluation precision: 0.66667, recall: 0.66667, F1: 0.66667
[2022-07-18 17:43:11,928] [    INFO] - best F1 performence has been updated: 0.00000 --> 0.66667
!python evaluate.py \
    --model_path ./checkpoint2/model_best \
    --test_path ./data/dev.txt \
    --debug
[2022-07-18 17:45:15,480] [    INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load './checkpoint2/model_best'.
W0718 17:45:15.507431  9061 gpu_context.cc:278] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.2, Runtime API Version: 10.1
W0718 17:45:15.510736  9061 gpu_context.cc:306] device: 0, cuDNN Version: 7.6.
[2022-07-18 17:45:20,720] [    INFO] - -----------------------------
[2022-07-18 17:45:20,720] [    INFO] - Class Name: 新闻分类[农业,房产,金融]
[2022-07-18 17:45:20,720] [    INFO] - Evaluation Precision: 0.66667 | Recall: 0.66667 | F1: 0.66667
from pprint import pprint
import json
from paddlenlp import Taskflow

schema = '新闻分类[农业,房产,金融]'
few_ie = Taskflow('information_extraction', schema=schema,task_path='./checkpoint2/model_best')
results=few_ie(["美元再次举起金融屠刀,这10个国家的经济或即将岌岌可危”,西城再曝10家违规中介","维基链时代来临,瞬间将袭卷整个中国,暴涨800%!抢到即赚到!","我省发布今年全省城乡居民最低生活保障标准低限"])

print(results)
[2022-07-18 18:02:00,621] [    INFO] - We are using <class 'paddlenlp.transformers.ernie.tokenizer.ErnieTokenizer'> to load './checkpoint2/model_best'.


[{'新闻分类[农业,房产,金融]': [{'text': '金融', 'probability': 0.8674286780486753}]}, {'新闻分类[农业,房产,金融]': [{'text': '农业', 'probability': 0.4909489670645364}]}, {'新闻分类[农业,房产,金融]': [{'text': '农业', 'probability': 0.980139386504348}]}]

可以看出结果还是不错的,效果性能有,不过还是推荐使用专门的分类模型,ernie-3.0-效果会更好

7.总结

UIE(Universal Information Extraction):Yaojie Lu等人在ACL-2022中提出了通用信息抽取统一框架UIE。该框架实现了实体抽取、关系抽取、事件抽取、情感分析等任务的统一建模,并使得不同任务间具备良好的迁移和泛化能力。PaddleNLP借鉴该论文的方法,基于ERNIE 3.0知识增强预训练模型,训练并开源了首个中文通用信息抽取模型UIE。该模型可以支持不限定行业领域和抽取目标的关键信息抽取,实现零样本快速冷启动,并具备优秀的小样本微调能力,快速适配特定的抽取目标。

UIE的优势

使用简单: 用户可以使用自然语言自定义抽取目标,无需训练即可统一抽取输入文本中的对应信息。实现开箱即用,并满足各类信息抽取需求。

降本增效: 以往的信息抽取技术需要大量标注数据才能保证信息抽取的效果,为了提高开发过程中的开发效率,减少不必要的重复工作时间,开放域信息抽取可以实现零样本(zero-shot)或者少样本(few-shot)抽取,大幅度降低标注数据依赖,在降低成本的同时,还提升了效果。

效果领先: 开放域信息抽取在多种场景,多种任务上,均有不俗的表现。

本人本次主要通过情感分类这个案例分享给大家,主要对开源的paddlenlp的案例进行了细化,比如在结果可视化方面以及结果输入输出的增加,使demo项目更佳完善。

当然大家可以在项目基础上替换数据集,做成一个分类模型试试效果!先从二分类—多分类一一尝试

本人博客:https://blog.csdn.net/sinat_39620217?type=blog

此文仅为搬运,原作链接:https://aistudio.baidu.com/aistudio/projectdetail/4337189

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐