机器学习 —— 基础整理(二)朴素贝叶斯分类器;文本分类的方法杂谈
上一篇博客复习了贝叶斯决策论,以及生成式模型的参数方法。本篇就给出一个具体的例子:朴素贝叶斯分类器应用于文本分类。后面简单谈了一下文本分类的方法。
(五)朴素贝叶斯分类器(Naïve Bayes)
既然说到了朴素贝叶斯,那就从信息检索的一些概念开始说起好了。
一、以概率角度出发的文档生成
如果以概率角度看待文档生成,往往是假设词项(term)服从某个分布,然后从总体中抽样,抽出来的词项连在一起,组成文档。比如 $P(D|R=1)$ 可以认为是 $R=1$ 的文档的词项服从一个分布,然后从该分布中抽样生成D。
常用于文档生成的分布有多元贝努利分布(Multivariate Bernoulli distribution)和多项分布(Multinomial distribution)。
i. 多元贝努利分布考虑词项的出现与否、不考虑词项的位置。从名字上也很好理解,多元贝努利分布可以理解为多个变元的贝努利分布。例如,词典有四个词I、can、you、fly,出现概率分别为0.7、0.4、0.1、0.05(这四个值的加和与1没有任何关系,因为各自变元所服从的贝努利分布仅表示该词项出现/不出现的概率加和为1 ,但是各个变元之间没有关系),那么生成文档“I can fly fly fly fly fly”的概率是 $0.7 \times (1-0.4) \times 0.1 \times 0.05 $ 。
ii. 多项分布考虑词项的多次出现、不考虑词项的不出现、不考虑词项的位置和次序。如果说贝努利分布就是 $n$ 次独立重复的抛硬币实验,那多项分布就是 $n$ 次独立重复的抛不规则骰子实验。例如,词典有四个词I、can、you、fly,概率分别为0.4、0.3、0.2、0.1(多项分布,当然加和为1),那么生成文档“I can fly fly fly fly fly”的概率是 $\text{C}_7^1\times \text{C}_6^1\times \text{C}_5^5\times 0.4\times 0.2\times 0.1\times 0.1\times 0.1\times 0.1\times 0.1$ 。
二、概率检索模型
概率检索模型就是基于概率的信息检索模型,例如二值独立模型BIM、大名鼎鼎的BM25等都属于概率检索模型。二值独立模型(Binary independence model,BIM)是在给定查询Q和文档D的情况下,利用贝叶斯公式计算文档D是查询Q的相关文档的概率 $P(R=1|D,Q)$(对于同一查询来说,可简记为 $P(R=1|D)$ )。“二值”是指查询Q和文档Q都表示为词项出现与否的布尔向量,“独立”是指词项在文档中的出现是相互独立的。
1. 排序函数
对于每个查询Q,定义排序函数 $RSV(Q,D)$ 为:
$$RSV(Q,D)=\log\dfrac{P(R=1|D)}{P(R=0|D)}=\log\dfrac{\frac{P(D|R=1)P(R=1)}{P(D)}}{\frac{P(D|R=0)P(R=0)}{P(D)}}\propto \log\dfrac{P(D|R=1)}{P(D|R=0)}$$
其中 $P(D|R=1)$ 可以认为是在相关的情况下生成文档D的概率,$P(D|R=0)$ 是在不相关的情况下生成文档D的概率。
2. 模型参数计算
二值独立模型等价于后面要介绍的多元贝努利朴素贝叶斯模型。对于BIM模型来说,基于多元贝努利分布生成文档。那么,大小为 M 的词典会对应两套概率(这 2M 个参数就是模型参数):一套是 $R=1$ 条件下的,另一套是 $R=0$ 条件下的。
这 2M 个参数需要根据文档集来估计。这里就不详细解释了,直接看教材咯:[1] 的电子版在这里。
当根据文档集估计出这两套概率之后,在给定文档 D 时,就可以分别求得 $P(D|R=1)$ 和 $P(D|R=0)$ 。
二、查询似然模型与查询生成
查询似然模型(Query likelihood model,QLM)是在信息检索中最早应用也是最为基本的基于语言模型的信息检索模型。它为每篇文档D构建其对应的语言模型 $M_D$ ,检索目标是将文档按照其与查询相关的概率 $P(D|Q)$ 来排序。根据贝叶斯公式,
$$P(D|Q)=\frac{P(Q|D)P(D)}{P(Q)}\propto P(Q|D)P(D)$$
其中文档的先验信息 $P(D)$ 与查询无关,因此现在只考虑查询似然 $P(Q|D)$ 。查询似然 $P(Q|D)$ 可认为是根据文档D的语言模型 $M_D$ 来生成查询Q的概率,那么这种方法实际上是建模查询的生成过程,对于每个查询Q来说,排序函数就是 $P(D|Q)$ 。
用一元语言模型计算查询似然的方法等价于后面要介绍的多项式朴素贝叶斯模型,这里的文档相当于后者的类别。
三、朴素贝叶斯分类器
下面讨论朴素贝叶斯分类器(Naïve Bayes,NB)。NB是低方差高偏差的分类器,假设各个特征之间存在条件独立性假设:对于给定的类别,所有的特征相互独立。显然,这个假设把问题想的太简单了,但NB在文本分类任务上确实拥有很好的效果。
给定样本 $\textbf x=(x_1,x_2,...,x_d)^{\top}$ ,其属于类别 $\omega_i$ 后验概率可以表述为:
$$P(\omega_i|\textbf x)=\frac{p(\textbf x,\omega_i)}{p(\textbf x)}\propto p(\textbf x,\omega_i)=p(\textbf x|\omega_i)P(\omega_i)=P(\omega_i)\prod_{k=1}^dp(x_k|\omega_i)$$
其中 $d$ 是特征维数,$x_k$ 是样本在第 $k$ 个特征上的取值。可以看出其建模的是类别信息与特征的联合分布 $p(\textbf x,\omega_i)$ ,需要估计类条件概率密度,因此属于生成式模型。决策规则是最大后验概率决策。
可以证明,朴素贝叶斯在对数空间下是线性分类器(由于多个很小的数相乘会导致浮点数下界溢出的问题,实际使用中通常取对数)。
既然是生成式模型,那现在的问题就是如何估计先验概率 $P(\omega_i)$ 以及 $p(x_k|\omega_i)$ ,$i\in\{1,...,c\}$ 。
先验概率的估计可用MLE: $\hat P(\omega_i)=\dfrac{|D_i|}{|D|}$ ,其中 $|D_i|$ 是训练集 $D$ 的全部样本 $|D|$ 里属于 $\omega_i$ 类的样本数。$p(x_k|\omega_i)$ 的不同可以引出多种朴素贝叶斯算法,下面分别介绍。
1. Gaussian Naïve Bayes,高斯NB
使用参数方法,设特征分布服从高斯分布: $p(x_k|\omega_i)\sim N(\mu_{k,i},\sigma_{k,i}^2)$ ,则
$$\hat p(x_k|\omega_i)=\dfrac{1}{(2\pi)^{\frac12}\sigma_{k,i}}\exp(-\dfrac{(x_k-\mu_{k,i})^2}{2\sigma_{k,i}^2})$$
其中 $\mu_{k,i}$ 和 $\sigma_{k,i}^2$ 是 $\omega_i$ 类的样本集在第 $k$ 维特征上的均值和方差,然后估计均值和方差即可。
这里有个很有意思的地方:[3] 中指出,如果是二分类问题,并且假设各维特征的方差相等($\sigma_{k,i}=\sigma_{i}$),那么高斯NB对样本 $(\textbf x,y)$ 属于正类的后验概率输出值 $P(y=1|\textbf x)$ 刚好和二项Logistic回归有相同的形式(就是说“模样”是一样的,即 $\dfrac{1}{1+\exp(-(\textbf w^{\top}\textbf x+b))}$ ),各个特征的权重 $\textbf w$ 以及偏置 $b$ 的表达式由先验概率 $P(y=1)$ 和 $\mu_{k,i}$ 、$\sigma_{i}^2$ 唯一确定。
2. Multinomial Naïve Bayes,多项式NB
使用MLE估计:
$$\hat P(x_k|\omega_i)=\dfrac{|D_{i,x_k}|}{|D_i|}$$
分子表示 $\omega_i$ 类的训练样本构成的集合 $D_i$ 中,第 $k$ 个特征的取值为 $x_k$ 的样本数。
这里需要注意数据稀疏问题:如果后验概率的连乘式里有一项是0,那么就会导致后验概率为0。解决办法是使用平滑:
$$\hat P(x_k|\omega_i)=\dfrac{|D_{i,x_k}|+\alpha}{|D_i|+\alpha c_k}$$
其中 $c_k$ 表示第 $k$ 维特征的可能取值的个数,系数 $\alpha$ 显然需要非负。如果 $\alpha=1$ 就是Laplace平滑(加一平滑),相当于认为特征的分布是均匀分布,先验地认为每个特征取值在每个类中出现一次;如果 $0\leq\alpha<1$ 就是Lidstone平滑。
关于Laplace平滑,可以看看 [4] ,我扫了一眼感觉写的很有趣~
将朴素贝叶斯用到文档分类,可以将后验概率的表达式中连乘号的部分视作以多项式分布来生成文档(在文档的每个位置上生成一个词项,多项式NB的形式就等价于一元语言模型,文档中各个词项存在条件独立性)。那么,上式中的 $c_k$ 就是训练集的词表大小,而 $|D_{i,x_k}|$ 就是词项 $x_k$ 在 $D_i$ 中出现的次数。
看了一下sklearn的源码,直接用一个矩阵乘法就可以完成所有类别的 $|D_{i,x_k}|$ 的求取:
设训练数据的特征矩阵为 $X\in\mathbb R^{n\times V}$( $n$ 是训练样本的个数,$V$ 是词表大小),其每行都是一篇文档的高维稀疏向量表示, $X_{ij}$ 就是词表中的第 $j$ 个词项在第 $i$ 篇文档的词频 $\text{tf}_{j,i}$ ;训练数据的标签是 $y\in\mathbb R^n$ 。首先把 $y$ 转化成one-hot标签构成的矩阵:$Y\in\mathbb R^{n\times c}$ ,其中 $c$ 是类别个数,矩阵的每一行都是一篇文档的one-hot标签。那么,进行以下运算:
$$Y^{\top}X$$
得到一个 $\mathbb R^{c\times V}$ 的矩阵,其第 $i$ 行第 $j$ 列的元素就是词表中的第 $j$ 个词在类别 $\omega_i$ 的文档集 $D_i$ 中出现的次数。
所以说如果用多项式NB做文档分类的话,最“正统”的文档 $d$ 的向量表示是用词频 $\text{tf}_{t,d}$ 来表示,维数是词表大小。但是sklearn的源码中写道:The multinomial Naive Bayes classifier is suitable for classification with discrete features (e.g., word counts for text classification). The multinomial distribution normally requires integer feature counts. However, in practice, fractional counts such as tf-idf may also work. 也就是说用 $\text{tf}_{t,d}\text{-idf}_{t}$ 来表示的话分类器也是work的。
3. Bernoulli Naïve Bayes,多元贝努利NB
多元贝努利NB等价于 BIM 模型,对词表中的每个词项都对应一个二值变量,为1表示词项在文档中出现,为0则表示没有出现。这样的做法在对长文档分类时可能会出问题,因为没有考虑词频,只考虑了词的出现与否。
公式就不摆了。话说上学期IR课的期末考试就有道题就是论证多元贝努利NB是否是线性分类器 = =。。。
这里继续附上sklearn库中对于朴素贝叶斯算法的介绍:1.9. Naive Bayes 。
四、文本分类杂谈
文档分类的传统方法就是使用向量空间模型(Vector space model),利用词袋(Bag-of-Words,BoW)或者Bag of n-gram,使用 Tf 或 Tf-idf 作为词项的权重,进而对文档进行表示(特征维数为训练集的词表大小),然后再接上NB、SVM、LR等线性分类器输出文档类别。这种高维稀疏的文档向量表示通常会使用特征选择方法来降低维数,如互信息MI、信息增益IG和卡方统计量等。此外,特征还可以是词性特征,或者其他手工设计的特征,例如情感分类任务里可以利用情感词典提取一些特征,比如正向情感词占比等等。
除此之外,还可以用话题模型(Topic model),如 LSA、pLSA 来得到文档的分布向量表示(distributional representation),这两种方式所得到的向量维数相对要小很多。
现在来看的话,由于词的分布式表示(distributed representation,对于词来说就是常说的word embedding,之前写过博客)的出现,使得词拥有了低维稠密的语义向量表示。这种方法可以称为是基于表示学习的方法,其中有的是像上面的传统方法一样,无监督地获得词、句子或篇章的向量表示,不需要文档的类别标记信息;也有针对给定的任务,端到端地完成分类过程(不需要手工提取特征,可以看作是神经网络自动提取了特征),这种方法下神经网络在对文本进行向量表示的过程中需要用到类别标记信息(因为是反向传播来迭代更新的),是有监督的方式,因此得到的文本向量表示只能在本任务中使用,所以这种方法就是为了分类,不是得到一个通用的文档向量表示。在这种端到端的分类模型中,通常会利用无监督模型(如Word2Vec)预训练出词向量(如果实验语料足够大就用语料本身,不够大就用外部语料),作为初始值,并且在训练模型的过程中进行fine-tuning。当然了,如果语料特别大,直接随机初始化也是没问题的。
获得词级别的分布式表示方法,最经典的无监督方案就是Word2Vec和GloVe;有监督方案可以用fastText(相比于之前的CBOW,把预测中心词改成了预测标签;并且引入了n-grams信息来保留局部词序,用Hierarchical Softmax加速,整个模型速度真的超快;由于这种方法的最终目的是做分类,文档表示就是n-grams向量取平均值,所以也是一种有监督的文档表示方法)。
对于句子级别,最简单的方法就是把句子里的词的词向量求平均值或求和得到句子向量,可以叫Neural BoW,也就是神经词袋;“正式”一点的无监督学习句子嵌入的方法有Skip-thought等,也有一些新的方法比如CNN-LSTM auto-encoder。有监督的端到端分类方法有DAN(Iyyer et al., 2015,词向量取平均再接全连接神经网络)、CNN(word-level的CNN较早期的是14年Yoon Kim那篇经典的CNN for sentence classification、后续的还有char-level的CNN等)以及RNN、RecNTN(Socher et al, 2013)等。如果直接用这套方法做情感分类,名曰“自动提取特征”,和文本分类混为一谈了……
对于篇章级别,无监督的方法依然可以用Neural BoW,此外比较出名的有Doc2Vec(就是Word2Vec的作者在14年的那篇paragraph vector,这个模型相比于词向量模型来说只是引入了paragraph vector作为文档全局信息来辅助窗口词去预测下一个词,个人感觉有点太简单了);端到端有监督的方法,简单又快速的方法当属fastText,除此之外的话通常是先对句子建模,再用句子进行语义组合成篇章,依旧是CNN、RNN之类,并且可以引入attention机制(反正这种有监督的方法就是在改网络结构,attention那个公式看得我都要麻木了,真是群魔乱舞……)。
做过的一些实验
下面简单总结一下做过的三个文本分类作业,都是很简单的,没什么新意。
I. 垃圾短信分类
上学期和同学一起做了一个垃圾短信识别的作业,非常典型的短文本分类任务,语料为老师给的80w条短信,其中垃圾短信占1/10。语料只进行了常规的分词、去标点等,没有去停用词;也没有处理正负类样本的类不平衡。下面是部分结果(都没调过参数,要么默认值,要么随手设的,或者是以前其他实验用过的参数;有五个结果的意思是把数据集分成了五组,每组都做了一次测试集)。其中,fastText是原版的C++,SVM是小伙伴用Matlab做的,其余都是Python。DAN是低配版,其实叫Neural BoW更合适,就是在短信语料上用Skip-gram预训练词向量,将短信中的词的词向量直接求和得到句子向量,然后接两层全连接神经网络,词向量在训练分类器时不更新,本质上是个无监督特征提取 + 分类器的方案,而不是一个端到端的方案。训练时间那里包含训练词向量的时间和训练分类器的时间。
可以清楚地看到,同样是训练词向量和分类器的情况下,fastText在速度上的优势非常明显;端到端基于词的CNN的效果是最好的,几个指标都在99%以上,而端到端LSTM的效果没有DAN好,表格太长了没法截到一页里。
可能观众朋友们想问为什么只比较了这几种方法,而且连参数都不划个验证集调一下?我的回答是。。。因为一开始只做了这几个方法,发现效果都不错(至少我们可以接受。。。),就没有继续做这种体力活了。。。
II. 搜狗2006年新闻内容分类
上个实验我只是友情客串,当时我没选那个课,所以也就没做的太仔细。这个实验相对来说做的要完整一点,虽然我还是有很多不满意的地方。
这个实验数据来自于这学期的一门讨论课,是搜狗实验室2006年公布的一批中文新闻内容分类数据(应该是历史产物,现在搜狗官方的新闻语料已经是2012年的了;最近看到了一篇讲实操的博客,似乎用的语料也不一样),包括9个类别(比如财经、体育等等),每个类别1990篇新闻()。用8:2的比例划了训练数据和测试数据;在训练数据上,平均每篇文档有823个token。因为数据量不算大,所以涉及调参的过程都是在训练数据上用四折交叉验证,没有单独划验证集出来。
既然做实验就要有做实验的目的。在这个数据集上实验的目的是:考察单分类器和分类器集成的效果对比。所以,在预处理后用无监督的方法提特征,得到文档向量表示,并没有使用任何一种端到端的神经网络方法。
使用四种方法来得到文档向量:基于BoW的Tf和Tf-idf、基于话题模型的LDA、基于分布式表示的Doc2Vec。四组特征的基本信息:
比较了五个单分类器:线性分类器,包括Naïve Bayes、Logistic Regression、线性SVM;基于树的分类器,包括Random Forest、GBRT。
由于BoW得到的特征是高维稀疏的,之后进行了特征选择。采用两种方法:互信息和卡方统计量,在训练数据上做四折交叉验证,验证集的F1-micro随保留特征的百分比变化的变化趋势如下图:左图是Tf特征,右图是Tf-idf特征;圆点是卡方,方块是互信息;每种颜色代表一种分类器。横轴表示保留特征的百分比,越靠右则保留的越多。
可以非常勉强地看出,卡方的效果总体上略好于互信息。此外,各特征与各分类器的结合,对于特征选择的反映状况是不一样的。
下面就是分类实验了,每种分类器在各组特征表示下的超参数都相同(没有刻意调过,都是默认值或者随手设的值)。其中Tf特征和Tf-idf特征依照上面特征选择的结果进行了特征选择。结果如下:
大致有如下几个结论(换了数据集就不一定成立了):1. Naïve Bayes分类器搭配Tf特征的效果要好于搭配Tf-idf;2. SVM分类器搭配Tf-idf后,效果显著好于搭配Tf;3. Logistic Regression搭配Tf特征取得了最好的效果;4. 除了Tf特征外,在Tf-idf特征、LDA特征、Doc2Vec特征表示下,GBRT的效果都是五个分类器里最好的且SVM的效果都是第二好的,可见GBRT的强大,难怪在Kaggle里这么火;5. LDA特征单独使用,也就是直接拿来做分类的话,效果不佳,可能的原因是参数(主题个数、$\alpha$ 、$\beta$ )不好;6. Doc2Vec特征的效果最差,可能的原因也是参数不好,如果抛开模型本身太简单的因素,最本质的原因是数据集太小了,原文里拿来做实验的数据集是10w级。
接下来就是分类器集成的实验。首先就是最简单的投票法(Voting),将五个单分类器的结果投票。结果里可以看出,只有Tf特征下投票提升了最好的单分类器的效果,但是所有投票后的结果都好于第二好的单分类器。所以如果单分类器里有很强的分类器(这里就是GBRT)的话,投票是没有必要的。
然后是堆叠法(Stacking),这里用的是两层,第一层是五个基分类器,第二层是GBRT。为了连贯性,喂给堆叠法的Tf特征和Tf-idf特征都没做特征选择;所以为了严谨,这个表格在Tf特征和Tf-idf特征下的效果所对比的是没做特征选择时这两种特征的单分类器结果(没有列出来)。从结果看到,Stacking大部分情况下都可以提升单分类器的效果,并且在各种情况下全部beat了Voting;Tf-idf特征表示的情况下,Stacking取得了在本实验过程中最好的效果。
大致内容就是这些,不太好的地方:没有尝试Bag of n-gram(数据稀疏问题应该比BoW更严重),而且没有比较LDA特征辅助其他特征时会不会提升效果;此外,端到端的方法没有做。
另外,在其他组做presentation时,听到了如下几点印象比较深的:
1. 用聚类来做,然后和label进行比较。我觉得纯属多此一举,先不说训练集的标签被浪费了,在聚成几个类之后,怎么描述每个类呢,就是说给每个类打什么标签呢?
2. 端到端word-level的CNN效果在90%,但是我没听清楚他们的指标是accuracy还是什么,而且语料预处理、训练集测试集的划分都不一样,后来我问了一下,他们做的时候是把文档当成句子来做的。char-level的CNN效果很差。
3. 如果爬取今年的新闻做测试集,效果会奇差无比。这是因为数据的分布变了,每年的热点都在变,用词也在变,所以数据分布不同了,效果自然不会好。
III. 评论情感分类
这个数据集也是来自这学期的讨论课,是三个领域(酒店、图书、笔记本)的中文评论数据,每个领域2000条。这个数据集实在是太小了,没做出什么东西来。
比较特别的地方在于,我们跨领域去训练分类器,比如:用酒店领域的数据训练,笔记本领域的数据做测试集,这样做违背了机器学习的基本假设(训练数据和测试数据有相同分布),结果显然是:不管是传统方法(BoW,没有设计其他人工特征)还是端到端的深度学习方法(试了fastText和word-level的CNN),一旦训练数据混入了其他领域数据,效果都会下降。在此之前用Jensen-Shannon散度算了一下三个领域之间两两的相对距离,发现都挺大的……
$$D_{KL}(S||T)=\sum_{w_i}S(w_i)\log\frac{S(w_i)}{T(w_i)}$$
$$D_{JS}(S||T)=\frac12D_{KL}(S||M)+\frac12D_{KL}(T||M),\quad M=\frac12(S+T)$$
实际上,包括传统方法和深度学习方法,很早就有做领域自适应情感分类的paper了。比如其中一种传统方法是利用pivot feature,而深度学习方法可以用autoencoder,这里就不展开了。
参考资料:
[1] 《信息检索导论》及slides
[2]《统计学习方法》
[3] Tom M. Mitchell 的《Machine Learning》新版,CHAPTER 3 (这是 [2] 列出的第一篇参考文献)
[4] 理解朴素贝叶斯分类的拉普拉斯平滑