2.7 朴素贝叶斯

1.先从贝叶斯定理复习起

说起来,前面1.4 节讲概率的时候,我们已经见过贝叶斯定理了。这一章的主角朴素贝叶斯,本质上就是在这条定理上做了一道漂亮的近似。所以我们先把这条老定理请出来,再往前走一步。

贝叶斯定理的公式是这样的:

P(yx)=P(xy)P(y)P(x)P(y|x)=\frac{P(x|y)P(y)}{P(x)}

这里每个符号都得说清楚。yy 是我们想预测的类别(比如一封邮件到底是不是垃圾邮件),xx 是我们手上能观察到的特征(比如这封邮件里出现了哪些词)。P(y)P(y) 叫先验概率,是在没看到任何特征之前,我们对这个类别的把握。P(xy)P(x|y) 叫似然,意思是假如类别已经确定了,看到这些特征的可能性有多大。P(x)P(x) 叫证据,是这些特征总体出现的概率,起一个归一化的作用。P(yx)P(y|x) 才是我们要的那个数,叫后验概率,意思是在看到特征之后,这个类别的概率变成了多少。整个公式干的事,就是把先验 P(y)P(y) 拿似然 P(xy)P(x|y) 往上抬一下,再除以证据 P(x)P(x) 归个一,得到后验。

我打个比方。小张的妈妈前阵子去医院体检,某项肿瘤标志物化验结果是阳性,一家人吓得不轻。我陪她去复查的时候,大夫说这项化验的灵敏度(真正有病的人里被查出来的比例)和特异度(真正没病的人里被正确判定的比例)都不算低,可这种病在普通人群里的发病率其实很低,先验概率非常小,所以即便化验阳性,最终真的患病的后验概率也没有想象中那么高。大夫这番话,说穿了就是贝叶斯定理,先验太低,似然再往上抬,后验也抬不到天上去。这条思路放到机器学习里,就是用历史数据估出先验和似然,再拿新样本的特征去算后验,最后挑后验最大的那个类别当作预测。

2.朴素假设:给定类别,特征之间条件独立

贝叶斯定理看起来很美,可一旦真的想算 P(xy)P(x|y),就会撞上一堵墙。原因是 xx 往往是一堆特征凑起来的,记作 x=(x1,x2,,xn)x=(x_1,x_2,\dots,x_n),这里 xjx_j 表示第 jj 个特征(比如第 jj 个词出现还是没出现),nn 是特征总数。要把这么多特征一起考虑,联合概率 P(x1,x2,,xny)P(x_1,x_2,\dots,x_n|y) 算起来特别费劲,需要穷举各种特征组合的共现次数,数据量根本不够用。

朴素贝叶斯在这里加了一条关键假设:给定类别 yy 之后,各个特征之间条件独立。写成式子就是:

P(xy)=P(x1,x2,,xny)=j=1nP(xjy)P(x|y) = P(x_1,x_2,\dots,x_n|y) = \prod_{j=1}^{n} P(x_j|y)

这里 \prod 是连乘符号,意思是从第 11 个特征到第 nn 个特征,每个特征的条件概率全部乘在一起。\prod 这个符号我们在3.10 节讲梯度连乘的时候也见过,意思是一样的,就是把一串数全部乘起来。

这么一假设,那个难算的联合概率,就被拆成了一连串好算的单变量条件概率。每个 P(xjy)P(x_j|y) 只关心一个特征和一个类别,数起来轻松得多。代回贝叶斯定理,由于分母 P(x)P(x) 对所有类别都一样,比较的时候可以省掉,于是有:

P(yx)P(y)j=1nP(xjy)P(y|x) \propto P(y) \prod_{j=1}^{n} P(x_j|y)

这里 \propto 表示正比于,意思是左右两边只差一个对所有类别都相同的常数因子。预测的时候,我们对每个类别都算一遍右边的乘积,哪个最大,就把样本归到哪一类。这种挑法有个名字,叫最大后验估计。

3.为什么叫朴素

说到这儿大概你也感觉到了,条件独立这条假设其实相当强。现实里的特征常常是纠缠在一起的,一封邮件里出现免费和中奖这两个词,多半是一块儿冒出来的,谁也不真的独立于谁。可朴素贝叶斯偏偏假装,只要类别给定,这些词就互不影响了。这就是朴素这三个字的由来,朴素得近乎天真。

可就是这么一条近乎天真的假设,效果却出奇地好,尤其是文本分类这一摊事。个中缘由说起来也简单。我们最终要的只是哪个类别的后验最大,并不需要每个概率都估得特别准。即便假设不完全成立,那串连乘给出的排序通常仍然是对的,垃圾邮件还是会被判成垃圾邮件。我之前看过一本讲统计学习的书,里面有一句话印象很深,说朴素贝叶斯之所以效果好,关键在于它把有限的样本用到了极致,每个特征只单独计数,参数再省也够估。这话我一直记得。小明开了一家做酸菜鱼的小馆子,他凭经验判断回头客,看的就是几个互不相干的信号(客人来店里是周中还是周末、是不是带朋友来、点没点招牌酸菜鱼),这些信号之间其实也谈不上严格独立,可搭在一起就是相当准。朴素贝叶斯干的也是这件事。

