ML–朴素贝叶斯
朴素贝叶斯(Naive Bayes)算法是一种基于贝叶斯理论的有监督学习算法。之所以说"朴素",是因为这个算法是基于样本特征之间互相独立的"朴素"假设。正因为如此,由于不用考虑样本特征之间的关系,朴素贝叶斯分类器的效率是非常高的
主要涉及的知识点:
- 贝叶斯定理简介
- 朴素贝叶斯的简单应用
- 贝努利朴素贝叶斯,高斯朴素贝叶斯和多项式朴素贝叶斯
- 朴素贝叶斯实例–判断肿瘤是良性还是恶性
一.朴素贝叶斯基本概念
贝叶斯(Thomas Bayes)是一个英国数学家,贝叶斯在数学方面主要研究概率论,他创立了贝叶斯统计理论,该理论对现代概率论和数理统计又有很重要的作用
1.贝叶斯定理
在一个傍晚,准备回家。问题来了:要不要带伞呢?
已知:
天气预报说今日降水概率为50%---P(A)
晚高峰堵车的概率为80%---P(B)
如果下雨,晚高峰堵车的概率是95%---P(B|A)
则根据贝叶斯定理:
求得下雨的概率为:0.5*0.95/0.8=0.594
所以果断拿上了雨伞
2.朴素贝叶斯的简单应用
过去的7天当中,有3天下雨,4天没有下雨。用0代表灭有下雨,而1代表下雨,我们可以用一个数组来表示:
y=[0,1,1,0,1,0,0]
而在这7天当中,还有另外一些信息,包括刮北风,闷热,多云,以及天气预报给出的信息,如下表:
刮北风 | 闷热 | 多云 | 天气预报有雨 | |
---|---|---|---|---|
第1天 | 否 | 是 | 否 | 是 |
第2天 | 是 | 是 | 是 | 否 |
第3天 | 否 | 是 | 是 | 否 |
第4天 | 否 | 否 | 否 | 是 |
第5天 | 否 | 是 | 是 | 否 |
第6天 | 否 | 是 | 否 | 是 |
第7天 | 是 | 否 | 否 | 是 |
同样地,我们用0代表否,1代表是,可以得到另外一个数组:
X=[0,1,0,1],[1,1,1,0],[0,1,1,0],[0,0,0,1],[0,1,1,0],[0,1,0,1],[1,0,0,1]
我们来看一下数据的关系:
# 导入numpy
import numpy as np
# 将X,y赋值为np数组
X=np.array([
[0,1,0,1],
[1,1,1,0],
[0,1,1,0],
[0,0,0,1],
[0,1,1,0],
[0,1,0,1],
[1,0,0,1]
])
y=np.array([0,1,1,0,1,0,0])
# 对不同分类计算每个特征为1的数量
counts={}
for label in np.unique(y):
counts[label]=X[y==label].sum(axis=0)
# 打印计数结果
print("Feature counts:
{}".format(counts))
Feature counts:
{0: array([1, 2, 0, 4]), 1: array([1, 3, 3, 0])}
[结果分析] 下面解释一下这个结果的意思,当y=0时,也就在没有下雨的4天当中,有1天刮了北风,有2天比较闷热,而没有出现多云的情况,但这4天天气预报全部播放有雨。同时,在y=1时,也就是在下雨的3天当中,有1天刮了北风,3天全都比较闷热,且3天全部出现了多云的现象,有意思的是,这3天的天气预报都没有播报有雨
那么对于朴素贝叶斯来说,它会根据上述的计算来进行推理。它会认为,如果某一天天气预报没有播报有雨,但出现了多云的情况,它会倾向于把这一天放到"下雨"这个分类中
# 导入贝努利贝叶斯
from sklearn.naive_bayes import BernoulliNB
# 使用贝努利贝叶斯拟合数据
clf=BernoulliNB()
clf.fit(X,y)
# 要进行预测的这一天,没有刮北风,也不闷热,但是多云,天气预报没有说有雨
Next_Day=[[0,0,1,0]]
pre=clf.predict(Next_Day)
if pre==[1]:
print("要下雨了,快回家")
else:
print("放心,没有雨")
要下雨了,快回家
那么如果有另外一天,刮了北风,而且很闷热,但云量不多,同时天气预报说有雨
# 假设另外一天的数据如下
Another_Day=[[1,1,0,1]]
# 使用训练好的模型进行预测
pre2=clf.predict(Another_Day)
if pre2==[1]:
print("要下雨了,快回家")
else:
print("放心,没有雨")
放心,没有雨
现在大家可能很想知道朴素贝叶斯给出的预测准确率怎么样?我们可以用predict_proba
方法来测试一下:
# 模型预测分类的概率
print(clf.predict_proba(Next_Day))
print(clf.predict_proba(Another_Day))
[[ 0.13848881 0.86151119]]
[[ 0.92340878 0.07659122]]
[结果分析] 我们所预测的第一天,不下雨的概率大约是13.8%,而下雨的概率是86.2%。所预测的第二天,不下雨的概率大约是92.3%,而下雨的概率是7.7%
注意 不要太乐观!如果大家在scikit-learn
官网上查看文档,会发现一段很搞笑的描述—虽然朴素贝叶斯是相当好的分类器,但对于预测具体的数值并不是很擅长,因此predict_proba
给出的预测概率,大家也不要太当真
二.朴素贝叶斯算法的不同方法
朴素贝叶斯算法包含多种方法,scikit-learn
中,朴素贝叶斯有三种方法,分别是贝努利朴素贝叶斯(Bernoulli Naive Bayes)
,高斯贝叶斯(Gaussian Naive Bayes)
和多项式朴素贝叶斯(Multinomial Naive Bayes)
1.贝努利朴素贝叶斯
在上面的例子当中,我们使用了朴素贝叶斯算法中的一种方法,称为贝努利朴素贝叶斯(Bernoulli Naive Bayes)
,这种方法比较适合于符合贝努利分布的数据集,贝努利分布也被称为"二项分布"或者是"0-1分布"。比如我们进行抛硬币的游戏,硬币落下来只有两种可能的结果:正面或者反面
在刚才我们举的例子当中,数据集中的每个特征都只有0和1两个数值,这种情况下,贝努利贝叶斯的表现还不错。但如果我们用更复杂的数据集,看看效果:
# 导入数据集生成工具
from sklearn.datasets import make_blobs
# 导入数据集拆分工具
from sklearn.model_selection import train_test_split
# 生成样本数量为500,分类数为5的数据集
X,y=make_blobs(n_samples=500,centers=5,random_state=8)
# 将数据集拆分成训练集和训练集
X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=8)
# 使用贝努利贝叶斯拟合数据
nb=BernoulliNB()
nb.fit(X_train,y_train)
# 模型得分
print("模型得分:{:.3f}".format(nb.score(X_test,y_test)))
为了加大难度,我们令样本数量为500,而分类的数量为5个,也就是centers
参数等于5,运行代码:
模型得分:0.544
下面我们通过图像来了解一下贝努利朴素贝叶斯的工作过程:
# 导入画图工具
import matplotlib.pyplot as plt
# 限定横轴与纵轴的最大值
x_min,x_max=X[:,0].min()-0.5,X[:,0].max()+0.5
y_min,y_max=X[:,1].min()-0.5,X[:,1].max()+0.5
# 用不同的背景色表示不同的分类
xx,yy=np.meshgrid(np.arange(x_min,x_max,.02),np.arange(y_min,y_max,.02))
z=nb.predict(np.c_[(xx.ravel(),yy.ravel())]).reshape(xx.shape)
plt.pcolormesh(xx,yy,z,cmap=plt.cm.Pastel1)
# 将训练集和测试集用散点图表示
plt.scatter(X_train[:,0],X_train[:,1],c=y_train,cmap=plt.cm.cool,edgecolor='k')
plt.scatter(X_test[:,0],X_test[:,1],c=y_test,cmap=plt.cm.cool,marker='*',edgecolor='k')
plt.xlim(xx.min(),xx.max())
plt.ylim(yy.min(),yy.max())
plt.title("Classifier:BernoulliNB")
plt.show()
[结果分析] 我们可以看到贝努利朴素贝叶斯的模型十分简单,它分别在横轴等于0和纵轴等于0的位置画了两条直线,再用这两条直线形成的4个象限对数据进行分类。这是因为我们使用了贝努利朴素贝叶斯的默认参数binarize=0.0
,所以模型对于数据的判断是,如果特征1大于或等于0,且特征2大于或等于0,则将数据归为一类;如果特征1小于0,且特征2也小于0,则归为另一类而其余的数据全部归为第三类,难怪模型的得分这么差
2.高斯朴素贝叶斯
高斯朴素贝叶斯,顾名思义,是假设样本的特征符合高斯分布,或者说符合正态分布时所用的算法。接下来我们尝试用高斯朴素贝叶斯对刚刚生成的数据集进行拟合,看看结果:
# 导入高斯贝叶斯
from sklearn.naive_bayes import GaussianNB
# 使用高斯贝叶斯拟合数据
gnb=GaussianNB()
gnb.fit(X_train,y_train)
# 打印模型得分
print("模型得分:{:.3f}".format(gnb.score(X_test,y_test)))
模型得分:0.968
下面我们再次用图像来进行演示,以便了解高斯朴素贝叶斯的工作过程:
# 用不同色块来表示不同的分类
z=gnb.predict(np.c_[(xx.ravel(),yy.ravel())]).reshape(xx.shape)
plt.pcolormesh(xx,yy,z,cmap=plt.cm.Pastel1)
# 将训练集和测试集用散点图表示
plt.scatter(X_train[:,0],X_train[:,1],c=y_train,cmap=plt.cm.cool,edgecolor='k')
plt.scatter(X_test[:,0],X_test[:,1],c=y_test,cmap=plt.cm.cool,marker='*',edgecolor='k')
# 设定横轴纵轴的范围
plt.xlim(xx.min(),xx.max())
plt.ylim(yy.min(),yy.max())
plt.title("Classifier:GaussianNB")
plt.show()
事实上,高斯朴素贝叶斯也确实是能够胜任大部分的分类任务,这是因为在自然科学和社会科学领域,有大量的现象都是呈现正态分布的状态
3.多项式朴素贝叶斯
多项式朴素贝叶斯,从名字也可以推断出它主要是用于拟合多项式分布的数据集。可能多项式分布相对于二项式分布和高斯分布来说,我们接触得少一些
我们知道硬币只有两个面:正面和反面。而骰子有6个面,因此每掷一次骰子,结果都可能是从1–6这6个数字,如果我们掷n次骰子,而每个面朝上的次数的分布情况,就是一个多项式分布:
# 导入多项式朴素贝叶斯
from sklearn.naive_bayes import MultinomialNB
# 用多项式朴素贝叶斯拟合数据
mnb=MultinomialNB()
mnb.fit(X_train,y_train)
mnb.score(X_test,y_test)
注意 上面这段代码和我们使用贝努利朴素贝叶斯或是高斯朴素贝叶斯看起来没有什么区别,但是这样使用多项式朴素贝叶斯是错误的
[结果分析] 提示信息,输入的X值必须是非负的,这样的话,我们需要对数据进行一下预处理才行
# 导入多项式朴素贝叶斯
from sklearn.naive_bayes import MultinomialNB
# 导入数据预处理工具MinMaxScaler
from sklearn.preprocessing import MinMaxScaler
# 使用MinMaxScaler对数据进行预处理,使数据全部为非负值
scaler=MinMaxScaler()
scaler.fit(X_train)
X_train_scaled=scaler.transform(X_train)
X_test_scaled=scaler.transform(X_test)
# 用多项式朴素贝叶斯拟合数据
mnb=MultinomialNB()
mnb.fit(X_train_scaled,y_train)
mnb.score(X_test,y_test)
0.23999999999999999
如果我们用图形来表示的话,也可以直观地看出多项式朴素贝叶斯并不适合用来拟合这个数据集:
# 用不同色块来表示不同的分类
z=mnb.predict(np.c_[(xx.ravel(),yy.ravel())]).reshape(xx.shape)
plt.pcolormesh(xx,yy,z,cmap=plt.cm.Pastel1)
# 将训练集和测试集用散点图表示
plt.scatter(X_train[:,0],X_train[:,1],c=y_train,cmap=plt.cm.cool,edgecolor='k')
plt.scatter(X_test[:,0],X_test[:,1],c=y_test,cmap=plt.cm.cool,marker='*',edgecolor='k')
# 设定横轴纵轴的范围
plt.xlim(xx.min(),xx.max())
plt.ylim(yy.min(),yy.max())
plt.title("Classifier:MultinomialNB")
plt.show()
[结果分析] 从图看出多项式朴素贝叶斯所进行的分类确实比贝努利朴素贝叶斯的还要差一些,大部分数据点都被放到了错误的分类中
这是因为,多项式朴素贝叶斯只适合用来非负离散数值特征进行分类,典型的例子就是对转化为向量后的文本数据进行分类
注意 在本例中,我们使用了MinMaxScaler
对数据进行预处理,MinMaxScaler
的作用是将数据集中的特征值全部转化为0–1
三.朴素贝叶斯实战–判断肿瘤是良性还是恶性
我们会用到一个来自真实世界的数据集—威斯康星乳腺肿瘤数据集
1.对数据集进行分析
威斯康星乳腺肿瘤数据集是一个非常经典的用于医疗病情分析的数据集,它包括569个病例的数据样本,每个样本具有30个特征值,而样本共分为两类:分别是恶性(Malignant)和良性(Benign)
# 导入威斯康星乳腺肿瘤数据集
from sklearn.datasets import load_breast_cancer
cancer=load_breast_cancer()
# 打印数据集键值
print(cancer.keys())
dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename'])
下面我们来看一个分类的名称和特征的名称:
print("肿瘤的分类:",cancer['target_names'])
print("
肿瘤的特征:",cancer['feature_names'])
肿瘤的分类: ['malignant' 'benign']
肿瘤的特征: ['mean radius' 'mean texture' 'mean perimeter' 'mean area'
'mean smoothness' 'mean compactness' 'mean concavity'
'mean concave points' 'mean symmetry' 'mean fractal dimension'
'radius error' 'texture error' 'perimeter error' 'area error'
'smoothness error' 'compactness error' 'concavity error'
'concave points error' 'symmetry error' 'fractal dimension error'
'worst radius' 'worst texture' 'worst perimeter' 'worst area'
'worst smoothness' 'worst compactness' 'worst concavity'
'worst concave points' 'worst symmetry' 'worst fractal dimension']
2.使用高斯朴素贝叶斯进行建模
# 将数据集的数值和分类目标复制给X和y
X,y=cancer.data,cancer.target
# 使用数据集拆分工具拆分为训练集和测试集
X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=38)
print("训练集数据形态:",X_train.shape)
print("测试集数据形态:",X_test.shape)
训练集数据形态: (426, 30)
测试集数据形态: (143, 30)
下面我们开始用高斯朴素贝叶斯对训练数据集进行拟合,输入代码如下:
# 使用高斯朴素贝叶斯拟合数据
gnb=GaussianNB()
gnb.fit(X_train,y_train)
print("训练集得分:{:.3f}".format(gnb.score(X_train,y_train)))
print("测试集得分:{:.3f}".format(gnb.score(X_test,y_test)))
训练集得分:0.948
测试集得分:0.944
下面我们随便用其中一个样本(如第310个样本)让模型进行一下预测,看是否可以分到正确的分类中:
print("模型预测的分类是:{}".format(gnb.predict([X[310]])))
print("样本的正确分类是:",y[310])
模型预测的分类是:[1]
样本的正确分类是: 1
[结果分析] 模型对第310个样本所进行的分类和正确的分类完全一致,都是分类1,也就是说,这个样本的肿瘤是一个良性的肿瘤
3.高斯朴素贝叶斯的学习曲线
在机器学习中,有一个概念称为学习曲线(learning curve)
,指的是随着数据集样本数量的增加,模型的得分变化情况
# 导入学习曲线库
from sklearn.model_selection import learning_curve
# 导入随机拆分工具
from sklearn.model_selection import ShuffleSplit
# 定义一个函数绘制学习曲线
def plot_learning_curve(estimator,title,X,y,ylim=None,cv=None,n_jobs=1,train_sizes=np.linspace(.1,1.0,5)):
plt.figure()
plt.title(title)
if ylim is not None:
plt.ylim(*ylim)
# 设定横轴标签
plt.xlabel("Training examples")
plt.ylabel("Score")
train_sizes,train_scores,test_scores=learning_curve(estimator,X,y,cv=cv,n_jobs=n_jobs,train_sizes=train_sizes)
train_scores_mean=np.mean(train_scores,axis=1)
test_scores_mean=np.mean(test_scores,axis=1)
plt.plot(train_sizes,train_scores_mean,'o-',color='r',label='Training score')
plt.plot(train_sizes,test_scores_mean,'o-',color='g',label='Cross-validation score')
plt.legend(loc='lower right')
return plt
title="Learning Curves (Naive Bayes)"
cv=ShuffleSplit(n_splits=100,test_size=0.2,random_state=0)
estimator=GaussianNB()
plot_learning_curve(estimator,title,X,y,ylim=(0.9,1.01),cv=cv,n_jobs=4)
plt.show()