BERT论文解读及情感分类实战

news2024/11/27 0:39:43

文章目录

  • 简介
  • BERT文章主要贡献
  • BERT模型架构
  • 技术细节
    • 任务1 Masked LM(MLM)
    • 任务2 Next Sentence Prediction (NSP)
    • 模型输入
  • 下游任务微调
    • GLUE数据集
    • SQuAD v1.1 和 v2.0
    • NER
  • 情感分类实战
    • IMDB影评情感数据集
    • 数据集构建
    • 模型构建
    • 超参数设置
    • 训练结果
    • 注意事项

简介

本文将先介绍BERT架构和技术细节,然后介绍一个使用IMDB数据集情感分类的实战。
IMDB数据集分为25000条训练集和25000条测试集,是情感分类中的经典公开数据集
使用BERT模型进行情感分类,测试集准确率达到93%

模型和数据可供参考

BERT文章主要贡献

这篇文章的主要贡献是提出了一种新的语言表示模型——BERT(Bidirectional Encoder Representations from Transformers)。BERT的核心创新点和贡献如下:

深度双向预训练表示:与以往的语言表示模型不同,BERT旨在通过联合考虑所有层中的左侧和右侧上下文来预训练深度双向表示。这使得BERT能够在预训练阶段捕获更丰富的语言特征。

简化的任务特定架构修改:预训练的BERT模型可以通过添加少量额外的输出层来微调(fine-tune),从而适应广泛的任务,如问答和语言推断,而无需对模型架构进行大量特定任务的修改。

多项自然语言处理任务的新最佳结果:BERT在十一个自然语言处理任务上取得了新的最先进结果,包括将GLUE基准的分数推高到80.5%(绝对提高了7.7个百分点),MultiNLI准确率提高到86.7%(提高了4.6个百分点),SQuAD v1.1问答测试的F1分数提高到93.2(提高了1.5个百分点),以及SQuAD v2.0测试的F1分数提高到83.1(提高了5.1个百分点)。

预训练任务的重要性:BERT通过使用“掩码语言模型”(Masked Language Model, MLM)和“下一句预测”(Next Sentence Prediction, NSP)任务来展示深度双向预训练的重要性。MLM任务通过随机掩盖输入中的一些标记,然后预测这些掩盖标记的原始词汇ID,从而使得模型能够融合左右上下文。NSP任务则通过预测两个文本片段之间的关系来训练模型理解句子间的关系。

BERT模型架构

Description

BERT模型就是transformer的encoder堆叠而成,只是训练方式是有所讲究。
BERT能够在下游任务微调,模型结构也只需要改变输出层即可方便地适配下游任务。
[CLS]是添加在每个输入示例前面的一个特殊符号,用于整体信息的表示
[SEP]是一个特殊的分隔符标记(例如分隔问题/答案)

技术细节

BERT不使用传统的从左到右或从右到左的语言模型来预训练。相反,是使用两个无监督任务预训练BERT。

任务1 Masked LM(MLM)

直观地说,我们有理由相信深度双向模型严格地比从左到右模型或从左到左模型和从右到左模型的简单结合更强大。不幸的是,标准条件语言模型只能从左到右或从右到左进行训练,因为双向条件反射允许每个单词间接地“看到自己”,并且该模型可以在多层上下文中预测目标单词。

为了训练深度双向表示,只需随机屏蔽一定百分比的输入令牌,然后预测那些屏蔽的令牌。文章将此过程称为“masked LM”(MLM)。在这种情况下,被屏蔽的单词的最终隐藏向量被馈送到词汇表上的输出softmax中,然后得出预测。

文章随机屏蔽每个序列中15%的单词。然后只预测被屏蔽的单词。

尽管这能够获得双向预训练模型,但缺点是在预训练和微调之间造成了不匹配,因为[MASK]在微调过程中不会出现。为了缓解这种情况,我们并不总是用实际的[MASK]替换“屏蔽”单词。训练数据生成器随机选择15%的单词用于预测。在这些单词中,使用
(1)80%概率的替换为[MASK],即需要进行预测。 这是最常见的掩盖策略,模型需要学习根据上下文来预测原本的词汇,这样的训练方式使得模型能够更好地理解词汇在不同上下文中的含义。
(2)10%概率的替换为随机单词。 这种策略增加了训练数据的多样性,迫使模型不仅仅依赖于特定的掩盖词汇来做出预测。这种随机性有助于模型学习到更加鲁棒的上下文表示,因为它不能简单地记忆或依赖于特定的掩盖词汇。
(3)10%概率单词不变。 这种策略保留了原始词汇,不进行掩盖,这有助于模型学习到词汇本身的表示,同时也为模型提供了一些直接从输入中学习的机会,而不是完全依赖于上下文推断。

