陈强-机器学习及Python应用-8.3 朴素贝叶斯案例

前言

入门机器学习,记录学习日常,如有错误请多指正。
参考书目:机器学习及Python应用
数据集可在陈强教授主页下载

一、数据预处理

1.数据介绍

案例使用Hastie et al(2009)的Spam数据,演示使用朴素贝叶斯过滤垃圾邮件。spam共包含4601个观测值与58个变量。特征变量A.1至A.54分别表示54个不同的词汇或字符在邮件中的出现频率,变量A.55至A.57分别表示连续大写字母序列的平均长度、连续大写字母序列的最大长度、邮件中大写字母总数。响应变量spam取值为(email,spam)。

2.导入模块和数据文件

1)导入案例所需的全部模块

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
from sklearn.naive_bayes import MultinomialNB
from sklearn.naive_bayes import ComplementNB
from sklearn.naive_bayes import BernoulliNB
from sklearn.metrics import cohen_kappa_score
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import GridSearchCV

2)导入数据

spam=pd.read_csv('D:\数据集\\MLPython_Data\\spam.csv')

3.数据概况

print('数据形状:',spam.shape)
pd.options.display.max_columns=60#将pandas最大展示列数增大为60列
print('前5行数据',spam.head())
print('响应变量spam分布:',spam.spam.value_counts())#响应变量spam分布
print('正常邮件和垃圾邮件占比',spam.spam.value_counts(normalize=True))#normalize=True表示标准化
数据形状: (4601, 58)5行数据
     A.1   A.2   A.3  A.4   A.5   A.6   A.7   A.8   A.9  A.10  A.11  A.12  \
0  0.00  0.64  0.64  0.0  0.32  0.00  0.00  0.00  0.00  0.00  0.00  0.64   
1  0.21  0.28  0.50  0.0  0.14  0.28  0.21  0.07  0.00  0.94  0.21  0.79   
2  0.06  0.00  0.71  0.0  1.23  0.19  0.19  0.12  0.64  0.25  0.38  0.45   
3  0.00  0.00  0.00  0.0  0.63  0.00  0.31  0.63  0.31  0.63  0.31  0.31   
4  0.00  0.00  0.00  0.0  0.63  0.00  0.31  0.63  0.31  0.63  0.31  0.31   

   A.13  A.14  A.15  A.16  A.17  A.18  A.19  A.20  A.21  A.22  A.23  A.24  \
0  0.00  0.00  0.00  0.32  0.00  1.29  1.93  0.00  0.96   0.0  0.00  0.00   
1  0.65  0.21  0.14  0.14  0.07  0.28  3.47  0.00  1.59   0.0  0.43  0.43   
2  0.12  0.00  1.75  0.06  0.06  1.03  1.36  0.32  0.51   0.0  1.16  0.06   
3  0.31  0.00  0.00  0.31  0.00  0.00  3.18  0.00  0.31   0.0  0.00  0.00   
4  0.31  0.00  0.00  0.31  0.00  0.00  3.18  0.00  0.31   0.0  0.00  0.00   

   A.25  A.26  A.27  A.28  A.29  A.30  A.31  A.32  A.33  A.34  A.35  A.36  \
0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   
1   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   
2   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   
3   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   
4   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   0.0   

   A.37  A.38  A.39  A.40  A.41  A.42  A.43  A.44  A.45  A.46  A.47  A.48  \
0  0.00   0.0   0.0  0.00   0.0   0.0  0.00   0.0  0.00  0.00   0.0   0.0   
1  0.07   0.0   0.0  0.00   0.0   0.0  0.00   0.0  0.00  0.00   0.0   0.0   
2  0.00   0.0   0.0  0.06   0.0   0.0  0.12   0.0  0.06  0.06   0.0   0.0   
3  0.00   0.0   0.0  0.00   0.0   0.0  0.00   0.0  0.00  0.00   0.0   0.0   
4  0.00   0.0   0.0  0.00   0.0   0.0  0.00   0.0  0.00  0.00   0.0   0.0   

   A.49   A.50  A.51   A.52   A.53   A.54   A.55  A.56  A.57  spam  
