朴素贝叶斯
一、概述
1.1 概率分类器
1. 在许多分类算法应用中,特征和标签之间的关系并非是决定性的。比如说,我们想预测一个人究竟是否会在泰坦尼克号海难中生存下来,那我们可以建一棵决策树来学习我们的训练集。在训练中,其中一个人的特征为:30岁,男,普通舱,他最后在泰坦尼克号海难中去世了。当我们测试的时候,我们发现有另一个人的特征也为:30岁,男,普通舱。基于在训练集中的学习,我们的决策树必然会给这个人打上标签:去世。然而这个人的真实情况一定是去世了吗?并非如此。
2. 算法得出的结论,永远不是100%确定的,更多的是判断出了一种“样本的标签更可能是某类的可能性”,而非一种“确定”。我们通过某些规定,比如说,在决策树的叶子节点上占比较多的标签,就是叶子节点上所有样本的标签,来强行让算法为我们返回一个固定结果。但许多时候,我们也希望能够理解算法判断出的可能性本身。
3. 所以:我们都希望使用真正的概率来衡量可能性,因此就有了真正的概率算法:朴素贝叶斯。朴素贝叶斯是一种直接衡量标签和特征之间的概率关系的有监督学习算法,是一种专注分类的算法。朴素贝叶斯的算法根源就是基于概率论和数理统计的贝叶斯理论。
1.2 贝叶斯工作原理
1. 经过概率论与数理统计的学习,我们知道贝叶斯公式为:
2.(1)这个式子,就是我们一切贝叶斯算法的根源理论。我们可以把我们的特征X当成是我们的条件事件,而我们要求解的标签Y当成是我们满足条件后会被影响的结果,而两者之间的概率关系就是P(Y|X)。 (2)这个概率在机器学习中,被我们称之为是标签的后验概率(posterior probability),即是说我们先知道了条件,再去求解结果。而标签Y在没有任何条件限制下取值为某个值的概率,被我们写作P(Y),与后验概率相反,这是完全没有任何条件限制的,称为标签的先验概率(prior probability)。
3. 我们来看看我们贝叶斯理论等式的分母P(X),我们可以使用全概率公式来求解P(X):
其中m代表标签的种类,也就是说,对于二分类而言我们有:
1.3 贝叶斯的性质
1. 我们之前学习的分类算法总是有一个特点:这些算法先从训练集中学习,获取某种信息来建立模型,然后用模型去对测试集进行预测。比如逻辑回归,我们要先从训练集中获取让损失函数最小的参数,然后用参数建立模型,再对测试集进行预测。在比如支持向量机,我们要先从训练集中获取让边际最大的决策边界,然后用决策边界对测试集进行预测。相同的流程在决策树,随机森林中也出现,我们在fit的时候必然已经构造好了能够让对测试集进行判断的模型。而朴素贝叶斯,似乎没有这个过程,这说明,朴素贝叶斯是一个不建模的算法。
2. 假设,我们让X是“气温”,这就是我们的特征,Y是“七星瓢虫冬眠”,就是我们的标签。而我提出的要求“零下的时候,年龄为20天的瓢虫”就是没有标签的测试集。然后提出说,我要预测零下的时候,年龄为20天的瓢虫,会冬眠的概率,之后我们就顺理成章地算了出来。没有利用训练集求解某个模型的过程,也没有训练完毕后我们来做测试的过程,而是直接对有标签的数据提出要求,就可以得到预测结果了。
二、sklearn中的朴素贝叶斯
2.1 贝叶斯分类器
1. sklearn为我们提供了四个朴素贝叶斯的分类器:
类 | 含义 |
---|---|
naive_bayes.BernoulliNB | 伯努利分布下的朴素贝叶斯 |
naive_bayes.GaussianNB | 高斯分布下的朴素贝叶斯 |
naive_bayes.ComplementNB | 补集朴素贝叶斯 |
linear_model.BayesianRidge | 贝叶斯岭回归,在参数估计过程中使用贝叶斯回归技术来包括正则化参数 |
2. 虽然朴素贝叶斯使用了过于简化的假设,但是这个分类器在许多实际情况中都运行良好,著名的是文档分类和垃圾邮件过滤。而且由于贝叶斯是从概率角度进行估计,它所需要的样本量比较少,极端情况下甚至我们可以使用1%的数据作为训练集,依然可以得到很好的拟合效果。当然,如果样本量少于特征数目,贝叶斯的效果就会被削弱。
3. 与SVM和随机森林相比,朴素贝叶斯运行速度更快,因为求解P(Xi | Y)本质是在每个特征上单独对概率进行计算,然后再求乘积,所以每个特征上的计算可以是独立并且并行的,因此贝叶斯的计算速度比较快。不过相对的,由于假设太多,因此贝叶斯的运行效果不是那么好,所以贝叶斯的接口调用的predict_proba其实也不是总指向真正的分类结果,这一点需要注意。
2.2 高斯朴素贝叶斯GaussianNB
1. 模块:class sklearn.naive_bayes.GaussianNB (priors=None, var_smoothing=1e-09)
。
参数 | 含义 |
---|---|
prior | 可输入任何类数组结构,形状为(n_classes,),表示类的先验概率。如果指定,则不根据数据调整先验,如果不指定,则自行根据数据计算先验概率P(Y) |
var_smoothing | 浮点数,可不填(默认值= 1e-9),在估计方差时,为了追求估计的稳定性,将所有特征的方差中最大的方差以某个比例添加到估计的方差中。这个比例,由var_smoothing参数控制 |
2. 高斯朴素贝叶斯,通过假设P(Xi | Y)是服从高斯分布(也就是正态分布),来估计每个特征下每个类别上的条件概率。对于每个特征下的取值,高斯朴素贝叶斯有如下公式:
3. 进行一次预测:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.naive_bayes import GaussianNB
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
digits = load_digits()
X, y = digits.data, digits.target
Xtrain,Xtest,Ytrain,Ytest = train_test_split(X,y,test_size=0.3,random_state=420)
gnb = GaussianNB().fit(Xtrain,Ytrain)
#查看分数
acc_score = gnb.score(Xtest,Ytest)
print(acc_score ) #0.8592592592592593
2.3 探索贝叶斯:高斯朴素贝叶斯擅长的数据集
从图上来看,高斯贝叶斯属于比较特殊的一类分类器,其分类效果在二分数据和月亮型数据上表现优秀,但是环形数据不太擅长。我们之前学过的模型中,许多线性模型比如逻辑回归,线性SVM等等,在线性数据集上会绘制直线决策边界,因此难以对月亮型和环形数据进行区分,但高斯朴素贝叶斯的决策边界是曲线,可以是环形也可以是弧线,所以尽管贝叶斯本身更加擅长线性可分的二分数据,但朴素贝叶斯在环形数据和月亮型数据上也可以有远远胜过其他线性模型的表现。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_moons, make_circles, make_classification
from sklearn.naive_bayes import GaussianNB, MultinomialNB, BernoulliNB, ComplementNB
h = .02
names = ["Multinomial","Gaussian","Bernoulli","Complement"]
classifiers = [MultinomialNB(),GaussianNB(),BernoulliNB(),ComplementNB()]
X, y = make_classification(n_features=2, n_redundant=0, n_informative=2,
random_state=1, n_clusters_per_class=1)
rng = np.random.RandomState(2)
X += 2 * rng.uniform(size=X.shape)
linearly_separable = (X, y)
rng = np.random.RandomState(2)
X += 2 * rng.uniform(size=X.shape)
linearly_separable = (X, y)
datasets = [make_moons(noise=0.3, random_state=0),
make_circles(noise=0.2, factor=0.5, random_state=1),
linearly_separable
]
figure = plt.figure(figsize=(6, 9))
i = 1
for ds_index, ds in enumerate(datasets):
X, y = ds
X = StandardScaler().fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.4,
random_state=42)
x1_min, x1_max = X[:, 0].min() - .5, X[:, 0].max() + .5
x2_min, x2_max = X[:, 1].min() - .5, X[:, 1].max() + .5
array1, array2 = np.meshgrid(np.arange(x1_min, x1_max, 0.2),
np.arange(x2_min, x2_max, 0.2))
cm = plt.cm.RdBu
cm_bright = ListedColormap(['#FF0000', '#0000FF'])
ax = plt.subplot(len(datasets), 2, i)
if ds_index == 0:
ax.set_title("Input data")
ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train,
cmap=cm_bright, edgecolors='k')
ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test,
cmap=cm_bright, alpha=0.6, edgecolors='k')
ax.set_xlim(array1.min(), array1.max())
ax.set_ylim(array2.min(), array2.max())
ax.set_xticks(())
ax.set_yticks(())
i += 1
ax = plt.subplot(len(datasets), 2, i)
clf = GaussianNB().fit(X_train, y_train)
score = clf.score(X_test, y_test)
Z = clf.predict_proba(np.c_[array1.ravel(), array2.ravel()])[:, 1]
Z = Z.reshape(array1.shape)
ax.contourf(array1, array2, Z, cmap=cm, alpha=.8)
ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=cm_bright,
edgecolors = 'k')
ax.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap=cm_bright,
edgecolors = 'k', alpha = 0.6)
ax.set_xlim(array1.min(), array1.max())
ax.set_ylim(array2.min(), array2.max())
ax.set_xticks(())
ax.set_yticks(())
if ds_index == 0:
ax.set_title("Gaussian Bayes")
ax.text(array1.max() - .3, array2.min() + .3, ('{:.1f}%'.format(score * 100)),
size = 15, horizontalalignment = 'right')
i += 1
plt.tight_layout()
plt.show()
2.4 探索贝叶斯:高斯朴素贝叶斯的拟合效果与运算速度
1. 拟合曲线代码块:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.tree import DecisionTreeClassifier as DTC
from sklearn.linear_model import LogisticRegression as LR
from sklearn.datasets import load_digits
from sklearn.model_selection import learning_curve
from sklearn.model_selection import ShuffleSplit
from time import time
import datetime
#定义绘制学习曲线的函数
def plot_learning_curve(estimator,title, X, y,
ax, #选择子图
ylim=None, #设置纵坐标的取值范围
cv=None, #交叉验证
n_jobs=None #设定索要使用的线程
):
#train_sizes是每次建模后训练集上的样本数量
train_sizes, train_scores, test_scores = learning_curve(estimator #分类器
, X, y #特征矩阵和标签
,cv=cv #交叉验证模式
,n_jobs=n_jobs)
#n_jobs是说每次运行时可以允许算法使用多少运算资源
ax.set_title(title) #设置标题
if ylim is not None:
ax.set_ylim(*ylim)
ax.set_xlabel("Training examples")
ax.set_ylabel("Score")
ax.grid() #显示网格作为背景,不是必须
ax.plot(train_sizes, np.mean(train_scores, axis=1), 'o-'
, color="r",label="Training score")
ax.plot(train_sizes, np.mean(test_scores, axis=1), 'o-'
, color="g",label="Test score")
ax.legend(loc="best")
return ax
#导入数据,定义循环
digits = load_digits()
X, y = digits.data, digits.target
title = ["Naive Bayes","DecisionTree","SVM, RBF kernel","RandomForest","Logistic"]
model = [GaussianNB(),DTC(),SVC(gamma=0.001)
,RFC(n_estimators=50),LR(C=.1,solver="lbfgs")]
cv = ShuffleSplit(n_splits=50, test_size=0.2, random_state=0)
#一行五列,尺寸大小
fig, axes = plt.subplots(1,5,figsize=(30,6))
for ind,title_,estimator in zip(range(len(title)),title,model): #zip是个元组
times = time()
plot_learning_curve(estimator, title_, X, y,
ax=axes[ind], ylim = [0.7, 1.05],n_jobs=4, cv=cv)
print("{}:{}".format(title_,datetime.datetime.fromtimestamp(time()-
times).strftime("%M:%S:%f")))
plt.show()
2. 在这个对比之下,我们可以看出:贝叶斯是速度很快,但分类效果一般,并且初次训练之后的结果就很接近算法极限的算法,几乎没有调参的余地。