任务2 Next Sentence Prediction (NSP)

许多重要的下游任务,如问答(QA)和自然语言推理(NLI),都是基于理解两句之间的关系,而语言建模并不能直接捕捉到这一点。为了训练一个理解句子关系的模型,文章让模型在下一个句子预测任务上进行预训练,该任务可以从任何单语语料库中轻松生成。

具体而言,当为每个预训练示例选择句子A和B时,50%的概率B是A后面的下一个句子(标记为Is Next),50%的概率B是来自语料库的随机句子(标记为Not Next)。

模型输入

Description
Token Embeddings就是词的嵌入层表示,只不过句子开头要加[CLS]不同句子之间要加[SEP]。

[CLS]的用处如下:
句子表示:在预训练阶段,[CLS]标记的最终隐藏状态(即经过Transformer最后一层的输出)被用作整个输入序列的聚合表示(aggregate sequence representation)。这意味着[CLS]的表示捕捉了整个序列的上下文信息。

分类任务:在微调阶段,尤其是在句子级别或序列级别的分类任务中,[CLS]的最终隐藏状态被用来作为分类的输入特征。例如,在情感分析、自然语言推断或其他类似的任务中,[CLS]的输出向量会被送入一个额外的线性层(分类层),然后应用softmax函数来预测类别。

问答任务:在问答任务中,[CLS]也可以用来进行答案的预测。例如,在SQuAD问答任务中,模型会输出答案的开始和结束位置的概率分布,而[CLS]的表示有助于模型理解问题和段落之间的关系。

[SEP]用处如下:

分隔句子:
当BERT处理由多个句子组成的句子对时(例如,在问答任务中的问题和答案),[SEP]标记用来明确地分隔两个句子。它允许模型区分序列中的不同部分,尤其是在处理成对的句子时,如在自然语言推断或问答任务中。

输入表示:
在构建输入序列时,句子A(通常是第一个句子或问题)会以[CLS]标记开始,接着是句子A的单词,然后是[SEP]标记,然后是句子B(通常是第二个句子或答案)的单词…
通过在句子之间插入[SEP],模型可以明确地知道序列的结构,从而更好地处理和理解输入的文本。

位置嵌入:
与[CLS]类似,[SEP]也有一个对应的嵌入向量,这个向量是模型学习到的,并且与[CLS]的嵌入向量不同。这个嵌入向量帮助模型理解[SEP]标记在序列中的位置和作用。

注意力机制:
在Transformer模型的自注意力机制中,[SEP]标记使得模型能够区分来自不同句子的标记,这对于模型理解句子间关系的任务至关重要。

预训练和微调:
在预训练阶段,[SEP]帮助模型学习如何处理成对的句子,这在NSP(Next Sentence Prediction)任务中尤为重要。在微调阶段,[SEP]继续用于分隔句子对,使得模型能够适应各种需要处理成对文本的下游任务。

Segment Embeddings 用于标记是否属于同一个句子。

Position Embeddings 用于标记词的位置信息

下游任务微调

BERT能够轻松地适配下游任务,此时使用已经预训练好的BERT模型就能花很少的资源和时间得到很不错地结果,而不需要我们从头开始训练BERT模型。

接下来就看一下BERT在不同数据集是怎么使用的

GLUE数据集

GLUE(General Language Understanding Evaluation)基准测试是一组不同的自然语言理解任务的集合。任务描述如下:

MNLI(Multi-Genre Natural Language Inference):给定一对句子,预测第二个句子是否是第一个句子的蕴含、矛盾或中立。
QQP(Quora Question Pairs):判断Quora上的两个问题是否语义等价。
QNLI(Question Natural Language Inference):基于斯坦福问答数据集的二分类任务,判断问题和句子是否包含正确答案。
SST-2(Stanford Sentiment Treebank):电影评论中句子的情感分类任务。
CoLA(Corpus of Linguistic Acceptability):判断英语句子是否语法正确。
STS-B(Semantic Textual Similarity Benchmark):判断句子对在语义上的相似度。
MRPC(Microsoft Research Paraphrase Corpus):判断句子对是否语义等价。
RTE(Recognizing Textual Entailment):文本蕴含任务,与MNLI类似,但训练数据更少。
WNLI(Winograd NLI):自然语言推理数据集,但由于构建问题,该数据集的结果未被考虑。

