Bert讲解+基于Bert的情感多分类任务(超高精度)

NLP Project
Sentiment Analysis
实验流程:
1.数据集介绍
2.什么是Transformer?
3.Bert的介绍
4.代码和输出分析
5.结论
6.Readme

稍微说一下,这个是我NLP的大作业,因为最近正好在准备Transformer相关的论文,所以这里就用的Bert来做的,也获得了应该是最高分,总评97+(140人),所以大家可以放心食用,参考可以但是切勿抄袭!!!

注:这里是调用的Hugging Face的Transformer因为要用到参数,我可没办法预训练,所以如果大家想要去看Transformer的Attention底层实现的话可以去看看我的CoAtNet,虽然是CNN+TFM,但是有Attention的具体实现:
Google顶级网络CoAtNet

一.数据集介绍

我采用的sentiment analysis的数据集:
1.来自豆瓣的影评,28 部电影,超 70 万 用户,超 200 万条 评分/评论 数据打分为1-5星(中文)
数据集原链接
2.来自tweet某公司的好差评(英文)
因为是情感分类,所以我将前者每个分数影评随机抽取1w条,按照9:1分割为训练集和测试集,每个影评的label就是按照星级顺序从0递增,后者因为只有好坏评所以就是个二分类问题,label直接对应0 1即可,其中训练集好坏各1500条,测试集1500条(约100个差评)

二.Transformer概括

Transformer原意变形金刚
在这里插入图片描述
但是显然我们要讲的不是这个,而是如下图所示:
在这里插入图片描述
Transformer是谷歌在2017年6月提出的一篇论文,发表在NIPS2017上。

Transformer主体框架是一个encoder-decoder的结构,去掉了RNN序列结构,完全基于attention和全连接。在WMT2014英语翻译德语任务上,bleu值达到了28.4,达到当时的SOTA。其总体结构就是上面的咯。

想要细看Transformer的同学可以去看我前面写的一篇文章:
Transformer详解

这里我们就主要再回顾一下Transformer中的Encoder部分,因为是我们后面Bert要用到的,要保证我们没看过Transformer的小伙伴也能懂

1.Encoder

在这里插入图片描述
我们将Encoder分为两个部分,一个是输入层Embedding,另一个是编码层,也就是上面的block

1.1 Embedding
Embedding又分为两个部分,token embedding和positional encoding

1.1.1Token embedding
这个就是文本向量化,普遍有两种方法:

1)采用固定词向量,比如利用Word2vec预先训练好的。这种方式是LSTM时代常用的方式,比较简单省事,无需训练。但由于词向量是固定的,不能解决一词多义的问题,词语本身也没有结合上下文语境信息,另外对于不在词向量中的词语,比如特定领域词语或者新词,容易出现OOV问题。

2)随机初始化,然后训练。这种方式比较麻烦,需要大规模训练语料,但能解决固定词向量的一系列问题。Transformer采用了这种方式

关键点 1 Transformer的随机初始化

在这里插入图片描述
在这里插入图片描述
首先对于每个词,我们都会有一个随机的xi列向量对应,x1-m对应了文本所有词汇,共同组成文本矩阵X(有多少词就有多少列,行数对应的就是一个xi的行数),最关键的就是attention,注意力机制,我们分别有三个矩阵W(q,k,v),这个是就是随机初始化的,然后我们分别与原文本矩阵X相乘,得到QKV三个矩阵,分别为Query,Key和Value。这三个得到的矩阵就是我们实现self-attention的关键。

1.1.2 Positional encoding

位置编码其实也普遍有两种,一个固定的,一个训练的。
1)固定编码。Transformer采用了这一方式,通过奇数列cos函数,偶数列sin函数方式,利用三角函数对位置进行固定编码。
2)动态训练。BERT采用了这种方式。先随机初始化一个embedding table,然后训练得到table 参数值。predict时通过embedding_lookup找到每个位置的embedding。这种方式和token embedding类似。

直接挂原文图:在这里插入图片描述
在这里插入图片描述

1.2 Encodeing layer

1.2.1Mult-head Attention

