朴素贝叶斯对鸢尾花有监督分类

参考文献

引子

假如现在有一些鸢尾花的特征,也有其对应的标签(如下图所示),如何训练出一个模型,使得任意给一组标签,模型都能给出一个标签预测呢?

这是一个典型的有监督分类的问题,今天我们使用朴素贝叶斯模型来解决这个问题。

假设鸢尾花的特征向量表示为$\boldsymbol x = (x_1, x_2, x_3, x_4)$,可能的分类为$c_i(i取值为0、1、2,即有三个类别)$,利用贝叶斯公式求解在$\boldsymbol x$条件下,分类结果为$c_i$的概率。

这个式子告诉我们,后验概率$P(c_i|\boldsymbol x)$为先验概率$P(c_i)$再乘上一个因子$\frac{P(\boldsymbol x|c_i)}{P(\boldsymbol x)}$,这个因子是否大于1决定了起到的作用是抑制还是促进。这个很好理解,比如没有任何信息的时候,可以判断一个官为贪官的概率为0.5(先验),再知道该官员财产大于一千万后,则判断该官员为贪官的概率为0.8(后验)。

由于这个因子的分母不依赖于类别,当特征值给定时分母可以认为是一个常数,不会影响到最终的分类结果。真正难以计算的是因子的分子类条件概率$P(\boldsymbol x|c_i)$是所有属性上的联合分布。基于有限的训练样本直接计算联合概率,在计算上会遭遇组合爆炸,在数据上会遭遇样本稀疏的问题。这个也好理解,比如假设一个分类器有4个特征,每个特征有10个特征值,则这四个特征的联合概率分布是4维的,可能的情况就有$10^4$种。

为了解决这个问题,”朴素”的条件独立性假设开始发挥作用了:假设每个特征,对于其他特征是独立的。则(1.1)式进一步推导成为

其中$Z$只依赖$\boldsymbol x$,$n$代表特征个数。于是多维的联合概率分布降成了多个单随机变量概率分布的乘积。

相应的分类器则可定义为

其中$c_i$是变量,概率值最大时的$c_i$,即为分类结果。

实战

  首先我们使用datasets模块导入鸢尾花数据集,并将字典类型的数据集转化为DataFrame类型,方便进行行列索引操作。

1
2
3
4
5
6
from sklearn.datasets import load_iris
import pandas as pd
dict_ds = load_iris()
df_ds = pd.DataFrame(dict_ds.data, columns=dict_ds.feature_names)
df_ds['category'] = dict_ds.target #添加类别标签
print(df_ds)

  打印出前五行和后五行

1
2
3
4
5
6
7
8
9
10
11
12
sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)  category
0 5.1 3.5 1.4 0.2 0
1 4.9 3.0 1.4 0.2 0
2 4.7 3.2 1.3 0.2 0
3 4.6 3.1 1.5 0.2 0
4 5.0 3.6 1.4 0.2 0
.. ... ... ... ... ...
145 6.7 3.0 5.2 2.3 2
146 6.3 2.5 5.0 1.9 2
147 6.5 3.0 5.2 2.0 2
148 6.2 3.4 5.4 2.3 2
149 5.9 3.0 5.1 1.8 2

  每条记录包含四个特征,分别是:花萼长度,花萼宽度,花瓣长度,花瓣宽度。标签包含3个类别:0代表山鸢尾,1代表变色鸢尾,2代表维吉尼亚鸢尾。我们将利用这四个特征和分类标签来进行贝叶斯分类学习。

  在sklearn中,一共有三个朴素贝叶斯的分类算法类,分别是先验为高斯分布的GaussianNB,先验为多项式分布的MultinomialNB和先验为伯努利分布的BernoulliNB。由于鸢尾花样本特征的分布是基本连续的,所以我们使用GaussianNB作为分类算法。这个类的主要参数如下所示

priors:先验概率大小,如果没有给定,模型则根据样本数据自己计算(利用极大似然法)。
var_smoothing:可选参数,所有特征的最大方差

  属性有

class_prior_:每个样本的概率
class_count:每个类别的样本数量
classes_:分类器已知的标签类型
theta_:每个类别中每个特征的均值
sigma_:每个类别中每个特征的方差
epsilon_:方差的绝对加值方法

  方法和其他模型类似

fit(X,Y):在数据集(X,Y)上拟合模型。
get_params():获取模型参数。
predict(X):对数据集X进行预测。
predict_log_proba(X):对数据集X预测,得到每个类别的概率对数值。predict_proba(X):对数据集X预测,得到每个类别的概率。
score(X,Y):得到模型在数据集(X,Y)的得分情况。

  了解了这些参数之后,我们就可以尝试训练了。首先划分数据集前80%为训练集,后20%为测试集。然后实例化GaussianNB,进行训练。

1
2
3
4
5
6
7
# train_test_split (*arrays,test_size, train_size, rondom_state=None, shuffle=True, stratify=None)
# 建议使用此函数划分数据集,默认是打乱了原始数据的.
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
train_X, test_X, train_y, test_y = train_test_split(df_ds.values[:, :-1], df_ds.values[:, -1], test_size=0.2)
gnb = GaussianNB()
gnb.fit(train_X, train_y)

  训练过后,使用测试集来对模型进行检验,计算正确率与得分。
1
2
3
4
5
6
7
8
9
10
11
import matplotlib.pyplot as plt
%matplotlib inline
y = gnb.predict(test_X)
plt.scatter([i for i in range(len(test_X))], test_y, color="black", marker='v')
plt.scatter([i for i in range(len(test_X))], y, color="red", marker=".")
plt.yticks([0, 1, 2])
plt.xlabel("sample")
plt.ylabel("category")
plt.legend(['actual', 'predict'], bbox_to_anchor=(1, 0.9))
plt.savefig("predict0.2.jpg", dpi=800)
print("准确率:{:.2%}".format(gnb.score(test_X, test_y)))

  输出为

准确率:96.67%

predict0.2

  可以看出来朴素贝叶斯的分类能力还是比较强的,准确率达到96%以上,从图中也可以看的出来,30个样本中,只预测错误了一个。

理论

(未完,待续)