文本分类之特征工程

文本分类之特征工程

内容参考自http://www.jeyzhang.com/text-classification-in-action.html

本文的话题老生常谈,文本分类应该是很多NLPer非常常遇到和熟悉的任务之一了,下面总结一下博主在处理这类任务的过程中特征工程方面的经验,希望对各位NLP入门者或者在做此类任务的新手有所帮助。对于其他的文本处理任务,也会有一定的参考意义。

概述

文本分类,顾名思义,就是根据文本内容本身将文本归为不同的类别,通常是有监督学习的任务。根据文本内容的长短,有做句子、段落或者文章的分类;文本的长短不同可能会导致文本可抽取的特征上的略微差异,但是总体上来说,文本分类的核心都是如何从文本中抽取出能够体现文本特点的关键特征,抓取特征到类别之间的映射。所以,特征工程就显得非常重要,特征找的好,分类效果也会大幅提高(当然前提是标注数据质量和数量也要合适,数据的好坏决定效果的下限,特征工程决定效果的上限)。

也许会有人问最近的深度学习技术能够避免我们构造特征这件事,为什么还需要特征工程?深度学习并不是万能的,在NLP领域深度学习技术取得的效果有限(毕竟语言是高阶抽象的信息,深度学习在图像、语音这些低阶具体的信息处理上更适合,因为在低阶具体的信息上构造特征是一件费力的事情),并不是否认深度学习在NLP领域上取得的成绩,工业界现在通用的做法都是会把深度学习模型作为系统的一个子模块(也是一维特征),和一些传统的基于统计的自然语言技术的特征,还有一些针对具体任务本身专门设计的特征,一起作为一个或多个模型(也称Ensemble,即模型集成)的输入,最终构成一个文本处理系统。

特征工程

对于文本分类而言,工业界有哪些常用的特征呢?下面用一张图用来概括。

我主要将这些特征分为四个层次,由下往上,特征由抽象到具体,粒度从细到粗。我们希望能够从不同的角度和纬度来设计特征,以捕捉这些特征和类别之间的关系。下面详细介绍这四个层次上常用到的特征表示。

基于词袋模型的特征表示

词袋模型的基本思想是将文本符号化,将一段文本表示成一堆符号的集合;由于中文文本的多样性,通常导致构建的词袋维数较大,仅仅以词为单位(Unigram)构建的词袋可能就达到几万维,如果考虑二元词组(Bigram)、三元词组(Trigram)的话词袋大小可能会有几十万之多,因此基于词袋模型的特征表示通常是极其稀疏的。

词袋模型的one-hot表示示意图如下(假设我们构建了一个2w维的词袋模型,每一维表示一个词):

从上到下,可以看出几种不同的表示方法:

  • 第1种:Naive版本,不考虑词出现的频率,只要出现过就在相应的位置标1,否则为0;
  • 第2种:考虑词频(即term frequency),认为一段文本中出现越多的词越重要,因此权重也越大;
  • 第3种:考虑词的重要性,以TFIDF表征一个词的重要程度(不了解TFIDF的点这里)。简单来说,TFIDF反映了一种折中的思想:即在一篇文档中,TF认为一个词出现的次数越大可能越重要,但也可能并不是(比如停用词:“的”“是”之类的);IDF认为一个词出现在的文档数越少越重要,但也可能不是(比如一些无意义的生僻词)。

通常情况下,我们都会采用第3种方法。原因也很直观,文本中所出现的词的重要程度是不太一样的,比如上面的例子中“我”,“喜欢”,“学习”这3个词就要比其他词更为重要。除了TFIDF的表征方法,还有chi-square,互信息(MI),熵等其他一些衡量词重要性的指标(见这里)。但是一般TFIDF用得比较普遍。
经验总结:

  • 通常考虑unigram和bigram来构建词袋模型(trigram的话维数太高,取得的gain也不高);
  • 用TFIDF时,注意对TF作归一化,通常用词频除以文本的长度;
  • 如果构建的词袋维数太高,可以用TF(或者TFIDF)来卡,将一些不常见的词(会有很多噪音词,如联系方式、邮箱之类的)过滤掉;
  • 如果有一些先验的词袋,word count通常都是比较强的一维特征(比如情感分类中,正负情感词的出现次数),可以考虑;
  • 基于词袋模型构建的特征通常高维但稀疏,通常使用非线性模型取得的效果较线性的要好,推荐大家尝试使用一些基于决策树的boosting模型,如GBDT;这也很好理解,较线性模型而言,非线性模型能够学习出更加复杂的规则,对于文本而言,体现在能够一定程度上考虑词出现的语境(context)情况,比如,对于识别文本是否为骂人语料,文本中出现“妈”,同时也出现“你”,那么为骂人的概率会增大。

词袋模型比较简单直观,它通常能学习出一些关键词和类别之间的映射关系,但是缺点也很明显:

  • 丢失了文本中词出现的先后顺序信息;
  • 仅将词语符号化,没有考虑词之间的语义联系(比如,“麦克风”和“话筒”是不同的词,但是语义是相同的);

基于embedding的特征表示

上一部分介绍了基于词袋模型如何提取文本特征,这主要是从词形的角度考虑的,并没有考虑词语之间的语义关联信息。提到语义关联,大家都会联想到著名的word2vec。word2vec的原理很简单,基本思想是用词出现的上下文来表示这个词,上下文越接近的词之间的语义相似性越高(分布假说)。例如,上一小节中举到的例子,“话筒”和“麦克风”两者的上下文可能非常接近,因此会被认为是语义接近的。(不过语义接近并不代表含义接近,例如“黑色”和“白色”的上下文是相似的,但所代表的含义可能却是相反的)。

