Py学习  »  Python

从零开始学Python【38】--朴素贝叶斯模型(实战部分)

数据分析1480 • 4 年前 • 776 次点击  

【前言】

在《从零开始学Python【37】--朴素贝叶斯模型(理论部分)》中我们详细介绍了朴素贝叶斯算法的基本概念和理论知识,在这一期我们继续介绍该算法的实战案例。将会对高斯贝叶斯、多项式贝叶斯和伯努利贝叶斯三种分类器案例的做实战讲解。希望通过这部分内容的讲解,能够使读者对贝叶斯算法有一个较深的理解(文末有数据和源代码的下载链接)。


【高斯贝叶斯分类器】

面部皮肤区分数据集来自于UCI网站,该数据集含有两个部分,一部分为人类面部皮肤数据,该部分数据是由不同种族、年龄和性别人群的图片转换而成的;另一部分为非人类面部皮肤数据。两个部分的数据集一共包含245 057条样本和4个变量,其中用于识别样本是否为人类面部皮肤的因素是图片中的三原色R、G、B,它们的值均落在0~255;因变量为二分类变量,表示样本在对应的R、G、B值下是否为人类面部皮肤,其中1表示人类面部皮肤,2表示非人类面部皮肤。


通常情况下,研究人员会对样本是否为人类面部皮肤更加感兴趣,所以需要将原始数据集中因变量为1的值设置为正例、因变量为2的值设置为负例,代码如下:

# 导入第三方包
import pandas as pd

# 读入数据
skin = pd.read_excel(r'C:\Users\Administrator\Desktop\Skin_Segment.xlsx')
# 设置正例和负例
skin.y = skin.y.map({2:0,1:1})
skin.y.value_counts()

out:
0    194198
1     50859

如上结果所示,因变量0表示负例,说明样本为非人类面部皮肤,一共包含194 198个观测;因变量1表示正例,说明样本为人类面部皮肤,一共包含50 859个观测;因变量值为0和1之间的比例为5:1。接下来将该数据集拆分为训练集和测试集,分别用于模型的构建和模型的评估,代码如下:

# 导入第三方模块
from sklearn import model_selection

# 样本拆分
X_train,X_test,y_train,y_test = model_selection.train_test_split(skin.iloc[:,:3], skin.y, 
                                                    test_size = 0.25, random_state=1234)
# 调用高斯朴素贝叶斯分类器的“类”
gnb = naive_bayes.GaussianNB()
# 模型拟合
gnb.fit(X_train, y_train)
# 模型在测试数据集上的预测
gnb_pred = gnb.predict(X_test)

# 各类别的预测数量
pd.Series(gnb_pred).value_counts()

out:
0    50630
1    10635

如上结果所示,通过构建高斯朴素贝叶斯分类器,实现测试数据集上的预测,经统计,预测为负例的一共有50 630条样本、预测为正例的一共有10 635条样本。为检验模型在测试数据集上的预测效果,需要构建混淆矩阵和绘制ROC曲线,其中混淆矩阵用于模型准确率、覆盖率、精准率指标的计算;ROC曲线用于计算AUC值,并将AUC值与0.8相比,判断模型的拟合效果,代码如下:

# 导入第三方包
from sklearn import metrics
import matplotlib.pyplot as plt
import seaborn as sns

# 构建混淆矩阵
cm = pd.crosstab(gnb_pred,y_test)
# 绘制混淆矩阵图
sns.heatmap(cm, annot = True, cmap = 'GnBu', fmt = 'd')
# 去除x轴和y轴标签
plt.xlabel('Real')
plt.ylabel('Predict')
# 显示图形
plt.show()

如上图所示,将混淆矩阵做了可视化处理,其中主对角线的数值表示正确预测的样本量,剩余的4 720条样本为错误预测的样本。经过对混淆矩阵的计算,可以得到模型的整体预测准确率为92.30%。

print('模型的准确率为:\n',metrics.accuracy_score(y_test, gnb_pred))
print('模型的评估报告:\n',metrics.classification_report(y_test, gnb_pred))

模型的准确率为:
 0.922957643026

模型的评估报告:
              precision     recall    f1-score   support
          0       0.93      0.97      0.95     48522
          1       0.88      0.73      0.80     12743
avg / total       0.92      0.92      0.92     61265

进一步可以得到每个类别的预测精准率(precision=正确预测某类别的样本量/该类别的预测样本个数)和覆盖率(recall=正确预测某类别的样本量/该类别的实际样本个数),通过准确率、精准率和覆盖率的对比,模型的预测效果还是非常理想的。接下来绘制ROC曲线,用于进一步验证得到的结论,代码如下:

# 计算正例的预测概率,用于生成ROC曲线的数据
y_score = gnb.predict_proba(X_test)[:,1]
fpr,tpr,threshold = metrics.roc_curve(y_test, y_score)
# 计算AUC的值
roc_auc = metrics.auc(fpr,tpr)

# 绘制面积图
plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black')
# 添加边际线
plt.plot(fpr, tpr, color='black', lw = 1)
# 添加对角线
plt.plot([0,1],[0,1], color = 'red', linestyle = '--')
# 添加文本信息
plt.text(0.5,0.3,'ROC curve (area = %0.2f)' % roc_auc)
# 添加x轴与y轴标签
plt.xlabel('1-Specificity')
plt.ylabel('Sensitivity')
# 显示图形
plt.show()

如上图所示的ROC曲线,计算得到的AUC值为0.94,超过用于评判模型好坏的阈值0.8,故可以认为构建的贝叶斯分类器是非常理想的,进而验证了前文所得的结论。最后需要强调的是,利用高斯贝叶斯分类器对数据集进行分类时要求输入的数据集X为连续的数值型变量。


【多项式贝叶斯分类器】

蘑菇数据集来自于UCI网站,一共包含8 124条观测和22个变量,其中因变量为type,表示蘑菇是否有毒,剩余的自变量是关于蘑菇的形状、表面光滑度、颜色、生长环境等。首先将该数据集读入Python,并预览前5行数据,代码如下:

# 读取数据
mushrooms = pd.read_csv(r'C:\Users\Administrator\Desktop\mushrooms.csv')
# 数据的前5行,见表12-3
mushrooms.head()

如上表所示,表中的所有变量均为字符型的离散值,由于Python建模过程中必须要求自变量为数值类型,因此需要对这些变量做因子化处理,即把字符值转换为对应的数值。接下来利用pandas模块中的factorize函数对离散的自变量进行数值转换,代码如下:




    
# 将字符型数据做因子化处理,将其转换为整数型数据
columns = mushrooms.columns[1:]
for column in columns:
    mushrooms[column] = pd.factorize(mushrooms[column])[0]
mushrooms.head()

如上表所示,所有的字符型变量全部转换成了数值,而且每一列中的数值都代表了各自不同的字符值。需要注意的是,factorize函数返回的是两个元素的元组,第一个元素为转换成的数值,第二个元素为数值对应的字符水平,所以在类型转换时,需要通过索引方式返回因子化的值。接着就可以使用多项式贝叶斯分类器对如上数据集进行类别的预测,为了实现模型的验证,需要将该数据集拆分为训练集和测试集,代码如下:

# 将数据集拆分为训练集合测试集
Predictors = mushrooms.columns[1:]
X_train,X_test,y_train,y_test = model_selection.train_test_split(mushrooms[Predictors],
                                 mushrooms['type'], test_size = 0.25,
                                 random_state = 10)
# 构建多项式贝叶斯分类器的“类”
mnb = naive_bayes.MultinomialNB()
# 基于训练数据集的拟合
mnb.fit(X_train, y_train)
# 基于测试数据集的预测
mnb_pred = mnb.predict(X_test)

# 构建混淆矩阵
cm = pd.crosstab(mnb_pred,y_test)
# 绘制混淆矩阵图
sns.heatmap(cm, annot = True, cmap = 'GnBu', fmt = 'd')
# 去除x轴和y轴标签
plt.xlabel('')
plt.ylabel('')
# 显示图形
plt.show()

在如上的混淆矩阵图中,横坐标代表测试数据集中的实际类别值,纵坐标为预测类别值,正确预测无毒的有981个样本,正确预测有毒的有786个样本。

# 模型的预测准确率
print('模型的准确率为:\n',metrics.accuracy_score(y_test, mnb_pred))
print('模型的评估报告:\n',metrics.classification_report(y_test, mnb_pred))

模型的准确率为:
0.870014771049

模型的评估报告:
              precision    recall     f1-score    support
edible          0.85       0.92      0.88       1072
poisonous       0.90       0.82      0.86       959
avg / total     0.87       0.87      0.87       2031

基于混淆矩阵的进一步运算,可以得到如上所示的两部分结果,并从中发现,模型在测试数据集上的整体预测准确率为87%,而且从各类别值来看,无毒蘑菇的预测覆盖率为92%、有毒蘑菇的预测覆盖率为82%。总体来说,模型的预测效果还是非常理想的,接下来继续绘制ROC曲线,查看对应的AUC值的大小,代码如下:

# 计算正例的预测概率,用于生成ROC曲线的数据
y_score = mnb.predict_proba(X_test)[:,1]
fpr,tpr,threshold = metrics.roc_curve(y_test.map({'edible':0,'poisonous':1}), y_score)
# 计算AUC的值
roc_auc = metrics.auc(fpr,tpr)

# 绘制面积图
plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black')
# 添加边际线
plt.plot(fpr, tpr, color='black', lw = 1)
# 添加对角线
plt.plot([0,1],[0,1], color = 'red', linestyle = '--')
# 添加文本信息
plt.text(0.5,0.3,'ROC curve (area = %0.2f)' % roc_auc)
# 添加x轴与y轴标签
plt.xlabel('1-Specificity')
plt.ylabel('Sensitivity')
# 显示图形
plt.show()

如上图所示,ROC曲线下的面积为0.94,超过阈值0.8,可以认为模型的效果是可以接受的。需要注意的是,当因变量为字符型的值时,子模块metrics中的函数roc_curve必须传入数值型的因变量(如代码所示,将字符值和数值做了映射),否则会报错误信息。


对于离散型自变量的数据集而言,在分类问题上并非都可以使用多项式贝叶斯分类器,如果自变量在特定y值下的概率不服从多项式分布的话,分类器的预测效果就不会很理想。通常情况下,会利用多项式贝叶斯分类器作文本分类,如一份邮件是否垃圾邮件、用户评论是否为正面等。


【伯努利贝叶斯分类器】

用户对其购买的蚊帐进行评论,该数据集是通过爬虫的方式获得,一共包含10 644条评论,数据集中的Type变量为评论所对应的情绪。首先将爬虫获得的数据集读入Python中,并预览前几行数据,代码如下:

# 读入评论数据
evaluation = pd.read_excel(r'C:\Users\Administrator\Desktop\Contents.xlsx',sheetname=0)
# 查看数据前10行,见表12-6
evaluation.head(10)

如上表所示,数据集包含4个字段,分别是用户昵称、评价时间、评价内容和对应的评价情绪。从评价内容来看,会有一些“脏”文本在内,如数字、英文等,所以需要将这些“脏”文本删除,代码如下:

# 运用正则表达式,将评论中的数字和英文去除
evaluation.Content = evaluation.Content.str.replace('[0-9a-zA-Z]','')
evaluation.head()


经过数据的初步清洗后,下一步要做的就是对文本进行切词,但在切词前,通常需要引入用户自定义的词库和停止词。利用词典的目的是将无法正常切割的词实现正确切割(如“沙瑞金书记”会被切词为“沙”“瑞金”“书记”,为了避免这种情况,就需要将类似“沙瑞金”这样的词组合为词库),使用停止词的目的是将句子中无意义的词语删除(如“的”“啊”“我们”等)。

# 导入第三方包
import jieba

