基于ERNIR3.0模型的文本分类的开发与实践

参考:基于ERNIR3.0文本分类:(KUAKE-QIC)意图识别多分类(单标签) - 飞桨AI Studio星河社区 (baidu.com)

https://zhuanlan.zhihu.com/p/574666812?utm_id=0

文本分类任务是自然语言处理中最常见的任务,文本分类任务简单来说就是对给定的一个句子或一段文本使用文本分类器进行分类。

文本分类任务广泛应用于长短文本分类、情感分析、新闻分类、事件类别分类、政务数据分类、商品信息分类、商品类目预测、文章分类、论文类别分类、专利分类、案件描述分类、罪名分类、意图分类、论文专利分类、邮件自动标签、评论正负识别、药物反应分类、对话分类、税种识别、来电信息自动分类、投诉分类、广告检测、敏感违法内容检测、内容安全检测、舆情分析、话题标记等各类日常或专业领域中。

常见问题:

总体流程

主要场景

文本分类任务可以根据标签类型分为多分类(multi class)、多标签(multi label)、层次分类(hierarchical等三类任务,接下来我们将以下图的新闻文本分类为例介绍三种分类任务的区别。

  • 多分类🚶: 数据集的标签集含有两个或两个以上的类别,所有输入句子/文本有且只有一个标签。在文本多分类场景中,我们需要预测输入句子/文本最可能来自 n 个标签类别中的哪一个类别。以上图多分类中新闻文本为例,该新闻文本的标签为 娱乐。快速开启多分类任务参见 👉 多分类指南

  • 多标签👫 : 数据集的标签集含有两个或两个以上的类别,输入句子/文本具有一个或多个标签。在文本多标签任务中,我们需要预测输入句子/文本可能来自 n 个标签类别中的哪几个类别。以上图多标签中新闻文本为例,该新闻文本具有 相机 和 芯片 两个标签。快速开启多标签任务参见 👉 多标签指南 。

  • 层次分类👪 : 数据集的标签集具有多级标签且标签之间具有层级结构关系,输入句子/文本具有一个或多个标签。在文本层次分类任务中,我们需要预测输入句子/文本可能来自于不同级标签类别中的某一个或几个类别。以上图层次分类中新闻文本为例(新闻为根节点),该新闻一级分类标签为 体育,二级分类标签为 足球

问题 ---- 分类

问题 ---- lable --- {新闻,娱乐}

问题--分类级别1--分类级别2

方案一 :预训练模型微调

【方案选择】对于大多数任务,我们推荐使用预训练模型微调作为首选的文本分类方案,预训练模型微调提供了数据标注-模型训练-模型分析-模型压缩-预测部署全流程,有效减少开发时间,低成本迁移至实际应用场景。

预训练模型微调在预训练模型 [CLS] 输出向量后接入线性层作为文本分类器,用具体任务数据进行微调训练文本分类器,使预训练模型”更懂”这个任务。

[CLS] 输出向量 是针对整个输入序列的全局表示,用于下游任务中对序列的整体属性进行分类或判断,

 [MASK] 向量 则是在预训练阶段代表被遮盖词汇的预测表示,用于学习语言模型并通过预测任务提升模型对上下文的理解能力。

方案二:提示学习

提示学习(Prompt Learning)适用于标注成本高、标注样本较少的文本分类场景。在小样本场景中,相比于预训练模型微调学习,提示学习能取得更好的效果。

提示学习的主要思想是将文本分类任务转换为构造提示中掩码 [MASK] 的分类预测任务,也即在掩码 [MASK]向量后接入线性层分类器预测掩码位置可能的字或词。提示学习使用待预测字的预训练向量来初始化分类器参数(如果待预测的是词,则为词中所有字的预训练向量平均值),充分利用预训练语言模型学习到的特征和标签文本,从而降低样本需求。

我们以下图情感二分类任务为例来具体介绍提示学习流程,分类任务标签分为 0:负向 和 1:正向 。在文本加入构造提示 我[MASK]喜欢。 ,将情感分类任务转化为预测掩码 [MASK] 的待预测字是  还是 。具体实现方法是在掩码[MASK]的输出向量后接入线性分类器(二分类),然后用的预训练向量来初始化分类器进行训练,分类器预测分类为 0:不 或 1:很 对应原始标签 0:负向 或 1:正向

预训练模型微调则是在预训练模型[CLS]向量接入随机初始化线性分类器进行训练,分类器直接预测分类为 0:负向 或 1:正向

二分类/多分类任务指南

基于预训练模型微调的二分类/多分类端到端应用方案,打通数据标注-模型训练-模型调优-模型压缩-预测部署全流程

二分类/多分类数据集的标签集含有两个或两个以上的类别,所有输入句子/文本有且只有一个标签。在文本多分类场景中,我们需要预测输入句子/文本最可能来自 n 个标签类别中的哪一个类别。在本项目中二分类任务被视为多分类任务中标签集包含两个类别的情况,以下统一称为多分类任务。

多分类数据标注-模型训练-模型分析-模型压缩-预测部署流程图

https://github.com/PaddlePaddle/PaddleNLP/tree/develop/applications/text_classification/multi_class#readme

2.2 代码结构

2.3 数据准备

训练需要准备指定格式的本地数据集

训练、开发、测试数据集 文件中文本与标签类别名用tab符'\t'分隔开,文本中避免出现tab符'\t'

2.4 模型训练

python3 train.py --do_train --do_eval --do_export --model_name_or_path ernie-3.0-tiny-medium-v2-zh --output_dir checkpoint --device gpu:0 --early_stopping_patience 5 --learning_rate 3e-5 --max_length 128 --metric_for_best_model accuracy --load_best_model_at_end --logging_steps 5 --evaluation_strategy epoch --save_strategy epoch --save_total_limit 1

2.4.2 训练评估

训练后的模型我们可以开启debug模式,对每个类别分别进行评估,并打印错误预测样本保存在bad_case.txt

python train.py --do_eval --debug --device gpu:0 --model_name_or_path checkpoint --output_dir checkpoint --per_device_eval_batch_size 32 --max_length 128 --test_path './data/dev.txt'

2.5 模型预测 和 服务化
# 用于启动FastAPI应用的ASGI服务器。
import uvicorn
# FastAPI的核心组件,分别用于构建API应用和处理HTTP请求。
from fastapi import FastAPI,Request
# (来自anyio库):用于在FastAPI应用启动时设置线程限制。
from anyio.lowlevel import RunVar
from anyio import CapacityLimiter
import json
from typing import List
# PaddlePaddle深度学习框架
import paddle
from fastapi import FastAPI
# 基于PaddlePaddle的自然语言处理库
from paddlenlp import Taskflow
from paddlenlp.transformers import ErnieTokenizer, ErnieModel


# 加载静态文本分类模型
cls = Taskflow("text_classification", task_path='../../checkpoint/export', is_static_model=True)

# 加载ERNIE 3.0模型  
model = ErnieModel.from_pretrained('ernie-3.0-tiny-medium-v2-zh')
# 加载预训练模型参数 
model.set_state_dict(paddle.load('ernie_3.0_text_classification_model.pdparams')) 
# 将模型设置为评估模式  
model.eval()
# 加载分词器
tokenizer = ErnieTokenizer.from_pretrained('ernie-3.0-tiny-medium-v2-zh')

# 创建一个FastAPI实例app
app = FastAPI()

# 在应用启动时(@app.on_event("startup")),设置默认线程限制器为单线程(容量限制为1)
@app.on_event("startup")
def startup():
    print("start")
    RunVar("_default_thread_limiter").set(CapacityLimiter(1))

# 定义一个名为encode的异步路由,处理/路径上的POST请求
@app.post("/")
async def encode(request: Request):
    # 从请求体中获取并解析JSON数据
    request_body = await request.body()
    request_json = json.loads(request_body.decode("utf-8"))

    texts = [item["text"] for item in request_json["data"]]
    print(texts)

    result = cls(texts)
    print(result)
    # 使用分词器对文本进行编码
    # 对texts中的文本进行分词、填充、截断等预处理操作,生成模型所需的输入张量encoded_input(最大序列长度为512    
    encoded_input = tokenizer(texts, padding=True, truncation=True, return_tensors='pd', max_seq_len=512)
    #print(encoded_input)

    #使用无梯度计算模式(with paddle.no_grad():)执行以下步骤
    #获取模型的输出向量(第二个返回值),转换为NumPy数组并存储到search_vector 
    with paddle.no_grad():
        search_vector = model(**encoded_input)[1].numpy()
    #print(search_vector)

    print("end vector")
    # 获取预测结果  
    with paddle.no_grad():  
        logits = model(**encoded_input)  
        print(logits)
        probs = paddle.nn.functional.softmax(logits, axis=-1)  #计算模型输出的softmax概率分布(probs)  
        print(probs)
        #print("Shape of probs:", probs.shape)
        #print("Type of probs:", type(probs))
        print(paddle.argmax(probs, axis=-1))  #找出每个样本的概率最大类别的索引(paddle.argmax(probs, axis=-1) 
        print("this one")
        print(paddle.argmax(probs, axis=-1).numpy())
        print("this two") 
        #predicted_class = paddle.argmax(probs, axis=-1).numpy()[0]  # 获取概率最高的类别索引
        #print("this three " + predicted_class) 

    # 每个输入文本经过模型编码后得到的向量表示(列表形式) 
    return {"predictionList": result, "vector": search_vector.tolist(), "predicted_class": predicted_class}

nohup python3 -m uvicorn main:app --host 192.168.1.243 --port 4001 > paddlenlpweb.log 2>&1 &

检验:

curl -X POST http://192.168.1.243:4001/  -H 'Content-Type: application/json'  -d '{"data":[{"text": "智能大屏"}, {"text": "智能销售"}]}'

------------------------------------------------------------------------------------------------------------------------------

PaddleNLP 可指定模型名或模型参数文件路径通过from_pretrained() 方法加载不同网络结构的预训练模型,并在输出层上叠加一层线性层,且相应预训练模型权重下载速度快、稳定。

Transformer预训练模型汇总包含了如 ERNIE、BERT、RoBERTa等40多个主流预训练模型,500多个模型权重。

1.数据准备

  1.1加载数据集、自定义数据集

目前PaddleNLP的通用数据处理流程如下:

  1. 加载数据集(内置数据集或者自定义数据集,数据集返回 原始数据)。

  2. 定义 trans_func() ,包括tokenize,token to id等操作,并传入数据集的 map() 方法,将原始数据转为 feature 。

  3. 根据上一步数据处理的结果定义 batchify 方法和 BatchSampler 。

  4. 定义 DataLoader , 传入 BatchSampler 和 batchify_fn() 。

遇到的问题:如下 采用paddleNLP下文本分类实例进行分类训练后发现 生成的模型分类不准。打算自己开发脚本进行分类计算再进行服务化部署。

基于ERNIR3.0文本分类任务模型微调

PaddleNLP采用AutoModelForSequenceClassificationAutoTokenizer提供了方便易用的接口,可指定模型名或模型参数文件路径通过from_pretrained() 方法加载不同网络结构的预训练模型,并在输出层上叠加一层线性层,且相应预训练模型权重下载速度快、稳定。Transformer预训练模型汇总包含了如 ERNIE、BERT、RoBERTa等40多个主流预训练模型,500多个模型权重。下面以ERNIE 3.0 中文base模型为例,演示如何加载预训练模型和分词器:

from paddlenlp.transformers import AutoModelForSequenceClassification, AutoTokenizer
num_classes = 10
model_name = "ernie-3.0-base-zh"
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_classes=num_classes)
tokenizer = AutoTokenizer.from_pretrained(model_name)

