6.3 朴素贝叶斯分类器介绍
朴素贝叶斯可能是最优美的有实际效用的机器学习算法之一了。尽管它的名字叫做朴素,但当你看到它的分类效果的时候,你会发现它并不是那么朴素。它对无关特征的处理能力十分彪悍,无关特征会被自然忽略掉。用它进行学习和预测的速度都很快,而且它并不需要很大存储空间。所以,为什么叫它朴素 呢?
朴素 之所以成为它名字的一部分,是因为有一个能让贝叶斯方法最优工作的假设:所有特征需要相互独立。然而,在实际应用中,这种情况很少发生。尽管如此,在实践中,甚至在独立假设并不成立的情况下,它仍然能达到非常好的正确率。
6.3.1 了解贝叶斯定理
朴素贝叶斯的核心功能是跟踪哪个特征在哪个类别中出现。为了更容易理解这些,让我们假设下面这些变量的含义。我们将会用它们来解释朴素贝叶斯方法。
变量 | 可能的取值 | 含义 |
C | “pos”“neg” | 推文的类别(正或负) |
F 1 | 非负整数 | 统计awesome在推文中出现的次数 |
F 2 | 非负整数 | 统计crazy在推文中出现的次数 |
在训练阶段,我们学习了朴素贝叶斯模型,就是在已知特征F 1 和F 2 的情况下样本属于某类别C 的概率。这个概率可以写成P (C │F 1 ,F 2 )。
由于我们无法直接估计出这个概率,我们可以使用一个技巧,而这个技巧正是由贝叶斯发现的:
如果我们把A 替换成特征F 1 和F 2 共现的概率,把B想象成我们的类别C ,那么就会得到一个关联关系,可以帮我们求出数据样本属于某个特定类别的概率:
我们可以用其他概率来表达P (C │F 1 ,F 2 ):
我们也可以说:
先验(prior)和证据(evidence)的数值很容易确定。
P (C )就是在不知道数据时类别C 时的先验概率。这个数值可以通过计算训练集中属于特定类别的样本比例来得到。
P (F 1 ,F 2 )就是证据,或是说是特征F 1 和F 2 的概率。这可以通过计算训练集中含有特定特征值的样本比例来得到。
比较微妙的部分是计算似然(likelihood)P (F 1 ,F 2 │C )。这个值告诉我们,如果知道样本的类别C ,那么有多大的可能性可以看到特征值F 1 和F 2 。要估计这个概率,我们需要多一点思考。
6.3.2 朴素
在概率论中,我们还知道以下关系:
然而,这个式子本身并没有多大帮助,因为我们把一个困难的问题(估算P (F 1 ,F 2 │C ))变成了另外一个困难问题(估算P (F 2 │C ,F 1 ))。
然而,如果我们朴素地假设F 1 和F 2 相互独立,那么就可以把P (F 2 │C ,F 1 )简化成P (F 2 │C ),写成如下式子:
如果把所有东西都放在一起,我们就可以得到一个比较容易处理的式子:
有趣的是,我们随心所欲对假设进行调整,在理论上可能并不正确,但在实际应用中却会产生令人惊讶的效果。
6.3.3 使用朴素贝叶斯进行分类
对一个给定的新推文,剩下的工作就是简单地计算概率:
我们还需要选择概率最高的类别C best 。对于这两个类别来说,由于分母P (F 1 ,F 2 )都是一样的,所以我们可以把它们简单忽略,这并不会改变最后胜出的类别。
但要注意的是,我们不用再计算任何概率,相反,我们要根据给定的证据估算出哪个类别更有可能。这就是朴素贝叶斯为什么比较健壮的另一个原因:它对真实概率并不感兴趣,而只是注重哪个类别更有可能。简而言之,我们可以把它写成:
这里我们对所有类别C (“pos”和“neg”)都计算了argmax后面那一项,并返回数值最高的类别。
但在下面这个例子中,让我们将真实概率保留下来,并进行了一些计算,来看看朴素贝叶斯是如何工作的。为简单起见,假设推文只包括前面出现的两个词语awesome和crazy,并且,我们已经对少量推文进行了人工分类:
推文 | 类别 |
awesome | 正 |
awesome | 正 |
awesome crazy | 正 |
crazy | 正 |
crazy | 负 |
crazy | 负 |
在这里我们有6个推文,其中4个是正例,2个是负例,可以得到以下先验概率:
这意味着,我们在不知道推文本身任何信息的情况下,就可以假设推文是正例。
这里还落下了对P (F 1 │C )和P (F 2 │C )的计算。它们是特征F 1 和F 2 相对于类别C 的条件概率。
要得到这两个概率,可以用包含具体特征值的推文个数除以标注为类别C 的推文个数来计算。比如我们希望知道在类别为“正”的推文中看到一次awesome的概率;我们有以下公式:
既然在4个推文正例中有3个包含了词语awesome,那么显然,不包含awesome的推文正例的概率与此相反,这是因为我们只使用了词语个数为0或1的推文:
剩下的概率也与此类似(这里忽略了不在推文中出现的词语):
为了保持完整性,我们还要计算证据,使我们可以在下面这个示例推文中得到真实概率。对于F 1 和F 2 两个特征的具体值,我们计算证据如下:
符号"" 可以得到下面这些值:
现在拥有了对新推文进行分类所需的所有数据。接下来,唯一要做的就是解析推文,并提取特征。
推文 | F 1 | F 2 | 类别概率 | 分类结果 |
awesome | 1 | 0 | 正例 | |
crazy | 0 | 1 | 负例 | |
awesome crazy | 1 | 1 | 正例 | |
awesome text | 0 | 0 | 未定义,因为我们在推文中没见过这个词语 |
到现在为止,一切看起来都很好。对简单推文的分类看上去很有道理,除了最后一个会除以0。那么我们该如何处理这种情况呢?
6.3.4 考虑未出现的词语和其他古怪情况
在计算前面提到的概率时,我们其实欺骗了自己。我们并没有计算出真实概率,只是通过比例大致得出了一个近似值。当时我们假设训练语料已经告诉了我们关于真实概率的所有真相,但实际上并没有。一个只有6个推文的语料明显无法告诉我们曾写过的各种推文的所有信息。例如,肯定有一些推文用到了“text”这个词语,但我们还没有看到。显然,我们的近似太过粗糙了,我们应该把这些没有看到过的词语也考虑进去。在实践中,这通常是通过“加1平滑”(add-one smoothing)实现的。
提示 加1平滑有时也叫做加法平滑 (additive smoothing)或者拉普拉斯平滑 (Laplace smoothing)。注意,拉普拉斯平滑和拉普拉斯算子平滑 (Laplacian smoothing)没有任何关系。拉普拉斯算子平滑是关于多边形网格平滑的。如果你不是通过加1,而是通过一个可调整的大于0的参数alpha来平滑,那么这就叫做Lidstone平滑 。
这是一个非常简单的技术,只需要在所有计数上加1就可以实现了。它背后的假设是,即使我们在整个语料中并没有看到过某个词语,但仍有一点可能性是因为我们的推文样本中只是碰巧没有包含那个词语。所以,采用加1平滑之后,我们假装每个词语都出现过一次,虽然这和我们实际看到的不同。这意味着将不会按照下面的方式计算:
而是像现在这样计算:
我们为什么要在分母上加2呢?因为我们必须确保最后的结果仍然是一个概率。因此,我们就需要对计数进行归一化,使所有的概率相加得1。和当前数据集中的awesome一样,会出现两种情况:0次或1次。事实确实如此,我们得到的总体概率为1:
同样,我们也对先验概率进行平滑:
6.3.5 考虑算术下溢
这里还有另外一个路障。在现实中,我们要处理的概率值比这个简单例子要小得多。在现实中,我们不止使用两个特征,还可能将它们相乘。这将就会导致NumPy所提供的精度不够用:
>>> import numpy as np >>> np.set_printoptions(precision=20) # tell numpy tprint out more digits (default is 8) >>> np.array([2.48E-324]) array([ 4.94065645841246544177e-324]) >>> np.array([2.47E-324]) array([ 0.])
那么,有多大的可能性碰到形如2.47E-324 这样的数字呢?要回答这个问题,我们只需要想象一个条件概率0.000 1,然后把65个概率乘在一起(意思是说,我们有65个这样的低概率的特征值)。你就会看到算术下溢:
>>> x=0.00001 >>> x**64 # 仍然可以 1e-320 >>> x**65 # 哎哟 0.0
Python中的float 通常是由C中的double 实现的。要验证你的平台是否有这个问题,你可以通过以下方式查看:
>>> import sys >>> sys.float_info sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)
要进行移植,你可以改用其他数学函数库,例如mpmath(http://code.google.com/p/mpmath/ ),它允许任意精度。然而,它们的速度不够快,不足以代替NumPy。
幸运的是,我们有一个更好的方式来处理它,这和一个看起来很优美的关系式有关,你可能在学校的时候就已经知道它了:
如果把它应用到我们的例子里,就可以得到下面这个式子:
由于概率值处于0到1的区间之中,概率值取log后会处于-∞到0之间。你不要因为这个而感到不快。较高的值仍然强烈地预示着正确的类别——只不过它们现在是负值而已。
有一个需要注意的地方:实际上我们在公式中并没有使用log(前面的部分),而是只使用了概率的乘积。在这里很幸运,我们对概率的实际数值并不感兴趣。我们只想知道哪个类别具有最高的后验概率。我们是幸运的,因为如果我们发现:
那我们就可以有如下关系:
浏览一下前图就可以看到,曲线从左到右不会下降。简而言之,取对数并不会改变最高值。所以我们保留之前使用过的公式:
我们会用它来得到用这两个特征在真实数据上推算最佳类别的公式:
当然,如果只使用两个特征,效果不会很好。所以让我们重写这个公式,使它能允许包含任意多个特征:
我们已经准备好了,使用来自Scikit-learn工具箱的我们的第一个分类器吧。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论