文章目录
前言
入门机器学习,记录学习日常,如有错误请多指正。
参考书目:机器学习及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()
结果显示,高斯朴素贝叶斯估计的决策边界为椭圆形