0  0.00  0.000   0.0  0.778  0.000  0.000  3.756    61   278  spam  
1  0.00  0.132   0.0  0.372  0.180  0.048  5.114   101  1028  spam  
2  0.01  0.143   0.0  0.276  0.184  0.010  9.821   485  2259  spam  
3  0.00  0.137   0.0  0.137  0.000  0.000  3.537    40   191  spam  
4  0.00  0.135   0.0  0.135  0.000  0.000  3.537    40   191  spam  

响应变量spam分布:
 spam
email    2788
spam     1813
Name: count, dtype: int64

正常邮件和垃圾邮件占比:
 spam
email    0.605955
spam     0.394045
Name: proportion, dtype: float64

为考察特征变量分布,画出前五个变量直方图

spam.iloc[:,:5].plot.hist(subplots=True,bins=100)#前5个变量直方图。subplots=True表示分别画出直方图(不重叠在一张图上)
plt.show()

在这里插入图片描述
画出最后三个变量直方图

spam.iloc[:,-4:].plot.hist(subplots=True,bins=100)#最后3个变量的直方图
plt.show()

在这里插入图片描述
从图中可知,spam数据特征变量均为非负的连续变量,大多数取值接近于0。

4 分层抽样

根据spam数据框,定义数据矩阵X和相应向量y

X=spam.iloc[:,:-1]
y=spam.iloc[:,-1]

将全样本随机分为两部分,80%为训练集,20%为测试集

X_train, X_test,y_train,y_test=train_test_split(X,y,test_size=0.2,stratify=y,random_state=0)
#stratify=y表示根据响应变量y进行分层抽样

二、高斯朴素贝叶斯估计

高斯朴素贝叶斯(Gaussian Naive Bayes)‌假设每个特征变量的取值服从高斯分布,即正态分布,并且特征之间相互独立。

model=GaussianNB()
model.fit(X_train,y_train)
print('高斯朴素贝叶斯准确率:',model.score(X_test,y_test))
高斯朴素贝叶斯准确率: 0.8447339847991314

三、多项朴素贝叶斯估计

多项朴素贝叶斯(multinomial naive Bayes)假设特征变量服从多项分布。

model=MultinomialNB(alpha=1)#alpha为拉普拉斯修正
model.fit(X_train,y_train)
print('多项朴素贝叶斯准确率:',model.score(X_test,y_test))
多项朴素贝叶斯准确率: 0.7915309446254072

四、补集朴素贝叶斯估计

‌补集朴素贝叶斯(Complement Naive Bayes)是多项式朴素贝叶斯算法的一种改进形式。‌它包含的参数和多项式朴素贝叶斯非常相似,但补集朴拉叶斯特别适合于样本不平衡的数据集,并且在实验中,补集朴素贝叶斯的参数估计已经被证明比普通多项式朴素贝叶斯更稳定。

model=ComplementNB(alpha=1)
model.fit(X_train,y_train)
print('互补朴素贝叶斯准确率:',model.score(X_test,y_test))
补集朴素贝叶斯准确率: 0.7893593919652552

五、二项朴素贝叶斯估计

二项朴素贝叶斯(Bernoulli Naive Bayes)‌假设每个特征变量的取值服从伯努利分布,即二项分布,并且特征之间相互独立。虽然特征变量并不服从两点分布,但sklearn的BernoulliNB类默认以0为门槛值,将大于0的特征变量取值为1;反之则为0
1)以0为门槛值进行二项朴素贝叶斯估计

model=BernoulliNB(alpha=1)#alpha=1表示进行拉普拉斯修正
model.fit(X_train,y_train)
print('二项朴素贝叶斯准确率',model.score(X_test,y_test))
二项朴素贝叶斯准确率 0.8925081433224755

2)以0.1为门槛值进行二项朴素贝叶斯估计

model=BernoulliNB(binarize=0.1,alpha=1)
#用不同门槛值定义特征变量的两点分布,binarize=0.1表示以0.1为门槛值,大于0.1的特征变量取值为1
model.fit(X_train,y_train)
print('门槛值为0.1的二项朴素贝叶斯准确率',model.score(X_test,y_test))
门槛值为0.1的二项朴素贝叶斯准确率 0.9153094462540716

在此模型中,binarize和alpha可视为超参数,通过合理选择超参数,可最大化测试集预测准确率。
3)通过双重for循环选择超参数

