fasttext 文本分类_文本分类-基础算法

ef750bbc05022f480dfc706cdb79750d.png

前言

文本分类算是NLP比较简单的任务之一,并且目前由于预训练语言模型的出现,现在文本分类直接上bert效果就挺好。

为啥要写这个系列呢,其实自己也不是很想写,因为网上有很多教程了。但是由于自己一个月前做了三个文本分类的比赛(CCF BDCI2019的情感分析,智源的假新闻,CCF BDCI2019的识别实体及其情感)情感分析初赛第2,复赛第18。假新闻和实体情感分析都进了复赛。

想着还是把比赛中的一些收获总结下吧,写点东西记录下还是好的。

情感分析这个比赛挺可惜的,复赛时我自己由于实验室项目的事,抽不开功夫去多跑实验,可惜了。

写文不易,大家觉得有用,给个赞,给点更新的动力


nlp方面的比赛,大家一开始好像都是在bert的基础上做优化,初赛由于时间很长,我和队友在bert的基础上做了很多尝试(修改loss,修改网络结构,对数据分布做normalization,Bert字向量+别的字向量或词向量,伪标签,标签平滑等等),不得不说,参加比赛,确实可以学到一些东西。

word2vec

词向量或者字符向量时nlp任务里不可缺少的,一般可以用别人与训练好的词向量/字向量,或者自己训练。在特定任务中,可以针对特定数据集进行fine-tune or forzen。也可以像textcnn的实验中一样,做两个通道,一个是冻结词向量,一个是对词向量微调。最后拼接合并等等。

word2vec知乎上很多很好的文章,这里就不展开介绍了

TF-IDF

虽然现在有了bert,有了词向量,但是tf-idf在nlp的比赛中仍然是一个强特征,合理使用就可以提分,这个在后面的trick中介绍。tf-idf这里就不展开讲了,知乎上很多好文章可以用来学习。

n-gram

下图来自https://zhuanlan.zhihu.com/p/32965521

56405c82fc2a70d5725b9b6b309204b9.png

n-gram特征有时候对算法性能也有提升。我记得以前读实体识别的文章时,2018年ACL上的一篇名为 Chinese NER Using Lattice LSTM的paper就使用char向量和bichar向量结合的方式,我觉得这里的bichar就是一种n-gram特征。我们知道,中文分词准确率一直是一个问题。对于中文任务而言,分词质量的高低往往就直接影响了downstream tasks的性能(对于分词任务其主要的难点为OOB问题,即对于未登录词的处理,其次是歧义的问题),但是若直接使用单个汉字则又无法充分挖掘句子中潜在的句法结构语义信息,其效果往往不好。

虽然对于汉语的nlp任务,字符embed+LSTM+atten在bert这种预训练语言模型出现之前效果也还可以,但是单一的字符embedding被认为丢失了很多信息,我们知道,句子中每个字首先会通过char2id转换为对应的id,然后通过id获得对应char embedding。这个过程中,原本句子的词序信息就会被丢失(词序在某些任务中很重要,比如实体识别)。不过我在想通过lstm能重新恢复原本的词序/位置信息吗?(这里由于作者本人自身知识有限,也不清楚)

同时我们知道,汉语中各个汉字并非孤立存在,其真实的含义与上下文密切相关(如“长城”和“长高”,同一个“长”字前者为名词,后者为动词含义不同),单纯的char embending就不能表示这种差异。(不过现在预训练模型bert在特定任务上fineturn会解决这种问题)

一般做比赛时好像都认为,单纯的char embedding所提供的特征比较单一,所以一般会加别的特征,n-gram就是其中的一个,可以通过字符级别的n-gram来。

n-gram的优点:

  1. 为罕见的单词生成更好的单词向量:根据上面的字符级别的n-gram来说,即使这个单词出现的次数很少,但是组成单词的字符和其他单词有共享的部分,因此这一点可以优化生成的单词向量
  2. 在测试集上,即使单词没有出现在训练语料库中(OOV),仍然可以从字符级n-gram中构造单词的词向量,即字符级n-gram利用了单词的形态学信息。这个在形态学丰富的语言中挺有用。
  3. n-gram可以让模型学习到局部词序信息, 如果不考虑n-gram则便是取每个单词,这样无法考虑到词序所包含的信息,即也可理解为上下文信息,因此通过n-gram的方式关联相邻的几个词,这样会让模型在训练的时候保持词序信息

上面内容应该存在很多不准确,由于本人能力有限,上面都是自己胡乱的一些想法。看官们有自己的辨识力就行。

FastText

fastText模型架构和word2vec的CBOW模型架构非常相似,如下图所示

