机器学习 | Sklearn中的朴素贝叶斯全解

前期文章介绍了朴素贝叶斯理论,掌握理论后如何去使用它,是数据挖掘工作者需要掌握的实操技能,下面来看看Sklearn中都有哪些朴素贝叶斯。

朴素贝叶斯是运用训练数据学习联合概率分布 ,然后求得后验概率分布 。利用训练数据学习 和    的估计,得到联合概率分布:

朴素贝叶斯的基本假设是条件独立性

朴素贝叶斯是运用贝叶斯定理与基于条件独立性假设的联合概率模型进行分类预测

将输入 分到后验概率最大的类

sklearn中的朴素贝叶斯

不同的贝叶斯算法其实是假设 满足的统计学中的分布的不同,最常见的就是高斯分布、伯努利分布、多项式分布。Sklearn基于这些分布以及这些分布上的概率估计的改进,为我们提供了四个朴素贝叶斯的分类器。

含义
naive_bayes.BernoulliNB伯努利分布下的朴素贝叶斯
naive_bayes.GaussianNB高斯分布下的朴素贝叶斯
naive_bayes.MultinomialNB多项式分布下的朴素贝叶斯
naive_bayes.ComplementNB补集朴素贝叶斯

虽然朴素贝叶斯使用了过于简化的假设,这个分类器在文档分类和垃圾邮件过滤等领域中都运行良好。而且由于贝叶斯是从概率角度进行估计,它所需要的样本量比较少,极端情况下甚至我们可以使用 的数据作为训练集,依然可以得到很好的拟合效果。当然,如果样本量少于特征数目,贝叶斯的效果就会被削弱。

朴素贝叶斯运行速度相对更快,因为求解本质是在每个特征上单独对概率进行计算,然后再求乘积,所以每个特征上的计算可以是独立并且并行的。不过贝叶斯的运行效果相对较差,所以贝叶斯的接口调用的predict_proba其实也不是总指向真正的分类结果,这一点需要注意。

各个朴素贝叶斯在不同数据集上的效果

高斯朴素贝叶斯GaussianNB

高斯朴素贝叶斯是假设 服从高斯分布(正态分布)。来估计每个特征下每个类别的条件概率。

对于每个特征下的取值,高斯朴素贝叶斯有如下公式:

以最大化 为目标,高斯朴素贝叶斯会求解公式中的参数

求解出参数后,带入一个 的值,就能够得到一个的 概率取值。最后再求连乘便能够获得相应的概率。

from sklearn.naive_bayes import GaussianNB
GaussianNB(priors=None, var_smoothing=1e-09)

priors : array-like, shape (n_classes,)
可输入任何类数组结构,形状为(n_classes,)表示类的先验概率。如果指定,则不根据数据调整先验,如果不指定,则自行根据数据计算先验概率

var_smoothing : float, optional (default=1e-9)
浮点数,可不填(默认值= 1e-9)。在估计方差时,为了追求估计的稳定性,将所有特征的方差中最大的方差以某个比例添加到估计的方差中。此参数控制这个比例


应用实例

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=666)
GNB = GaussianNB().fit(Xtrain,Ytrain)
#查看分数
acc_score = GNB.score(Xtest,Ytest)
acc_score
>>> 0.8555555555555555
#查看预测结果
Y_pred = GNB.predict(Xtest)
#查看预测的概率结果
prob = GNB.predict_proba(Xtest)
prob.shape #每一列对应一个标签下的概率
>>> (540, 10)
prob[1,:].sum() #每一行的和都是一
>>>1.0000000000000002
prob.sum(axis=1)
>>> "略"
混淆矩阵看贝叶斯的分类结果

注意,ROC曲线是不能用于多分类的。多分类状况下最佳的模型评估指标是混淆矩阵和整体的准确度。

from sklearn.metrics import confusion_matrix as CM
CM(Ytest,Y_pred)

>>>
array([[57,  0,  0,  0,  0,  0,  0,  1,  0,  0],
       [ 0, 44,  1,  0,  0,  0,  0,  0,  4,  0],
       [ 0,  4, 36,  0,  1,  0,  0,  0, 12,  0],
       [ 0,  1,  1, 40,  0,  4,  0,  1, 14,  0],
       [ 0,  0,  0,  0, 53,  1,  0,  2,  0,  0],
       [ 0,  0,  0,  0,  0, 49,  0,  2,  1,  0],
       [ 0,  0,  1,  0,  1,  0, 45,  0,  0,  0],
       [ 0,  0,  0,  0,  0,  0,  0, 52,  0,  0],
       [ 0,  1,  2,  0,  0,  0,  0,  5, 49,  0],
       [ 1,  3,  0,  0,  1,  3,  0,  3,  7, 37]], dtype=int64)