Description
对于多个句子的,输入形式就是[CLS]+句子1+[SEP]+句子2+…
对于单个句子的就是[CLS]+句子
然后最后一层输出的[CLS]用来接个全连接层进行分类,适配不同任务需要。

SQuAD v1.1 和 v2.0

SQuAD(Stanford Question Answering Dataset)是问答任务的数据集,包括SQuAD v1.1和SQuAD v2.0两个版本。任务描述如下:

SQuAD v1.1:给定一个问题和一段文本,预测答案在文本中的位置。
SQuAD v2.0:与SQuAD v1.1类似,但允许问题没有答案,使问题更具现实性。
Description
对于SQuAD v1.1,输入格式为[CLS]+问题+[SEP]+段落信息
因为这个数据集就是问题能够在段落中找到答案,构造一个得分,得分最大的作为预测值,具体如下:
首先引入S和E两组可训练参数,用于计算答案的开始和结束文章

计算开始位置的公式如下:
Description
S用于开始位置的计算,Ti表示最后一层的文本信息表示,Pi表示答案从第i个位置开始的概率

用同样的方法,我们也能计算出结束位置的概率

Description
当i<=j时,二者之和最大就是预测出的答案的位置

对于SQuAD v2.0,可能存在没有答案的情况,那么就是计算一个没有答案的得分:
Description
C就是[CLS]
此时如果没有答案的分数要比找到的答案的分数要好,那么就预测为没有答案。

NER

对于命名实体识别的任务,BERT实现起来也是非常简单。
Description
只需要对最后一层的每个单词预测对于的实体标记即可。

情感分类实战

IMDB影评情感数据集

IMDb Movie Reviews数据集是一个用于情感分析的标准二元分类数据集,它包含来自互联网电影数据库(Internet Movie Database,简称IMDB)的50,000条评论,这些评论被标记为正面或负面。

评论数量和平衡性:数据集包含50,000条评论,其中正面和负面评论的数量是相等的,即各占一半。

评分标准:评论是基于10分制的评分进行分类的。负面评论的评分在0到4分之间,而正面评论的评分在7到10分之间。

评论选择:为了确保数据集中的评论具有高度的两极性,选择了评分差异较大的评论。每部电影最多只包含30条评论。
Description
可以看一下榜单,目前在paperwithcode上最高是96.68%,看这模型的名字就不太好惹,但是我们这里简单使用BERT接个全连接进行二分类,也能达到93%

数据集构建

# 定义数据集类
class SentenceDataset(Dataset):
    def __init__(self, sentences, labels, tokenizer, max_length=512):
        self.sentences = sentences
        self.labels = labels
        self.tokenizer = tokenizer
        self.max_length = max_length

    def __len__(self):
        return len(self.sentences)

    def __getitem__(self, idx):
        # 对文本进行编码
        encoded = self.tokenizer.encode_plus(
            self.sentences[idx],
            add_special_tokens=True,
            max_length=self.max_length,
            return_token_type_ids=False,
            padding='max_length',
            truncation=True,
            return_attention_mask=True,
            return_tensors='pt'
        )
        # 获取编码后的数据和注意力掩码
        input_ids = encoded['input_ids']
        attention_mask = encoded['attention_mask']

        # 返回编码后的数据、注意力掩码和标签
        return input_ids, attention_mask, self.labels[idx]

因为BERT是WordPiece嵌入的,所以需要使用他专门的切词工具才能正常使用,因此在数据预处理的过程中,可以切好词转化为bert字典中的id,这样直接喂入bert就能得到我们要的句子bert向量表示了,然后就可以用来分类了。

模型构建

使用transformers中预训练好的BERT模型(bert-base-uncased)
我们可以先来看一下bert模型的输入输出:

from transformers import BertTokenizer, BertModel

# 初始化分词器和模型
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')

sentences = ["Hello, this is a positive sentence."]

# 对句子进行编码
encoded_inputs = tokenizer(sentences, padding=True, truncation=True, return_tensors='pt', max_length=512)
outputs = model(**encoded_inputs)

Description
可以看到分词器的输出encoded_inputs由三部分组成,维度都是[batch_size, seq_len]
Description
可以看到bert模型的输出为:
outputs[0]是[batch_size, seq_len, hidden_size]
outputs[1]是[batch_size, hidden_size]
outputs[0]就是每个词的表示
outputs[1]就是[CLS],可以看成这句话的表示
对于我们的任务,就是实现情感分类,因此直接使用outputs[1]接全连接就行了

核心代码如下:

# 定义一个简单的全连接层来进行二分类
class BertForSequenceClassification(nn.Module):
    def __init__(self, bert, num_labels=2):
        super(BertForSequenceClassification, self).__init__()
        self.bert = bert #BERT模型
        self.classifier = nn.Linear(bert.config.hidden_size, num_labels)

    def forward(self, input_ids, attention_mask):
        outputs = self.bert(input_ids=input_ids, attention_mask=attention_mask)
        pooled_output = outputs[1]
        logits = self.classifier(pooled_output)
        return logits

代码就是非常简洁,当然,如果想要更好地效果,可以直接加个LSTM、BiLSTM+Attention等来更好地进行语义编码,操作空间还是很大地。

超参数设置

Description
batch_size=64 需要50多G显存才能跑起来,现存小的话可以开4
lr=2e-5就是微调大模型的常用学习率
epoch=2 其实结果已经很不错了,这可能就是微调的魅力
num_labels = 2因为数据集是二分类任务

因为这个实战是个简洁版本,所以超参数也设定的很少,代码也是很简洁,适合初学者参考学习

训练结果

Description
可以看到测试集的准确率最高为93.56%
还是很不错的
不过我并没有固定随机种子
可能多跑几次能够还有望超越93.56%

注意事项

train_sentences, train_labels = get_data(r'./data/train_data.tsv')
test_sentences, test_labels = get_data(r'./data/test_data.tsv')
# 初始化BERT tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
bert_model = BertModel.from_pretrained('bert-base-uncased').to(device)

模型和数据都有,运行的适合需要将模型和数据的路径修改为自己的路径

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MES实施优势有哪些?MES制造执行系统的主要内容

各个行业之间也开始进入到了激烈的竞争当中&#xff0c;很多企业为了能够有效提升企业竞争力&#xff0c;都会通过提升自身实力的方式来提升竞争力。一些制造业也会在经营过程当中使用到MES系统&#xff0c;那么&#xff0c;mes系统的优势有哪些呢&#xff1f; 1、优化企业现场…

O2OA开发平台如何查看数据表结构?

在访问后端api地址&#xff0c;页面最下方有列示平台的各个服务&#xff0c;点击进入可查看具体的表内容 后端api地址&#xff1a; http://{hostIP}/x_program_center/jest/list.html 其中&#xff1a;{hostIP}为中心服务器所在域名或者IP地址 如下图&#xff1a;

位像素海外仓管理系统对接ERP系统教程,一对一教学

在海外仓管理过程中&#xff0c;对接ERP系统的重要性不言而喻的。这种对接不仅能让数据实时共享&#xff0c;还能让海外仓管理者优化整个供应链管理流程。 因此&#xff0c;今天小编就来教大家&#xff0c;海外仓仓库系统是怎么对接ERP物流系统的&#xff1f; 1.分析需求 在对接…

C/C++:实现一个柱状统计图

C/C&#xff1a;实现一个柱状统计图 如果想要实现一个柱状统计图&#xff0c;其实有两点需要考虑&#xff1a; 数据的存储方式数据的收集数据的显示 我们以统计字符的个数为例子&#xff0c;进行说明。 首先是数据的存储方式&#xff0c;怎么存储最好呢&#xff1f; C的STL…

数据结构课程设计(八)---排序算法比较 [排序]

1.8.1 题目内容 1.8.1-A [问题描述] 利用随机函数产生10个样本&#xff0c;每个样本有50000个随机整数&#xff08;并使第一个样本是正序&#xff0c;第二个样本是逆序&#xff09;&#xff0c;利用直接插入排序、希尔排序&#xff0c;冒泡排序、快速排序、选择排序、堆排序&a…

搭建Maven的Nexus3私服

搭建Maven的Nexus3私服 1、常见的Maven私服产品 Apache的ArchivaJFrog的ArtifactorySonatype的Nexus&#xff08;[ˈneksəs]&#xff09;&#xff08;当前最流行、使用最广泛&#xff09; 2. windows java8安装和配置私服Nexus3 参考&#xff1a; https://zhuanlan.zhihu…

【INNODB引擎篇】深奥探究Innodb存储引擎

&#x1f525;作者主页&#xff1a;小林同学的学习笔录 &#x1f525;mysql专栏&#xff1a;小林同学的专栏 目录 1.InnoDB引擎 1.1 逻辑存储结构 1.2 架构 1.2.1 概述 1.2.2 内存结构 1.2.3 磁盘结构 1.2.4 后台线程 1.3 事务原理 1.3.1 事务基础 1.3.2 redo log 1.…