3a9224cfc228ce4ca6dfd4cb0e35af3c.png
图1 fasttext模型图

从图1可以看出,fastText模型和CBOW一样也只有三层:输入层、隐含层、输出层(Hierarchical Softmax)。输出层没啥好说的。fasttext的隐藏层是把输入层的向量求平均。那输入层到底是怎么做的呢?且听我慢慢道来。

cbow是将每个词先用one-hot编码,然后再经过随机初始化的look-up table。fasttext也是相似,区别就是fastText在输入时,将单词的字符级别的n-gram向量作为额外的特征,所以fastText的输入是多个单词及其n-gram特征,这些特征用来表示单个文档。

在word2vec中,我们并没有直接利用构词学中的信息。无论是在skip-gram还是cbow中,我们都将形态不同的单词用不同的向量来表示。例如,“girl”和“girlfriend”分别用两个不同的向量表示,而模型中并未直接表达这两个向量之间的关系。而字符级的n-gram可以抓住单词的形态学信息,在fasttext词向量中直接利用了构词学中的信息(形态学信息)。

我觉得字符级n-gram的引入,有以下几点好处:

  1. 对于像英语、芬兰语这种形态学比较丰富的语言,字符级的n-gram抓住了单词的形态学信息。在fasttext词向量中直接利用了构词学中的信息。
  2. 为罕见的单词生成更好的单词向量。根据上面的字符级别的n-gram来说,即使这个单词出现的次数很少,但是组成单词的字符和其他单词有共享的部分,因此这一点可以优化生成的单词向量
  3. 一定程度上解决了OOV问题。即使单词没有出现在训练语料库中,仍然可以从字符级n-gram中构造单词的词向量。
  4. word-n-gram可以让模型学习到局部词序信息,可以在一定程度上捕捉词序信息。

cbow先将词进行one-hot编码,其实在我看来就是构造了word2id词典,这样就可以通过lookup-table 查询到对应的词向量。我不清楚fasttext是不是也是这么做的,但我觉得通过构建vocab2id这个map,就能实现one-hot编码的作用,所以下面keras实现中也是这么做的。

下面是我看到的别人的评论,我记录了下来

fastText作者的实现是句子中的单词再加上n-gram作为输入的。并且为了节省内存,n-gram需要经过哈希处理,哈希到同一个位置的不同n-gram是会共享一个embedding的。
fastText单词的embedding跟常见的embedding方法没什么不同,都是先随机初始化,然后再通过反向传播学习参数,维度就是人工指定的一个超参数,是词向量的维度。不过有两点要注意的地方,第一是如果你说的fastText的embedding是通过supervised的方式来训练一个文本分类器并同时学习embedding的话,那么这个embedding的学习过程就是有监督的,与word2vec等无监督的模型是有一定区别的;第二是fastText还会学习n-gram(这里的n-gram其实有两种,分别是char-n-gram和word-n-gram,就不展开说了,真正有兴趣的话可以读读源码)的embedding,这使得它可以在一定程度上捕捉词序信息。为了节省空间,fastText在原作者的实现中并不是每一个n-gram都学习一个单独的embedding,而是首先将n-gram进行hash,hash到同一个位置的多个n-gram是会共享一个embedding的。

fasttext的输出使用了层次softmax,对层次softmax不了解的可以看下面这篇文章,我觉得讲的挺清楚的,适合入门了解。

我想我是她的海:一篇浅显易懂的word2vec原理讲解​zhuanlan.zhihu.com
ebf7938579d747ffd37ac2e062aabca3.png

FastText keras简单实现(主要是为了理解算法)

model 

完整版请看

https://github.com/CandyConfident/textclassification_baseline/tree/master/fasttext​github.com

TextCNN

想着先记录下自己对textcnn的认识,做比赛时自己还看了下textcnn的论文Convolutional Neural Networks for Sentence Classification 和 A Sensitivity Analysis of Convolutional Neural Networks for Sentence Classification(这会在想,菜是原罪呀,textcnn还要在比赛的时候预习,佛了)

e2b98454f64ccd56dba59234419f6736.png
图1

191e969352135ac571561b3bea0cb778.png
图2

基本上textcnn的原理看这两幅图就可以理解,textcnn是2014年提出的,2013年谷歌提出了word2vec。当时cnn在计算机视觉任务中取得了很多成绩,作者kim便将其用到了nlp上面,结果十分简单的cnn结构也取得了很好的效果。

