什么是朴素贝叶斯
今天阴天,天气预报说有60%的概率下雨,有20%的概率打雷,下雨后发生打雷的概率只有10%。根据朴素贝叶斯模型就可以求出,打雷后下雨的概率。
朴素贝叶斯模型是一种简单但非常强大的分类器,在垃圾邮件过滤、疾病诊断、文本分类等方面都显示出了巨大的成效。这里的”朴素“是指:设特征之间是相互独立的
朴素贝叶斯模型
我们收到一封邮件出现“中奖”等词,需要判断它是否垃圾邮件。首先根据以往的邮件统计出在垃圾邮件中出现词“中奖”的概率
P
(
中奖
∣
垃圾
)
P(中奖|垃圾)
P(中奖∣垃圾),所有邮件中出现垃圾邮件的概论
P
(
垃圾
)
P(垃圾)
P(垃圾),“中奖”在所有邮件中出现的概论
P
(
中奖
)
P(中奖)
P(中奖),根据朴素贝叶斯模型就求出,邮件中出现“中奖”这个词,是垃圾邮件的概率:
P
(
垃圾
∣
中奖
)
=
P
(
中奖
,
垃圾
)
P
(
中奖
)
=
P
(
中奖
∣
垃圾
)
P
(
垃圾
)
P
(
中奖
)
P(垃圾|中奖)=\frac{P(中奖,垃圾)}{P(中奖)}=\frac{P(中奖|垃圾)P(垃圾)}{P(中奖)}
P(垃圾∣中奖)=P(中奖)P(中奖,垃圾)=P(中奖)P(中奖∣垃圾)P(垃圾)
**联合概率:**指包含多个条件且所有条件同时成立的概率。比如上式中的
P
(
中奖
,
垃圾
)
P(中奖,垃圾)
P(中奖,垃圾),包含“中奖”这个词有是垃圾邮件的概率。
**边缘概率:**仅考虑单一事件发生的可能性,而不考虑其它事件发生的可能性。比如上式中的 P ( 中奖 ) P(中奖) P(中奖)和 P ( 垃圾 ) P(垃圾) P(垃圾)。
**条件概率:**在某一事件发生的情况下,另一事件发生的概率。比如上式中的 P ( 中奖 ∣ 垃圾 ) P(中奖|垃圾) P(中奖∣垃圾)
**先验概率:**根据以往的经验,不用采样就可以得到的概率。比如上式中的 P ( 中奖 ∣ 垃圾 ) P ( 垃圾 ) P ( 中奖 ) P(中奖|垃圾)P(垃圾)P(中奖) P(中奖∣垃圾)P(垃圾)P(中奖)
**后验概率:**指某件事已经发生,依据这件事发生的事实来推断某一个事件在该事件前发生的概率。比如上式中的 P ( 垃圾 ∣ 中奖 ) P(垃圾|中奖) P(垃圾∣中奖)
公式的推导
由上面我们得到贝叶斯公式的一般形式
P
(
B
∣
A
)
=
P
(
A
,
B
)
P
(
A
)
=
P
(
A
∣
B
)
P
(
B
)
P
(
A
)
P(B|A)=\frac{P(A,B)}{P(A)}=\frac{P(A|B)P(B)}{P(A)}
P(B∣A)=P(A)P(A,B)=P(A)P(A∣B)P(B)
由公式我们将问题转换为求以下先验概率分布及条件概率分布。
先验概率分布:
P
(
Y
=
c
k
)
,
k
=
1
,
2...
,
k
P(Y=c_k),k=1,2...,k
P(Y=ck),k=1,2...,k
其中
c
k
c_k
ck代表各个类,
K
K
K为类的总数量,P(Y=c_k)
代表在训练集中某一类出现的概率。
条件概率分布:
P
(
X
=
x
∣
Y
=
c
k
)
=
P
(
X
(
1
)
=
x
(
1
)
,
.
.
.
,
X
(
n
)
=
x
(
n
)
∣
Y
=
c
k
)
,
k
=
1
,
2
,
.
.
.
,
K
P(X=x|Y=c_k)=P(X^{(1)}=x^{(1)},...,X^{(n)}=x^{(n)}|Y=c_k),k=1,2,...,K
P(X=x∣Y=ck)=P(X(1)=x(1),...,X(n)=x(n)∣Y=ck),k=1,2,...,K
这里指
Y
=
c
k
Y=c_k
Y=ck的情况下各个特征分别为其某一可取值的概率,上式既有条件也有联合,因此也可称为条件联合概率。
通过条件概率和先验概率(边缘概率)可得到联合概率分布 P ( X , Y ) P(X,Y) P(X,Y)。
我们可以对条件概率分布作条件独立性的假设,虽然该假设可能并不符合实际情况,但在该假设下的一些模型的预测准确率足够用以做出决策,因此可以忽略条件独立性假设造成的误差。
所以在事件独立的情况下,
P
(
A
,
B
)
=
P
(
A
)
∗
P
(
B
)
P(A,B)=P(A)*P(B)
P(A,B)=P(A)∗P(B),因此原来的条件概率可转换为多个独立事件的条件概率的连乘:
P
(
X
=
x
∣
Y
=
c
k
)
=
P
(
X
(
1
)
=
x
(
1
)
,
.
.
.
,
X
(
n
)
=
x
(
n
)
∣
Y
=
c
k
)
=
∏
j
=
1
n
P
(
X
(
j
)
=
x
(
j
)
∣
Y
=
c
k
)
P(X=x|Y=c_k)=P(X^{(1)}=x^{(1)},...,X^{(n)}=x^{(n)}|Y=c_k)=\prod_{j=1}^nP(X^{(j)}=x^{(j)}|Y=c_k)
P(X=x∣Y=ck)=P(X(1)=x(1),...,X(n)=x(n)∣Y=ck)=j=1∏nP(X(j)=x(j)∣Y=ck)
在此假设下,后验概率的计算用到贝叶斯定理:
P
(
Y
=
c
k
∣
X
=
x
)
=
P
(
X
=
x
,
Y
=
c
k
)
P
(
X
=
x
)
=
P
(
X
=
x
∣
Y
=
c
k
)
P
(
Y
=
c
k
)
P
(
X
=
x
)
=
P
(
Y
=
c
k
)
∏
j
−
1
n
P
(
X
(
j
)
=
x
(
j
)
∣
Y
=
c
k
)
P
(
X
=
x
)
P(Y=c_k|X=x)=\frac{P(X=x,Y=c_k)}{P(X=x)}=\frac{P(X=x|Y=c_k)P(Y=c_k)}{P(X=x)}=\frac{P(Y=c_k)\prod_{j-1}^nP(X^{(j)}=x^{(j)}|Y=c_k)}{P(X=x)}
P(Y=ck∣X=x)=P(X=x)P(X=x,Y=ck)=P(X=x)P(X=x∣Y=ck)P(Y=ck)=P(X=x)P(Y=ck)∏j−1nP(X(j)=x(j)∣Y=ck)
因为x为新输入实例的特征向量,因此对于Y取任意值,分母不变。我们将后验概率最大的类作为x的类输出,在对比大小时,相同的分母可以忽略,所以最后朴素贝叶斯公式可转换为:
y
=
arg
max
P
(
Y
=
c
k
)
∏
j
−
1
n
P
(
X
(
j
)
=
x
(
j
)
∣
Y
=
c
k
)
y=\arg \max P(Y=c_k)\prod_{j-1}^n P(X^{(j)}=x^{(j)}|Y=c_k)
y=argmaxP(Y=ck)j−1∏nP(X(j)=x(j)∣Y=ck)
其中,
P
(
Y
=
c
k
)
P(Y=c_k)
P(Y=ck)即先验概率(边缘概率),
P
(
X
(
j
)
=
x
(
j
)
∣
Y
=
c
k
)
P(X^{(j)}=x^{(j)}|Y=c_k)
P(X(j)=x(j)∣Y=ck)即条件概率,可以依据训练集得出。
上式代表计算在某一特征向量的情况下计算该实例为某一类的概率,将最大的概率所对应的类认为是该点的类。
那么接下来的问题就转变为先验概率和条件概率的计算,这两个概率分布的计算都很简单。
先验概率的计算方法:
P
(
Y
=
c
k
)
=
∑
j
−
1
n
I
(
y
i
=
c
k
)
N
P(Y=c_k)=\frac{\sum_{j-1}^n I(y_i=c_k)}{N}
P(Y=ck)=N∑j−1nI(yi=ck)
其中
N
N
N为训练集中实例的总数,
I
I
I为指示函数,表示若括号中条件成立则取1,否则取0。比如训练集一共有100个实例,其中分类为A的实例有50个,则
P
(
Y
=
A
)
=
50
/
100
=
1
/
2
P(Y=A)=50/100=1/2
P(Y=A)=50/100=1/2。
条件概率的计算方法,原理同上:
P
(
X
(
j
)
=
a
(
j
)
∣
Y
=
c
k
)
=
∑
i
−
1
n
I
(
x
i
(
j
)
=
a
(
j
)
∣
y
i
=
c
k
)
∑
i
−
1
n
I
(
y
i
=
c
k
)
P(X^{(j)}=a^{(j)}|Y=c_k)=\frac{\sum_{i-1}^n I(x_i^{(j)}=a^{(j)}|y_i=c_k)}{\sum_{i-1}^nI(y_i=c_k)}
P(X(j)=a(j)∣Y=ck)=∑i−1nI(yi=ck)∑i−1nI(xi(j)=a(j)∣yi=ck)
贝叶斯估计
如果新出现的实例中有一个特征的取值在训练集中从来没有出现过,根据朴素贝叶斯公式中的 ∏ j − 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) \prod_{j-1}^n P(X^{(j)}=x^{(j)}|Y=c_k) ∏j−1nP(X(j)=x(j)∣Y=ck)这一连乘项中的一个值为0,即 ∏ j − 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) = 0 \prod_{j-1}^n P(X^{(j)}=x^{(j)}|Y=c_k)=0 ∏j−1nP(X(j)=x(j)∣Y=ck)=0 ,最终导致 Y Y Y取任意情况下后验概率皆为0,无法对新输入实例进行分类。
为了解决这一问题引入了贝叶斯估计
贝叶斯估计的先验概率为:
P
λ
(
Y
=
c
k
)
=
∑
j
−
1
n
I
(
y
i
=
c
k
)
+
λ
N
+
K
λ
P_\lambda(Y=c_k)=\frac{\sum_{j-1}^n I(y_i=c_k)+\lambda}{N+K\lambda}
Pλ(Y=ck)=N+Kλ∑j−1nI(yi=ck)+λ
条件概率为:
P
λ
(
X
(
j
)
=
a
(
j
)
∣
Y
=
c
k
)
=
∑
i
−
1
n
I
(
x
i
(
j
)
=
a
(
j
)
∣
y
i
=
c
k
)
+
λ
∑
i
−
1
n
I
(
y
i
=
c
k
)
+
S
j
λ
P_\lambda(X^{(j)}=a^{(j)}|Y=c_k)=\frac{\sum_{i-1}^n I(x_i^{(j)}=a^{(j)}|y_i=c_k)+_\lambda}{\sum_{i-1}^nI(y_i=c_k)+S_j\lambda}
Pλ(X(j)=a(j)∣Y=ck)=∑i−1nI(yi=ck)+Sjλ∑i−1nI(xi(j)=a(j)∣yi=ck)+λ
式中
λ
≥
0
\lambda≥0
λ≥0,当
λ
=
1
\lambda=1
λ=1,则称为拉普拉斯平滑(一般都使
λ
=
1
\lambda=1
λ=1)。
K
K
K为训练集中Y的类的数量,
S
j
S_j
Sj为特征
x
(
j
)
x^{(j)}
x(j)可取值的数量。
也就是说,对于先验概率,拉普拉斯平滑在其分子加1,在分母加上类别的数量。对于条件概率,则是在分子加1,在分母加上该特征的可取值的数量。
尽管 λ \lambda λ会让后验概率与未加入 λ \lambda λ之前有所变化,但是在数据量非常大的情况下, λ \lambda λ的加入所引起的后验概率的变化是在可接受范围的,对最终的分类结果影响可以忽略不计。
代码实现
import pandas as pd
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from numpy import var
import math
# 加载鸢尾花数据集
iris = datasets.load_iris()
df = pd.DataFrame(iris.data)
df['label'] = iris.target
df.columns = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'label']
# 加载鸢尾花数据集
x = iris.data
y = iris.target
# 划分训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=100)
class Model:
def __init__(self):
self.y= None
self.classes=None
self.classes_num=None
self.parameters =[]
def _calculate_prior(self, c):
'''
先验函数,也就是求先验概率
利用极大似然估计的结果得到
'''
frequency = np.mean(self.y == c)
return frequency
# 贝叶斯估计的先验概率
frequency = (np.sum(self.y == c) + 1) / (len(X) + self.classes_num)
def _calculate_likelihood(self, mean, var, X):
"""
似然函数
"""
# 高斯概率
eps = 1e-4 # 防止除数为0
coeff = 1.0 / math.sqrt(2.0 * math.pi * var + eps)
exponent = math.exp(-(math.pow(X - mean, 2) / (2 * var + eps)))
return coeff * exponent
def _calculate_probabilities(self, X):
posteriors = [] # 后验概率
for i,c in enumerate(self.classes):
# p(y)
posterior = self._calculate_prior(c)
# p(x | y)
for feature_value, params in zip(X, self.parameters[i]):
# 独立性假设
# P(x1,x2|Y) = P(x1|Y)*P(x2|Y)
likelihood = self._calculate_likelihood(params["mean"], params["var"], feature_value)
posterior *= likelihood
posteriors.append(posterior)
# 返回具有最大后验概率的类别
return self.classes[np.argmax(posteriors)]
def fit(self, train_data, train_label):
self.y = train_label
self.classes = np.unique(y) # 类别
self.classes_num = len(self.classes)
# 计算每个特征针对每个类的均值和方差
for i, c in enumerate(self.classes):
# 选择类别为c的X
X_where_c = train_data[np.where(self.y == c)]
self.parameters.append([])
# 添加均值与方差
for col in X_where_c.T:
parameters = {"mean": col.mean(), "var": col.var()}
self.parameters[i].append(parameters)
def predict(self, X):
y_pred = [self._calculate_probabilities(sample) for sample in X]
return y_pred
def score(self, X, y):
y_pred = self.predict(X)
accuracy = np.sum(y == y_pred, axis=0) / len(y)
return accuracy
model = Model()
model.fit(x_train, y_train)
# 测试数据
print(model.score(x_test, y_test))