#更改超参数binarize和alpha最大化测试集预测准确率
best_score=0
for binarize in np.arange(0,1.1,0.1):
    for alpha in np.arange(1.0e-10,1.1,0.1):
        model = BernoulliNB(binarize=binarize,alpha=alpha)
        model.fit(X_train, y_train)
        score=model.score(X_test, y_test)
        if score>best_score:
            best_score=score
            best_parameters={'binarize':binarize,'alpha':alpha}
            
print('best_score=',best_score)
print(best_parameters)
best_score= 0.9218241042345277#最佳预测准确率
{'binarize': np.float64(0.30000000000000004), 'alpha': np.float64(1.0000000001)}#最佳超参数

该超参数基于测试集数据,对于未见过的未来数据未必也是最佳超参数,可通过以下方式进行优化。

六、超参数优化

1.将全样本分为训练集、验证集、测试集

1)进行数据抽样。其中训练集用于估计模型,验证集用于选择超参数,测试集仅用于评估测试误差

X_trainval,X_test,y_trainval,y_test=train_test_split(X,y,test_size=0.2,stratify=y,random_state=0)
X_train,X_val,y_train,y_val=train_test_split(X_trainval,y_trainval,test_size=0.25,stratify=y_trainval,random_state=123)
print('训练集样本容量:',y_train.shape)
print('验证集样本容量:',y_val.shape)
print('测试集样本容量:',y_test.shape)
训练集样本容量: (2760,)
验证集样本容量: (920,)
测试集样本容量: (921,)

2)使用双重for循环,在训练集中进行估计,再根据验证集的预测准确率,选择最优的超参数

best_val_score=0
for binarize in np.arange(0,1.1,0.1):
    for alpha in np.arange(1.0e-10,1.1,0.1):
        model = BernoulliNB(binarize=binarize,alpha=alpha)
        model.fit(X_train, y_train)
        score=model.score(X_val, y_val)
        if score>best_val_score:
            best_val_score=score
            best_val_parameters={'binarize':binarize,'alpha':alpha}
print('best_val_score=', best_val_score)
print(best_val_parameters)

3)使用训练集和验证集的合集进行二项朴素贝叶斯估计,并计算测试集的预测准确率。

model=BernoulliNB(**best_val_parameters)
model.fit(X_trainval,y_trainval)
print('测试集预测准确率:',model.score(X_test,y_test))
测试集预测准确率: 0.9207383279044516

2.交叉验证法

1)使用sklearn模块的GridSearthCV类进行交叉验证

kfold=StratifiedKFold(n_splits=10,shuffle=True,random_state=1)
#将样本分为10折,StratifiedKFold类可保证每折子样本中各类别数据的比重,故更适用于分类问题

param_grid={'binarize':np.arange(0,1.1,0.1),'alpha':np.arange(1e-10,1.1,0.1)}
#定义字典形式的超参数网格
model=GridSearchCV(BernoulliNB(),param_grid,cv=kfold)
model.fit(X_trainval,y_trainval)

最佳超参数和预测准确率:

print('测试集预测准确率:',model.score(X_test,y_test))
print('最佳参数组合:',model.best_params_)
测试集预测准确率: 0.9153094462540716
最佳参数组合: {'alpha': np.float64(1e-10), 'binarize': np.float64(0.1)}

2)考察相应数据指标
每个超参数组合的详细交叉验证信息

results=pd.DataFrame(model.cv_results_)#获得每个超参数组合的详细交叉验证信息,并数据框化
print(results.head(2))#考察前两行
   mean_fit_time  std_fit_time  mean_score_time  std_score_time   param_alpha  \
0       0.008322      0.000536         0.001889        0.000297  1.000000e-10   
1       0.009395      0.001012         0.002503        0.000508  1.000000e-10   

   param_binarize                             params  split0_test_score  \
0             0.0  {'alpha': 1e-10, 'binarize': 0.0}           0.885870   
1             0.1  {'alpha': 1e-10, 'binarize': 0.1}           0.910326   

   split1_test_score  split2_test_score  split3_test_score  split4_test_score  \
0           0.907609           0.858696           0.894022           0.888587   
1           0.926630           0.877717           0.904891           0.902174   

   split5_test_score  split6_test_score  split7_test_score  split8_test_score  \