这里就是精华所在了,是transformer的核心
首先我们要介绍一下细节部分:
在这里插入图片描述
在这里插入图片描述
QKV矩阵怎么得到我们上面已经说过了,这里我们就来根据图像讲解一下具体怎么运算的,计算过程:
Q.shape=[m,dk]
K^T .shape=[dk,m]
V.shape=[m,dv]
A=QK^T,shape=[m,m]
A=A/sqrt(dk)
B=softmax(A,dim=0), 按列计算softmax, shape=[m,m]
B.shape=[m,m],每一行相当于是某一个querry对所有K的attention比例。
out = B *V ,shape=[m,dv]
可以看到这里我们的dk必须相同,即QK维度需要相同,因为要做矩阵运算,但是dv可以完全随机的,但是为了方便我们还是用的一个维度的。这是为了防止在使用大dk时,A矩阵数值上过大,使得经过softmax变成B以后,梯度太小。

然后就是最关键的Mult-head attention layer
Transformer采用了多头感知机,把原来的QKV矩阵都通过Wi(q,k,v)线性映射到Q‘K’V‘上,这个时候就和上面是一样的内容了,结构如下图所示

在这里插入图片描述
我们可以看到,如果是h头,那么拿Q举例子,就被分割成h个512/h维的向量,因为这里的QKV就是上一层encoder的输出,或者是第一层的embedding转化得到的。其实我们第一层的QKV就是随机初始化得到的矩阵,通过Wiqkv矩阵映射,然后我们后面训练Wiqkv就可以得到好的结果了,在经历我们上面讲的sclaed attention层之后,因为是h块,所以我们要通过concat连接,随后再通过一个线性连接输出,这里是因为要兼容不相等(这个输出和下一个输入的维度)

在encoder里,每一个self-attention sub-layer的Q,K,V都是上一个encode layer的输出。
意思就是Q,K,V是同一个东西。这样的好处是,当前encode layer的每一个位置,都可以处理上一层encode layer的每一个位置。

1.2.2Add&Norm
这里就是个残差连接然后归一化,把原来的输入+经过Mult-head Attention层的结果当作新的输出往下传递。

1.2.3Feed Forward
这个说白了就是个全连接层而已。

三.Bert

上面讲完了Encoder再讲Bert就方便多了

Bert是缩写,它的的全名叫做:

Bidirectional
Encoder
Represented from
Transformers

也就是基于Transformer的双向表征编码器,说白点就是个会联系上下文的结构model了,但是效果非常惊人,彻底引爆NLP领域,获得了NAACL自然语言顶会之一的Best Paper,结构如下图所示:

在这里插入图片描述

先看下输入编码:
在这里插入图片描述
可以看到是三个Vector相加得到的结果:
第一个就是和Transformer一样的Token Embedding,但是加入了CLS和SEP,分明句首和句尾,当然CLS还有保存整句语义信息的功能。
第二个是表明句子属于哪一个,比如第一句全是1,第二句就全是2
第三个是位置向量,这里和Transformer用的不一样,是随机向量然后通过训练得到的

它相比于Transformer就多了两个改进:
1)利用Masked token自监督学习
超大数据集的”钞能力”,用4块谷歌TPU跑了四天的数据集,我换算了下维基百科我的GPU2060就要跑半年多…
这里的Masked自监督学习其实就是MLM任务,只是这里叫的感觉很高大上,
其中15%的单词被加上掩码,但是可以知道我们真实用的时候不可能给输入句子加掩码,所以为了匹配下游任务
其中80%为掩码
10%为真实值
10%为随机单词

2)NSP问题
就是下一个句子的预测,用到的是[CLS]的最终态隐层信息,大概可以理解为句子的分类信息吧
和上面操作差不多,就是50%下一句是真的句子,50%是随机的句子,让他自己学

四.具体情感分析代码

1.文件分割,见data中筛选.py,将超大.csv文件通过rating也就是星级分割,获取目标数据集
在这里插入图片描述

2.文件读入,如上所述每个星级对应相应label
直接用例如:
data_worse = pd.read_csv(‘data/1.csv’)
data_worse[‘label’] = 0
进行每个任务读入就行了。

3.Tokenize,也就是预处理类似于token embedding,用tokenize.encode/plus方法
在这里插入图片描述

每个地方的工作注释里面我也写的很清楚了,这个主要是为了到时候按照规范输入Bert网络,要作为id和mask输入,而这个tokenize也是通过原来训练就得到的一个例如W2V的词典而已,很好理解,但是要注意我们要设置一个最大的长度,不然就会把句子阶段产生歧义,其中的mask作用就是把原来没有那那么长的句子补全,输入后让网络直到其原长度。

4.预处理Bert模型调用
这里其实有很多选择,因为预训练模型太多了,我就加载了一个相对较好的一个网络:
在这里插入图片描述