BML Codelab (baidu.com)

代码如下 

pip install openpyxl

import pandas as pd

import paddle

from paddlenlp.transformers import ErnieForSequenceClassification, ErnieTokenizer

# 1. 读取Excel文件中的数据

data = pd.read_excel('sentense3.xlsx')

texts = data['sentences'].tolist()

labels = data['labels'].tolist()

# 查看数据

data

# 2. 初始化 ERNIE 3.0 模型和分词器

model_name = 'ernie-3.0-tiny-medium-v2-zh'  # 根据实际情况选择模型版本

tokenizer = ErnieTokenizer.from_pretrained(model_name)

num_classes = len(set(labels))  # 计算类别数量

model = ErnieForSequenceClassification.from_pretrained(model_name, num_classes=num_classes)

# 3. 数据预处理

def preprocess_fn(text, label):

    inputs = tokenizer(text, max_seq_length=128, padding='max_length', truncation=True)

    return {'input_ids': paddle.to_tensor(inputs['input_ids']),

            'token_type_ids': paddle.to_tensor(inputs['token_type_ids']),

            'label': paddle.to_tensor([label])}

# 转换为模型可以接受的数据格式

dataset = [preprocess_fn(text, label) for text, label in zip(texts, labels)]

# 4. 创建数据加载器

