文本分类任务算法演变
1.深度学习-pipeline
代码结构:
- config.py 输入模型配置参数,如学习率等
- loader.py 加载数据集,做预处理,为训练做准备
- model.py 定义神经网络模型结构
- evaluate.py 定义评价指标,每轮训练后做评测
- main.py 模型训练主流程
1.1fastText
示意图:
释义:
1.首先一个句子进来,我们将它拆分成一个个的字,每个字都有其对应的向量(假如是10个字;
矩阵为: 10 * hidden_size
)
2.我们将矩阵进行Mean pooling就得到1 * hidden_size
3.在经过一个线性层,加入我们需要分3类;则有:1 * hidden_size x hidden_size * 3
得到一个1 * 3
的结果
上面就是这个算法的整个过程
问题: 上述的10个字的句子,经过pooling之后,就损失了句子语序信息,或者某些特殊词组合的含义?
解决办法: 我们就针对某些常见的组合,有一些特定的向量
,而不是拆成字;比如:吃早饭了吗? 早饭是一个词,对应一个向量,其他的一个字一个向量,这样可以一定程度上保留语义、语序信息
1.2LSTM
释义: 使用RNN(LSTM、GRU)对文本进行编码,使用最后一个位置的输出向量进行分类;这里的LSTM和GRU都是RNN的一个变种。其中LSTM、GRU我们认为在相同的数据训练上,效果差不多,但都明显优于RNN。
方法:
LSTM通过将RNN的隐单元复杂化一定程度规避了梯度消失和信息遗忘的问题。
1.2.1公式详解
(一)
理解:
- 这个公式可以看作一个简化的RNN;
ft * C(t-1) 相当于RNN中的前一个神经元计算的值。it * ^Ct就是当前序列计算的值,
两个相加就是对下一步的输出。- 在这里f即单词
forget
;简单来说,前面的计算的记忆内容,有一些并不重要
,我们可以选择的遗忘
,便于记录当前序列计算的特征。这个遗忘的参数就是ft
;- i即单词
input
;当前序列呢,也有一些可能是没有价值的特征,避免影响后续,所以有个input参数,来计算出当前序列那些是有价值的,这个参数就是it
。- 所以算法,相当于对上一步和当前步的计算结果都有考量。
(二)
理解:
- 首先上述公式
()内
的内容,其实都是一个RNN,其中W表示
序列中各个模块的权重参数
;h、x表示RNN中的上一步输出值、输入值
,x表示当前序列时间步的输入
,b就是bias偏置项
。妥妥的标准RNN- 在1的基础上,公式前面都过了
一层激活层,分别是sigmoid、tanh
- ^Ct就是当前时间步的记忆
(三)
理解:
- ot,
o就是单词OUT
,即输出的意思,使用的激活函数是sigmoid;内部是一个传统的RNN算法,只是权重和咱们在前面步骤的不一样而已。- ht是当前
时间步真正的输出
;这个输出是当前的记忆经过激活函数tanh后;再乘ot;即输出门的参数
;意思即:当前时间步计算的记忆不应当作为输出,输出应该是有一个表达的转换的,这个转换的参数就叫输出门(ot)
- 在LSTM中输出包含两个,
一个是ht,即输出值;Ct即记忆值;
Ct相当于某个时间步的记忆,需要输出后,则需要加工,即得到ht
1.2.2可视化
- 左侧两个箭头输入,分别是Ct,ht
- 左侧下面的箭头分叉,从左往右依次为: ft、it、^Ct、ot
1.3TextCNN
释义: 利用一维卷积对文本进行编码,编码后的文本矩阵通过pooling转化为向量,用于分类。
注意: 在CV中我们的卷积核是正方形的,左右移动,上下移动;但是在NLP中,由于一个向量代表一个词,所以卷积是上下进行的
。类比理解:
就像是我们中文里,单独的字含义并不明确,有一些是四个字的成语、五个字的成语,或者是两个字的;这样就可以把他们的特征卷积提取
出来。但是一般不会超过5个字
,这个和语言的特性有关,毕竟在中文中,超5个字的词相对还是比较少的。
示意图:
1.4Gated CNN
Gated CNN简单来说是基于CNN的一种改进,用下面示意图表示:
相对于CNN来说,用了两个卷积,增加了可训练的参数量;
核心注意点:
1.新增的卷积,经过了一层激活函数Sigmoid,再与另外一个卷积结果相乘得到输出。
释义:
我们知道Sigmoid激活函数是(0,1)的数,和我们前面说LSTM中的门机制一样。这是一种重要思想。
1.5TextRCNN
释义: 咱们深度学习模型就像搭积木一样;这里的TextRCNN同样是如此,将RNN和CNN相结合起来
。
- 上面图示中,首先输入进行embedding,通过一个RNN的LSTM得到输出。
- LSTM的输出不一定适合CNN的输入,所以就转换为对于的字符,再进行embedding
- 将embedding的内容再交给后续的TextCNN继续计算。
1.6Bert
Bert编码示意图:
对于Bert的编码结果,文本分类有多种的使用方式,虽然方式不同,但逻辑相同,都是取其某些层的编码结果,再进行分类
而已。
1.6.1取[cls] token对应的向量
释义:
我们知道Bert会标注输入句子的首位,用首位的向量表示。在Bert网络结构,完成计算后,也会输出;在这里我们就取输出的[cls]对应向量,进行分类
- 以基础的Bert为例,取出的
[cls]向量为:1 * 768
- 假如要分10类,则使
用768 * 10的矩阵和[cls]向量相乘即
可- 为什么用Bert的cls;是因为Bert本身在做上下句关系时,用的这个cls
1.6.2将整句话的向量取max/average pooling
释义:
我们把Bert会的输出的向量,整体取出来,过pooling层,进行分类。
- 以基础的Bert为例,输出向量为
[cls]向量为:20 * 768
- 那我们将结果pooling后,得到
1* 768
- 假如要分10类,则使
用768 * 10的矩阵与pooling后的向量相乘即
可
4.这里pooling不会损失语义信息,因为输入token是经过bert网络计算过后的。
1.6.3将Bert编码后的向量再输入LSTM或CNN
释义: 就是字面意思,将Bert的输出,再接RNN、CNN网络,完成分类。
1.6.4将Bert中间层的结果取出加入运算等
释义: 上面的内容,我们都是取Bert最后一层的输出进行处理。
1.经过实验后发现,标准的Bert是12层,我们其中,第三层的结果,进行分类,也能取到较好的效果。
2.注意,实验的结果表示,如果需要分类的任务越复杂,则Bert的网络层数应该越深才行。
2数据问题
2.1数据稀疏问题
释义: 训练数据量小,模型在训练样本上能收敛,但预测准确率很低
解决方案:
1.标注更多的数据
2.尝试构造训练样本(数据增强)
3.更换模型(如使用预训练模型等)减少数据需求
4.增加规则弥补
5.调整阈值,用召回率换准确率
比如:3分类预测[0.3,0.4,0.3],就不采纳模型的预测结果,必须要达到0.6以上才采纳,那么预测的数据就变少了,但是准确率提升
了,
6.重新定义类别(减少类别)
2.2标签不均衡问题
释义: 部分类别样本充裕,部分类别样本极少
解决办法:
- 解决数据稀疏的所有的方法依然适用
- 过采样 复制指定类别的样本,在采样中重复
- 降采样 减少多样本类别的采样,随机使用部分,多余的样本进行测试
- 调整样本权重 通过损失函数权重调整来体现,比如,教育的材料权重调整时,损失函数增大。
3.多标签分类问题
释义: 多标签 不同于 多分类;多标签是M选N;多分类是M选1。比如:一部电影即属于动作片,也属于科幻片,就是多标签分类问题。
多标签问题的转化
-
分解为多个独立的二分类问题
举例: 比如有3个标签分类,就训练三个模型,每个模型都是二分类,判断材料是本模型和不是本模型;1/0 -
将多标签分类问题转换为多分类问题
举例: 比如有动作电影、科幻电影、枪战片三类;把可能的搭配全部梳理出来
- 动作、科幻;2. 动作、枪战; 3. 科幻、枪战
- 动作 5. 枪战 6.科幻
这样的多标签分类,就变成了一个6分类的问题,但是这个方案,对于标签较少比较合适,如果标签过多,可以详细,分类将膨胀。
- 更换loss直接由模型进行多标签分类
举例: 比如使用,BCELoss损失函数。
理解: 比如我们正常的分类,输入是标记一个,即[1,0,0,0,0];但是多标签分类,可以标记多个:[1,0,1,0,1],即表示数据属于1、3、5类;通过BCELoss损失函数,可以进行计算和参数更新。
其示意图公式如下: