(六十四)朴素贝叶斯算法
作者:互联网
贝努利朴素贝叶斯
朴素贝叶斯算法会把数据集中的各个特征看作完全独立的,而不考虑特征之间的相关关系。贝努利朴素贝叶斯(Bernoulli Naive Bayes)这种方法比较适合于服从贝努利分布的数据集,即每个特征都只有两个类型。
以一个简单的应用为例说明贝叶斯定理以及贝努利贝叶斯算法:过去的7天当中,有5天下雨,2天没有下雨,用0代表没有下雨,而1代表下雨,我们可以用一个数组来表示:y = [1, 1, 0, 0, 1, 1, 1]。而在这7天当中,还有另外一些信息,包括刮北风、闷热、多云,以及天气预报给出的信息,如下表所示(其中0代表否,1代表是):
日期 | 刮北风 | 闷热 | 多云 | 天气预报有雨 | 实际是否下雨 |
---|---|---|---|---|---|
1 | 0 | 1 | 0 | 1 | 1 |
2 | 1 | 1 | 1 | 0 | 1 |
3 | 0 | 1 | 1 | 0 | 0 |
4 | 0 | 0 | 0 | 1 | 0 |
5 | 0 | 1 | 1 | 0 | 1 |
6 | 0 | 1 | 0 | 1 | 1 |
7 | 1 | 0 | 0 | 1 | 1 |
假如我们要预测某一天基于刮北风、闷热、非多云以及天气预报有雨的信息下(x=[1,1,0,1]),实际是否会下雨,根据贝叶斯公式P(A|B) = P(B|A)·P(A) / P(B),以及以下类先验概率和条件概率:
- P(下雨)=5/7,P(不下雨)=2/7
- P(刮北风|下雨)=2/5, P(刮北风|不下雨)=(0+1)/(2+2)=1/4(拉普拉斯修正:分子+1,分母第二个2为刮北风特征的类别数)
- P(闷热|下雨)=4/5,P(闷热|不下雨)=1/2
- P(非多云|下雨)=3/5,P(非多云|不下雨)=1/2
- P(预报有雨|下雨)=3/5, P(预报有雨|不下雨)=1/2
- 特征之间相互独立时,若d为特征数目,xi为x在第i个特征上的取值,则有:
令事件m=“刮北风、闷热、非多云以及天气预报有雨”,可得:
- P(下雨|m) = P(下雨)·P(刮北风|下雨)·P(闷热|下雨)·P(非多云|下雨)·P(预报有雨|下雨) / P(m) = 0.0823 / P(m)
- P(不下雨|m) = P(不下雨)·P(刮北风|不下雨)·P(闷热|不下雨)·P(非多云|不下雨)·P(预报有雨|不下雨) / P(m) = 0.009 / P(m)
该天下雨的概率是不下雨的9倍左右,因此,朴素贝叶斯分类器应该将x=[1,1,0,1]这一天预测为下雨。下面我们来验证一下:
import numpy as np
from sklearn.naive_bayes import BernoulliNB,GaussianNB,MultinomialNB
from sklearn.preprocessing import MinMaxScaler,KBinsDiscretizer
from sklearn.model_selection import train_test_split
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([1, 1, 0, 0, 1, 1, 1])
y = np.array([1, 1, 0, 0, 1, 1, 1])
clf = BernoulliNB()
clf.fit(X, y)
#要进行预测的这一天,刮北风、闷热、非多云以及天气预报有雨
Next_Day = [[1, 1, 0, 1]]
print(clf.predict(Next_Day),clf.predict_proba(Next_Day))
[1] [[0.11115226 0.88884774]]
可以看出,朴素贝叶斯分类器把这一天放到了会下雨的分类当中,下雨的概率达到了89%。虽然朴素贝叶斯是相当好的分类器,但scikit-learn官网文档中提到,其对于预测具体的数值并不是很擅长,因此Predict_proba给出的预测概率不要太当真。
高斯朴素贝叶斯
高斯朴素贝叶斯(Gaussian Naive Bayes),是假设样本的特征服从正态分布时所用的算法,可以应用于任何连续数值型的数据集当中。
以内置的威斯康星乳腺肿瘤数据集来进行试验,它包括569个病例的数据样本,每个样本具有30个特征值,而样本共分为两类:分别是恶性(Malignant)和良性(Benign)。
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
X, y = cancer.data, cancer.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=38)
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
从结果可以看到,GaussianNB在训练集和测试集的得分都非常不错,均接近95%。
在机器学习中,有一个概念称为学习曲线(learning curve),指的是随着数据集样本数量的增加,模型得分变化情况。需要使用到的函数有:
- sklearn.model_selection.ShuffleSplit,又叫交叉验证生成器,用于将样本集合随机“打散”后划分为训练集、测试集。主要参数n_splits为划分训练集、测试集的次数,默认为10;test_size和train_size为测试集和训练集的比例或个数。
- sklearn.model_selection.learning_curve,确定交叉验证的针对不同训练集大小的训练和测试分数。主要参数有estimator:实现“ fit”和“ predict”方法的对象类型;train_sizes:训练示例的相对或绝对数量,将用于生成学习曲线的横坐标;cv:确定交叉验证拆分策略,一般为ShuffleSplit函数。具体可参考Zen of Data Analysis:sklearn中的学习曲线learning_curve函数。
下面绘制高斯朴素贝叶斯在上述肿瘤数据集中的学习曲线,输入代码如下:
from sklearn.model_selection import learning_curve
from sklearn.model_selection import ShuffleSplit#随机拆分工具
import matplotlib.pyplot as plt
def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None,
n_jobs=1, train_sizes=np.linspace(.1, 1.0, 5)):
#这里的train_sizes是相对数量
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_sizes是绝对数量
train_scores_mean = np.mean(train_scores, axis=1)#对100次的训练数据得分进行平均
test_scores_mean = np.mean(test_scores, axis=1)#对100次的测试数据得分进行平均
plt.grid()
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()
由于train_sizes = np.linspace(.1, 1.0, 5) = array([0.1 , 0.325, 0.55 , 0.775, 1. ]),test_size = 0.2,因此对应的5个训练集大小为[569×0.8×0.1=45, … , 569×0.8×1=455],即横坐标对应的[45, 147, 250, 352, 455]共5种size,对应生成5个测试集size;ShuffleSplit函数中n_splits=100对每一size随机划分训练集、测试集100次,用GaussianNB训练,得到5×100的train_scores得分数据和5×100的test_scores得分数据,然后分别对这5个size的模型得分求平均后画图即可。
从图中可以看到,在训练数据集中,随着样本量的增加,模型的得分是逐渐降低的。这是因为随着样本数量增加,模型要拟合的数据越来越多,难度也越来越大。而模型的交叉验证得分的变化相对没有那么明显,从10个样本左右一直到接近500个样本为止,分数一直在0.94附近浮动,说明高斯朴素贝叶斯在预测方面对于样本数量的要求并没有那么苛刻。所以如果你的样本数量比较少的话,可以考虑使用朴素贝叶斯算法来进行建模。
多项式朴素贝叶斯
多项式实验中的实验结果都很具体,它所涉及的特征往往是次数,频率,计数,出现与否这样的离散的正整数,因此,sklearn中的多项式朴素贝叶斯(Multinomial Naive Bayes)不接受负值的输入。由于这样的特性,多项式朴素贝叶斯的特征矩阵经常是稀疏矩阵,并且它经常被用于文本分类。
sklearn.naive_bayes.MultinomialNB (alpha=1.0,fit_prior=True, class_prior=None),其中:
- alpha : 默认为1.0,拉普拉斯或利德斯通平滑的参数λ;
- fit_prior:默认为True,是否学习先验概率P(Y=c)。如果设置为false,则所有的样本类别输出都有相同的类别先验概率。即认为每个标签类出现的概率是1/k,k为classes的个数;
- class_prior:形似数组的结构,结构为(n_classes,),默认为None,类的先验概率P(Y=c)。如果没有给出具体的先验概率则自动根据数据来进行计算。
- 若不输入第三个参数class_prior让MultinomialNB自己从训练集样本来计算先验概率,此时的先验概率为P(Y=Ck)=mk/m。其中m为训练集样本总数量,mk为输出为第k个类别的训练集样本数。总结如下:
下面使用make_blobs生成一个两类、两特征、一共500个样本的数据集:
from sklearn.datasets import make_blobs
#建立数据集
class_1 = 200
class_2 = 300
centers = [[0.0, 0.0], [4.0, 4.0]] #设定两个类别的中心
clusters_std = [1, 1] #设定两个类别的方差
X, y = make_blobs(n_samples=[class_1, class_2],centers=centers,\
cluster_std=clusters_std, random_state=0, shuffle=False)
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=0)
plt.scatter(X[:,0],X[:,1],c=y,cmap=plt.cm.cool,edgecolors='k')#注意c参数和cmap的用法
考虑到多项式朴素贝叶斯的性质,为了提高模型得分,需要将数据转换成离散、非负的。先考虑使用sklearn. preprocessing. MinMaxScaler()将数据归一化到[0,1],再进行多项式朴素贝叶斯建模:
mms = MinMaxScaler().fit(Xtrain)#归一化
Xtrain_ = mms.transform(Xtrain)
Xtest_ = mms.transform(Xtest)
mnb = MultinomialNB().fit(Xtrain_,Ytrain)
#根据数据获取的每个标签类的先验概率
np.exp(mnb.class_log_prior_)
Out[44]: array([0.41142857, 0.58857143])
mnb.score(Xtest_,Ytest)
Out[45]: 0.6266666666666667
模型得分比较低,是因为虽然数据均为非负,但是多项式朴素贝叶斯更擅长离散/分类型变量的建模,可以考虑将[0,1]内的连续数据离散化再建模。preprocessing.KBinsDiscretizer可生成k个等宽箱的离散化特征,默认输出onehot编码的稀疏矩阵:
kbs = KBinsDiscretizer(n_bins=10, encode='onehot').fit(Xtrain)
Xtrain_ = kbs.transform(Xtrain)
Xtest_ = kbs.transform(Xtest)
Xtest_
Out[49]:
<150x20 sparse matrix of type '<class 'numpy.float64'>'
with 300 stored elements in Compressed Sparse Row format>
mnb2 = MultinomialNB().fit(Xtrain_, Ytrain)
mnb2.score(Xtest_,Ytest)
Out[50]: 0.9933333333333333
可以发现,同样的数据,如果对连续变量进行离散化分箱处理,多项式贝叶斯的效果会大大提高。
总结
贝努利朴素贝叶斯适合于二项式分布的数据集,而多项式朴素贝叶斯适合计数类型的数据集,即非负、离散数值的数据集,而高斯朴素贝叶斯适用的面就要广得多,它可以应用于任何连续数值型的数据集当中,如果是符合正态分布的数据集的话,高斯朴素贝叶斯模型的得分会更高。
参考文献
段小手《深入浅出Python机器学习》,清华大学出版社
https://blog.csdn.net/gracejpw/article/details/102381511
https://blog.csdn.net/gracejpw/article/details/102370364
标签:plt,六十四,下雨,贝叶斯,算法,train,test,朴素 来源: https://blog.csdn.net/hzk427/article/details/111597340