5.网络搭建,Bert调用+全连接+relu+全连接(768(bert固定输出维度)100(全连接层,随便多少层,适当就好)n(n分类))
这里的网络搭建其实如果你只是调用的话就非常简单,但其实我们也可以自己来搭建。如果直接调用的话就是如下:
在这里插入图片描述

这里只要你的预训练模型加载的是被喂过超大数据集的模型的话,后面的网络随便搭两层就够了,这里我我就用的是两层Linear层,用的是ReLu激活函数,注意其实Attention类用的很多是GeLu,我试过后者,发现效果变差了很多,大概会降低到快90%的精度,所以还是得选择较为适合的激活函数,但是其种MLP中间节点数就不是很重要了,我试过50,100,200,除了训练速度有差别,精度几乎没有很大变化。

6.优化器以及精度,调用自适应函数,以及lookback等训练操作也是调用函数。
建议使用Adam,比SGD要稍微好一些。

7.训练搭建,因为训练时间较长,所以我们选择批量,并且批量打印,batch size 我这里用的32(大型的一般用512)
但是注意batch_size太大的话收敛比较慢,不过其实我们用的本来就是以及大量预训练过的,所以这里大一些也无所谓,但是要注意你的GPU显存可能会越界,不过Bert large也没有特别大,大概就是220M这个样子的Params,一般稍微好一点的GPU都不会越界,毕竟只是Fine-tuning,训练集不大。

8.评估,也就是测试集loss以及accuracy计算,通过argmax函数获取概率最大下标,即logits的max,对应序号即为label,故可直接if ==判断。
在这里插入图片描述

9.开始训练and测试,采用同步,当然可以将evalution参数设置为flase,就可以单纯训练,不进行测评,设置epoch即可选择训练次数。
因为这里采用的训练方法和普通的深度学习训练方法都是一样的所以就不特意来说了,直接把结果拿出来看一下吧:
在这里插入图片描述

这是个四分类的结果,98.77的精度,可以看到只一个Epoch就非常高的精度了,所以也没有必要继续去训练了,这可能就是Fine-tuning的魅力吧。

10.整体细节较多,主要为各种数据类型的转换以及GPU与CPU的交互,因为大型网络,所以CPU速度过慢,batch size 32跑20个size就要300s,换GPU只需10s

注:如果测试集没有label,可采用分割训练集得到验证集的方式去调节网络参数, 这个我在二分类里面有做:
在这里插入图片描述

还有ROC指标图像如下:
在这里插入图片描述

五.实验结论

实验的结论:
对于原语言环境的分类任务,Bert毋庸置疑的是给出了超高的准确率,以及良好的下游任务衔接框架,即通过超大数据的Pre-training结合我们想要做的任务的Fine-tuning,非常好用简单,毕竟人家都帮你跑了参数,你自己下个接口调用网络参数就好了。然后就是对于语境迁移还有少样本学习通过改变训练集对应个数(即权重)的方法得出的结论,毕竟我们自己只做了个微调处理,而且原模型缺乏归纳偏置,所以想要获得极好的泛化性还是要改变原模型,比如通过CNN增强Transformer。

个人感受:
我只能感叹Transformer模型和Bert是真的强,前者结合RNN和CNN,放弃序列结构增加并行减少参数,用“钞能力”来了一次暴力美学,但是还是有很多问题,在近几年已经获得了很好的提升,各大榜单被以Transformer当作backbone的模型刷了一遍又一遍,让我不得不佩服大家到底有多卷….我还是希望大家应该更有一些创新精神而不是说找到个模型通过改变什么一点点获得好结果就拿出来发表,这就是纯粹为了发论文而发论文,希望大家以后不要这样,而是能够像最初的Model作者一样,来一次真正的美学!

完整代码现在还发不出来,因为我课程还没有结课,成绩是我看到老师打的分,但是以防万一,等结课后再给完整代码。

六.Readme
这里面包含所有的讲解,已经我做的一些其他的内容还有调试
Reference为整个Project中的参考信息来源,

该Project数据集
1.来自豆瓣的影评,28 部电影,超 70 万 用户,超 200 万条 评分/评论 数据,
打分为1-5分,该project取1为差评,label 0
3为中评,label 1
5为好评,label 2
我筛选了其中好中差评各10000条,其中取27000条为训练集,3000条为测试集

2.来自某公司的推特评论
共1500+1500训练集,好评/差评,测试集约1500条,约100差评

