公号:Python 自习室
朴素贝叶斯法是基于贝叶斯定理和特征条件独立假设的分类方法。朴素贝叶斯法实现简单,学习与预测的效率都很高,被广泛应用于文本分类、垃圾邮件过滤、自然语言处理等场景。下面我们来介绍贝叶斯定理,在介绍贝叶斯定理之前,先介绍下条件概率和全概率公式。
条件概率
所谓条件概率,就是在事件 B 发生的条件下,事件 A 发生的概率,用 P(A|B) 来表示。在下面的文氏图中,定义了事件 A 和 B,以及他们的交集 Acap B,overline A 为 A 的补集。则在事件 B 发生的条件下,事件 A 发生的概率为 P(A|B) = displaystyle{P (A cap B)} over displaystyle{P(B)} => P(A cap B)=P(A|B)P(B);同理可得,P(B|A) = displaystyle{P (A cap B)} over displaystyle{P(A)} => P(A cap B)=P(B|A)P(A),于是 P(A|B)P(B) = P(B|A)P(A)。
全概率公式
在上面的文氏图中,P(B) = P(A cap B) P(overline A cap B),代入上面的条件概率公式,P(B) = P(A)P(B|A) P(overline A)P(B|overline A),这便是全概率公式。
贝叶斯定理
贝叶斯定理是 Thomas Bayes 为了解决一个逆概率问题所写的一篇文章。在当时,人们已经能够计算正向概率。例如,一个袋子里装有 m 个白球 和 n 个黑球,把手伸进去摸到白球的概率是多少?这就是一个正向概率问题,而逆概率问题正好反过来,我们事先并不知道袋子里黑球和白球的比例,而是不断伸手去摸球,根据摸到球的颜色来推测黑球与白球的比例。由上面的条件概率,我们可以推导出 P(A|B) = displaystyle P(B|A)P(A) over displaystyle P(B) 这便是贝叶斯定理。P(A) 称为先验概率,P(A|B) 称为后验概率,即在事件 B 发生之后,我们对事件 A 概率的重新评估。
朴素贝叶斯分类器
朴素贝叶斯的思想基础是,对于待分类项 x,求解在 x 出现的条件下各个类别出现的概率,哪个最大,就认为 x 属于哪个类别。设输入空间 mathcal X subseteq R^n 为 n 维向量的集合,输出空间为类标记集合 mathcal Y = {c_1, c_2, ..., c_k},输入为特征向量 x in mathcal X,输出为类标记 y in mathcal Y。X 是定义在输入空间 mathcal X 上的随机向量,Y 是定义在输出空间 mathcal Y 上的随机变量。假设有一个待分类项 x,为了确定 x 属于哪个类别,我们需要计算 P(Y=c_k|X=x), k=1, 2, ..., K,选取概率P(Y=c_k|X=x) 最大时对应的类别为 x 对应的类别。基于贝叶斯定理可以写为: P(Y=c_k|X=x) = displaystyle P(X=x|Y=c_k)P(Y=c_k) over displaystyle P(X=x) ,k=1, 2, ..., K 基于此公式来估计后验概率 P(Y=c_k|X=x) 的主要困难在于:条件概率 P(X=x|Y=c_k) 是所有特征上的联合概率,难以从有限的训练样本直接估计而得。为避开这个障碍,朴素贝叶斯分类器采用了“特征条件独立假设”:对已知类别,假设所有特征相互独立。换言之,假设每个特征独立地对分类结果发生影响(这也是算法被叫做朴素贝叶斯分类器的原因)。基于特征条件独立假设,可将上述公式重写为: displaystyle P(Y=c_k|X=x)
= displaystyle prod_{j=1}^nP(X^{(j)}=x^{(j)}|Y=c_k)P(Y=c_k) over displaystyle P(X=x),k=1,2,...,K 由于对所有的类别,P(X=x) 相同,所以 y = {underset {c_k}{operatorname {arg,max} }} P(Y=c_k)displaystyleprod_{j=1}^nP(X^{(j)}=x^{(j)}|Y=c_k)(y 便是 x 的分类) 下面便是如何求得,P(Y=c_k) 和 P(X^{(j)}=x^{(j)}|Y=c_k)。
极大似然估计
在朴素贝叶斯分类器中,学习意味着估计 P(Y=c_k) 和 P(X^{(j)}=x^{(j)}|Y=c_k)。可以应用极大似然估计法估计相应的概率。我们先通过一个例子来了解下极大似然估计。假设一个袋子里装有黑白两种颜色的球,黑白球的数量以及比例未知,我们不可以一次把袋子中的球倒出来,只能每次从袋子中任意取一个球,记录球的颜色,然后将球放回袋子。假如我们重复取球 100 次之后,在取出的 100 个球中,有白球 60 个,黑球 40 个,那么请问袋子里黑白球的比例最有可能是多少?也许你会回答黑白球的比例为 4:6,那么这个答案背后的理论支撑是什么呢?我们假设袋子里黑球的比例为 p,那么白球的比例为 1-p,因为每抽一个球出来,在记录颜色之后,把抽出的球放回了袋子里,所以每次抽出来的球的颜色服从同一独立分布。这里我们把一次抽出来球的颜色称为一次抽样。题目中在一百次抽样中,60 次是白球的,40 次为黑球事件的概率是P(样本结果|M)。如果第一次抽样的结果记为 x_1,第二次抽样的结果记为 x_2...,第一百次抽样的结果为 x_{100}。那么样本结果为 (x_1,x_2.....,x_{100})。这样,我们可以得到如下表达式:
P(样本结果|M)
= P(x_1,x_2,…,x_{100}|M)
= P(x_1|M)P(x_2|M)…P(x_{100}|M)=<br> p^{40}(1-p)^{60},
由于最后的概率只和 p 有关,不同的 p 会导致不同的结果。那么如何求 p 的值呢?极大似然估计采取的方法是让这个样本结果出现的可能性最大,也就是使得p^{40}(1-p)^{60}值最大,那么我们就可以看成是p的方程,求导即可!令导数为0,即可求出 p=0.4。这便是极大似然估计的思想。
先验概率 P(Y=c_k) 的极大似然估计是 P(Y=c_k) = displaystylesum_{i=1}^NI(y_i=c_k) over displaystyle N,k=1,2,...,K, 设第 j 个特征 x^{(j)} 可能取值的集合为 {a_{j1}, a_{j2},...,a_{js_j}},条件概率 P(X^{(j)}=a_{jl}|Y=c_k) 的极大似然估计是
P(X^{(j)}=a_{jl}|Y=c_k) = displaystylesum_{i=1}^NI(x_i^{(j)} = a_{jl}, y_i=c_k) over displaystylesum_{i=1}^N(y_i=c_k),j=1,2,...,n;l=1,2,...,S_j;k=1,2,...,K, 式中,x_i^{(j)} 是第 i 个样本的第 j 个特征;a_{jl} 是第 j 个特征可能取的第 l 个值;I 为指示函数。
例子
下面我们通过一个例子来看下朴素贝叶斯分类器的工作过程,下表格包含了天气状况和是否去打高尔夫的关系,表中 outlook、temperature、humidity、windy 为特征,取值的集合分别为 {rainy, overcast, sunny},{hot, mild, cool},{high, normal},{false, true},playgolf 为类标记,取值为 {no, yes}。如果今天天气的状况为 (sunny, hot, normal, false),我们是否要去打高尔夫球呢?
outlook | temperature | humidity | windy | playgolf |
---|---|---|---|---|
rainy | hot | high | false | no |
rainy | hot | high | true | no |
overcast | hot | high | false | yes |
Sunny | mild | high | false | yes |
Sunny | cool | normal | false | yes |
Sunny | cool | normal | true | no |
overcast | cool | normal | true | yes |
rainy | mild | high | false | no |
rainy | cool | normal | false | yes |
Sunny | mild | normal | false | yes |
rainy | mild | normal | true | yes |
overcast | mild | high | true | yes |
overcast | hot | normal | false | yes |
Sunny | mild | high | true | no |
根据极大似然估计,容易计算下列概率:
P(playgolf=yes) = 9 over 14,P(playgolf=no) = 5 over 14 P(outlook=rainy|playgolf=yes) = 2 over 9,P(outlook=overcast|playgolf=yes) = 4 over 9,P(outlook=sunny|playgolf=yes) = 1 over 3, P(outlook=rainy|playgolf=no) = 3 over 5,P(outlook=overcast|playgolf=no) = 0 over 5,P(outlook=sunny|playgolf=no) = 2 over 5,
P(temperature=hot|playgolf=yes) = 2 over 9,P(temperature=mild|playgolf=yes) = 4 over 9,P(temperature=cool|playgolf=yes) = 1 over 3, P(temperature=hot|playgolf=no) = 2 over 5,P(temperature=mild|playgolf=no) = 2 over 5,P(temperature=cool|playgolf=no) = 1 over 5,
P(humidity=high|playgolf=yes) = 1 over 3,P(humidity=normal|playgolf=yes) = 2 over 3, P(humidity=high|playgolf=no) = 4 over 5,P(humidity=normal|playgolf=no) = 1 over 5,
P(windy=false|playgolf=yes) = 2 over 3,P(windy=true|playgolf=yes) = 1 over 3, P(windy=false|playgolf=no) = 2 over 5,P(windy=true|playgolf=no) = 3 over 5,
对给定的(sunny, hot, normal, false)计算:
P(playgolf=yes)P(outlook=sunny|playgolf=yes)P(temperature=hot|playgolf=yes)
P(humidity=Normal|playgolf=yes)P(windy=false|playgolf=yes)
= 9 over 14 1 over 3 2 over 9 2 over 3 2 over 3 approx 0.0211
P(playgolf=no)P(outlook=sunny|playgolf=no)P(temperature=hot|playgolf=no)
P(humidity=normal|playgolf=no)P(windy=false|playgolf=no)
= 5 over 14 2 over 5 2 over 5 1 over 5 2 over 5 approx 0.0046
由于 0.0211 > 0.0046,所以今天去打高尔夫。
贝叶斯估计
用极大似然估计可能会出现所要估计的概率值为 0 的情况。这时会影响到后验概率的计算结果,是分类产生偏差。解决这一问题的方法是采用贝叶斯估计。具体地,条件概率的贝叶斯估计是
P_lambda(X^{(j)}=a_{jl}|Y=c_k) = displaystylesum_{i=1}^NI(x_i^{(j)} = a_{jl}, y_i=c_k) lambda over displaystylesum_{i=1}^N(y_i=c_k) S_jlambda,式中lambda geq 0。 等价于在随机变量各个取值的频数上赋予一个正数 lambda > 0。当 lambda = 0 时就是极大似然估计。常取 lambda = 1,这时称为拉普拉斯平滑。同样,先验概率的贝叶斯估计是 P_lambda(Y=c_k) = displaystylesum_{i=1}^NI(y_i=c_k) lambda over displaystyle N Klambda, 使用贝叶斯估计计算上面例子中的概率为:
P(playgolf=yes) = 10 over 16,P(playgolf=no) = 6 over 16 P(outlook=rainy|playgolf=yes) = 1 over 4,P(outlook=Overcast|playgolf=yes) = 5 over 12, P(outlook=sunny|playgolf=yes) = 1 over 3,P(outlook=rainy|playgolf=no) = 1 over 2,P(outlook=Overcast|playgolf=no) = 1 over 8,P(outlook=sunny|playgolf=no) = 3 over 8,
P(temperature=hot|playgolf=yes) = 1 over 4,P(temperature=mild|playgolf=yes) = 5 over 12, P(temperature=cool|playgolf=yes) = 1 over 3,P(temperature=hot|playgolf=no) = 3 over 8,P(temperature=mild|playgolf=no) = 3 over 8,P(temperature=cool|playgolf=no) = 1 over 4,
P(humidity=high|playgolf=yes) = 4 over 11,P(humidity=normal|playgolf=yes) = 7 over 11, P(humidity=high|playgolf=no) = 5 over 7,P(humidity=normal|playgolf=no) = 2 over 7,
P(windy=false|playgolf=yes) = 7 over 11,P(windy=true|playgolf=yes) = 4 over 11, P(windy=false|playgolf=no) = 3 over 7,P(windy=true|playgolf=no) = 4 over 7,
对给定的(sunny, hot, normal, false)计算:
P(playgolf=Yes)P(outlook=sunny|playgolf=yes)P(temperature=hot|playgolf=yes)
P(humidity=normal|playgolf=yes)P(windy=false|playgolf=yes)
= 10 over 16 1 over 3 1 over 4 7 over 11 7 over 11 approx 0.0211
P(playgolf=no)P(outlook=sunny|playgolf=no)P(temperature=hot|playgolf=no)
P(humidity=normal|playgolf=no)P(windy=false|playgolf=no)
= 6 over 14 2 over 5 2 over 5 1 over 5 2 over 5 approx 0.0065
由于 0.0211 > 0.0065,所以今天去打高尔夫。
鸢尾花分类
- 导入库
import pandas as pd
from sklearn import metrics
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler
from sklearn.naive_bayes import GaussianNB
- 读取数据
iris_data = pd.read_csv('Iris.csv')
- 将数据集分成训练数据集和测试数据集
y = iris_data['Species']
x = iris_data[['SepalLengthCm', 'SepalWidthCm', 'PetalLengthCm', 'PetalWidthCm']]
train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.3, random_state=33)
- 使用训练数据对模型进行训练,并使用得到的模型对测试数据进行测试。
gnb = GaussianNB()
gnb.fit(train_x, train_y)
predict_y = gnb.predict(test_x)
- 输出测试结果
print("NB准确率: %.4lf" % accuracy_score(test_y, predict_y))
- output
NB准确率: 0.9556
糖尿病的预测
糖尿病的预测是根据患者的一些信息来预测患者是否有糖尿病。下面我们通过 Scikit-learn 中的 $k$-NN 算法对患者是否患有糖尿病进行预测。
- 导入库
import pandas as pd
from sklearn import metrics
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler
- 读取数据
diabetes_data = pd.read_csv('diabetes.csv')
- 对数据进行清洗,对于某列数据中的0值,使用这一列值的平均值进行填充。
diabetes_data.replace({
'Glucose': 0,
'BloodPressure': 0,
'SkinThickness': 0,
'BMI': 0,
'Insulin': 0
}, np.NaN, inplace=True)
glucose_mean = diabetes_data['Glucose'].mean()
blood_pressure_mean = diabetes_data['BloodPressure'].mean()
skin_thickness_mean = diabetes_data['SkinThickness'].mean()
bmi_mean = diabetes_data['BMI'].mean()
insulin_mean = diabetes_data['Insulin'].mean()
diabetes_data['Glucose'].replace(np.NaN, glucose_mean, inplace=True)
diabetes_data['BloodPressure'].replace(np.NaN, blood_pressure_mean, inplace=True)
diabetes_data['SkinThickness'].replace(np.NaN, skin_thickness_mean, inplace=True)
diabetes_data['BMI'].replace(np.NaN, bmi_mean, inplace=True)
diabetes_data['Insulin'].replace(np.NaN, insulin_mean, inplace=True)
- 将数据集分成训练数据集和测试数据集。
y = diabetes_data['Outcome']
x = diabetes_data[['Pregnancies', 'Glucose', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI', 'DiabetesPedigreeFunction', 'Age']]
train_x, test_x, train_y, test_y = train_test_split(x, y, test_size=0.2, random_state=0)
- 对数据进行规范化。
sc_x = StandardScaler()
train_x = sc_x.fit_transform(train_x)
test_x = sc_x.fit_transform(test_x)
- 使用训练数据对模型进行训练,并使用得到的模型对测试数据进行测试。
gnb = GaussianNB()
gnb.fit(train_x, train_y)
predict_y = gnb.predict(test_x)
- 输出测试结果
print("NB 准确率: %.4lf" % accuracy_score(test_y, predict_y))
- output
NB 准确率: 0.7835
总结
本文阐述了朴素贝叶斯法的理论知识,并通过两个例子介绍了朴素贝叶斯法的实际应用。