高斯朴素贝叶斯的拟合效果与运算速度

通过绘制高斯朴素贝叶斯的学习曲线与分类树,随机森林和支持向量机的学习曲线的对比,来探索高斯朴素贝叶斯算法在拟合上的性质。使用sklearn中自带的绘制学习曲线的类learning_curve,在这个类中执行交叉验证并从中获得不同样本量下的训练和测试的准确度。

导入需要的包

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_scores, test_scores = learning_curve(estimator, X, y,cv=cv,n_jobs=n_jobs)
    ax.set_title(title,fontsize=25)
    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
X.shape
X #是一个稀疏矩阵
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):
    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()

>>> Naive Bayes:00:00:712082
>>> DecisionTree:00:00:810831
>>> SVM, RBF kernel:00:06:286785
>>> RandomForest:00:05:779048
>>> Logistic:00:12:875796

从对比图中可以看出:

贝叶斯是速度很快,但分类效果一般,并且初次训练之后的结果就很接近算法极限的算法,几乎没有调参的余地。

  • 如果追求对概率的预测,并且希望越准确越好,应该先选择逻辑回归。

  • 如果数据十分复杂,或者是稀疏矩阵,选择朴素贝叶斯。

  • 如果分类的目标不是要追求对概率的预测,可以先试试高斯朴素贝叶斯的效果(运算很快速,不需要太多的样本),如果效果很不错,就很幸运地得到了一个表现优秀又快速的模型。如果没有得到比较好的结果,可以选择再更换成更加复杂的模型。

多项式朴素贝叶斯MultinomialNB

与高斯朴素贝叶斯原理类似,只是假设概率分布是服从一个简单多项式分布。

多项式分布擅长的是分类型变量,因在其原理假设中, 的概率是离散的,并且不同下的 相互独立,互不影响。

多项式实验中的实验结果都很具体,它所涉及的特征往往是次数,频率,计数,出现与否这样的概念,这些概念都是离散的正整数,因此sklearn中的多项式朴素贝叶斯不接受负值的输入。

sklearn.naive_bayes.MultinomialNB (alpha=1.0,
                                   fit_prior=True,
                                   class_prior=None,
                                  )

alpha : float, optional (default=1.0)
拉普拉斯或利德斯通平滑的参数,如果设置为0则表示完全没有平滑选项。但是需要注意的是,平滑相当于人为给概率加上一些噪音,因此设置得越大,多项式朴素贝叶斯的精确性会越低(虽然影响不是非常大),布里尔分数也会逐渐升高。

fit_prior : boolean, optional (default=True)
是否学习先验概率 。如果设置为"False",则不使用先验概率,而使用统一先验概率(uniform prior),即认为每个标签类出现的概率是 "1/n_classes"。

class_prior : array-like, size (n_classes,), optional (default=None)
类的先验概率 。如果没有给出具体的先验概率则自动根据数据来进行计算。


应用实例

数据模型准备

# 导包
from sklearn.preprocessing import MinMaxScaler
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_blobs
from sklearn.metrics import brier_score_loss
# 生成数据集
class_1 = 500
class_2 = 500 #两个类别分别设定500个样本
centers = [[0.0, 0.0], [2.0, 2.0]] #设定两个类别的中心
clusters_std = [0.5, 0.5] #设定两个类别的方差
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=666)

#先归一化,保证输入多项式朴素贝叶斯的特征矩阵中不带有负数
mms = MinMaxScaler().fit(Xtrain)
X_train = mms.transform(Xtrain)
X_test = mms.transform(Xtest)

mnb = MultinomialNB().fit(X_train, Ytrain)

#重要属性:调用根据数据获取的,每个标签类的对数先验概率log(P(Y))
#由于概率永远是在[0,1]之间,因此对数先验概率返回的永远是负值
mnb.class_log_prior_
>>> array([-0.6761496 , -0.71043868])
(Ytrain == 1).sum()/Ytrain.shape[0]
>>> 0.49142857142857144
#可以使用np.exp来查看真正的概率值
np.exp(mnb.class_log_prior_)
>>> array([0.50857143, 0.49142857])
#重要属性:返回一个固定标签类别下的每个特征的对数概率log(P(Xi|y))
mnb.feature_log_prob_
>>> array([[-0.78906788, -0.60562732],
       [-0.73070241, -0.65695144]])