0           0.883152           0.877717           0.855978           0.899457   
1           0.904891           0.885870           0.875000           0.910326   

   split9_test_score  mean_test_score  std_test_score  rank_test_score  
0           0.891304         0.884239        0.015620               67  
1           0.896739         0.899457        0.015179                1  

3)对平均预测准确率(mean_test_score)进行可视化

scores=np.array(results.mean_test_score).reshape(11,11)#将results.mean_test_score排列成11*11的矩阵
fig, ax=plt.subplots(figsize=(10,5))
ax=sns.heatmap(scores,cmap='Blues',annot=True,fmt='.3f')#annot=True表示热图每小格加注释(即scores取值),fmt='.3f'表示保留三位小数
ax.set_xlabel('binarize')
ax.set_xticklabels(np.round(np.arange(0,1.1,0.1),2))#np.round()保留n位小数
ax.set_ylabel('alpha')
ax.set_yticklabels(np.round(np.arange(0,1.1,0.1),2))
plt.tight_layout()
plt.show()

在这里插入图片描述
4)考察预测效果指标
计算预测概率和预测值

prob=model.predict_proba(X_test)
pred=model.predict(X_test)
print('前三行预测概率:',prob[:3])
print('前三行预测值',pred[:3])
前三行预测概率:
 [[4.74516086e-05 9.99952548e-01]
 [9.99997682e-01 2.31825593e-06]
 [9.98936191e-01 1.06380916e-03]]
 
前三行预测值 ['spam' 'email' 'email']

计算混淆矩阵

table=pd.crosstab(y_test,pred,rownames=['Actual'],colnames=['Predicted'])#测试集混淆矩阵
print(table)
Predicted  email  spam
Actual                
email        525    33
spam          45   318

根据混淆矩阵,考察准确率、错误率、真阳率、真阴率、查全率/召回率

table=np.array(table)#数组化,便于计算
TN=table[0,0]
FN=table[0,1]
FP=table[1,0]
TP=table[1,1]
Accuracy=(TN+TP)/np.sum(table)
Error_rate=1-Accuracy
Sensitivity=TP/(FP+TP)
Specificity=TN/(TN+FN)
Recall=TP/(FN+TP)
print('准确率:',Accuracy)
print('错误率:',Error_rate)
print('真阳率:',Sensitivity)
print('真阴率:',Specificity)
print('查全率/召回率',Recall)
准确率: 0.9153094462540716
错误率: 0.08469055374592838
真阳率: 0.8760330578512396
真阴率: 0.9408602150537635
查全率/召回率 0.905982905982906

画出roc曲线

from sklearn.metrics import RocCurveDisplay

RocCurveDisplay.from_estimator(model, X_test, y_test, plot_chance_level=True)#model是一个已经用训练数据拟合好的模型对象,plot_chance_level=True表示绘制斜线
plt.show()

在这里插入图片描述
也可使用以下方式绘制ROC曲线,所得结果一致

from sklearn.metrics import roc_curve,RocCurveDisplay,roc_auc_score
d={'email':0,'spam':1}
y_test=y_test.map(d)#将响应变量取值变为0,1
prob = prob[:,1]
fpr,tpr,threshold=roc_curve(y_test, prob)#传入样本真实标签和样本属于正类的概率,计算真阳率和假阳率
auc_score=roc_auc_score(y_test,prob)#计算ROC曲线下面积
RocCurveDisplay(fpr=fpr, tpr=tpr,roc_auc=auc_score).plot()
plt.title('ROC Curve')
plt.show()

七、高斯朴素贝叶斯决策边界

使用iris数据的两个特征变量进行高斯朴素贝叶斯估计

X,y=load_iris(return_X_y=True)
X2=X[:,2:4]
model=GaussianNB()
model.fit(X2,y)
print(model.score(X2,y))
准确率: 0.96

画出决策边界


plot_decision_regions(X2,y,model)
plt.xlabel('petal_length')
plt.ylabel('petal.width')
plt.title('decision boundary for Gaussian Naive Bayes')
plt.show()

在这里插入图片描述
结果显示,高斯朴素贝叶斯估计的决策边界为椭圆形

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值