朴素贝叶斯
概述
在许多分类算法应用中,特征和标签之间的关系并非是决定性的。比如说,我们想预测一个人究竟是否会在泰坦尼克号海难中生存下来,那我们可以建一棵决策树来学习我们的训练集。在训练中,其中一个人的特征为:30岁,男,普通舱,他最后在泰坦尼克号海难中去世了。当我们测试的时候,我们发现有另一个人的特征也为:30岁,男,普通舱。基于在训练集中的学习,我们的决策树必然会给这个人打上标签:去世。然而这个人的真实情况一定是去世了吗?并非如此。
也许这个人是心脏病患者,得到了上救生艇的优先权。又有可能,这个人就是挤上了救生艇,活了下来。对分类算法来说,基于训练的经验,这个人"很有可能"是没有活下来,但算法永远也无法确定"这个人一定没有活下来"。即便这个人最后真的没有活下来,**算法也无法确定基于训练数据给出的判断,是否真的解释了这个人没有存活下来的真实情况。这就是说,算法得出的结论,永远不是100%确定的,更多的是判断出了一种"样本的标签更可能是某类的可能性",而非一种"确定"。**我们通过某些规定,比如说,在决策树的叶子节点上占比较多的标签,就是叶子节点上所有样本的标签,来强行让算法为我们返回一个固定结果。但许多时候,我们也希望能够理解算法判断出的可能性本身。
每种算法使用不同的指标来衡量这种可能性。比如说,决策树使用的就是叶子节点上占比较多的标签所占的比例(接口predict_proba调用),逻辑回归使用的是sigmoid函数压缩后的似然(接口predict_proba调用),而SVM使用的是样本点到决策边界的距离(接口decision_function调用)。但这些指标的本质,其实都是一种"类概率"的表示,我们可以通过归一化或sigmoid函数将这些指标压缩到0~1之间,让他们表示我们的模型对预测的结果究竟有多大的把握(置信度)。但无论如何,我们都希望使用真正的概率来衡量可能性,因此就有了真正的概率算法: 朴素贝叶斯。
朴素贝叶斯是一种直接衡量标签和特征之间的概率关系的有监督算法,它既可以做回归也可以分类,不过多是用于分类之中。朴素贝叶斯的算法根源就是基于概率论和数理统计的贝叶斯理论,因此它是根正苗红的概率模型。接下来,我们就来认识一下这个简单快速的概率算法。
冬眠的瓢虫:朴素贝叶斯是如何工作的
朴素贝叶斯被认为是最简单的分类算法之一。首先,我们需要了解一些概率论的基本理论。假设有两个随机变量X,Y,他们分别可以取值为x和y。有这两个随机变量,我们可以定义两种概率函数:
联合概率:X取值为x和Y取值为y两个事件同时发生的概率,表示为
P ( X = x , Y = y ) P(X=x,Y=y) P(X=x,Y=y)
条件概率:再X取值为x的前提下,Y取值为y的概率,表示为:
P ( Y = y ∣ X = x ) P(Y=y|X=x) P(Y=y∣X=x)
举个例子,我们让X为"气温",Y为"七星瓢虫冬眠",则X和Y可能的取值分为别x和y,其中 x = 0 , 1 x={0,1} x=0,1,0表示没有下降到0度以下,1表示下降到了0度以下。 y = 0 , 1 y = {0,1} y=0,1,其中0表示否,1表示是。
两个事件分别发生的概率就为:
P ( X = 1 ) = 50 P(X=1) = 50% P(X=1)=50,则是说明,气温下降到0度以下的可能性为50%,则 P ( X = 0 ) = 1 − P ( X = 1 ) = 50 P(X=0) = 1- P(X=1) = 50% P(X=0)=1−P(X=1)=50。
P ( Y = 1 ) = 30 P(Y=1) = 30% P(Y=1)=30,则是说明,七星瓢虫会冬眠的可能性为70%,则 P ( Y = 0 ) = 1 − P ( Y = 1 ) = 30 P(Y=0) = 1- P(Y=1) = 30% P(Y=0)=1−P(Y=1)=30。
则这两个事件的联合概率为 P ( X = 1 , Y = 1 ) P(X=1,Y=1) P(X=1,Y=1),这个概率代表了气温下降到0度以下和七星瓢虫去冬眠这两件事情同时,独立发生的概率。而两个事件之间的条件概率为 P ( Y = 1 ∣ X = 1 ) P(Y=1|X=1) P(Y=1∣X=1),这个概率代表了,当气温下降到0度以下这个条件被满足之后,七星瓢虫会去冬眠的概率。也就是说,气温下降到0度以下,一定程度上影响了七星瓢虫去冬眠这个事件。
在概率论中,我们可以证明,两个事件的联合概率等于这两个事件任意条件概率 * 这个条件事件本身的概率。
P ( X , Y ) = P ( Y ∣ X ) ∗ P ( X ) = P ( X ∣ Y ) ∗ P ( Y ) P(X,Y) = P(Y|X)*P(X) = P(X|Y)*P(Y) P(X,Y)=P(Y∣X)∗P(X)=P(X∣Y)∗P(Y)
由上面的式子,我们可以得到贝叶斯理论等式:
P ( Y ∣ X ) = P ( X ∣ Y ) ∗ P ( Y ) P ( X ) P(Y|X) = \frac{P(X|Y)*P(Y)}{P(X)} P(Y∣X)=P(X)P(X∣Y)∗P(Y)
而这个式子,就是我们一切贝叶斯算法的根源理论。我们可以把我们的特征 X X X当成是我们的条件事件,而我们要求解的标签 Y Y Y当成是我们被满足条件后会被影响的结果,而两者之间的概率关系就是 P ( Y ∣ X ) P(Y|X) P(Y∣X),这个概率在机器学习中,被我们称之为是标签的后验概率(posterior probability),即是说我们先知道了条件,再去求解结果。而标签 Y Y Y在没有任何条件限制下取值为某个值的概率,被我们写作 P ( Y ) P(Y) P(Y),与后验概率相反,这是完全没有任何条件限制的,标签的先验概率(prior probability)。而我们的 P ( X ∣ Y ) P(X|Y) P(X∣Y)被称为"类的条件概率",表示当 Y Y Y的取值固定的时候, X X X为某个值的概率。那现在,有趣的事情就出现了
P(Y|X)
假设,我们依然让 X X X是"气温",这就是我们的特征, Y Y Y是"七星瓢虫冬眠",就是我们的标签。现在,我们建模的目的是,预测七星瓢虫是否会冬眠。在许多教材和博客里,大家会非常自然地开始说,我们现在求的就是我们的 P ( Y ∣ X ) P(Y|X) P(Y∣X),然后根据贝叶斯理论等式开始做各种计算和分析。现在请问大家,我写作 P ( Y ∣ X ) P(Y|X) P(Y∣X)的这个概率,代表了什么呢?更具体一点,这个表达,可以代表多少种概率呢?
数学中的第一个步骤,也就是最重要的事情,就是定义清晰。现在我们的 Y Y Y有两种取值,而 X X X也有两种取值,就让 P ( Y ∣ X ) P(Y|X) P(Y∣X)概率的定义变得很模糊,排列组合之后竟然有4种可能。在机器学习当中,一个特征 X X X下取值可能远远不止两种,标签也可能是多分类的,还会有多个特征,排列组合以下,到底求解的 P ( Y ∣ X ) P(Y|X) P(Y∣X)是什么东西,是一个太让人感到混淆的点。同理, P ( X ) P(X) P(X)随着标签中分类的个数,可以有不同的取值。 P ( X ∣ Y ) P(X|Y) P(X∣Y)也是一样。
机器学习中的简写 P ( Y ) P(Y) P(Y),通常表示标签取到少数类的概率,少数类往往使用正样本表示,也就是 P ( Y = 1 ) P(Y=1) P(Y=1),本质就是所有样本中标签为1的样本所占的比例。如果没有样本不均衡问题,则必须在求解的时候明确,你的Y的取值是什么。
在机器学习当中,对每一个样本,我们不可能只有一个特征 X X X,而是会存在着包含 n n n个特征的特征向量 。因此机器学习中的后验概率,被写作 P ( Y ∣ X ) P(Y|X) P(Y∣X),其中 X X X中包含样本在 n n n各特征上的取值 。以此为基础,机器学习中,对每一个样本我们有:
P ( Y = 1 ∣ X 1 , X 2 , X 3 , . . . X n ) = P ( X 1 , X 2 , X 3 , . . . X n ∣ Y = 1 ) ∗ P ( Y = 1 ) P ( X 1 , X 2 , X 3 , X n ) P(Y=1 | X_1,X_2,X_3,...X_n) = \frac{P(X_1,X_2,X_3,...X_n|Y=1) *P(Y=1)}{P(X_1,X_2,X_3,X_n)} P(Y=1∣X1,X2,X3,...Xn)=P(X1,X2,X3,Xn)P(X1,X2,X3,...Xn∣Y=1)∗P(Y=1)
对于分子而言, 就是少数类占总样本量的比例, 则需要稍微复杂一点的过程来求解。假设我们只有两个特征,由联合概率公式,我们可以有如下证明:
P ( X 1 , X 2 ∣ Y = 1 ) = P ( X 1 , X 2 , Y = 1 ) P ( Y = 1 = P ( X 1 , X 2 , Y = 1 ) P ( X 2 , Y = 1 ) ∗ P ( X 2 , Y = 1 ) P ( Y = 1 ) = P ( X 1 ∣ X 2 , Y = 1 ) ∗ P ( X 2 ∣ Y = 1 ) 假设 X 1 和 X 2 之间是有条件独立的: 原式 = P ( X 1 ∣ Y = 1 ) ∗ P ( X 2 ∣ Y = 1 ) P(X_1,X_2|Y=1) = \frac{P(X_1,X_2,Y=1)}{P(Y=1}\\ = \frac{P(X_1,X_2,Y=1)}{P(X_2,Y=1)} *\frac{P(X_2,Y=1)}{P(Y=1)}\\ = P(X_1|X_2,Y=1)*P(X_2|Y=1)\\ 假设X_1和X_2之间是有条件独立的:\\ 原式 = P(X_1|Y=1)*P(X_2|Y=1) P(X1,X2∣Y=1)=P(Y=1P(X1,X2,Y=1)=P(X2,Y=1)P(X1,X2,Y=1)∗P(Y=1)P(X2,Y=1)=P(X1∣X2,Y=1)∗P(X2∣Y=1)假设X1和X2之间是有条件独立的:原式=P(X1∣Y=1)∗P(X2∣Y=1)
若推广到n个X上,则有:
P ( X = X 1 , X 2 , . . . X n ∣ Y = 1 ) = ∏ i = 1 n P ( X i ∣ Y = 1 ) P(X = X_1,X_2,...X_n|Y=1) = \prod_{i=1}^{n}P(X_i|Y=1) P(X=X1,X2,...Xn∣Y=1)=i=1∏nP(Xi∣Y=1)
这个式子证明,在 Y = 1 Y=1 Y=1的条件下, X X X的多种取值被同时取到的概率,就等于 Y = 1 Y=1 Y=1的条件下, X X X的多种取值被分别取到的概率相乘。其中,假设 X 1 X_1 X1与 X 2 X_2 X2是有条件独立则可以让公式 P ( X 1 ∣ X 2 , y = 1 ) = P ( X 1 ∣ Y = 1 ) P(X_1|X_2,y=1) = P(X_1|Y=1) P(X1∣X2,y=1)=P(X1∣Y=1),这是在假设 X 2 X_2 X2是一个对 X 1 X_1 X1在某个条件下的取值完全无影响的变量。
假设特征之间是有条件独立的,可以解决众多问题,也简化了很多计算过程,这是朴素贝叶斯被称为"朴素"的理由。因此,贝叶斯在特征之间有较多相关性的数据集上表现不佳,而现实中的数据多多少少都会有一些相关性,所以贝叶斯的分类效力在分类算法中不算特别强大。但无论如何,有了这个式子,我们就可以求解出我们的分子了。
对于贝叶斯理论等式的分母而言,我们可以使用全概率公式来求解 P ( X ) P(X) P(X):
P ( X ) = ∑ i = 1 m P ( Y i ) ∗ P ( X ∣ Y i ) P(X) = \sum^{m}_{i=1}P(Y_i)*P(X|Y_i) P(X)=i=1∑mP(Yi)∗P(X∣Yi)
其中 m m m代表标签的种类,也就是说,对于二分类而言我们有:
P ( X ) = P ( Y = 1 ) ∗ P ( X ∣ Y = 1 ) + P ( Y = 0 ) ∗ P ( X ∣ Y = 0 ) P(X)= P(Y=1)* P(X|Y=1) + P(Y=0)*P(X|Y=0) P(X)=P(Y=1)∗P(X∣Y=1)+P(Y=0)∗P(X∣Y=0)
与众不同的朴素贝叶斯
很多算法先从训练集中学习,获取某种信息来建立模型,然后用模型去对测试集进行预测。比如逻辑回归,我们要先从训练集中获取让损失函数最小的参数,然后用参数建立模型,再对测试集进行预测。在比如支持向量机,我们要先从训练集中获取让边际最大的决策边界,然后用决策边界对测试集进行预测。相同的流程在决策树,随机森林中也出现,我们在fit的时候必然已经构造好了能够让对测试集进行判断的模型。而朴素贝叶斯,似乎没有这个过程。
这说明,朴素贝叶斯是一个不建模的算法。以往我们学的不建模算法,比如KMeans,比如PCA,都是无监督学习,而朴素贝叶斯是第一个有监督的,不建模的分类算法。训练集和测试集都来自于同一个不可获得的大样本下,并且这个大样本下的各种属性所表现出来的规律应当是一致的,因此训练集上计算 出来的各种概率,可以直接放到测试集上来使用。即便不建模,也可以完成分类。
实际中,朴素贝叶斯的决策方案公式没有之前那么简单:
P ( Y = 1 ∣ X 1 , X 2 , X 3 , . . . X n ) = P ( Y = 1 ) ∗ ∏ i = 1 n P ( X i ∣ Y = 1 ) P ( X 1 , X 2 , X 3 , . . . X n ) P(Y=1| X_1,X_2,X_3,...X_n) = \frac{P(Y=1)*\prod^{n}_{i=1}P(X_i|Y=1)}{P(X_1,X_2,X_3,...X_n)} P(Y=1∣X1,X2,X3,...Xn)=P(X1,X2,X3,...Xn)P(Y=1)∗∏i=1nP(Xi∣Y=1)
对于这个公式来说,从训练集中求解 P ( Y = 1 ) P(Y=1) P(Y=1)很容易, P ( X 1 , X 2 , X 3 , . . . X n ) P(X_1,X_2,X_3,...X_n) P(X1,X2,X3,...Xn)和 P ( X 1 , X 2 , X 3 , . . . X n ∣ Y = 1 ) P(X_1,X_2,X_3,...X_n|Y=1) P(X1,X2,X3,...Xn∣Y=1)这部分就没有这么容易了。
虽然朴素贝叶斯使用了过于简化的假设,这个分类器在许多实际情况中都运行良好,著名的是文档分类和垃圾邮件过滤。而且由于贝叶斯是从概率角度进行估计,它所需要的样本量比较少。当然,如果样本量少于特征数目,贝叶斯的效果就会被削弱。
与SVM和随机森林相比,朴素贝叶斯运行速度更快,因为求解 P ( X i ∣ Y ) P(X_i|Y) P(Xi∣Y)本质是在每个特征上单独对概率进行计算, 然后再求乘积,所以每个特征上的计算可以是独立并且并行的,因此贝叶斯的计算速度比较快。不过相对的,贝叶斯的运行效果不是那么好,所以贝叶斯的接口调用的predict_proba其实也不是总指向真正的分类结果。
代码
python
from sklearn.datasets import load_iris
import pandas as pd
data = load_iris()
col = list(data.target_names) #获取类名
df = pd.DataFrame(data.data) #获取特征
df['label'] = data.target #获取标签,数值型:0,1,2
P_class = df.label.value_counts()/len(df)
n_classes = 3
n_features = 4
theta = np.zeros((n_classes, n_features))
sigma = np.zeros((n_classes, n_features))
for c in set(df.label):
for x in df.columns[:-1]:
df_temp = df[df.label==c]
theta[c,x] = df_temp[x].mean()
sigma[c,x] = df_temp[x].std()
from math import pi,e
sample = df.iloc[0,:4].values.reshape(-1,)
a = (1/(sigma*(2*pi)**0.5))*e**-((sample-theta)**2/(2*sigma**2))
b = np.log(a)
c = np.sum(b,axis=1) + np.log(P_class.values)
max_class = np.argmax(c)
参考
菜菜的机器学习sklearn
Python手写实现朴素贝叶斯(从原理到代码) - 知乎 (zhihu.com)