【人人都能学得会的NLP - 文本分类篇 02】使用DL方法做文本分类任务

news/2024/11/29 8:39:29/文章来源:https://www.cnblogs.com/fasterai/p/18575643

【人人都能学得会的NLP - 文本分类篇 02】使用DL方法做文本分类任务


NLP Github 项目:

  • NLP 项目实践:fasterai/nlp-project-practice

    介绍:该仓库围绕着 NLP 任务模型的设计、训练、优化、部署和应用,分享大模型算法工程师的日常工作和实战经验

  • AI 藏经阁:https://gitee.com/fasterai/ai-e-book

    介绍:该仓库主要分享了数百本 AI 领域电子书

  • AI 算法面经:fasterai/nlp-interview-handbook#面经

    介绍:该仓库一网打尽互联网大厂NLP算法面经,算法求职必备神器

  • NLP 剑指Offer:https://gitee.com/fasterai/nlp-interview-handbook

    介绍:该仓库汇总了 NLP 算法工程师高频面题


1、文本分类介绍

NLP中, 文本分类是一项基础且广泛应用的任务, 它将文本依据规则标准划分到为特定的类别空间,并且它能够应用到多种应用场景

  • 垃圾邮件识别:判断一封邮件“是”“否”为垃圾邮件
  • 情感分析: 判断一篇文章情感极性为“正面”“负面”“中性”
  • 聊天意图:判断聊天文本中用户的意图为“发货时间”、“运费价格”等

……

以新闻文本分类为例,我们来了解文本分类任务的类型

1、“今天大盘涨了3%,地产、传媒板块领涨”

2、“NBA湖人队赢下了与掘金队的比赛”

上述分别属于财经体育类新闻。

3、“汪峰今天发布了新歌,娱乐圈又将有大事发生”

以3为例的新闻既可以归为音乐类新闻,又可以归为娱乐类新闻,与1和2有一定区别。

定义:

由上述例子,我们可以得到不同的分类任务类型:多分类任务和多标签分类任务。

  • 多分类任务中一条数据只有一个标签,但是标签有多种类别,就比如1、2的新闻属于具体的单一新闻类别。
  • 多标签分类任务指的是一条数据可能有一个或者多个标签,就比如3的新闻具有多个新闻类别。

任务实现过程:

完成的分类任务一般需要经历两个阶段,分别是训练阶段预测阶段

  • 训练阶段的目标是基于训练集数据(带label)。训练文本分类模型
  • 预测阶段的目标是使用上一阶段训练好的模型,对于新数据(无label),预测其类别。

技术演化:

分类模型随着技术的进步也经历从规则匹配—>机器学习—>传统深度学习—>前沿深度学习以下四个阶段的演化:

  • 规则匹配:基于关键词匹配等方法
  • 机器学习:基于LR、SVM、集成学习等方法
  • 传统深度学习:基于FastText、TextCNN、BiLSTM等神经网络方法
  • 前沿深度学习:基于Transformer、BERT的pretrain-finetune和prompt范式。

效果评估:

在模型完成后,需要根据预测结果对模型的效果进行整体评估

对于分类任务,直观指标可以由准确率(accuracy)进行表示:

  • 准确率:分类正确的样本数占样本总数的比例,通过记录正确的数量占比即可。

但是上述指标评估存在一定局限,以情感分析任务为例,如果我们想评测模型(1)返回的正面情绪结果中的正确数量,或者在(2)所有真实的正面情绪文本中,模型识别出来的数量,只依靠准确率无法实现任务。(3)且在样本类别数量极度不平衡下,准确率的意义不大,例如,一批数据中,正样本只有几个,即使没能正确预测出来,准确率依旧很高。

基于上述情况,需要考虑新的评测指标,如果我们用的是个二分类的模型,那么把预测情况与实际情况的所有结果两两混合,结果就会出现以下4种情况,就组成了如下所示的混淆矩阵

精准率(Precision)又叫查准率,它是针对预测结果而言的,它的含义是在所有被预测为正的样本中实际为正的样本的概率,意思就是在预测为正样本的结果中,我们有多少把握可以预测正确,其公式如下,它能够解决上述局限中(1)的情况:

召回率(Recall)又叫查全率,它是针对原样本而言的,它的含义是在实际为正的样本中被预测为正样本的概率,其公式如下,它能够解决上述局限中(2)的情况:

一般来讲,准确率和召回率会互相矛盾,假设你想实现较高的召回率,那尽可能召回样本越多越好,这样子的精准率就会下降。通常,如果想要找到精准率与召回率之间的一个平衡点,我们就需要一个新的指标:F1分数。F1分数同时考虑了查准率和查全率,让二者同时达到最高,取一个平衡,其计算公式如下,一般任务用F1进行衡量较为科学。

举例如下:

模型预测结果:0,1,0,0,1

实际样本结果:1,1,0,1,0

各项评测指标如下所示:

准确率(accuracy):2/5 = 0.4

精准率(precision):1/2 = 0.5

召回率(recall):1/3 = 0.333

F1:2*p*r/(p+r) = 0.4

当回顾文本分类的全流程后,我们接下来引入较为主流的模型进行文本分类任务的处理。

2、深度学习模型

机器学习模型依赖特征工程的构建,与此同时,深度学习模型开始显示出其突出优势

  1. 首先,深度学习模型不依赖于传统的特征工程,它可以通过其网络结构的变化来拟合任意复杂的函数。
  2. 其次,深度学习模型能够实现传统离散型编码稠密词向量表示的转换,用向量空间实现语义空间的映射,为自然语言天然的离散特性提供良好的解决方案。

基于深度学习模型的良好效果,也有越来越多的研究尝试使用这种方法。

传统深度学习模型一般指神经网络引进之后,预训练模型出现之前NLP领域的研究方法,这类方法不用手动设置特征和规则,节省了大量的人力资源,但仍然需要人工设计合适的神经网络架构来对数据集进行训练。常见的方法比如CNN(衍生模型有TextCNN等)、RNN(衍生模型有LSTM、BiLSTM等)。

CNN模型以TextCNN为例分析:

  1. 首先在embedding层输入原始文本
  2. 卷积层拥有多种种类的卷积核,关注文本不同维度的特征
  3. 再通过池化层对于不同特征进行组合
  4. 最终通过全连接层实现多分类任务

RNN模型以 BilSTM为例分析:

  1. 原始输入序列x,经过前后两个LSTM层后,语义表征被拼接(concat)作为时间步的语义表示。
  2. 接着取BilSTM最后一个时间步的输出,接入全连接层,做分类任务
  3. 通过对于全连接层的n个数值使用softmax函数,返回每个标签对应的概率,概率最大即为预测标签。

传统深度学习模型语义表示等方面仍存在局限,Transformer和BERT模型的诞生为NLP领域提供新的发力点

BERT模型为核心前沿深度学习模型围绕面向的服务对象产生了两大范式

一类是以下游任务为核心,模型“迁就”下游任务的pretrain-finetuning范式,指的是先在大的无监督数据集上进行预训练,学习到一些通用的语法和语义特征,然后利用预训练好的模型在下游任务的特定数据集上进行fine-tuning,使模型更适应下游任务,针对于本任务则是分类任务,其特点是不需要大量的有监督下游任务数据,模型主要在大型无监督数据上训练,只需要少量下游任务数据来微调少量网络层即可。

另一类是以模型为核心,下游任务围绕模型改善的prompt范式,这种范式解决了传统pretrain-finetuning范式中pretrain和finetuning学习目标不匹配的局限,让下游任务根据模型pretrain的机制进行调整,从而激发预训练模型的潜能。

下图LM即是语言模型,该图也形象的体现了二者服务对象的显著区别

接下来,通过一个简单的例子来对BERT模型实现文本分类任务进行介绍:

Bert模型实现文本分类任务,在一张图的体现如下所示:我们输入原始句子,然后将通过模型输出的【C】标识符当作是句子标识符,再接入全连接层进行分类任务。

如果分过程来看:模型首先经过以MLM(mask language model)机制为执行目标的pretrain阶段,这类似于我们常见的完形填空任务;之后,再在完成预训练模型的基础上执行句子标签预测,即fine-tuning阶段,过程如下所示:

上述过程也体现了这一范式存在两阶段任务目标不一致的问题。

如果我们以电影评论的情感分类任务为例,我们来辨析pretrain-finetune范式prompt范式的区别

