社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

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

数据分析1480 • 7 年前 • 902 次点击  

【前言】

在《从零开始学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