#重要属性:在fit时每个标签类别下包含的样本数。
#当fit接口中的sample_weight被设置时,该接口返回的值也会受到加权的影响
mnb.class_count_
>>> array([356., 344.])

分类效果

#一些传统的接口
mnb.predict(X_test)
mnb.predict_proba(X_test)
mnb.score(X_test,Ytest)
>>> 0.48
brier_score_loss(Ytest,mnb.predict_proba(X_test)[:,1],pos_label=1)
>>> 0.2500632505306737

得分太低

哑变量处理

#来试试看把Xtiain转换成分类型数据吧
#注意我们的Xtrain没有经过归一化,因为做哑变量之后自然所有的数据就不会又负数了
from sklearn.preprocessing import KBinsDiscretizer
kbs = KBinsDiscretizer(n_bins=10, encode='onehot').fit(Xtrain)
Xtrain_ = kbs.transform(Xtrain)
Xtest_ = kbs.transform(Xtest)
mnb = MultinomialNB().fit(Xtrain_, Ytrain)
mnb.score(Xtest_,Ytest)
>>> 1.0
brier_score_loss(Ytest,mnb.predict_proba(Xtest_)[:,1],pos_label=1)
>>> 0.0012837499315398737

可以看出,多项式朴素贝叶斯的基本操作和代码都非常简单。同样的数据,如果采用哑变量方式的分箱处理,多项式贝叶斯的效果会突飞猛进。特别适合用在文本分类中。

伯努利朴素贝叶斯BernoulliNB

伯努利分布又称二项分布,它是一种现实中常见,并且拥有很多优越数学性质的分布。

伯努利贝叶斯类BernoulliNB假设数据服从多元伯努利分布,并在此基础上应用朴素贝叶斯的训练和分类过程。多元伯努利分布简单来说,就是数据集中可以存在多个特征,但每个特征都是二分类的,可以以布尔变量表示,也可以表示为{0,1}或者{-1,1}等任意二分类组合。因此,这个类要求将样本转换为二分类特征向量,如果数据本身不是二分类的,那可以使用类中专门用来二值化的参数binarize来改变数据。

伯努利朴素贝叶斯是处理二项分布,所以它更加在意的是"存在与否",而不是"出现多少次"这样的次数或频率,这是伯努利贝叶斯与多项式贝叶斯的根本性不同。在文本分类的情况下,伯努利朴素贝叶斯可以使用单词出现向量(而不是单词计数向量)来训练分类器。文档较短的数据集上,伯努利朴素贝叶斯的效果会更加好。

sklearn.naive_bayes.BernoulliNB (alpha=1.0, 
                                 binarize=0.0, 
                                 fit_prior=True, 
                                 class_prior=None)

alpha : float, optional (default=1.0)
拉普拉斯或利德斯通平滑的参数,如果设置为0则表示完全没有平滑选项。但是需要注意的是,平滑相当于人为给概率加上一些噪音,因此设置得越大,多项式朴素贝叶斯的精确性会越低(虽然影响不是非常大),布里尔分数也会逐渐升高。

binarize : float or None, optional (default=0.0)
将特征二值化的阈值,如果设定为None,则会假定说特征已经被二值化完毕。

fit_prior : boolean, optional (default=True)
是否学习先验概率 。如果设置为"False",则不使用先验概率,而使用统一先验概率(uniform prior),即认为每个标签类出现的概率是 "1/n_classes"。

class_prior : array-like, size=[n_classes,], optional (default=None)
类的先验概率 。如果没有给出具体的先验概率则自动根据数据来进行计算。


补集朴素贝叶斯ComplementNB

补集朴素贝叶斯(complement naive Bayes,CNB)算法是标准多项式朴素贝叶斯算法的改进。CNB能够解决样本不平衡问题,并且能够一定程度上忽略朴素假设的补集朴素贝叶斯。在实验中,CNB的参数估计已经被证明比普通多项式朴素贝叶斯更稳定,并且它特别适合于样本不平衡的数据集。

关于补集朴素贝叶斯具体是如何逃避了我们的朴素假设,或者如何让我们的样本不均衡问题得到了改善,背后有深刻的数学原理和复杂的数学证明过程,大家如果感兴趣可以参阅这篇论文:

Rennie, J. D., Shih, L., Teevan, J., & Karger, D. R. (2003 "论文"). Tackling the poor assumptions of naive bayes text classifiers. In ICML (Vol. 3, pp. 616-623).

