多项式朴素贝叶斯MultinomialNB
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=420)
#先归一化,保证输入多项式朴素贝叶斯的特征矩阵中不带有负数,多项式朴素贝叶斯拒绝负值输入
mms = MinMaxScaler().fit(Xtrain)
Xtrain_ = mms.transform(Xtrain)
Xtest_ = mms.transform(Xtest)
mnb = MultinomialNB().fit(Xtrain_,Ytrain)
#重要属性:调用根据数据获取的,每个标签类的对数先验概率log(P(Y))
#由于概率永远是在[0,1]之间,因此对数先验概率返回的永远是负值
mnb.class_log_prior_
结果:
array([-0.69029411, -0.69600841])
y的两种取值的先验概率,基本上一致表示不存在样本不均衡问题
print((Ytrain == 1).sum()/Ytrain.shape[0])
print(mnb.class_log_prior_ .shape)
#可以使用np.exp来查看真正的概率值
print(np.exp(mnb.class_log_prior_))
#重要属性:返回一个固定标签类别下的每个特征的对数概率log(P(Xi|y))
print(mnb.feature_log_prob_)
print(mnb.feature_log_prob_.shape)
结果:
0.49857142857142855
(2,)
[0.50142857 0.49857143]
[[-0.76164788 -0.62903951]
[-0.72500918 -0.6622691 ]]
(2, 2)
#一些传统的接口
mnb.predict(Xtest_)
结果:
mnb.predict_proba(Xtest_)
结果:
mnb.score(Xtest_,Ytest)
#0.5433333333333333
brier_score_loss(Ytest,mnb.predict_proba(Xtest_)[:,1],pos_label=1)
#0.24977828412546035
效果不太理想,思考一下多项式贝叶斯的性质,我们能够做点什么呢?
多项式贝叶斯处理的是分类型数据而不是连续型数据
#来试试看把Xtiain转换成分类型数据吧
#注意我们的Xtrain没有经过归一化,因为做哑变量之后自然所有的数据就不会又负数了
from sklearn.preprocessing import KBinsDiscretizer #对连续型变量进行分箱
kbs = KBinsDiscretizer(n_bins=10, encode='onehot').fit(Xtrain)
Xtrain_ = kbs.transform(Xtrain)
Xtest_ = kbs.transform(Xtest)
Xtrain_.shape #把原先的两个特征分了10个箱锁分出来的哑变量
#(700, 20)
mnb = MultinomialNB().fit(Xtrain_, Ytrain)
mnb.score(Xtest_,Ytest)
#0.9966666666666667
brier_score_loss(Ytest,mnb.predict_proba(Xtest_)[:,1],pos_label=1)
#0.0014593932778211862
可以看出,多项式朴素贝叶斯的基本操作和代码都非常简单。同样的数据,如果采用哑变量方式的分箱处理,多项式贝叶斯的效果会突飞猛进。作为在文本分类中大放异彩的算法,我们将会在案例中来详细讲解多项式贝叶斯的使用, 并为大家介绍文本分类的更多细节。
伯努利朴素贝叶斯BernoulliNB
多项式朴素贝叶斯可同时处理二项分布(抛硬币)和多项分布(掷骰子),其中二项分布又叫做伯努利分布,它是一 种现实中常见,并且拥有很多优越数学性质的分布。因此,既然有着多项式朴素贝叶斯,我们自然也就又专门用来处理二项分布的朴素贝叶斯:伯努利朴素贝叶斯。
伯努利贝叶斯类BernoulliN假设数据服从多元伯努利分布,并在此基础上应用朴素贝叶斯的训练和分类过程。多元伯努利分布简单来说,就是数据集中可以存在多个特征,但每个特征都是二分类的,可以以布尔变量表示,也可以表示 为{0,1}或者{-1,1}等任意二分类组合。因此,这个类要求将样本转换为二分类特征向量,如果数据本身不是二分类的,那可以使用类中专门用来二值化的参数binarize来改变数据。
多项式朴素贝叶斯——这个特征对这个样本而言发生了几次(单词计数)
伯努利朴素贝叶斯——这个特征发生了吗?发生还是没发生(单词出现)
伯努利朴素贝叶斯与多项式朴素贝叶斯非常相似,都常用于处理文本分类数据。但由于伯努利朴素贝叶斯是处理二项分布,所以它更加在意的是“存在与否”,而不是“出现多少次”这样的次数或频率,这是伯努利贝叶斯与多项式贝叶斯的根本性不同。在文本分类的情况下,伯努利朴素贝叶斯可以使用单词出现向量(而不是单词计数向量)来训练分类器。文档较短的数据集上,伯努利朴素贝叶斯的效果会更加好。如果时间允许,建议两种模型都试试看。
来看看伯努利朴素贝叶斯类的参数:
from sklearn.naive_bayes import BernoulliNB
#普通来说我们应该使用二值化的类sklearn.preprocessing.Binarizer来将特征一个个二值化
#然而这样效率过低,因此我们选择归一化之后直接设置一个阈值
#归一化,消除负数
mms = MinMaxScaler().fit(Xtrain)
Xtrain_ = mms.transform(Xtrain)
Xtest_ = mms.transform(Xtest)
#不设置二值化
bnl_ = BernoulliNB().fit(Xtrain_, Ytrain)
bnl_.score(Xtest_,Ytest)
brier_score_loss(Ytest,bnl_.predict_proba(Xtest_)[:,1],pos_label=1)
#0.49666666666666665
#0.25000009482193225
#设置二值化阈值为0.5
bnl = BernoulliNB(binarize=0.5).fit(Xtrain_, Ytrain)
bnl.score(Xtest_,Ytest)
brier_score_loss(Ytest,bnl.predict_proba(Xtest_)[:,1],pos_label=1)
#0.9833333333333333
#0.010405875827339534
和多项式贝叶斯一样,伯努利贝叶斯的结果也受到数据结构非常大的影响。因此,根据数据的模样选择贝叶斯,是贝叶斯模型选择中十分重要的一点。
探索贝叶斯:贝叶斯的样本不均衡问题
from sklearn.naive_bayes import MultinomialNB, GaussianNB, BernoulliNB
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_blobs
from sklearn.preprocessing import KBinsDiscretizer
from sklearn.metrics import brier_score_loss as BS,recall_score,roc_auc_score as AUC
class_1 = 50000 #多数类为50000个样本
class_2 = 500 #少数类为500个样本
centers = [[0.0, 0.0], [5.0, 5.0]] #设定两个类别的中心
clusters_std = [3, 1] #设定两个类别的方差
X, y = make_blobs(n_samples=[class_1, class_2],centers=centers,cluster_std=clusters_std,random_state=0, shuffle=False)
name = ["Multinomial" ,"Gaussian","Bernoulli"]
models = [MultinomialNB(),GaussianNB(),BernoulliNB()]
for name,clf in zip(name,models):
Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)
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)))
结果:
Multinomial
Brier:0.007
Accuracy:0.990
Recall:0.000
AUC:0.991
Gaussian
Brier:0.006
Accuracy:0.990
Recall:0.438
AUC:0.993
Bernoulli
Brier:0.009
Accuracy:0.987
Recall:0.771
AUC:0.987
从结果上来看,多项式朴素贝叶斯判断出了所有的多数类样本,但放弃了全部的少数类样本,受到样本不均衡问题影响最严重。高斯比多项式在少数类的判断上更加成功一些,至少得到了43.8%的recall。伯努利贝叶斯虽然整体的准确度和布里尔分数不如多项式和高斯朴素贝叶斯和,但至少成功捕捉出了77.1%的少数类。可见,伯努利贝叶斯最能够忍受样本不均衡问题。
可是,伯努利贝叶斯只能用于处理二项分布数据,在现实中,强行将所有的数据都二值化不会永远得到好结果,在我们有多个特征的时候,我们更需要一个个去判断究竟二值化的阈值该取多少才能够让算法的效果优秀。这样做无疑是非常低效的。那如果我们的目标是捕捉少数类,我们应该怎么办呢?高斯朴素贝叶斯的效果虽然比多项式好,但是也没有好到可以用来帮助我们捕捉少数类的程度——43.8%,还不如抛硬币的结果。因此,孜孜不倦的统计学家们改进了朴素贝叶斯算法,修正了包括无法处理样本不平衡在内的传统朴素贝叶斯的众多缺点,得到了新兴贝叶斯算法:补集朴素贝叶斯。
改进多项式朴素贝叶斯:补集朴素贝叶斯ComplementNB
from sklearn.naive_bayes import ComplementNB
from time import time
import datetime
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=420)
#预处理
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"))
结果:
Multinomial
Brier:0.007
Accuracy:0.990
Recall:0.000
AUC:0.991
00:00:060486
Gaussian
Brier:0.006
Accuracy:0.990
Recall:0.438
AUC:0.993
00:00:018604
Bernoulli
Brier:0.009
Accuracy:0.987
Recall:0.771
AUC:0.987
00:00:028316
Complement
Brier:0.038
Accuracy:0.953
Recall:0.987
AUC:0.991
00:00:030288
可以发现,补集朴素贝叶斯牺牲了部分整体的精确度和布里尔指数,但是得到了十分高的召回率Recall,捕捉出了98.7%的少数类,并且在此基础上维持了和原本的多项式朴素贝叶斯一致的AUC分数。和其他的贝叶斯算法比起来,我们的补集朴素贝叶斯的运行速度也十分优秀。如果我们的目标是捕捉少数类,那我们毫无疑问会希望选择补集朴素贝叶斯作为我们的算法。