整体思路,通过Bert model的调用对文本实现情感分类任务,
1.文件分割,见data中筛选.py,将超大.csv文件通过rating也就是星级分割,获取目标数据集
2.文件读入,如上所述三个星级对应三个label
3.Tokenize,也就是预处理类似于token embedding,用tokenize.encode/plus方法
4.预处理Bert模型调用
5.网络搭建,Bert调用+全连接+relu+全连接(768(bert固定输出维度)100(全连接层,随便多少层,适当就好)n(n分类))
6.优化器以及精度,调用自适应函数,以及lookback等训练操作也是调用函数。
7.训练搭建,因为训练时间较长,所以我们选择批量,并且批量打印,batch size 我这里用的32(大型的一般用512)
8.评估,也就是测试集loss以及accuracy计算,通过argmax函数获取概率最大下标,即logits的max,对应序号即为label,故可直接if ==判断。
9.开始训练and测试,采用同步,当然可以将evalution参数设置为flase,就可以单纯训练,不进行测评,设置epoch即可选择训练次数。
10.整体细节较多,主要为各种数据类型的转换以及GPU与CPU的交互,因为大型网络,所以CPU速度过慢,batch size 32跑20个size就要300s,换GPU只需10s
注:如果测试集没有label,可采用分割训练集得到验证集的方式去调节网络参数,下列[1]就是采用这种方式。

2021.11.4-11.8
运行记录:
训练集每类9k数据集,训练集一般为每类1k;
1.利用英文数据集进行二分类,因为数据可能过于中和,运行正确率在85%左右,其中测试集没有label输出自己评价可以发现测试集正确率和验证集类似,大约85%,epoch为2
2.利用上述影评二分类,label 0 1 对应1 5星影评,正确率在99%+
3.利用上述影评三分类,label 0 1 2对应1 3 5星影评,正确率在99%左右
4.利用上述影评四分类,label 0 1 2 3对应1 3 4 5星影评,小数据训练,135星各9k训练集,4星10个训练集,输出相同大小,准确率78%左右,也就是说基本预测错误,说明不可以进行小规模训练。
5.利用上述影评五分类,label 0 1 2 3 4对应1 2 3 4 5星影评,正确率97%+
6.利用上述影评五分类,label 0 1 2 3 4对应1 2 3 4 5星影评,但是测试集用另外的电影影评,正确率为很低,会发现大部分评价都偏移为另一同类,应该是因为电影不通的缘故,
导致某些学习的信息在评判标准上会有统一误差,普遍是评级下降,比如真实5星4星都转化为3星,而三星及以下直接归类为1星,这应该是学习方式的原因,但是除了应该正确归类的,
剩下都都是偏移一类,所以另一方面来说又比较准确,因为至少分类一致,不过这也说明二分类的话就是完全没有问题了,可以应用于其他语境sentiment analysis

总结:以此类推,只要是语境相同,多分类问题的准确率是十分可观的,但是只要语境不通就容易整体向下偏移。而且都是一个epoch的结果,因为准确率已经十分可观了,
之前训练过多epoch,英文文本二分类问题,就是[1],增加的正确率并没有非常高,因为其实我们的网络主要起的是微调作用,因为调用了预训练bert模型。

2021.11.9
运行记录更新:
发现通过更改各类训练样本的个数,将偏移改正
例如对应改为1w,1k,1k,1k,1k,1星正确率达到99%+,但是因为还要考虑其他类别文本,
现尝试将训练集改为两端峰值如下:
8k 1k 2k 1k 8k正确率:
一星:97%
二星:99.8%
三星:8%,基本上归类到二星去了,
四星:16%,同上
五星:7%,同上
所以说基本上不适合不同语境

经过多次更改,发现效果不大,但是产生了新的想法,
如果把意义相同的更改label距离效果会不会很好,试了之后发现没有任何用处
因为一开始觉得可能是label的数值有所影响,但是后来发现是用的差求loss,没有关系。

又试了下500 100 500 100 1k
三星:97%
但是一星正确率又下去了

这就说明其实我们如果进行多分类,测试集用的其他语境的时候,可以通过调整训练集的
数量分布来使其中的某几个分类达到期望水准,但是也可能是训练集的文本不适合新的
情感分类的标准,毕竟主题不一样,所以那种通用的感觉要训练基础的常用语句。

如有问题联系作者
注:此为自然语言处理大作业
2021.11.8

Github代码(开源免费)
CSDN代码(付费)
记得Star哦!😃

  • 37
    点赞
  • 247
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 51
    评论
评论 51
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Miracle_ZXY

希望大家还可以多多打赏:)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值