词袋模型比较简单直观,它通常能学习出一些关键词和类别之间的映射关系,但是缺点也很明显:

丢失了文本中词出现的先后顺序信息;
仅将词语符号化,没有考虑词之间的语义联系(比如,“麦克风”和“话筒”是不同的词,但是语义是相同的);
基于embedding的特征表示
上一部分介绍了基于词袋模型如何提取文本特征,这主要是从词形的角度考虑的,并没有考虑词语之间的语义关联信息。提到语义关联,大家都会联想到著名的word2vec。word2vec的原理很简单,基本思想是用词出现的上下文来表示这个词,上下文越接近的词之间的语义相似性越高。例如,上一小节中举到的例子,“话筒”和“麦克风”两者的上下文可能非常接近,因此会被认为是语义接近的。(不过语义接近并不代表含义接近,例如“黑色”和“白色”的上下文是相似的,但所代表的含义可能却是相反的)。

目前做word embedding的方法很多,比较流行的有下面两种:

  • word2vec
  • GloVe

word2vec和GloVe两者的思想是类似的,都是用词的上下文来表示这个词,但是用的方法不同:word2vec是predict-based,用一个3层的NN模型来预测词的上下文(或者反过来),词向量是训练过程的中间产物;而GloVe则是count-based的方法,通过对共现词矩阵做降维来获取词的向量。两者在效果上相差不大,但GloVe模型的优势在于矩阵运算可以并行化,这样训练速度能加快。具体两者的差别可以参考Quora上的回答。

有了word embedding之后,我们怎么得到文本的embedding呢?

对于短文本而言,比较好的方法有:

(1) 取短文本的各个词向量之和(或者取平均)作为文本的向量表示;

(2) 用一个pre-train好的NN model得到文本作为输入的最后一层向量表示;

除此之外,还有TwitterLda,TwitterLda是Lda的简化版本,针对短文本做主题刻画,实际效果也还不错。

基于embedding的特征刻画的是语义、主题层面上的特征,较词匹配而言,有一定的泛化能力。

基于NN模型抽取的特征

NN的好处在于能end2end实现模型的训练和测试,利用模型的非线性和众多参数来学习特征,而不需要手工提取特征。CNN和RNN都是NLP中常用的模型,两个模型捕捉特征的角度也不太一样,CNN善于捕捉文本中关键的局部信息,而RNN则善于捕捉文本的上下文信息(考虑语序信息),并且有一定的记忆能力,两者都可以用在文本分类任务中,而且效果都不错

对于简单的文本分类任务,用几个简单的NN模型基本就够了(调参数也是一大累活儿)。网上有很多关于NN的实现,这里推荐一个TensorFlow的实现版本,里面有一个浅层的CNN和RNN实现(word-based和chat-based都有),代码也很好懂,可以快速实验验证效果。地址在这里。

最后我们可以将这些NNs预测的分值作为我们分类系统的一个特征,来加强分类系统的性能。

基于任务本身抽取的特征

这一部分的特征主要是针对具体任务而设计的,通过我们对数据的观察和感知,也许能够发现一些可能有用的特征。有时候,这些手工特征对最后的分类效果提升很大。举个例子,比如对于正负面评论分类任务,对于负面评论,包含负面词的数量就是一维很强的特征。

这部分的特征设计就是在拼脑力和拼经验,建议可以多看看各个类别数据找找感觉,将那些你直观上感觉对分类有帮助的东西设计成特征,有时候这些经验主义的东西很有用(可能是模型从数据学习不出来的)。

特征融合

在设计完这些特征之后,怎么融合更合适呢?对于特征维数较高、数据模式复杂的情况,建议用非线性模型(如比较流行的GDBT, XGBoost);对于特征维数较低、数据模式简单的情况,建议用简单的线性模型即可(如LR)。下面分享一个我做特征融合的模型框架,任务是正负面评论分类(负面评论定义是不适合出现在网络上的评论,如政治敏感、带有人身攻击、强烈负面情绪的评论)。

其中,橙色框表示模型,蓝色框表示用到的特征,[]里面表示特征的维数。这里需要注意的是,训练子模型(GBDT/DNN)的训练数据和训练融合模型(LR)的训练数据需要不一样,这也很好理解,就是防止子模型因为“见过”这些训练数据而产生偏向于子模型的情况。实际的模型训练中,可以用training数据集作为子模型的训练数据,dev数据集作为最终融合模型的训练数据。

模型融合能够从多个角度更加全面地学习出训练数据中的模式,往往能比单个模型效果好一点(2~3个点左右)。

另外,通过观察LR模型给各个特征分配的权重大小和正负,我们可以看出对于训练数据而言,这些特征影响分类的重要程度(权重大小(绝对值)),以及特征影响最终分类目标的极性。特别的,我们可以通过观察那些手工特征的权重来验证这些特征的有效性和有效程度。

总结

这篇文章主要从宏观上介绍了对于文本分类任务而言设计特征的思路,对于其他的NLP任务,也可以参考类似的方法。总而言之,特征工程的核心是尽量从多个角度和纬度来捕捉数据中的模式,并用数值特征来加以刻画。最近流行的深度学习模型可以end2end地学习数据中隐含的模式,免去了人工提取特征的麻烦,然而对于信息高度抽象的文本数据而言,深度学习模型能取得的效果有限,在实际的产品中,我们往往会加入一些传统的基于统计学习的自然语言技术,以及根据我们对业务和数据的理解而人工设计的特征,来最终实现一个比较优良的结果。