简单来说,CNB使用来自每个标签类别的补集的概率,并以此来计算每个特征的权重。

其中表示每个样本, 表示在样本 上对于特征 的下的取值,在文本分类中通常是计数的值或者是TF-IDF值。 是像标准多项式朴素贝叶斯中一样的平滑系数。 指的是一个特征 下,所有标签类别不等于 值的样本的特征取值之和。而 是所有特征下,所有标签类别不等于 值得样本的特征取值之和。其实就是多项式分布的逆向思路。

对它取对数后得到权重。 或除以它的L2范式,以解决了在多项式分布中,特征取值比较多的样本(比如说比较长的文档)支配参数估计的情况。

基于这个权重,补充朴素贝叶斯中一个样本的预测规则为:

即求解出的最小补集概率所对应的标签就是样本的标签,因为 的概率越小,则意味着 的概率越大,所以样本属于标签类别

sklearn.naive_bayes.ComplementNB (alpha=1.0,
                                  fit_prior=True, 
                                  class_prior=None, 
                                  norm=False)

alpha : float, optional (default=1.0)
拉普拉斯或利德斯通平滑的参数,如果设置为0则表示完全没有平滑选项。但是需要注意的是,平滑相当于人为给概率加上一些噪音,因此设置得越大,多项式朴素贝叶斯的精确性会越低(虽然影响不是非常大),布里尔分数也会逐渐升高。

norm : boolean, optional (default=False)
在计算权重的时候是否适用L2范式来规范权重的大小。默认不进行规范,即不跟从补集朴素贝叶斯算法的全部内容,如果希望进行规范,请设置为True。

fit_prior : boolean, optional (default=True)
是否学习先验概率 。如果设置为"False",则不使用先验概率,而使用统一先验概率(uniform prior),即认为每个标签类出现的概率是 "1/n_classes"。

class_prior : array-like, size=[n_classes,], optional (default=None)
类的先验概率 。如果没有给出具体的先验概率则自动根据数据来进行计算。


from sklearn.naive_bayes import ComplementNB
from time import time
import datetime
from sklearn.metrics import brier_score_loss as BS
from sklearn.metrics import recall_score
from sklearn.metrics import roc_auc_score as AUC
name = ["Multinomial","Gaussian","Bernoulli","Complement"]
models = [MultinomialNB(),GaussianNB(),BernoulliNB(),ComplementNB()]
for name,clf in zip(name,models):
    times = time()
    Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,
                                                    test_size=0.3,
                                                    random_state=666)
    #预处理
    if name != "Gaussian":
        kbs = KBinsDiscretizer(n_bins=10, encode='onehot').fit(Xtrain)
        Xtrain = kbs.transform(Xtrain)
        Xtest = kbs.transform(Xtest)
    clf.fit(Xtrain,Ytrain)
    y_pred = clf.predict(Xtest)
    proba = clf.predict_proba(Xtest)[:,1]
    score = clf.score(Xtest,Ytest)
    print(name)
    print("\tBrier:{:.3f}".format(BS(Ytest,proba,pos_label=1)))
    print("\tAccuracy:{:.3f}".format(score))
    print("\tRecall:{:.3f}".format(recall_score(Ytest,y_pred)))
    print("\tAUC:{:.3f}".format(AUC(Ytest,proba)))
    print(datetime.datetime.fromtimestamp(time()-times).strftime("%M:%S:%f"))

从结果上来看,多项式朴素贝叶斯判断出了所有的多数类样本,但放弃了全部的少数类样本,受到样本不均衡问题影响最严重。高斯比多项式在少数类的判断上更加成功一些,至少得到了51.4%的recall。伯努利贝叶斯虽然整体的准确度和布里尔分数不如多项式和高斯朴素贝叶斯和,但至少成功捕捉出了77.1%的少数类。可见,伯努利贝叶斯最能够忍受样本不均衡问题。

补集朴素贝叶斯牺牲了部分整体的精确度和布里尔指数,但是得到了十分高的召回率Recall,捕捉出了 97.9%的少数类,并且在此基础上维持了和原本的多项式朴素贝叶斯一致的AUC分数。和其他的贝叶斯算法比起来,我们的补集朴素贝叶斯的运行速度也十分优秀。如果我们的目标是捕捉少数类,那我们毫无疑问会希望选择补集朴素贝叶斯作为我们的算法。

布里尔分数可以参见概率类模型评估指标

推荐阅读


-- 数据STUDIO -- 

  • 6
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值