首先原始句子输入是:“特效非常炫酷,我很喜欢”,其对应的情感标签为:positive。

我们能够发现,prompt范式能够通过提示模版更改自然语言另一种表达形式,从而使数据更适配模型,达到更佳的效果。

基于BiLSTM模型做文本分类

传统深度学习以BiLSTM模型中的Model类为例进行介绍:

class BiLSTMModel(nn.Module):def __init__(self, vocab_size, embed, hidden_dim, n_layers, dropout,label_size):super(BiLSTMModel, self).__init__()self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')self.n_layers = n_layers  # LSTM的层数self.hidden_dim = hidden_dim  # 隐状态的维度,即LSTM输出的隐状态的维度self.embedding_dim = embed  # 将单词编码成多少维的向量self.dropout = dropout  # dropout的比例self.label_size = label_size #最终输出的维度self.embedding = nn.Embedding(vocab_size, self.embedding_dim)self.lstm = nn.LSTM(input_size=self.embedding_dim,hidden_size=self.hidden_dim,num_layers=self.n_layers,bidirectional=True,dropout=self.dropout)# 初始时间步和最终时间步的隐藏状态作为全连接层输入self.fc = nn.Linear(self.hidden_dim * 2, self.label_size)def forward(self, inputs, labels=None):# 再提取词特征,输出形状为( batch,seq_len 词向量维度)#pytorch的lstm规定input维度是(seq_len,batch,embedding)首先变换input的维度inputs = inputs.transpose(0, 1)embeddings = self.embedding(inputs)# rnn.LSTM只传入输入embeddings,因此只返回最后一层的隐藏层在各时间步的隐藏状态。# outputs形状是(词数, 批量大小, 2 * 隐藏单元个数)lstm_output, (h_n,c_n) = self.lstm(embeddings)  # output, (h_n,c_n) h_n为最后一个时间步,从它得到前、后向的最后一步output_fw = h_n[-2, :, :]  # 正向最后一次的输出output_bw = h_n[-1, :, :]  # 反向最后一次的输出lstm_output = torch.cat([output_fw, output_bw], dim=-1)logits = self.fc(lstm_output) #经过全连接层得到分类概率best_labels = torch.argmax(logits, dim=-1)# best_labels = best_labels.long()if labels is not None:loss_func = nn.CrossEntropyLoss()  # 分类问题loss = loss_func(logits, labels)return best_labels, lossreturn best_labels

基于BERT模型做文本分类

前沿深度学习以BERT模型中的Model类为例进行介绍:

class BertFCModel(nn.Module):def __init__(self, bert_base_model_dir, label_size, drop_out_rate=0.5,loss_type='cross_entropy_loss', focal_loss_gamma=2, focal_loss_alpha=None):super(BertFCModel, self).__init__()self.label_size = label_sizeassert loss_type in ('cross_entropy_loss', 'focal_loss', 'label_smoothing_loss')  # # 支持label_smoothing_lossif focal_loss_alpha:  # 确保focal_loss_alpha合法,必须是一个label的概率分布assert isinstance(focal_loss_alpha, list) and len(focal_loss_alpha) == label_sizeself.loss_type = loss_type  # 添加loss_typeself.focal_loss_gamma = focal_loss_gammaself.focal_loss_alpha = focal_loss_alphaself.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')# 根据预训练文件名,自动检测bert的各种变体,并加载if 'albert' in bert_base_model_dir.lower():self.bert_tokenizer = BertTokenizer.from_pretrained(bert_base_model_dir)self.bert_model = AlbertModel.from_pretrained(bert_base_model_dir)elif 'electra' in bert_base_model_dir.lower():self.bert_tokenizer = ElectraTokenizer.from_pretrained(bert_base_model_dir)self.bert_model = ElectraModel.from_pretrained(bert_base_model_dir)else:self.bert_tokenizer = BertTokenizer.from_pretrained(bert_base_model_dir)self.bert_model = BertModel.from_pretrained(bert_base_model_dir)self.dropout = nn.Dropout(drop_out_rate)# 定义linear层,进行 hidden_size->hidden_size 的映射,可以使用其作为bert pooler# 原始bert pooler是一个使用tanh激活的全连接层self.linear = nn.Linear(self.bert_model.config.hidden_size, self.bert_model.config.hidden_size)# 全连接分类层self.cls_layer = nn.Linear(self.bert_model.config.hidden_size, label_size)def forward(self, input_ids, attention_mask, token_type_ids=None, position_ids=None,head_mask=None, inputs_embeds=None, labels=None):bert_out = self.bert_model(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids,position_ids=position_ids, head_mask=head_mask, inputs_embeds=None, return_dict=False)if isinstance(self.bert_model, ElectraModel):# Electra使用gelu激活,自己实现gelu的bert poolerlast_hidden_state, = bert_outx = last_hidden_state[:, 0, :]  # take <s> token (equiv. to [CLS])x = self.dropout(x)x = self.linear(x)x = get_activation("gelu")(x)  # although BERT uses tanh here, it seems Electra authors used gelu herex = self.dropout(x)pooled_output = xelse:last_hidden_state, pooled_output = bert_outcls_outs = self.dropout(pooled_output)logits = self.cls_layer(cls_outs)best_labels = torch.argmax(logits, dim=-1)best_labels = best_labels.to(self.device)if labels is not None:# 根据不同的loss_type,选择不同的loss计算if self.loss_type == 'cross_entropy_loss':loss = CrossEntropyLoss(ignore_index=-1)(logits, labels)  # label=-1被忽略elif self.loss_type == 'focal_loss':loss = FocalLoss(gamma=self.focal_loss_gamma, alpha=self.focal_loss_alpha)(logits, labels)else:loss = LabelSmoothingCrossEntropy(alpha=0.1)(logits, labels)  # # 支持label_smoothing_lossreturn best_labels, lossreturn best_labels  # 直接返回预测的labelsdef get_bert_tokenizer(self):return self.bert_tokenizer