修改cmd默认编码(win10系统) 亲测有效

win10系统,CMD默认字符编码序号是936,输入"chcp"命令可以看到此编号,右键cmd窗口–属性,同样也可以看到此编号.如下图: 我需要把字符编码序号936变更为65001,即UTF-8编码. 网上搜到的教程主要有两种: 教程一修改注册表的方法:https://learnku.com/articles/55553 测…

Project Euler_Problem 193_Few Repeated Digits_欧拉筛+容斥公式

原题目&#xff1a; 题目大意&#xff1a; 解题思路&#xff1a; 代码&#xff1a; void serch(ll I,ll sum,ll used) {ll i, j, l, x,y;for (i 1; i < I; i) {if (sum * D[i] > N)break;x sum * D[i];y N / x;if (used % 2 0) {ans1 ans1 - y;}else {ans1 ans1 y…

如何将对象转换成json字符串,以json格式输出,并获取到其中的特定字段

小王学习录 Json格式示例 1&#xff1a;简单的 JSON 对象示例 2&#xff1a;JSON 对象嵌套示例 3&#xff1a;JSON 数组示例 4&#xff1a;混合使用对象和数组 使用Gson将java对象转换成json字符串哪些数据类型的对象可以使用Gson转换为json字符串如何使用Gson将java对象转换成…

Meta推出全新定制AI芯片,加速追赶对手的步伐

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Nginx讲解 很详细了!!!

hello&#xff0c;你好鸭&#xff0c;我是Ethan&#xff0c;很高兴你能来阅读&#xff0c;昵称是希望自己能不断精进&#xff0c;向着优秀程序员前行!&#x1f4aa;&#x1f4aa;&#x1f4aa; 目前博客主要更新Java系列、数据库、项目案例、计算机基础等知识点。感谢你的阅读和…

有图片转成PDF文件格式的方法吗?分享图片转成PDF文件的方法

将图片转换为PDF文件是一个相对简单的过程&#xff0c;但也需要一定的步骤和注意事项。下面&#xff0c;我将详细介绍如何将图片转换为PDF文件&#xff0c;包括所需的工具、步骤以及可能遇到的问题和解决方案。 首先&#xff0c;我们需要一个能够将图片转换为PDF文件的工具。市…

C语言进阶课程学习记录-数组指针和指针数组分析

C语言进阶课程学习记录-数组指针和指针数组分析 实验-数组指针的大小实验-指针数组小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 实验-数组指针的大小 #include <stdio.h>typedef int(AINT…

ABAP-CPI-Odata POST-create_deep_entity 多层嵌套的处理及CPI端的调用

该文章演示怎么在OData里,创建一个多套多的请求结构,传入数据处理后,返回多层级的处理结果;以及如何在CPI里写groovy脚本,去解析它;最后如何用postman模拟外围系统,调用CPI这个接口,从而去调用Odata接口 假如想用SAP Odata去实现传入多层级的数据,进行创建或者根据传入…

【网站项目】摄影竞赛小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

【c++】优先级队列|反向迭代器(vector|list)

优先级队列的常用函数的使用 #include<iostream> #include<queue> using namespace std;int main() {priority_queue<int>st;st.push(1);st.push(7);st.push(5);st.push(2);st.push(3);st.push(9);while (!st.empty()){cout << st.top() << &qu…

安达发|APS计划排程系统做出详细生产作业计划需要哪些条件

在制造行业中&#xff0c;APS&#xff08;高级计划排程系统&#xff09;是核心的信息系统之一&#xff0c;它负责制定详细的生产作业计划以确保生产效率和交货期的准确性。要做出有效的详细生产作业计划&#xff0c;需要满足一系列条件要求&#xff0c;以下是一些关键因素&…

FME学习之旅---day24

我们付出一些成本&#xff0c;时间的或者其他&#xff0c;最终总能收获一些什么。 高级地理数据库 教程&#xff1a;地理数据库转换 上述教程包括 如何使用 Esri 模板地理数据库 该内容在FME学习之旅day19 已经学习过 使用地理数据库属性域&#xff1a;编写编码属性域 属…

css里面的浮动笔记

参考链接&#xff1a; (图文详细)最通俗易懂的CSS 浮动float属性详解_css float简单理解-CSDN博客 经验分享&#xff1a;CSS浮动(float,clear)通俗讲解 - 杨元 - 博客园 (cnblogs.com) 要点&#xff1a; 浮动元素只会影响后面标准流的元素 &#xff0c;如果在它之前有一个标…