# 加载自定义词库
jieba.load_userdict(r'C:\Users\Administrator\Desktop\all_words.txt')
# 读入停止词
with open(r'C:\Users\Administrator\Desktop\mystopwords.txt', encoding='UTF-8'as words:
    stop_words = [i.strip() for i in words.readlines()]
# 构造切词的自定义函数,并在切词过程中删除停止词
def cut_word(sentence):
    words = [i for i in jieba.lcut(sentence) if i not in stop_words]
    # 切完的词用空格隔开
    result = ' '.join(words)
    return(result)

# 调用自定义函数,并对评论内容进行批量切词
words = evaluation.Content.apply(cut_word)
# 前5行内容的切词效果
words[:5]

out:
0    包装 破损 拉链 处有 线头 阻碍 拉链 拉动 没有 安装 第一次 安装 费劲
1    上长 三根 空杆 装
2    没有 送货上门 失望
3    挺不错 破 口子 不知道 啥时候 弄 不错 老婆 怀孕 蚊香 不错
4    不错 效果

如上结果所示,通过调入第三方包jieba实现中文的切词,并在切词过程中加入自定义词库和删除停止词。接下来利用如上的切词结果,构造文档词条矩阵,矩阵的每一行代表一个评论内容,矩阵的每一列代表切词后的词语,矩阵的元素为词语在文档中出现的频次。代码如下:

# 导入第三方包
from sklearn.feature_extraction.text import CountVectorizer

# 计算每个词在各评论内容中的次数,并将稀疏度为99%以上的词删除
counts = CountVectorizer(min_df = 0.01)
# 文档词条矩阵
dtm_counts = counts.fit_transform(words).toarray()
# 矩阵的列名称
columns = counts.get_feature_names()
# 将矩阵转换为数据框,即X变量
X = pd.DataFrame(dtm_counts, columns=columns)
# 情感标签变量
y = evaluation.Type
X.head()

如上表所示,将文档词条矩阵转换为数据框后得到一个庞大的稀疏矩阵,即数据框中的大部分值为0。为了避免数据框的列数过多,在构造文档词条矩阵时做了相应的限制条件,即代码中的CountVectorizer(min_df = 0.01),表示词语所对应的文档数目必须在所有文档中至少占1%的比例,最终得到上表中所呈现的99个变量。有了如上的数据框,接下来要做的就是将数据集拆分为训练集和测试集,并利用训练集构建伯努利贝叶斯分类器,利用测试集对分类器的预测效果进行评估,具体代码如下:

# 将数据集拆分为训练集和测试集
X_train,X_test,y_train,y_test = model_selection.train_test_split(X,y,test_size = 0.25,
                                                         random_state=1)
# 构建伯努利贝叶斯分类器
bnb = naive_bayes.BernoulliNB()
# 模型在训练数据集上的拟合
bnb.fit(X_train,y_train)
# 模型在测试数据集上的预测
bnb_pred = bnb.predict(X_test)
# 构建混淆矩阵
cm = pd.crosstab(bnb_pred,y_test)
# 绘制混淆矩阵图
sns.heatmap(cm, annot = True, cmap = 'GnBu', fmt = 'd')
# 去除x轴和y轴标签
plt.xlabel('Real')
plt.ylabel('Predict')
# 显示图形
plt.show()

如上结果所示,从混淆矩阵图形来看,伯努利贝叶斯分类器在预测数据集上的效果还是非常棒的,绝大多数的样本都被预测正确(因为主对角线上的数据非常大),而且总的预测准确率接近85%。

# 模型的预测准确率
print('模型的准确率为:\n',metrics.accuracy_score(y_test, bnb_pred))
print('模型的评估报告:\n',metrics.classification_report(y_test, bnb_pred))

模型的准确率为:
 0.84704998121

模型的评估报告:
              precision    recall     f1-score   support
   Negative      0.82        0.90      0.86      1341
   Positive      0.88        0.80      0.84      1320
   avg / total   0.85        0.85      0.85      2661

从模型的评估报告来看,预测为消极情绪的覆盖率0.9相比于积极情绪的覆盖率0.8要更高一些,但总体来说模型的预测效果还是不错的。同理,再绘制一下关于模型在测试数据集上的ROC曲线,代码如下:

# 计算正例Positive所对应的概率,用于生成ROC曲线的数据
y_score = bnb.predict_proba(X_test)[:,1]
fpr,tpr,threshold = metrics.roc_curve(y_test.map({'Negative':0,'Positive':1}), y_score)
# 计算AUC的值
roc_auc = metrics.auc(fpr,tpr)

# 绘制面积图
plt.stackplot(fpr, tpr, color='steelblue', alpha = 0.5, edgecolor = 'black')
# 添加边际线
plt.plot(fpr, tpr, color='black', lw = 1)
# 添加对角线
plt.plot([0,1],[0,1], color = 'red' , linestyle = '--')
# 添加文本信息
plt.text(0.5,0.3,'ROC curve (area = %0.2f)' % roc_auc)
# 添加x轴与y轴标签
plt.xlabel('1-Specificity')
plt.ylabel('Sensitivity')
# 显示图形
plt.show()

如上图所示,绘制的ROC曲线所对应的AUC值为0.93,同样是一个非常高的数值,再结合模型准确率、覆盖率等指标,可以认为该模型在测试数据集上的预测效果是非常理想的。需要说明的是,如果训练数据集是关于词语在各文档中出现的频次,直接调用BernoulliNB类是没有问题的,因为该“类”中参数binarize默认值为0,即如果词的频次大于0,则对应的变量值在模型运算时会转换成1,否则转换为0。


【结语】

OK,关于贝叶斯算法的实战我们就分享到这里,如果你有任何问题,欢迎在公众号的留言区域表达你的疑问。同时,也欢迎各位朋友继续转发与分享文中的内容,让更多的人学习和进步。


关注公众号,回复【贝叶斯】便可获得文内的数据和代码~

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/34025
 
776 次点击