PS:上面是部分核心代码


【动手学 RAG】系列文章:

  • 【RAG 项目实战 01】在 LangChain 中集成 Chainlit
  • 【RAG 项目实战 02】Chainlit 持久化对话历史
  • 【RAG 项目实战 03】优雅的管理环境变量
  • 【RAG 项目实战 04】添加多轮对话能力
  • 【RAG 项目实战 05】重构:封装代码
  • 【RAG 项目实战 06】使用 LangChain 结合 Chainlit 实现文档问答
  • 【RAG 项目实战 07】替换 ConversationalRetrievalChain(单轮问答)
  • 【RAG 项目实战 08】为 RAG 添加历史对话能力
  • More...

【动手部署大模型】系列文章:

  • 【模型部署】vLLM 部署 Qwen2-VL 踩坑记 01 - 环境安装
  • 【模型部署】vLLM 部署 Qwen2-VL 踩坑记 02 - 推理加速
  • 【模型部署】vLLM 部署 Qwen2-VL 踩坑记 03 - 多图支持和输入格式问题
  • More...

【人人都能学得会的NLP】系列文章:

  • 【人人都能学得会的NLP - 文本分类篇 01】使用ML方法做文本分类任务
  • 【人人都能学得会的NLP - 文本分类篇 02】使用DL方法做文本分类任务
  • 【人人都能学得会的NLP - 文本分类篇 03】长文本多标签分类分类如何做?
  • 【人人都能学得会的NLP - 文本分类篇 04】层次化多标签文本分类如何做?
  • 【人人都能学得会的NLP - 文本分类篇 05】使用LSTM完成情感分析任务
  • 【人人都能学得会的NLP - 文本分类篇 06】基于 Prompt 的小样本文本分类实践
  • More...

本文由mdnice多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/843254.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第九章课后习题

习题9.2点击查看代码 import numpy as np from scipy.stats import norm, chi2, chisquare import pylab as pltn = 50; k = 6 a = np.loadtxt(ti9.2.txt).flatten() mu = a.mean(); s = a.std(ddof=1) x1 = a.min(); x2 = a.max() x = np.linspace(14.55, 15.55, k) bin = np.…

2024年各编程语言运行100万个并发任务需要多少内存?

你还记得2023年那篇比较各种流行编程语言异步编程内存消耗比较的文章吗? 现在是2024年底,我很好奇在一年时间里,随着各种语言的最新版本发布,情况有什么变化。 让我们再次进行基准测试,看看结果! 基准 用于基准测试的程序与去年相同:让我们启动 N 个并发任务,每个任务等…