batch_size = 6

data_loader = paddle.io.DataLoader(dataset, batch_size=batch_size, shuffle=True)

# 5. 定义优化器和其他训练参数

optimizer = paddle.optimizer.AdamW(parameters=model.parameters(), learning_rate=5e-5)

num_epochs = 3  #根据实际需求调整训练轮数

# 6. 开始训练

for epoch in range(num_epochs):

    lit = 0

    for batch_data in data_loader:

        input_ids = batch_data['input_ids']

        token_type_ids = batch_data['token_type_ids']

        labels = batch_data['label']

        logits = model(input_ids=input_ids, token_type_ids=token_type_ids)

        loss = paddle.nn.functional.cross_entropy(logits, labels)

        loss.backward()

        optimizer.step()

        optimizer.clear_grad()

        lit += 1

        print(f"Epoch {epoch + 1}: step:{str(lit)}")

    print(f"Epoch {epoch + 1}: Loss: {loss.numpy().mean()}")

model_path = "ernie_3.0_text_classification_model.pdparams"

paddle.save(model.state_dict(), model_path)  # 保存模型参数

右键 下载模型

上传至 Linux 服务 作为本地模型

python3 train.py --do_train --do_eval --do_export --model_name_or_path ernie-3.0-tiny-medium-v2-zh --output_dir checkpoint --device gpu:0 --early_stopping_patience 5 --learning_rate 3e-5 --max_length 128 --metric_for_best_model accuracy --load_best_model_at_end --logging_steps 5 --evaluation_strategy epoch --save_strategy epoch --save_total_limit 1

c参考:

越学越有趣:『手把手带你学NLP』系列项目05 ——文本情感分析的那些事儿-CSDN博客

nohup python3 -m uvicorn main_new:app --host 192.168.1.243 --port 6001 > paddlenlpweb_new.log 2>&1 &

curl -X POST http://192.168.1.243:6001/  -H 'Content-Type: application/json'  -d '{"data":[{"text": "智能大屏"}, {"text": "智能销售"}]}'

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值