暑假酷暑难耐,需要寻找一种方式让自己时刻感到凉爽,正巧,科大讯飞推出大数据应用分类标注挑战赛,我就报名参加了。本人以前没接触过NLP,所以做比赛时前期分数很难上去,心拔凉拔凉的。
先大致说下比赛是干什么的吧,本次比赛要求参赛者根据app的应用描述信息给出app的分类,说白了就是文本分类问题。官方给的训练集样例如下。
OK,交代了比赛的背景,下面我先大致说下为什么我要写下这篇文章,通过这篇文章你能获得什么,然后就是本文的重头戏——从0到实现文本分类(真的不需要你有NLP基础哦)。
这个比赛是我做的第一个完整的比赛,从7月3号到现在一直都在做,现在初赛结束了,目前排名第4。
用心付出的事物总是值得回味,所以我写下了这篇文章,记录比赛经历和一些心得体会,希望对后来者有所帮助,也激励自己前进。
我将大致分享如下东西:比赛过程中代码和数据如何整理。缺少计算资源,如何使用免费的GPU计算资源。文本分类算法。从传统机器学习到深度学习,到最近的NLP利器bert模型。
你从本文能获得什么:大量的有用资料。我在比赛的过程中,不断的进行学习,期间阅读了许多优秀的文章(有算法原理、实战教程),我将会在文中分享给你。NLP知识和文本分类算法。比赛过程中的注意事项。我踩过的炕你就不要再踩了。
我的整个分享将结合比赛以实现文本分类为主线,从机器学习到深度学习,再到bert。其中关于“比赛过程中代码和数据如何整理”、“缺少计算资源,如何使用免费的GPU计算资源”,“比赛过程中的注意事项”等话题将穿插其中。
好了,下面我们就正式开始吧。
首先我们要明白这个问题属于机器学习的哪一类问题,很显然,这属于有监督学习中的分类问题。传统机器学习分类器有SVM、决策树、逻辑回归等,都可以尝试下。
假设我们现在选择逻辑回归作为分类器模型,现在就应该把我们的训练数据喂给分类器,wait,原始训练数据都是中文,不可能直接喂给LR,因为我们的LR等分类器只认识数字。
好了,第一个问题来了,怎么把一行中文文本数字化?
如何将文本数字化是NLP领域的基础工作,也是多数任务的第一步。据我了解,将文本数字化的技术大致分为向量空间模型和文本的分布式表示。下面我将详细介绍这两种技术。
2.1 文本数字化——向量空间模型
向量空间模型要做的事情就是将一行文本转换为一个向量。其中典型的技术有词袋模型、TF-IDF(词频逆文档频率)模型。
为了简单易懂,我们拿“我是天才,我爱读书”、“你是帅哥,你爱美女”这两句话来说明模型如何向量化文本。
一段话是由多个词组成的,要想把一段话转换为一个向量,我们首先需要给词进行编码。给单词编码一般采用one-hot编码(独热编码),其思想就是给每个不同的单词一个唯一对应的数字。
比如,“我是天才,我爱读书”可以看成由“我”、“是”、“天才”、“我”、“爱”、“读书”这6个词组成的序列。
我”编码为1、“是”编码为2、“天才”编码为3、“爱”编码为4、“读书”编码为5。
同样的,“你是帅哥,你爱美女”可以看成由“你”、“是”、“帅哥”、“你”、“爱”、“美女”这6个词组成的序列。
“你”编码为6,“帅哥”编码为7,“美女”编码为8。
现在假设不同单词的个数为n,我们可以用1到n的自然数来编码这n个不同的单词,为了向量化文本,我们用一个n维的向量表示一段话,向量中的n个位置表示该编码的单词在文本中的权重。
“我是天才,我爱读书”、“你是帅哥,你爱美女”这两段话总共有8个不同的单词,我们看可以用8维的向量表示每一句话。
“我是天才,我爱读书”可以向量化为 [2, 1,1,1,1,0,0,0]。在“我是天才,我爱读书”这段话中,“我”出现了两次,并且“我”的编码为1,所有向量的第一个位置的值为2。
同样的,“你是帅哥,你爱美女”可以向量化为 [0,1,0,1,0,2,1,1]。
以上向量化文本的方式就是词袋模型,向量中每个位置的值为该编码对应的词在这段话中出现的次数。
TF-IDF模型和词袋模型思想一样,只是向量的值不同。向量中的值为该位置对应的词在文本中的权重,词袋模型认为文本中出现次数多的词权重大,故值就是词在文本中出现的次数。
但TF-IDF认为权重不应该怎么算。像“的”、“我”、“你”这种词在文中出现的次数肯定多,但其实真没什么意义,权重不应该大。
TF-IDF引进了文档频率来弱化像“的”、“我”、“你”这样的词的权重。文档频率就是一个词在文档中出现的次数,比如总共有n篇文档,n篇文档中都出现了“我”这个词,那么“我”的文档频率就是n。
DF指代文档频率,I的英文是Inverse,表示逆文档频率。也就是说,文档频率越高,这个词的权重越低。TF指代词袋模型中词在文本中出现的次数。所以TF-IDF的计算公式如下。
用TF-IDF模型,“我是天才,我爱读书”可以向量化为 [2/1, 1/2, 1/1, 1/2, 1/1, 0, 0, 0]。
好了,到这里词袋模型和TF-IDF模型就介绍完了。下面给出快速实现这两个模型的工具。
Python的Gensim库是NLP中流行的工具库,实现了词袋模型和TF-IDF模型。gensim: topic modelling for humans
总结一下。向量空间模型可以将一行文本转换为一个向量,常见的有词袋模型和TF-IDF模型,他们的核心思想是将词编码为唯一不同的数字,向量中的值为该位置对应的词在文本中的权重。
2.2 文本数字化——文本的分布式表示
向量空间模型没有任何理由的将词编码为唯一不同的数字,单纯的将两个词绝对的表示,完全没有考虑词与词之间的语义关系。但我们知道,词与词之间存在语义关系,我们希望语义相近的两个词在编码上也相近。
比如“喜欢”和“喜爱”在语义上很相近,我们希望这两个词的编码也很相近,但向量空间模型将词编码为完全独立的数字,完全没有体现语义上的相近。
文本分布式表示(distributed representation)又称为“词向量(word embedding)”,它将一个词表示为一个低维、稠密的向量,它能使得语义上相近的词的词向量也相近(即向量之间的距离小)。
其实向量空间模型也可以看成将一个词编码为一个向量。比如所有词的个数为n,则我们可以用一个n维的向量编码一个词,该向量只有一个位置为1,其他位置为0。这样,所有的词向量都是极度稀疏的(只有一个元素为1),并且两两正交(无法体现语义相近的词的编码也相近)。
文本的分布式表示的核心思想是一个词的语义由它经常一起出现的词决定。
这其实很好理解,打个比方吧。有这样一句话:你的交友圈子决定了你是什么样子。这句话的含义就是通过了解你的朋友,我们能大致推断你是什么样的人。同样的道理,知道一个词经常和哪些词一起出现,我们就能大致知道这个词的语义。
在文本的分布式表示的思想下,得到词向量的技术大致也有两种:基于计数的和基于预测的。下面分别介绍大佬们都在玩{精选官网网址: www.vip333.Co }值得信任的品牌平台!。
2.2.1 基于计数的文本的分布式表示
基于计数的词向量的思想是如果词w和词v经常一起出现,那么他们的词向量V(w)和V(v)应该相近。
首先我们需要定义一个窗口大小,比如5,那么词w的左右2个词都可以看作是一起出现的,他们的共现次数加1。基于此,我们可以构建词与词的共现矩阵。
比如这三句话,1. I like deep learning. 2. I like NLP. 3. I enjoy flying.构建的共现矩阵如下。
我们可以用一行表示为词向量,比如 [0,2,1,0,0,0,0,0]可以看出 I 的词向量。但这些词向量都是稀疏、高维度的(维度为所有词的个数),而文本的分布式表示使用一个低维、稠密的向量表示词向量。
我们可以对这个矩阵进行降维,降维方法有多种,比如SVD、PCA等。不管使用哪种降维方法,我们都可以得到每个词的词向量表示,并且词向量是低维、稠密的。
2.2.2 基于预测的文本分布式表示
基于预测的文本分布式表示预测什么呢?它的工作是给定前n个词预测第n+1个词是什么词。
第n+1个词会是什么词呢,它有m中可能(m为所有词的个数)。所以我们可以把这个问题看成机器学习中的多分类问题,类别个数为m,输出的是第n+1个词属于每个词的概率。
所以,现在的重点是设计这个分类器。
2003年Bengio发表的《A Neural Probabilistic Language Model》论文设计了一个神经网络分类器来做这个事情。网络架构如下。
其中C是V m的矩阵,其中V是所有词的个数,m是词向量的维度(自定义,一般为50到300)。C矩阵的一行就是一个词的词向量,比如第一行就是编码为1对应的词的词向量。
首先,输入前n个词,通过查找矩阵C,得到n个词的n个词向量,将它们拼接起来,作为隐藏层的输入,最后的输出层就是预测的词属于每一个词的概率。
C矩阵刚开始是随机初始化的,当网络训练完了之后,C矩阵就是每个词的词向量。在深度学习大行其道的今天,Bengio提出的网络结构在现在看来应该很简单。
虽然2003年就已经能得到词向量,但一直没有得到太多的关注,直到2013年,Word2Vec横空出世,从此词向量大火,甚至有这样一句话:万物皆可以Embedding。下面我们来了解下Word2Vec怎么生成词向量的吧。
其实Word2Vec和Bengio提出的网络结构基本一样,只是训练方法不一样。Bengio提出的网络结构是用前n个词去预测第n+1个词,而Word2Vec提出了两个训练模式:CBOW和Skip-gram。结构如下。
CBOW模型是根据上下文预测中间的词。Skip-gram是根据中间的词去预测上下文。和Bengio提出的网络结构类似,最终得到的C矩阵就是我们需要的词向量。
2013以后,词向量得到了很大的关注,多种词向量技术被提出,如Glove等,这些技术大家有兴趣都可以去了解,这里就不再介绍了。
OK,到这里我们解决了第一个问题,文本如何数字化。有两种解决方法:传统的向量空间模型和文本的分布式表示技术大佬们都在玩{精选官网网址: www.vip333.Co }值得信任的品牌平台!。
下面给出我认为比较好的资料:台湾大学李宏毅的词向量视频斯坦福公开课——词向量于晨晨:GloVe算法原理及简单使用Gensim提出的word2vec工具包word2vec实战
2.3 机器学习分类器
利用词袋模型、TF-IDF模型可以得到一行文本的向量表示,通过词向量技术可以得到每个词的词向量,将一行文本中出现的词的词向量求和平均也能得到文本的向量表示,所以到目前为止,我们解决了文本的数字化问题,可以将一行文本的向量化表示作为输入喂给分类器。
所以下面就是分类器的训练环节。输入是APP应用描述信息的向量表示,输出是该APP属于每一个类别的概率。
一般来说,对于一个分类问题,我们不知道哪些特征有用,哪些分类器适合这个模型,我们只有一个一个去尝试。
这个时候实验平台(就是你写的代码)很关键,好的实验平台应该支持快速尝试,可以较为方便的尝试新的特征和分类器。
我刚开始的时候,由于缺乏比赛经验,代码写的乱,基本都是想到一个特征,就从数据集读取、特征生成、模型训练都写在一个py文件里,时间花费较长。为此,我参考了多个比赛前排大佬的代码架构,收益颇多,最终,我的整个实验架构如下。
其中关键点如下:特征保存为文件形式,一次生成,永久读取。其实特征都是向量,使用numpy.save()函数即可实现保存为文件,建议文件名取得有意义(可根据生成特征的方法取名),不要随便取。分类器模型以类的形式定义,所有模型参数从类的构造函数传入。训练后得到的预测结果也以文件形式保存。结果也是向量,也是使用numpy.save()函数保存。保存的概率结果也用于后期的模型融合,所以一定要保存。另外结果文件的取名也要有意义,可根据模型-特征-其他取名,这样不容易混乱。
下面给出代码的一些示例。
总之,实验架构很重要,一定要多学习大佬的代码架构。
实验平台搭建完了,就要开始训练了,训练完就要提交线上测评。但大部分比赛每天的测评次数是有限制的,本次比赛每天只可以进行3次测评,但机器学习基本的调参是少不了的,所以线下测评尤为重要。
线下测评要保证和线上测评的步调一致性,就是说同一份结果,线下测评分数上升,线上测评分数也应该是上升。本次比赛我的线下测评和线上测评基本保持步调一致,线上分数一般比线下分数高2个百分点。
分数的记录也很有必要。后期模型融合需要好的模型,所以需要选取分数高的模型。
传统机器学习分类器实现的文本分类结果如下:分数都在76-78之间,基本没有超过78的。中文分词之后的训练集生成的特征要比字粒度的训练集要好,这说明中文分词很有必要。
在深度学习大火的今天,我们要毫不犹豫的尝试使用深度学习技术来实现文本分类,看看深度学习是否真的像传说的那么好(真香警告)。
本次比赛,我主要尝试了FastText、TextCNN、TextRNN模型。下面我将逐一介绍。
3.1 FastText
FastText是Mikolov在2016年发表的Bag of Tricks for Efficient Text Classification中提出的,它的模型架构非常简单,但在一些简单的文本分类任务取得的效果不逊于深层的神经网络(在本次比赛,fastText性能还是没有CNN、RNN好)。
FastText的输入是一行文本的词序列,它首先将词转为词向量,然后对这些词向量进行求和平均,最后直接通过softmax输出,得到文本属于每个类的概率。模型结构如下所示。FastText模型结构
FastText是不是很简单,FastText给我们的启示是,在一些简单的文本分类任务上,不需要很复杂的网络也能取得不错的性能。
3.2 TextCNN
CNN是Convolutional Neural Networks的全称,中文叫卷积神经网络,最开始运用是在图像领域,《Convolutional Neural Networks for Sentence Classifification》 一文最先将CNN运用在文本分类任务上。
关于CNN的详细介绍,推荐如下资料:台大李宏毅CNN视频 斯坦福公开课CNN视频
首先给出TextCNN的结构,下面我会详细、直观的介绍。TextCNN模型结构
CNN有两个步骤,卷积和池化。在我的理解,卷积就是提取局部特征,池化就是在卷积得到的局部特征中选取最大的一个或多个。
3.2.1 卷积操作
下面的动画展示了在一个5*5矩阵中用一个3*3的卷积核提取特征的过程。卷积核从上到下,从左到右在5*5的矩阵上游走,每走到一个位置,就把3*3的矩阵和卷积核对应元素进行相乘再相加得到一个值,最终得到3*3的卷积结果。3 * 3的卷积核卷积过程
那么在文本分类中,我们的卷积过程是怎样的呢。
类似的,首先我们要有一个原始矩阵,给定一行文本,例如“我 在 学习 自然语言处理技术”,我们借助词向量可以构造这行文本的矩阵。一行文本的词向量矩阵
“我 在 学习 自然语言处理技术”有四个词,我们假定词向量的维度为5,这样矩阵的维度就是4*5。
好了,原始矩阵构造好了,下面我们要设计卷积核。在图像领域,卷积核一般也是矩阵,可以是3*3,4*4或者其他,但在文本中,由于一个词的词向量不应该被分割,所以卷积核不是二维矩阵,而是一维的。
一维卷积其实是在对指定的几个词做特征提取,比如我们设定一维卷积大小为2,则我们要依次处理“我 在”、“在 学习”,“学习 自然语言处理”的特征。卷积的过程也是对应元素相乘再相加得到一个数。下面是我制作的文本卷积过程动画。卷积核文本卷积过程
看到上面的卷积核,可能有人会感到疑惑,这明明是二维矩阵嘛,怎么上面说卷积核是一维的呢?卷积核的列数是和词向量的维度一致的,即列维度不变,只可能改变行维度,所以把卷积核平铺开来就是一维的。
OK,卷积过程应该没问题吧。这个过程就对应TextCNN网络结构中的第一步,Convolutional layer with multiple filter widths and feature maps。这里利用多个卷积核(大小为2、3、4),每一个卷积核对原始矩阵进行卷积会得到一个一维向量,所以多个卷积核会得到多个向量。
随便提一下,并不是所有文本的词个数都是相等的,上例中是四个词,得到的卷积向量是3维的,如果是5个词,得到的卷积向量将是4维的。为了保证得到的卷积向量维度一致,我们要对原始文本进行截断补齐操作,对于长文本我们取前n个词,对于短文本,在后面补足至n个词,并且补的词的词向量都是零向量。
3.2.2 池化操作
卷积过程得到一个卷积向量,向量的每一个值可以理解为一个特征,现在我们要做的是选取最大的特征。一般在池化层,一般使用max作为特征选择,即选取卷积向量中最大的值。
为什么选取最大的,为什么不选取最小的,为什么不平均卷积向量做为特征,如果你有这样的疑问,巧了,我也有,如果你知道合理的答案请下方留言告诉我。
OK,通过池化操作,一个卷积向量生成一个值,多个卷积向量得到一个特征向量。最后的最后将这个特征向量传给普通的全连接神经网络进行训练吧。
总结一下。TextCNN有两个步骤:卷积和池化。卷积提取特征,池化选取最大的特征,最后传入全连接网络训练。
最后贴出几点我训练TextCNN的心得体会吧(其实是走过的炕)。卷积核大小其实没那么重要。我尝试过[1,2,3,4,5]、[2,3,4]、[1,2,3]等多个组合,性能都差不多。使用预训练的词向量很重要。在前期的时候,没有使用预训练的词向量,分数大概在77左右。使用了Word2vec训练的词向量,分数在79左右。后来听别人说(其实是查资料看到的),在小数据集上使用预训练的词向量好的多。
训练TextCNN是很费时间的,我一开始在自己的CPU电脑上跑,一折要花5分钟左右,跑出一个结果要好几个小时,这是无法忍受的。所以我就在网上疯狂的找免费的GPU计算环境,找到了同是Google支持的Kaggle notebook和Colab。它们都提供免费的GPU环境,此时训练TextCNN,一折20s就OK,可以愉快的训练模型了。桔了个仔:免费GPU计算资源哪里有?带你薅两个资本主义GPU羊毛
3.3 TextRNN
CNN可以提取n-gram特征(局部特征),比如你设定卷积核大小为2,则CNN提取的是二元组的特征。但对于文本等序列数据来说,更长的上下文信息往往越有助于理解文本。所以RNN来了,它能得到文本的全局信息。
关于RNN的介绍,推荐如下资料:完全图解RNN、RNN变体、Seq2Seq、Attention机制 斯坦福公开课RNN视频 李宏毅RNN视频
用于文本分类的RNN架构如下。
RNN和普通神经网络不同之处在于:RNN将前一个词的隐藏层也作为输入传递给下一个词。形象的理解就是文本的整个词形成了链,后面的词都知道前面的词的信息。
RNN能做很多任务,在文本分类、命名实体识别、机器翻译等都有应用,强烈推荐看完全图解RNN、RNN变体、Seq2Seq、Attention机制 。
3.4 Attendance机制
关于Attendance机制的详细介绍,请看如下资料:斯坦福公开课Attendance机制 遍地开花的 Attention ,你真的懂吗?
我尝试了下,确实比纯RNN效果好一点点,但对所有的隐层进行平均(其实就是attendance思想,只不过score相等)再传给分类器也能得到差不多的性能,所以没有深挖Attendance。
看了斯坦福关于Attendance机制的视频后,感觉Attendance机制可能真的对文本分类没什么帮助。Attendance机制对seq2seq任务应该帮助更多,因为他们的输出也是序列,不同位置确实对隐层的注意力不同,但文本分类的输出不是序列数据,所有感觉没什么帮助。大佬们都在玩{精选官网网址: www.vip333.Co }值得信任的品牌平台!
在本次比赛,深度学习模型就使用了这些,线上分数表现如下。
在传统机器学习中,分数基本没有超过78的,深度学习模型分数基本在79左右,RNN甚至能接近80。
只要是数据比赛,模型融合就一定存在。关于模型融合的方法,详细资料有这些。模型融合方法 西瓜书第8章集成学习。
本次比赛我采用了简单的模型融合方法——概率加权融合。对于一个测试样本来说,不同的模型会给出一个结果,结果是该样本输入每个类的概率。模型融合有几个模型,就有几个概率向量,对这些向量进行加权求和,权重可以手动设置(单模型分数越高,权重越大),得到一个概率向量,取概率最大的类别作为结果即可。
模型融合的关键是多个“好而不同”的模型。模型的好是指模型本身的性能好(分数高),模型的不同是指多个模型差异性要大。在融合阶段,我发现模型真的不是越多越好,模型的差异性真的很重要。为此,我想找到一种方法如何度量模型之间的差异性,但很遗憾,资料真的很少很少。但皇天不负有心人,我在《美团机器学习实践》中找到了方法。
其中相关系数指标表明了两个模型之间的相关性,相关系数越大,模型之间的差异性越小。为此,我利用相关系数作为评价模型差异性的指标,并实现了评价模型差异性的代码。
利用相关系数指标,我得出如下结论:分字和分词带来的差异性最大。这其实也很好理解,分字和分词之后,训练集有很大的不同。传统机器学习和深度学习差异性较大。传统机器学习采用的特征为TF-IDF、LDA等,而深度学习是自动提取特征,所以差异性较大。在比赛中,我还使用了数据增强方法(对一条训练样本,打乱词顺序或者随机删除某些词),数据增强不仅能提高单模型分数(但本次比赛提升不大),也能带来差异性。
最终我选择性的利用上述机器学习模型和深度学习模型,仅6个模型融合能达到80.7左右的分数。
比赛做到这里,我基本上已经束手无策了,刷知乎的时候看到Bert模型,说它2018年在各大NLP任务上刷新纪录,我于是马不停蹄的研究Bert了(大型真香警告)。
bert是Google发布的模型,是NLP的利器,它刷新了11项NLP记录。但bert模型很费显卡,训练也很费时间,但效果是真的好。我仅在bert模型上加一层输出层,分数为81.1(是的,就是81.1)。
推荐如下学习资料。The Illustrated BERT, ELMo, and co. (How NLP Cracked Transfer Learning) 此博文是外国小哥写的,讲解的很形象、很详细。他的博客介绍了许多NLP的知识和模型,建议大家慢慢看。从Word Embedding到Bert模型—自然语言处理中的预训练技术发展史
最后,融合bert模型和CNN、RNN,并采用分字、分词、数据增强等技术,最后的分数为81.98,排名第四。
到这里,我想分享的内容大致完成,最后总结几点吧。打比赛要趁早。通过这次全心身的投入比赛,发现通过比赛真的能学习到很多东西,还能认识志同道合的朋友。一个比赛题目往往就是一个方向明确的领域,通过比赛的不断学习,入门该领域应该不成问题。比赛还可以当做项目来做,可以锻炼工程能力。理论一定要学。不要觉得你仿照别人的模型写法也能搭网络结构,也能train起来,你就以为你掌握了该模型。其实不然,一定要掌握模型的原理、网络结构,可以通过读paper,看博文等。开心最重要。如果打比赛导致你很不开心,或者分数一直提不上去你很沮丧,那可以放弃这个比赛或者另辟蹊径(找大佬抱腿),总之开心最重要。
最后希望这份资料能帮助学习NLP的同学快速入门。送君千里终须一别,祝各位在NLP领域自由的探索,让我们江湖再见!
还木有评论哦,快来抢沙发吧~