高斯贝叶斯分类器的计算过程还是比较简单的,其关键的核心是假设数值型变量服从正态分布,如果实际数据近似服从正态分布,分类结果会更加准确。sklearn模块提供了实现该分类器的计算功能,它就是naive_bayes子模块中的GaussianNB类,由于该“类”仅包含一个参数,且参数的默认值是以各类别的频率作为先验概率,因此在调用GaussianNB类构造高斯贝叶斯分类器时,可以不传递任何参数值
GaussianNB(priors=None)
priors:用于指定因变量各类别的先验概率,默认以数据集中的类别频率作为先验概率。
面部皮肤区分数据集来自于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'Skin_Segment.xlsx')
# 设置正例和负例
skin.y = skin.y.map({2:0,1:1})
skin.y.value_counts()
out:
0 194198
1 50859
Name: y, dtype: int64
skin.head()
B G R y
0 74 85 123 1
1 73 84 122 1
2 72 83 121 1
3 70 81 119 1
4 70 81 119 1
如上结果所示,因变量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)
# 导入第三方模块
from sklearn import naive_bayes
# 调用高斯朴素贝叶斯分类器的“类”
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
dtype: int64
通过构建高斯朴素贝叶斯分类器,实现测试数据集上的预测,经统计,预
测为负例的一共有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()
print('模型的准确率为:\n',metrics.accuracy_score(y_test, gnb_pred))
print('模型的评估报告:\n',metrics.classification_report(y_test, gnb_pred))
out:
模型的准确率为:
0.9229576430261976
模型的评估报告:
precision recall f1-score support
0 0.93 0.97 0.95 48522
1 0.88 0.73 0.80 12743
accuracy 0.92 61265
macro avg 0.90 0.85 0.88 61265
weighted avg 0.92 0.92 0.92 61265
将混淆矩阵做了可视化处理,其中主对角线的数值表示正确预测的样本量,剩余的4 720条样本为错误预测的样本。经过对混淆矩阵的计算,可以得到模型的整体预测准确率为92.30%;进一步可以得到每个类别的预测精准率(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()
计算得到的AUC值为0.94,超过用于评判模型好坏的阈值0.8,故可以认为构建的贝叶斯分类器是非常理想的,进而验证了前文所得的结论。
最后需要强调的是,利用高斯贝叶斯分类器对数据集进行分类时要求输入的数据集X为连续的数值型变量。
如果数据集中的自变量X均为离散型变量,就无法使用高斯贝叶斯分类器,而应该选择多项式贝叶斯分类器。
多项式贝叶斯分类器的计算过程也同样比较简单,如需使用Python实现该分类器的构造,可以直接导入sklearn的子模块naive_bayes模块,然后调用MultinomialNB类。
MultinomialNB(alpha = 1.0, fit_prior = True, class_prior = None)
alpha:用于指定平滑系数α的值,默认为1.0。
fit_prior:bool类型参数,是否以数据集中各类别的比例作为P(Ci)的先验概率,默认为True。
class_prior:用于人工指定各类别的先验概率P(Ci),如果指定该参数,则参数fit_prior不再有效。
蘑菇数据集来自于UCI网站,一共包含8 124条观测和22个变量,其中因变量为type,表示蘑菇是否有毒,剩余的自变量是关于蘑菇的形状、表面光滑度、颜色、生长环境等。首先将该数据集读入Python
# 导入第三方包
import pandas as pd
# 读取数据
mushrooms = pd.read_csv(r'mushrooms.csv')
# 数据的前5行
mushrooms.head()
out:
type cap_shape cap_surface cap_color bruises odor gill_attachment gill_spacing gill_size gill_color ... stalk_surface_above_ring stalk_surface_below_ring stalk_color_above_ring stalk_color_below_ring veil_color ring_number ring_type spore_print_color population habitat
0 poisonous convex smooth brown yes pungent free close narrow black ... smooth smooth white white white one pendant black scattered urban
1 edible convex smooth yellow yes almond free close broad black ... smooth smooth white white white one pendant brown numerous grasses
2 edible bell smooth white yes anise free close broad brown ... smooth smooth white white white one pendant brown numerous meadows
3 poisonous convex scaly white yes pungent free close narrow brown ... smooth smooth white white white one pendant black scattered urban
4 edible convex smooth gray no none free crowded broad black ... smooth
表中的所有变量均为字符型的离散值,由于Python建模过程中必须要求自
变量为数值类型,因此需要对这些变量做因子化处理,即把字符值转换为对应的数值。接下来利用pandas模块中的factorize函数对离散的自变量进行数值转换
# 将字符型数据作因子化处理,将其转换为整数型数据
columns = mushrooms.columns[1:]
for column in columns:
mushrooms[column] = pd.factorize(mushrooms[column])[0]
mushrooms.head()
out:
type cap_shape cap_surface cap_color bruises odor gill_attachment gill_spacing gill_size gill_color ... stalk_surface_above_ring stalk_surface_below_ring stalk_color_above_ring stalk_color_below_ring veil_color ring_number ring_type spore_print_color population habitat
0 poisonous 0 0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0 0 0
1 edible 0 0 1 0 1 0 0 1 0 ... 0 0 0 0 0 0 0 1 1 1
2 edible 1 0 2 0 2 0 0 1 1 ... 0 0 0 0 0 0 0 1 1 2
3 poisonous 0 1 2 0 0 0 0 0 1 ... 0 0 0 0 0 0 0 0 0 0
4 edible 0 0 3 1 3 0 1 1 0 ... 0
所有的字符型变量全部转换成了数值,而且每一列中的数值都代表了各自
不同的字符值。需要注意的是,factorize函数返回的是两个元素的元组,第一个元素为转换成的数值,第二个元素为数值对应的字符水平,所以在类型转换时,需要通过索引方式返回因子化的值
from sklearn import model_selection
# 将数据集拆分为训练集合测试集
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)
from sklearn import naive_bayes
from sklearn import metrics
import seaborn as sns
import matplotlib.pyplot as plt
# 构建多项式贝叶斯分类器的“类”
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('Real')
plt.ylabel('Predict')
# 显示图形
plt.show()
# 模型的预测准确率
print('模型的准确率为:\n',metrics.accuracy_score(y_test, mnb_pred))
print('模型的评估报告:\n',metrics.classification_report(y_test, mnb_pred))
out:
模型的准确率为:
0.8700147710487445
模型的评估报告:
precision recall f1-score support
edible 0.85 0.92 0.88 1072
poisonous 0.90 0.82 0.86 959
accuracy 0.87 2031
macro avg 0.87 0.87 0.87 2031
weighted avg 0.87 0.87 0.87 2031
在如上的混淆矩阵图中,横坐标代表测试数据集中的实际类别值,纵坐标为预测类别值,正确预测无毒的有981个样本,正确预测有毒的有786个样本。基于混淆矩阵的进一步运算,可以得到如上所示的两部分结果,并从中发现,模型在测试数据集上的整体预测准确率为87%,
而且从各类别值来看,无毒蘑菇的预测覆盖率为92%、有毒蘑菇的预测覆盖率为82%。总体来说,模型的预测效果还是非常理想的,接下来继续绘制ROC曲线,查看对应的AUC值的大小,
from sklearn import metrics
# 计算正例的预测概率,用于生成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值下的概率不服从多项式分布的话,分类器的预测效果就不会很理想。通常情况下,会利用多项式贝叶斯分类器作文本分类,如一份邮件是否垃圾邮件、用户评论是否
为正面等。
当数据集中的自变量X均为0-1二元值时(例如在文本挖掘中,判断某个词语是否出现在句子中,出现用1表示,不出现用0表示),通常会优先选择伯努利贝叶斯分类器。利用该分类器计算概率值P(X|Ci)时,会假设自变量X的条件概率满足伯努利分布
伯努利贝叶斯分类器的计算与多项式贝叶斯分类器的计算非常相似,在文本分类问题中,如果构造的数据集是关于词语出现的次数,通常会选择多项式贝叶斯分类器进行预测;如果构造的数据集是关于词语是否会出现的0-1值,则会选择伯努利贝叶斯分类器进行预测。当读者需要构造伯努利贝叶斯分类器时,可以直接调用sklearn子模块naive_bayes中的BernoulliNB类。
BernoulliNB (alpha = 1.0, binarize=0.0, fit_prior = True, class_prior = None)
alpha:用于指定平滑系数α的值,默认为1.0。
binarize:如果该参数为浮点型数值,则将以该值为界限,当自变量的值大于该值时,自变量的值将被转换为1,否则被转换为0;如果该参数为None时,则默认训练数据集的自变量均为0-1值。
fit_prior:bool类型参数,是否以数据集中各类别的比例作为P(Ci
)的先验概率,默认为True。
class_prior:用于人工指定各类别的先验概率P(Ci),如果指定该参数,则参数fit_prior不再有效。
用户对其购买的蚊帐进行评论,该数据集是通过爬虫的方式获得,一共包含10 644条评论,数据集中的Type变量为评论所对应的情绪。首先将爬虫获得的数据集读入Python中,并预览前几行数据
import pandas as pd
# 读入评论数据
evaluation = pd.read_excel(r'Contents.xlsx',sheet_name=0)
# 查看数据前10行
evaluation.head(10)
out:
NickName Date Content Type
0 AdJQKzNaZWAV 2016-04-14 23:30:42 想知道是不是卖家给我发错货了,怎么四个连接铁通的仅一个能连上,**块钱的东西说便宜也不至于廉... Negative
1 sdmjk 2013-06-24 22:37:51 垃圾。\n两个管两头一样粗,得自己用钳子摄细才能装上\n管子很软很细\n总的评价 - 就是两... Negative
2 f***n 2015-06-05 21:10:31 我就无语了...难弄到死..又没说明书..过段差评.. Negative
3 jd_817039867 2014-04-13 22:43:38 不满意,明明写的落地!结果差一截!而且自垂度不怎么好~还要用夹子夹!没有我在附近小超市买的质... Negative
4 jd_wscj529 2014-06-09 13:06:17 标的次日到达,结果快递用了四天,蚊帐杆底座太小,管壁太薄,而且蚊帐也没宣传那么垂地,此次购物... Negative
5 q***r 2017-04-25 00:24:25 真的很好,比超市卖的便宜多了,刚回来打算在买个给我嫂子家,发货也很快没想到这么便宜也能买的这... Positive
6 A***波 2017-05-11 06:53:47 最不满意的一次网购。直接看图。1.8的床两头1.8中间只有1.5了,两边的纱像少了一截,绷得... Negative
7 巴***住 2017-05-15 11:05:01 很不错的,就是拉丝不够好啊 Positive
8 j***w 2016-05-15 09:25:49 薄,漂亮 Positive
9 1***n 2017-05-15 09:00:43 味道有点大,线头有点多。装好后有点摇晃,拉链处开了几处,要不是等着急用,真心是要退 Positive
数据集包含4个字段,分别是用户昵称、评价时间、评价内容和对应的评价
情绪。从评价内容来看,会有一些“脏”文本在内,如数字、英文等,所以需要将这些“脏”文本删除
# 运用正则表达式,将评论中的数字和英文去除
evaluation.Content = evaluation.Content.str.replace('[0-9a-zA-Z]','')
evaluation.head()
out:
NickName Date Content Type
0 AdJQKzNaZWAV 2016-04-14 23:30:42 想知道是不是卖家给我发错货了,怎么四个连接铁通的仅一个能连上,**块钱的东西说便宜也不至于廉... Negative
1 sdmjk 2013-06-24 22:37:51 垃圾。\n两个管两头一样粗,得自己用钳子摄细才能装上\n管子很软很细\n总的评价 - 就是两... Negative
2 f***n 2015-06-05 21:10:31 我就无语了...难弄到死..又没说明书..过段差评.. Negative
3 jd_817039867 2014-04-13 22:43:38 不满意,明明写的落地!结果差一截!而且自垂度不怎么好~还要用夹子夹!没有我在附近小超市买的质... Negative
4 jd_wscj529 2014-06-09 13:06:17 标的次日到达,结果快递用了四天,蚊帐杆底座太小,管壁太薄,而且蚊帐也没宣传那么垂地,此次购物... Negative
经过数据的初步清洗后,下一步要做的就是对文本进行切词,但在切词前,通常需要引入用户自定义的词库和停止词。利用词典的目的是将无法正常切割的词实现正确切割(如“沙瑞金书记”会被切词为“沙”“瑞金”“书记”,为了避免这种情况,就需要将类似“沙瑞金”这样的词组合为词库),使用停止词的目的是将句子中无意义的词语删除(如“的”“啊”“我们”等)。
# 导入第三方包
import jieba
# 加载自定义词库
jieba.load_userdict(r'all_words.txt')
# 读入停止词
with open(r'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 垃圾 \n 钳子 摄细 装 \n 管子 很软 \n 评价 垃圾
2 我就 无语 难弄 .. 说明书 .. 过段 差评 ..
3 不满意 写 落地 差一截 垂度 ~ 夹子 夹 没有 超市 买 质量好 换季 卖得 价钱 便宜
4 标的 到达 快递 四天 蚊帐 底座 太小 管壁 太薄 蚊帐 也没 宣传 垂地 购物 失败
Name: Content, dtype: object
如上结果所示,通过调入第三方包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()
out:
一根 下单 不值 不好 不想 不满意 不知道 不行 不错 买回来 ... 还好 还行 退货 送货 速度 钢管 防蚊 非常好 颜色 麻烦
0 0 0 0 0 0 0 0 0 0 0 ... 0 0 1 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
2 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 1 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
5 rows × 99 columns
将文档词条矩阵转换为数据框后得到一个庞大的稀疏矩阵,即数据框中的
大部分值为0。为了避免数据框的列数过多,在构造文档词条矩阵时做了相应的限制条件,即代码中的CountVectorizer(min_df = 0.01),表示词语所对应的文档数目必须在所有文档中至少占1%的比例,最终得到表12-8中所呈现的99个变量。有了如上的数据框,接下来要做的就是将数据集拆分为训练集和测试集,并利用训练集构建伯努利贝叶斯分类器,利用测试集对分类器的预测效果进行评估
from sklearn import model_selection
from sklearn import naive_bayes
from sklearn import metrics
import matplotlib.pyplot as plt
import seaborn as sns
# 将数据集拆分为训练集和测试集
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()
# 模型的预测准确率
print('模型的准确率为:\n',metrics.accuracy_score(y_test, bnb_pred))
print('模型的评估报告:\n',metrics.classification_report(y_test, bnb_pred))
out:
模型的准确率为:
0.8467317806160781
模型的评估报告:
precision recall f1-score support
Negative 0.82 0.89 0.85 1341
Positive 0.88 0.80 0.84 1321
accuracy 0.85 2662
macro avg 0.85 0.85 0.85 2662
weighted avg 0.85 0.85 0.85 2662
# 计算正例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()