4.拉普拉斯平滑:别让一个零清零一整项

朴素贝叶斯还有一个绕不开的坑,叫零概率问题。式子里那一串连乘,只要有一个 P(xjy)P(x_j|y) 等于零,整个乘积就全清零了,别的特征再怎么指向这一类都没用。这显然不合理。比方说训练集里所有的正常邮件都没有出现过中奖这个词,那 P(中奖正常)=0P(\text{中奖}|\text{正常})=0,于是来一封新邮件,只要它带着中奖这个词,不管它还带着多少明显正常的内容,正常这一类的得分都是零,这封邮件只能被判成垃圾。

解决办法叫拉普拉斯平滑,做法很温和,给每个特征的计数都加一个小常数。设类别 yy 下第 jj 个特征取某个值的计数是 Nj,yN_{j,y}Nj,yN_{j,y} 就是训练集里属于类别 yy、且第 jj 个特征取该值的样本数),该特征可能取值的种数记作 KjK_jKjK_j 是第 jj 个特征能取多少种不同的值),平滑后的概率是:

P(xjy)=Nj,y+αNy+αKjP(x_j|y) = \frac{N_{j,y} + \alpha}{N_y + \alpha K_j}

这里 α\alpha 是平滑常数,通常取 11NyN_y 是训练集里属于类别 yy 的样本总数。直观上理解,就是假装每个特征的每个取值都额外多见了 α\alpha 次,再正常不过的零计数也就被抬成了一个很小但不是零的数。这一招和1.4 节信息论里讲到的那种给极端情况留点余地的思路是相通的,遇到没见过的情况,总要留一点点余地,别把话说死。

5.经典应用:从垃圾邮件到情感分类

朴素贝叶斯最经典的应用大概就是垃圾邮件分类了。做法是把一封邮件先切成一个个词,再统计哪些词出现、哪些不出现,或者直接用词频当特征。常见的有两个套路,一个叫多项式模型,把每个词的出现次数当作特征。另一个叫伯努利模型,只看一个词出现没出现,出现记 11,没出现记 00。无论哪种,最后都套进上面那套连乘公式里,比较垃圾邮件和正常邮件两类后验哪个大。

情感分类的路数也差不多,把一条评论切成词之后,统计正面情感词(喜欢、好看、惊喜)和负面情感词(失望、踩雷、难看)的分布,再用带类别标注的训练集估出每个词的条件概率。新来一条评论,套朴素贝叶斯,就能大致判断它是好评还是差评。我记得某部讲互联网创业的连续剧里有个桥段,主人公团队靠一个朴素贝叶斯模型,从海量用户评论里筛出了对产品的真实不满,模型虽朴素,胜在好实现、好解释,上线也快。这种轻量级方案在做初版分类、做基线对比的时候特别讨喜。后面我们会讲到的深度文本模型,效果当然更好,可朴素贝叶斯这个基线,至今仍然值得先跑一版看看。

6.手算一个小例子:免费中奖的邮件

光讲公式不练手总是虚的,我们来算一个小例子。假设训练集里有10封邮件,其中4封是垃圾邮件,6封是正常邮件。先验就有了:

P(垃圾)=410=0.4,P(正常)=610=0.6P(\text{垃圾})=\frac{4}{10}=0.4, \quad P(\text{正常})=\frac{6}{10}=0.6

我们挑免费和中奖这两个关键词做特征,只看它们出现还是没出现。统计下来是这样的:在4封垃圾邮件里,3封含免费,3封含中奖。在6封正常邮件里,1封含免费,0封含中奖。

现在来了一封新邮件,里面既有免费又有中奖,我们想判断它是不是垃圾。先用朴素贝叶斯那套连乘算垃圾这一类(这里 \cdot 表示乘法):

P(垃圾免费,中奖)P(垃圾)P(免费垃圾)P(中奖垃圾)P(\text{垃圾}|\text{免费},\text{中奖}) \propto P(\text{垃圾}) \cdot P(\text{免费}|\text{垃圾}) \cdot P(\text{中奖}|\text{垃圾})

把数代进去:

0.4×34×34=0.4×0.75×0.75=0.2250.4 \times \frac{3}{4} \times \frac{3}{4} = 0.4 \times 0.75 \times 0.75 = 0.225

再算正常这一类,问题就来了:

0.6×16×06=00.6 \times \frac{1}{6} \times \frac{0}{6} = 0

中奖这个词在正常邮件里一次都没出现过,条件概率直接为零,整项连乘清零,正常这一类的得分成了零。这显然不合理,正是拉普拉斯平滑出场的时候。这里的特征是出现和没出现两种取值,所以 Kj=2K_j=2,取 α=1\alpha=1,重新估一遍:

P(免费垃圾)=3+14+2=460.667P(\text{免费}|\text{垃圾})=\frac{3+1}{4+2}=\frac{4}{6}\approx0.667 P(中奖垃圾)=3+14+2=460.667P(\text{中奖}|\text{垃圾})=\frac{3+1}{4+2}=\frac{4}{6}\approx0.667 P(免费正常)=1+16+2=28=0.25P(\text{免费}|\text{正常})=\frac{1+1}{6+2}=\frac{2}{8}=0.25 P(中奖正常)=0+16+2=18=0.125P(\text{中奖}|\text{正常})=\frac{0+1}{6+2}=\frac{1}{8}=0.125

现在再算两类的得分:

P(垃圾)0.4×0.667×0.6670.178P(\text{垃圾}|\dots) \propto 0.4 \times 0.667 \times 0.667 \approx 0.178 P(正常)0.6×0.25×0.1250.0188P(\text{正常}|\dots) \propto 0.6 \times 0.25 \times 0.125 \approx 0.0188

垃圾这一类的得分大约是正常这一类的9倍多,于是模型把这封邮件判成垃圾邮件。要是再除以两类得分之和做一下归一化,垃圾这一类的后验大约是 0.9050.905,也就是九成把握。这个结果和我们的直觉吻合,免费加中奖,听着就不太正经。

7.收个尾

朴素贝叶斯讲到这里就差不多了。说起来它和前面几篇都有点缘分,根上的贝叶斯定理接的是1.4 节的概率,连乘符号在3.10 节的梯度链里见过,平滑常数那种给极端情况留余地的思路,也和信息论里的一些做法相通。后面我们聊线性回归(2.1 节)、聊降维里的PCA(1.3 节讲过特征值),多少都会回头呼应这些老朋友。朴素贝叶斯虽朴素,却一直是个值得先跑一版的好基线。

今天就先到这儿,下一章见。

练习

Q1. 朴素贝叶斯"朴素"在哪?现实中特征明显不独立(比如"免费"和"中奖"常一起出现),为啥这套近乎天真的假设效果反而不错?

"朴素"在它假设给定类别之后各个特征条件独立,把难算的联合概率 P(x1,,xny)P(x_1,\dots,x_n|y) 拆成连乘 jP(xjy)\prod_j P(x_j|y)。现实中特征常常纠缠在一起,这条假设近乎天真。可效果出奇地好,尤其是文本分类——因为我们最终要的只是哪个类别的后验最大,并不需要每个概率都估得特别准,即便假设不完全成立,那串连乘给出的排序通常仍然是对的,垃圾邮件照样被认出来。说白了它把有限的样本用到了极致,每个特征只单独计数,参数再省也够估。

Q2. 训练集 4 封垃圾、6 封正常;垃圾里 3 封含"免费"、3 封含"中奖",正常里 1 封含"免费"、0 封含"中奖"。来一封既含"免费"又含"中奖"的邮件,不做平滑会出什么问题?取 α=1\alpha=1 拉普拉斯平滑后(特征取值种数 Kj=2K_j=2),垃圾类得分大约是正常类的几倍?

不做平滑的话,P(中奖正常)=0/6=0P(\text{中奖}|\text{正常})=0/6=0,正常类那串连乘直接清零,邮件不管还带多少正常内容都只能被判成垃圾,显然不合理。平滑后 P(免费垃圾)=3+14+2=460.667P(\text{免费}|\text{垃圾})=\frac{3+1}{4+2}=\frac{4}{6}\approx0.667P(中奖垃圾)0.667P(\text{中奖}|\text{垃圾})\approx0.667,垃圾得分 0.4×0.667×0.6670.178\propto0.4\times0.667\times0.667\approx0.178P(免费正常)=28=0.25P(\text{免费}|\text{正常})=\frac{2}{8}=0.25P(中奖正常)=18=0.125P(\text{中奖}|\text{正常})=\frac{1}{8}=0.125,正常得分 0.6×0.25×0.1250.0188\propto0.6\times0.25\times0.125\approx0.0188。垃圾得分大约是正常类的 9 倍多,判成垃圾。

Q3. 拉普拉斯平滑公式 P(xjy)=Nj,y+αNy+αKjP(x_j|y)=\frac{N_{j,y}+\alpha}{N_y+\alpha K_j} 里每个符号什么意思?为什么朴素贝叶斯非加这一步不可?

Nj,yN_{j,y} 是训练集里属于类别 yy 且第 jj 个特征取该值的样本数,NyN_y 是类别 yy 的样本总数,KjK_j 是第 jj 个特征能取多少种不同的值,α\alpha 是平滑常数(通常取 1)。直观上就是假装每个特征的每个取值都额外多见了 α\alpha 次。非加不可,是因为朴素贝叶斯靠连乘算后验,只要有一个 P(xjy)=0P(x_j|y)=0,整项乘积就清零,别的特征再怎么指向这一类都没用——拉普拉斯平滑把零抬成一个很小但不是零的数,避免一个没见过的取值清零整项,遇到没见过的情况总要留点余地。

相关标签
机器学习朴素贝叶斯贝叶斯