朴素贝叶斯(Naive Bayes)算法是基于贝叶斯定理和特征条件独立性假设(在给定样本类别的前提下,任意特征的取值与其他特征都不相关)的分类方法。适用于文本分类、情感分析、垃圾邮件分类等文本分析问题,此外朴素贝叶斯还应用于医学诊断和推荐系统。
原理
贝叶斯定理
假设 X,YX,YX,Y 是一对随机变量,它们的联合概率 p(X=x,Y=y)p(X=x,Y=y)p(X=x,Y=y) 是指 XXX 取值 xxx 且 YYY 取值 yyy 的概率。条件概率 p=(X=x∣Y=y)p=(X=x|Y=y)p=(X=x∣Y=y) 是指 YYY 在取值 yyy 的情况下,变量 XXX 取值 xxx 的概率。且 XXX 和 YYY 的联合概率和条件概率满足
p(X,Y)=p(X∣Y)p(Y)=p(Y∣X)p(X)p(X, Y)=p(X|Y)p(Y)=p(Y|X)p(X)p(X,Y)=p(X∣Y)p(Y)=p(Y∣X)p(X)
可得贝叶斯定理
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)
假设 XXX 代表样本,YYY 代表样本的类别标签,贝叶斯定理是一种把先验知识和从样本获取的证据相结合的统计原理。其中 p(Y)p(Y)p(Y) 称为先验分布,p(X∣Y)p(X|Y)p(X∣Y) 称为似然函数,p(X)p(X)p(X) 称为证据,p(Y∣X)p(Y|X)p(Y∣X) 称为后验分布。
朴素贝叶斯模型
给定带类标签的数据集 DDD 中的样本分布于 mmm 个类别 C1,C2,…,CmC_1,C_2,…,C_mC1,C2,…,Cm,每一个样本以 ddd 维属性向量形式表示,X=(x1,x2,…,xd)X=(x_1,x_2,…,x_d)X=(x1,x2,…,xd)
贝叶斯分类器的目标:
计算后验概率 P(Ci∣X)P(C_i|X)P(Ci∣X),确定最大的一个 P(Ck∣X)P(C_k|X)P(Ck∣X),CkC_kCk 即为样本 XXX 的类别
P(Ci∣X)=P(X∣Ci)P(Ci)P(X)P(C_i|X)=\frac{P(X|C_i)P(C_i)}{P(X)}P(Ci∣X)=P(X)P(X∣Ci)P(Ci)
其中 P(X)P(X)P(X) 可用全概率公式计算,即 P(X)=∑i=1mP(X∣Ci)P(Ci)P(X)=\displaystyle \sum^{m}_{i=1}{P(X|C_i)P(C_i)}P(X)=i=1∑mP(X∣Ci)P(Ci)。
由于 P(X)P(X)P(X) 对所有的类都相同,看作常量,只需要比较分子 P(X∣Ci)P(Ci)P(X|C_i)P(C_i)P(X∣Ci)P(Ci) 即可。
P(Ci)P(C_i)P(Ci):可近似为 ∣Ci,D∣/∣D∣|C_{i,D}|/|D|∣Ci,D∣/∣D∣,Ci,DC_{i,D}Ci,D 为 DDD 中类别为 CiC_iCi 的样本的数量。
P(X∣Ci)P(X|C_i)P(X∣Ci):朴素的假设——假定各个属性之间相互独立,因此
P(X∣Ci)=∏P(xk∣Ci)P(X|C_i)=\prod{P(x_k|C_i)}P(X∣Ci)=∏P(xk∣Ci)
分类属性 fff:P(xk∣Ci)P(x_k|C_i)P(xk∣Ci)可近似为count(f=xk)/∣Ci,D∣count(f=x_k)/|C_{i,D}|count(f=xk)/∣Ci,D∣,其中 count(f=xk)count(f=x_k)count(f=xk) 为属性 fff 取值为 sks_ksk 的样本的数量。
连续值属性 fff:假定其值服从均值为 μ\muμ 标准差为 σ\sigmaσ 的高斯分布,即
g(x,μ,σ)=12πσe−x−μ2σ2g(x,\mu,\sigma)=\frac{1}{\sqrt{2\pi}\sigma}e^{-\frac{x-\mu}{2\sigma^2}}g(x,μ,σ)=2πσ1e−2σ2x−μ
则
P(xk∣Ci)=g(xk,μCi,σCi)P(x_k|C_i)=g(x_k,\mu_{C_i},\sigma_{C_i})P(xk∣Ci)=g(xk,μCi,σCi)
其中 μci\mu c_iμci 和 σci\sigma c_iσci 分别为类别 CiC_iCi 中样本属性 fff 的均值和标准差。
对上式的解释: g(x,μ,σ)g(x,\mu,\sigma)g(x,μ,σ) 是高斯分布的概率密度函数。对于连续值属性 fff,P(f=xk)P(f=x_k)P(f=xk) 的值接近于0。如:P(Height=70)=?P(Height=70)=?P(Height=70)=?,可计算 P(69≤Height≤70)P(69\leq Height\leq 70)P(69≤Height≤70) 或 P(70≤Height≤71)P(70\leq Height\leq 71)P(70≤Height≤71)
一般计算的是 fff 的取值落入 xkx_kxk 附近很小的邻域内的概率,即
P(xk−ϵ/2≤f≤xk+ϵ/2)≈ϵ×g(xk,μ,σ)P(x_k-\epsilon/2\leq f\leq x_k+\epsilon/2)\approx\epsilon\times g(x_k,\mu,\sigma)P(xk−ϵ/2≤f≤xk+ϵ/2)≈ϵ×g(xk,μ,σ)
带入公式
P(X∣Ci)=∏k=1nP(xk∣Ci)P(X|C_i)=\prod^{n}_{k=1}P(x_k|C_i)P(X∣Ci)=k=1∏nP(xk∣Ci)
P(Ci∣X)=P(X∣Ci)P(Ci)P(X)P(C_i|X)=\frac{P(X|C_i)P(C_i)}{P(X)}P(Ci∣X)=P(X)P(X∣Ci)P(Ci)
P(X)=∑i=1mP(X∣Ci)P(Ci)P(X)=\displaystyle \sum^{m}_{i=1}{P(X|C_i)P(C_i)}P(X)=i=1∑mP(X∣Ci)P(Ci)
可约去邻域大小 ϵ\epsilonϵ。因此,只需计算概率密度函数的值。
若该属性的取值不服从高斯分布,可用核密度估计(KDE,Kernel density Estimation)方法计算其概率。
如高斯核的情况
P(xk∣Ci)=1∣Dch∣∑xj∈K(xk,xj[f])P(x_k|C_i)=\frac{1}{|D_ch|}\displaystyle \sum_{x_j\in }K(x_k,x_j[f])P(xk∣Ci)=∣Dch∣1xj∈∑K(xk,xj[f])
K(a,b)=12πe(a−b)22h2K(a,b)=\frac{1}{\sqrt{2\pi}}e^{\frac{(a-b)^2}{2h^2}}K(a,b)=2π1e2h2(a−b)2
其中 hhh 为带宽
0概率问题:拉普拉斯校正(Laplacian Smoothing)
若训练集中类别 CiC_iCi 不包含 f=xkf=x_kf=xk 的样本,用公式 P(xk)=∣Ci,D∣/∣D∣P(x_k)=|C_{i,D}|/|D|P(xk)=∣Ci,D∣/∣D∣ 估计概率,会得到 P(xk∣Ci)=0P(x_k|C_i)=0P(xk∣Ci)=0。
则样本 XXX 中只要包含 f=xkf=x_kf=xk 的属性,无论其他属性如何取值,P(X∣Ci)p(Ci)P(X|C_i)p(C_i)P(X∣Ci)p(Ci) 均为0。
拉普拉斯校正方法:
P(xk∣Ci)=count(Ak=xk)+1∣Ci,D∣+count(Ak)P(x_k|C_i)=\frac{count(A_k=x_k)+1}{|C_{i,D}|+count(A_k)}P(xk∣Ci)=∣Ci,D∣+count(Ak)count(Ak=xk)+1
其中 count(f)count(f)count(f) 为属性 fff 的不同取值的数目。
例如训练集中类别 CiC_iCi 包含1000个样本,其中
income=′low′(0)income='low'(0)income=′low′(0)
income=′medium′(990)income='medium'(990)income=′medium′(990)
income=′high′(10)income='high'(10)income=′high′(10)
则
P(income=′low′∣Ci)=1/1003P(income='low'|C_i)=1/1003P(income=′low′∣Ci)=1/1003
P(income=′medium′∣Ci)=991/1003P(income='medium'|C_i)=991/1003P(income=′medium′∣Ci)=991/1003
P(income=′high′∣Ci)=11/1003P(income='high'|C_i)=11/1003P(income=′high′∣Ci)=11/1003
举例
数据:
x1x_1x1 | x2x_2x2 | YYY | |
---|---|---|---|
1 | 1 | S | -1 |
2 | 1 | M | -1 |
3 | 1 | M | 1 |
4 | 1 | S | 1 |
5 | 1 | S | -1 |
6 | 2 | S | -1 |
7 | 2 | M | -1 |
8 | 2 | M | 1 |
9 | 2 | L | 1 |
10 | 2 | L | 1 |
11 | 3 | L | 1 |
12 | 3 | M | 1 |
13 | 3 | M | 1 |
14 | 3 | L | 1 |
15 | 3 | L | -1 |
- 训练结果:
C1:Y=1C2:Y=−1C_1:Y=1 \qquad C_2:Y=-1C1:Y=1C2:Y=−1
P(C1)=9/15P(C2)=6/15P(C_1)=9/15 \qquad P(C_2)=6/15P(C1)=9/15P(C2)=6/15
P(x1=1∣C1)=2/9P(x1=2∣C1)=3/9P(x1=3∣C1)=4/9P(x_1=1|C_1)=2/9\qquad P(x_1=2|C_1)=3/9\qquad P(x_1=3|C_1)=4/9P(x1=1∣C1)=2/9P(x1=2∣C1)=3/9P(x1=3∣C1)=4/9
P(x2=S∣C1)=1/9P(x2=M∣C1)=4/9P(x2=L∣C1)=4/9P(x_2=S|C_1)=1/9\qquad P(x_2=M|C_1)=4/9\qquad P(x_2=L|C_1)=4/9P(x2=S∣C1)=1/9P(x2=M∣C1)=4/9P(x2=L∣C1)=4/9
P(x1=1∣C2)=3/6P(x1=2∣C2)=2/6P(x1=3∣C2)=1/6P(x_1=1|C_2)=3/6\qquad P(x_1=2|C_2)=2/6\qquad P(x_1=3|C_2)=1/6P(x1=1∣C2)=3/6P(x1=2∣C2)=2/6P(x1=3∣C2)=1/6
P(x2=S∣C2)=3/6P(x2=M∣C2)=2/6P(x2=L∣C2)=1/6P(x_2=S|C_2)=3/6\qquad P(x_2=M|C_2)=2/6\qquad P(x_2=L|C_2)=1/6P(x2=S∣C2)=3/6P(x2=M∣C2)=2/6P(x2=L∣C2)=1/6 - 验证
对于 t=(2,S)t=(2,S)t=(2,S):
P(C1∣t)=P(C1)P(x1=2∣C1)P(x2=S∣C1)=9/15∗3/9∗1/9=1/45P(C_1|t)=P(C_1)P(x_1=2|C_1)P(x_2=S|C_1)=9/15*3/9*1/9=1/45P(C1∣t)=P(C1)P(x1=2∣C1)P(x2=S∣C1)=9/15∗3/9∗1/9=1/45
P(C2∣t)=P(C2)P(x1=2∣C2)P(x2=S∣C2)=6/15∗2/6∗3/6=1/15P(C_2|t)=P(C_2)P(x_1=2|C_2)P(x_2=S|C_2)=6/15*2/6*3/6=1/15P(C2∣t)=P(C2)P(x1=2∣C2)P(x2=S∣C2)=6/15∗2/6∗3/6=1/15
所以 t=(2,S)t=(2,S)t=(2,S) 的标签 YYY 的预测值为 −1-1−1。
代码实现
朴素贝叶斯模型
class bayes:
def __init__(self, lam=None):
self.y_p = {} # 存储不同标签的概率
self.y_x_cnt = [] # 存储相同标签下不同属性值的数量
self.lam = lam # 是否使用拉普拉斯校正,为数值型数据
self.y_x_p = [] # 存储相同标签下不同属性值的概率
self.y_pre = [] # 存储预测结果
def get_y_p(self, data):
if self.lam == None:
y_labels = np.unique(data[:, -1])
for y_label in y_labels:
self.y_p[y_label] = len(data[data[:, -1] == y_label]) / len(data)
else:
y_labels = np.unique(data[:, -1])
for y_label in y_labels:
self.y_p[y_label] = (len(data[data[:, -1] == y_label]) + self.lam) / (len(data) + self.lam*len(y_labels))
def get_y_x_cnt(self, data):
for y_label in self.y_p.keys():
y_label_cnt = len(data[data[:, -1] == y_label])
for x_k in range(0, data.shape[1]-1):
x_k_count = Counter(data[data[:, -1] == y_label][:,x_k])
self.y_x_cnt.append([y_label,y_label_cnt,x_k,dict(x_k_count)])
def get_y_x_p(self):
if self.lam == None:
for i in range(0, len(self.y_x_cnt)):
p = {}
for key in self.y_x_cnt[i][3].keys():
p[key] = self.y_x_cnt[i][3][key] / self.y_x_cnt[i][1]
self.y_x_p.append([self.y_x_cnt[i][0], self.y_x_cnt[i][1], self.y_x_cnt[i][2], p])
else:
for i in range(0, len(self.y_x_cnt)):
p = {}
length = len(self.y_x_cnt[i][3].keys())
for key in self.y_x_cnt[i][3].keys():
p[key] = (self.y_x_cnt[i][3][key]+self.lam) / (self.y_x_cnt[i][1]+length*self.lam)
self.y_x_p.append([self.y_x_cnt[i][0], self.y_x_cnt[i][1], self.y_x_cnt[i][2], p])
def fit(self, data):
data = np.array(data)
self.get_y_p(data)
self.get_y_x_cnt(data)
self.get_y_x_p()
def predict(self, y_test):
y_test = np.array(y_test)
y_x_p_arr = np.array(self.y_x_p)
for j in range(0, len(y_test)):
y = y_test[j]
result = {}
for key in self.y_p.keys():
result[key] = 1*self.y_p[key]
for i in range(0,len(y)):
result[key]=result[key]*y_x_p_arr[(y_x_p_arr[:,0]==key)&(y_x_p_arr[:,2]==i)][:,3][0][str(y[i])]
self.y_pre.append(max(result, key=result.get))
return self.y_pre
模型运用
数据集(使用原理介绍时的例子)
x = np.array([[1,1,1,1,1,2,2,2,2,2,3,3,3,3,3],['S','M','M','S','S','S','M','M','L','L','L','M','M','L','L']])
y = np.array([-1,-1,1,1,-1,-1,-1,1,1,1,1,1,1,1,-1])
df = pd.DataFrame()
df["x1"] = x[0]
df["x2"] = x[1]
df["label"] = y
朴素贝叶斯
model1 = bayes()
model1.fit(df)
y_test = [[2, 'S'],[2, 'L']]
model1.predict(y_test)
使用拉普拉斯校正
model = bayes(lam = 1)
model.fit(df)
y_test = [[2, 'S'],[2, 'L']]
model.predict(y_test)
总结
优点
- 思想简单,易于实现
- 分类准确率高——即使在属性之间存在相关性的情况下
- 数学基础严密,结果可解释性强
- 可以增量式进行训练
缺点
- 朴素的假设不成立:实际数据的属性之间往往存在相关性