用星球助手导出帖子的手把手教程

当我们把星球的帖子下载到本地电脑之后, 如果想要导出成PDF或者Word之类的格式进行学习, 该怎么弄呢? 其实也是相当简单的. 到"搜索"模块里, 选择"帖子", 输入关键词后者留空都可以, 点击"搜索", 在出现的帖子的右上角有三个图标, 从左到右分别…

OpenVZ 8.0 - 基于容器的 Linux 开源虚拟化解决方案

OpenVZ 8.0 - 基于容器的 Linux 开源虚拟化解决方案OpenVZ 8.0 - 基于容器的 Linux 开源虚拟化解决方案 Open source container-based virtualization for Linux 请访问原文链接:https://sysin.org/blog/openvz-8/ 查看最新版。原创作品,转载请保留出处。 作者主页:sysin.or…

Virtuozzo Hybrid Server 8.0 - 容器、计算和存储虚拟化平台

Virtuozzo Hybrid Server 8.0 - 容器、计算和存储虚拟化平台Virtuozzo Hybrid Server 8.0 - 容器、计算和存储虚拟化平台 The VMware alternative for service providers and enterprises 请访问原文链接:https://sysin.org/blog/virtuozzo-hybrid-server-8/ 查看最新版。原创…

一款开源、免费、美观的 Avalonia UI 原生控件库 - Semi Avalonia

前言 最近发现DotNetGuide技术社区交流群有不少小伙伴在学习Avalonia,今天大姚给大家分享一款开源、免费、美观的 Avalonia UI 原生控件库:Semi Avalonia。Avalonia项目介绍 Avalonia是一个强大的框架,使开发人员能够使用.NET创建跨平台应用程序。它使用自己的渲染引擎绘制U…

用星球助手搜索本地帖子的手把手教程

通过知识星球手机App或者网页的人都知道, 搜点东西是真费劲, 要么没法搜到想要的东西, 要么就是搜出来一大堆有用的没用的, 根本找不到自己真正想要的东西. 好在星球助手极大程度解决了这个问题. 我们将帖子下载到本地电脑上之后, 我们可以用更加精准的关键词进行搜索, 针对问答…

读数据质量管理:数据可靠性与数据质量问题解决之道18数据发现

数据发现1. 让元数据为业务服务 1.1. 在过去十多年中,数据团队越来越擅长收集大量的数据 1.2. 公司如今正在收集越来越多关于其数据的数据,也就是元数据1.2.1. dbt等ETL解决方案让跟踪和使用元数据变得容易,而云服务提供商则使栈中数据解决方案之间的元数据的互操作性变得更…

初见客户交流指南:精准高效,建立信任

在已知客户对产品有大致需求(询盘)的背景下,初次拜访交流至关重要。以下是一份实用的交流指南: 充分准备 自我介绍清晰明了 简洁明了地告知客户您的基本信息,包括姓名、籍贯、公司名称、职位以及您在决策过程中的角色。 树立专业形象,传达出“业务方面,找我就对了”的自…

《数字经济产业链》及其相关公司 —— 深度梳理

《数字经济产业链》及其相关公司 —— 深度梳理 来源:行业研究报告发布时间:2023 年 05 月 22 日 11:07:35(网经社讯)产业链分析 1、产业链概述 2、数字经济行业上游: (1)5G 根据中国信通院、中国移动、中国电信、中国联通、工信部,综合市场公开资料,2025 年中国 5G …

写一个方法检测页面中的所有标签是否正确闭合

function checkTagClosure(htmlString) {// 使用栈来跟踪打开的标签const tagStack = [];// 正则表达式匹配标签 (包括自闭合标签)const tagRegex = /<\/?([a-zA-Z][a-zA-Z0-9]*)\s*\/?>/g;let match;while ((match = tagRegex.exec(htmlString)) !== null) {const tag…

【api开发】API通信协议总结

API协议 1. REST(Representational State Transfer) 一种用于设计网络应用程序的架构风格。它强调无状态通信,使用标准的HTTP方法(GET、POST、PUT、DELETE),并通过URL识别资源。 2. GraphQL 一种API查询语言,允许客户端精确请求它们所需的数据,不多也不少。这种效率是G…