textcnn算是最早将cnn用到nlp领域内吧(我猜的,错了不负责任),textcnn的具体结构如图2,

  • 输入 Embedding:第一层是图中最左边的7乘5的句子矩阵,每行是词向量。在英文中是词向量,中文可以用字符向量,也可以分词后训练成词向量。
  • 特征抽取 Convolution:卷积层本质上是个特征抽取层卷积层本质上是个特征抽取层,可以设定超参数F来指定卷积层包含多少个卷积核(Filter),对于某个Filter,可以看作是一个大小为k*d(其中k是Filter指定的窗口大小,d是Word Embedding长度)的滑动窗口,对于句子来说,虽然用词向量将句子表示成了二维矩阵,但是卷积核在维度=1的方向(横向)移动是没有意义的,因为每行就是一个词向量。所以textcnn中卷积核只在维度=0的方向上从输入矩阵的第一个字开始不断往后移动。textcnn中使用了不同kernel_sizes的一维卷积层(对应到图二中就是kernel_sizes=(2,3,4) 的一维卷积层),同时每个kernel_sizes的卷积核的个数有多个(对应图二中是2个),但每个卷积核的参数应该不是共享的,不然没啥意义了我觉得。这样,每个卷积核从第一个词滑动到最后一个词,就生成了一个特征向量,叫做feature map,多个卷积核就生成多个feature map。
  • 降维 MaxPolling:第三层是一个1-max pooling层,这样不同长度的特征向量经过pooling层之后都能变成定长的表示。
CNN网络的pooling层有什么用?​www.zhihu.com
daec410242b773e7c7ec66ffa8c7a562.png
  • FullConnection and Softmax:最后接一层全连接的 softmax 层,输出每个类别的概率。

b007efef7ca2b9c1db0fedaa39c02a64.png

trick 1

这里其实有一个小trick可以尝试,我看到很多文本分类的分享(多标签或者多类别)都会用到bert+RCNN这个结构,也算是一个很好的baseline模型。

rcnn这个模型原理简单来说就是

先看图

736db9289828de4f515b2d29d17d3279.png
RCNN模型

获得句子的词向量表示S_i(w)

  1. S_i(w)通过双向LSTM或GRU获得S_i_f(w)和S_i_b(w)
  2. 将S_i(w),S_i_f(w),S_i_b(w)三个进行拼接得到新的词向量[S_i(w),S_i_f(w),S_i_b(w)],
  3. 当我们获得了单词wi的表示Xi后,我们将一个线性变换与tanh激活函数一起应用到Xi
  4. 再将全连接网络的输出进行MaxPooling
  5. 最后将其输入一个全连接+softmax分类器中实现分类

看下面五个公式因该会很清楚,需要注意的是,这里maxpooling是在时序维度上,不是在位置维度上,详见RCNN模型图

还有W(2)是个矩阵,而不是一个向量,所以y_i是向量,而不是单个值

a23a957f29a6f7fc3ad74a81074b5cf0.png

efdee2cd4ca63aed04297c1fcba4e318.png

47b648998c4642d8d98ea5245668f955.png

aa0ce510e01033044dc19be9b36813ab.png

0c8d260b31cc9c2fa3b33651e1442f52.png

这里我看到一般rcnn的实现,最后都会接一个MaxPooling层。

15de1b4e01ab9b8c4a27cf4524820010.png
图3 maxpooling

一般我们最常用的就是MaxPooling(也可以叫1-max pooling),提取出feature map那个的最大值,通过选择每个feature map的最大值,可捕获其最重要的特征。这样每一个卷积核得到特征就是一个值,对所有卷积核使用max pooling,再拼接起来,可以得到最终的特征向量,这个特征向量再输入softmax layer做分类。整个过程如图1中所示。

CNN中采用Max Pooling操作有几个好处:

  • MaxPooling可以保证特征的位置与旋转不变性,因为不论这个强特征在哪个位置出现,都会不考虑其出现位置而能把它提出来。但是对于NLP来说,这个特性其实并不一定是好事,因为在很多NLP的应用场合,特征的出现位置信息是很重要的,比如主语出现位置一般在句子头,宾语一般出现在句子尾等等。
  • MaxPooling能减少模型参数数量,有利于减少模型过拟合问题。因为经过Pooling操作后,往往把2D或者1D的数组转换为单一数值,这样对于后续的Convolution层或者全联接隐层来说无疑单个Filter的参数或者隐层神经元个数就减少了。
  • 对于NLP任务来说,MaxPooling可以把变长的输入X整理成固定长度的输入。因为CNN最后往往会接全联接层,而其神经元个数是需要事先定好的,如果输入是不定长的那么很难设计网络结构。通过Pooling操作,每个Filter固定取1个值,那么有多少个Filter,Pooling层就有多少个神经元,这样就可以把全联接层神经元个数固定住(如图3所示),这个优点也是非常重要的。

