文本多分类踩过的坑

Part I:背景

Part II:数据不均衡问题

Part III:过拟合问题

Part IV:Attention机制

Part V:调参技巧

Part I:背景

文本多分类是我大四入门NLP时的第一个任务,做了以下工作:

1. 基于CNN的方法:普通CNN分类[1],我自己改进的UCN

2. 基于LSTM的方法:GRU,LSTM,BLSTM,Hierarchy-LSTM,RCNN

3. 结合CNN和LSTM的方法,主要在C-LSTM[2]模型上进行改进。

4. 最后跑了MLP进行对照,用TF-IDF特征和LSI特征。(漏了SVM大佬)

这篇文章仅针对模型不work的一些问题进行总结,不会对模型进行详细介绍,相关综述可以看这里:基于深度学习的文本分类综述

分类函数:

我研究的是一对多的文本多分类问题,二分类任务通常使用sigmoid函数进行分类,多分类任务通常使用softmax函数,使得所有类别的概率之和为1:

《文本多分类踩过的坑》

从这个公式可以看出,指数函数会使得原本就大的数值会变得更大,而原本小的数值会变得更小。所以softmax比较适合一对一的多分类问题,也就是类与类之间是互斥的,但是并不适用于一对多的多分类问题。

所以我把这个多分类问题转化成K个二分类问题,假设类别数量为K,这里我用K个sigmoid函数来代替softmax函数,分别对应K个类别的分类概率。这个任务应用场景是AI+法律,类别数接近800,倒也不是什么大问题,比较难的是类别不均衡问题。

衡量指标:

在分类问题中,最常见的评估指标是准确率(Accuracy),定义为在测试集中预测正确的样本占总样本的比例。但是对于不均衡数据,有些类别样本非常多,有些类别样本非常少,那么样本中占大部分的类别就会对Accuracy造成很大的影响,比如,模型可能直接全部预测为这个类别,Accuracy依然还是很高,难以很好地评估模型的好坏。

因此对于不均衡数据,这里介绍精确率(Precision),召回率(Recall)与F1值。

Precision定义为预测正确的真正样本占所有被预测为正样本的比例,Recall定义为预测正确的真正样本占所有真正样本的比例,而F1值定义为Precision和Recall的调和平均值:

《文本多分类踩过的坑》

可以看到F1值会更接近P和R中较小的值,我们也可以自己设置Precision和Recall的重要性:

《文本多分类踩过的坑》

《文本多分类踩过的坑》 >1的时候Recall影响比较大, 《文本多分类踩过的坑》 <1的时候Precision影响比较大。

我当时用的衡量指标是F1值,当然也可以用ROC曲线或AUC曲线来衡量。

Part II:数据不均衡问题

数据不均衡问题在这里体现为类别不均衡,假设正例很少,负例很多,我们可以从数据本身和模型两方面来解决这个问题:

一. 数据方面

既然正负例数据比重失调,那我们可以对原始数据集进行采样调整,使得调整后的数据集正负例数据接近,再进行训练。常见的采样方法包括欠采样和过采样两种。

1. 欠采样方法

欠采样是丢弃训练集的一些负例,那么经过调整后的数据集往往会远小于原始数据集。

但是欠采样往往要丢弃很多负例,可能会损失很多重要信息,因此我们在采样的时候,可以尽量选取比较重要的核心负例,丢弃没那么重要的负例。

2. 过采样方法

过采样是对正例进行扩充(数据增强),增加正例使得正反例数据接近,那么经过调整后的数据集往往会远大于原始数据集,增加了时间开销。

在NLP中我了解到的数据增强方法主要有:

1)加入噪声:在正例中加入一些噪声,比如随机删掉某些词,随机打乱词语顺序(在文本分类任务上或许可以,但是在一些有语序要求的任务上就不行),生成新的数据。

2)复述生成(paraphrasing):属于seq2seq任务了,在问答系统有一个领域叫做问题复述,根据原始问题生成格式更好的问题,相当于修正不规范的问题,将新问题代替旧问题输入到问答系统中,我觉得的也算是一种数据增强方法了吧。

其实我自己平时并不用数据增强方法,因为我觉得文本和图像不一样,图像对某一些像素加入噪声,影响可能不是很大,但是在文本中一点小的改动可能就会带来很大的影响,你删掉的词很有可能就是比较重要的词,如果你删掉的词不重要那相当于删去停词,打乱顺序在很多任务上就更不行了,因为句子本身是有语序的,有上下文关系。文本生成倒是一种 我认为相对比较好的数据增强方式。

二. 模型方面

这种方法直接基于原始训练集来学习,对原始模型进行改进。

1. 阈值移动

这是很常见的一种做法,对算法的决策过程做了改进。在使用sigmoid函数来预测正负例的时候,我们通常设置阈值为0.5,激活值大于0.5的时候判定为正例,否则为负例。

如今我们正负例比例失调,可以调低阈值,相当于放宽对正例的分类要求,提高对负例的分类要求。比如将阈值设置成0.3,那么激活值大于0.3就可以判定为正例了。

我当时也是用的这种方法,手动改阈值,这也是比较麻烦的一点,不好调超参,我专门设置了好几组超参阈值来自动选择最优的那组。

2. 加权Loss

这种方法不需要调整sigmoid阈值,而是修改了Loss函数。

原始的Loss函数是binary cross entropy,极小化负对数似然函数:

《文本多分类踩过的坑》

y是label,取值为1或者0,a是sigmoid激活值,从公式中可以看到正例loss和负例loss是等权平均的,因此我们可以提高正例loss比重,使得正例更加重要,强迫模型去学好正例。

另外,我做序列标注任务也遇到过数据不均衡问题,在对一段文本进行标注的时候,某些tag非常非常多,某些tag非常非常少,这里用的是softmax+交叉熵,pytorch提供了一个函数,可以直接设置正确tag所对应的权重。

在设置权重的时候也有一些方法,可以根据这些tag在整个序列中的标注次数进行设置。比如,我有两个tag,tag a出现了100次,tag b出现了2次,那么我可以设置tag a的比重是2/100,tag b的比重是98/100,或者直接设置一个固定的比重。

如果权重设置得太狠了,最终也可能导致tag b预测太多,又需要强行调整很麻烦,所以我会对Loss进行动态调整,如果这个位置的tag预测对了,我就恢复正常Loss比重,否则设置加权Loss进行惩罚。

Part III:过拟合问题

过拟合的表现为在训练集上表现很好,在测试集上表现不好。过拟合一般是因为数据太少,或者模型太复杂,对数据中的一些误差也进行了拟合,对这个训练集拟合得非常好,但是改了一下数据集,就不work了。在很多文本分类任务上,fastText其实已经足够了,其实不需要太过于复杂的模型。(我做这个任务的时候fastText好像还没有出来)

我们依然可以从数据和模型两方面出发来解决。

关于数据,我们可以增加标注数据(深度学习比较依赖于大量的数据,从大量的数据中学习数据特征),或者进行数据增强(Part II有提到),让模型见到更多的数据去学习。

关于模型,我在用LSTM的时候出现了过拟合,因为用到了两层双向LSTM(相当于四层普通LSTM的参数量),Attention机制(拟合得更好了)。

过拟合一方面体现在,在训练集上效果有提升,但是在测试集上效果反而降了。我当时主要从减少参数,简化模型这方面入手,比如:减小hidden state维度,使用GRU来代替LSTM,使用一层双向叠加一层单向LSTM,使用CNN叠加一层LSTM。

另外关于双向LSTM,我现在有一种思路是,可以在正向LSTM后直接接上反向LSTM,虽然做法略显粗糙,但是相比原始双向LSTM可以减小一倍的参数量。

过拟合另一方面体现在,在训练集上,同一段文本,我稍微修改了一些词语,删掉或者加入“的”这类词语,预测结果就有改变。这说明,LSTM把不重要的词语也拟合进去了,这些词语本来是不重要的,但是LSTM依然进行了学习。

比较粗暴的方法是加入去停词,进行特征工程,不让LSTM学习这些词。

或者一种更优雅的方式是使用stop,skip或者skim reading,进行信息过滤,在我另一个回答中第二点有提到 利用RL辅助学习语义向量

对付过拟合问题,还有一些其它的tricks:

1)正则项,在Loss中加入权重大小,限制权重变大,限制模型的学习能力。

2)Dropout,我一般设置dropout的阈值是0.3,对于每一个神经元,按照一定的概率暂时丢弃,相当于每个batch都在训练不同的网络。需要在训练的时候对权重进行放大,或者在测试的时候对权重进行缩小,使得dropout后的分布可以拟合之前的分布。

3)加噪声,对输入或者权重加高斯噪声,目的也是为了减小权值。我之前做过一个序列预测任务,对输入加了噪声后效果是有提升的。

4)Ensemble模型

集成学习可以构建或者结合多个学习器进行学习,分类问题一般由这些个体学习器进行投票来决定,在一定程度上可以降低过拟合。个体学习器应该好而不同,具有一定的准确率,又要有多样性,如果个体学习器之间差异很小,集成就没有多大作用了。

我在文本分类任务上没有进行ensemble,但是我在别的任务上有用到。

集成学习根据个体学习器之间的依赖关系,可以分成Bagging和Boosting两大类:

Bagging:并行化学习方法,个体学习器之间没有依赖关系,可以降低方差。

主要思想是:对数据集进行回放采样,产生k个子训练集集,用这k个子训练集分别训练出k个学习器,最后使用这些学习器进行投票或者加权平均,得到最终结果。

Boosting:串行化学习方法,可以降低偏差(描述预测结果和真实结果的差距)。

主要思想是:基于整个训练集先学第一个学习器,然后根据前面的学习结果来调整样本发布,再训练第二个学习器,以此类推,最后再结合所有学习器。

经典代表是xgboost,可以在降低偏差的基础上,又可以降低方差,因为它借鉴了随机森林,随机选择特征(通过随机性降低过拟合),也在Loss上加入了正则项。我之前做过一个kaggle小比赛,正负例比重达到1:3000,直接上xgboost跑出了一个不错的效果。

Part IV:Attention机制

Attention机制最初是用于机器翻译(seq2seq)中,在Decoder每一步的词语c上,用该步的信息对Encoder所有步的词语进行对齐,捕捉跟这个词c比较有关的词语,这种利用到外部信息的方式我们叫做align attention,只利用自身信息的方式叫做self attention。

《文本多分类踩过的坑》
《文本多分类踩过的坑》

Align Attention又可以分成soft attention,hard attention和local attention几种形式,soft方式对所有词进行加权对齐,而hard方式直接对齐到某个词,local方式结合了soft和hard,对齐到某一个区域。

那么Attention机制用在文本分类中,我们可以看做是对句子进行加权,提高重要词语的注意力,减小其它词语的关注度。后面也可以对Attention进行可视化,可以比较直观得看到哪些词比较重要,哪些词没有关注到,比较具有可解释性。需要注意过拟合。

我当时自己实现了一个粗暴的Attention,直接用最后一个step的hidden state经过全连接层,生成所有step权重(也就是一个维度为句子长度的向量,再进行softmax归一化),再对所有step的hidden state进行加权平均,得到最终的一个语义向量,来表征整个文本。我当时考虑到,建模到最后一个step的时候,这个hidden state其实能包含前面所有信息(当然会有信息衰减),那么用这个state去生成权重应该算是合理。

现在看来,这种方式无法从直观上解释,向量中的这个权重到底是怎么对应到那个词的,因为我仅仅用最后一个state去生成所有的权重,没有和每一步的state做对齐。

正确的打开方式应该是:用每一步的信息来算该步的权重,保证了每个权重值是根据这个词来算的,比较合理也比较直观。

《文本多分类踩过的坑》
《文本多分类踩过的坑》

用每一步的hidden state通过一个共享的全连接层,生成向量ut:

《文本多分类踩过的坑》

每一步对ut和uw进行点乘可以得到一个数,那我们把所有step的这些数值拼接成一个向量,再进行softmax归一化就得到权重了:

《文本多分类踩过的坑》

注意:这里uw也是一个权重,我觉得或许可以直接把所有ht等权平均作为uw。

另外从层次上看,Attention机制在文本上又可以分成单词级别和句子级别,在单词级别上挑选重要的单词,在句子级别上挑选重要的句子。

Part V:调参经验

最后放上一些我自己的调参经验。

1. 数据预处理:数据归一化,权重初始化,在词典中去掉词频太低的词(因为这些词本身出现很少,我们不需要特意进行拟合),用word2vec预训练词向量,我喜欢把数据保存成H5py文件,再写个DataLoader读batch数据,数据也要进行shuffle。

2. Hidden state维度,我在分类任务,阅读理解任务上一般设置维度是128,在seq2seq任务上会设置比较大一些,512或者1024都可以有。

3. Batch Normalization你值得拥有,保你梯度无忧,又可以加快训练速度。

4. Adam你也值得拥有,收敛也很快,但是最后的准确率可能比不上SGD+Momentum,你可以先用Adam,下降到一定范围了再换SGD。

5. TensorBoard也要拥有,方便调参。

6. Relu也是一个好东西,除了最后一层分类层以外(或者其它需要限制范围到[0, 1]之间),激活函数我都用Relu,收敛速度也很快,保证了一定的梯度。

7. 到底要不要分词?其实直接用字向量也可以,因为现有的分词工具可能也会分得不准确。我做过实验,字向量其实也可以比较好地表征文本。

8. ……….

引用文献:

[1] Chen, Yahui. “Convolutional neural network for sentence classification.” (2015).

[2] Zhou, Chunting, et al. “A C-LSTM neural network for text classification.” arXiv preprint arXiv:1511.08630 (2015).

    原文作者:susht
    原文地址: https://zhuanlan.zhihu.com/p/35586145
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