但是,CNN模型采取max-over-time-pooling也有缺点:

  • 首先特征的位置信息在这一步骤完全丢失。在卷积层其实是保留了特征的位置信息的(卷积核相当于k-gram,它捕获到的是单词的k-gram片段信息,这些k-gram片段就是CNN捕获到的特征,k的大小决定了能捕获多远距离的特征)。但是通过取唯一的最大值,现在Pooling层只知道这个最大值是多少,其出现位置信息并没有保留;
  • 另外一个明显的缺点是:有时候有些强特征会出现多次,出现次数越多说明这个特征越强,但是因为Max Pooling只保留一个最大值,就是说同一特征的强度信息丢失了。

对于情感分类的比赛,例如句子“最近某某地方发现一个小伙要跳河,原因是每天996,小伙有点撑不住,但是又没办法,因为不干活没饭吃,小伙觉得很难受,不知如何是好,只好跳河了结余生。这时民警李XX正好路过,李不顾冬天河水的冰凉,脱掉外套就跳入河中,救起了被996折磨的小伙,李这种救人于危难之中的精神,如冬天的暖阳,温暖着社会和我们。。。”让判断这句话的情感,显然这句话是个正面情感,但若使用maxpooling,也许会发生问题,因为句子的前半段是负面,这个时候,特征的位置信息对于判别整体情感倾向是有帮助作用的,所以引入位置信息应该有帮助。所以可以尝试使用K-MaxPooling or Chunk-MaxPooling。但一般我看到的pooling层都是同时用max pooling 和 ave pooling,然后将两者的输出concat。

TextCNN keras简单实现

完整代码请看

https://github.com/CandyConfident/textclassification_baseline/tree/master/textcnn​github.com
class 

TextRNN

textrnn的结构如下图所示,很简单,属于入门级网络,这里不做介绍了。在做文本分类比赛时,我用的第一个baseline是bert+dropout+dense+softmax,第二个baseline就是bert+dropout+bilstm+dropout+dense+softmax,但三个就是bert+dropout+rcnn+dense+softmax。所以下面这个网络结构其实很经典也很实用。

9362ddbe688e311565f47324b61d093d.png

TextRNN keras简单实现

class 

完整代码请看

CandyConfident/textclassification_baseline​github.com
eca4ecd895228d198c2f8ce44ab31b57.png

HAN (主要针对长文本【篇章级别】分类)

在HAN网络提出之前,哈工大团队提出 Document Modeling with Gated Recurrent Neural Network for Sentiment Classification. Duyu Tang, Bing Qin , Ting Liu. In EMNLP, 2015,该文章提出了一种层次神经网络的结构做篇章级别的情感分析,取得了很好的效果:

cd4e64778c54b8b82738be11aec8f5bd.png

模型比较简单,这里不详细介绍,有兴趣得看官可以看论文。

2016年另外一个团队提出了HAN网络,也是使用层次神经网络的结构做篇章级别的文本分析。并且和上篇论文有很多相似之处。文章利用word att ention 和 sentence attention来更好的实现加权平均,取得了很好的效果。

f9385ee1031ebae9e836d27902e04937.png

Hierarchical Attention Networks for Document Classification. Zichao Yang1, Diyi Yang1, Chris Dyer and et al. In NAACL-HLT, 2016

  1. 首先是词语到句子级别的,利用词向量,通过双向GRU,对一句话中的词抽取特征
  2. 考虑到在每个句子中,各个词对句子信息的贡献不同。随后作者利用word attention 来对BI-GRU抽取的词特征加权求和,生成句子表示(句向量);
  3. 然后是句子到文章级别的,一篇文章有多个句子,把它们看成是一个时间序,使用双向GRU对所有句子进行整合,生成新的句向量;
  4. 对句向量使用sentence attention ,具体过程和word attention相似,获得文章表示
  5. 最后,用Softmax做分类。

详细过程看下图(既然有前人写好了,我就不再重复了),下图来自,若侵权立即删除

老宋的茶书会:几个可作为Baseline的文本分类模型​zhuanlan.zhihu.com
e657460c2676f5aafa63b8d7113326dc.png

a3089242fbe6770adee7cb4a37419ecd.png

8102551f4c876603565452b000287feb.png

4baad89d45023c213d4a525fc88f36f1.png

HAN keras简单实现

class 

attention layer

"""

完整代码请看

https://github.com/CandyConfident/textclassification_baseline/tree/master/HAN​github.com

先写在这,由于本人姿势水平有限,肯定会出现错误,望各位看官不吝指教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值