一、什么是贝叶斯定理?
在讲解之前,我们可以先看一下百度百科的有关词条:
贝叶斯定理是关于随机事件事件A和B的条件概率(或边缘概率)的一则定理。其中P(A|B)是在B发生的情况下A发生的可能性。
贝叶斯定理也称贝叶斯推理,早在18世纪,英国学者贝叶斯(1702~1763)曾提出计算条件概率的公式用来解决如下一类问题:假设H[1],H[2]…,H[n]互斥且构成一个完全事件,已知它们的概率P(H[i]),i=1,2,…,n,现观察到某事件A与H[1],H[2]…,H[n]相伴随机出现,且已知条件概率P(A|H[i]),求P(H[i]|A)。
如你所见,数学的概念总是比较抽象的,那么为了更好的理解贝叶斯定理,这里我将先引用一个例子:
假设一个常规的检测结果的敏感度与可靠度均为99%,也就是说,当被检者吸毒时,每次检测呈阳性的概率为99%。而被检者不吸毒时,每次检测呈阴性的概率为**99%。从检测结果的概率来看,检测结果是比较准确的,但是贝叶斯定理却可以揭示一个潜在的问题。假设某公司将对其全体雇员进行一次鸦片吸食情况的检测,已知有0.5%**的雇员吸毒。我们想知道,每位医学检测呈阳性的雇员吸毒的概率有多高?令“A”为雇员吸毒事件,“B”为检测结果呈阳性,那么我们想知道如果一次检查中 员工检测结果为阳性且本身是吸毒的概率是多少时,该如何考虑呢?
这里首先我们要圈出几个敏感数字:99%、0.5%
读题我们可知:
吸毒且呈阳性:99%
不吸毒但呈阳性:1-99%=0.1%
那么根据贝叶斯公式:
P
(
A
∣
B
)
=
P
(
A
)
⋅
P
(
B
∣
A
)
P
(
B
)
P(A|B) = \frac{P(A)·P(B|A)}{P(B)}
P(A∣B)=P(B)P(A)⋅P(B∣A)
可将问题转化为:
P
(
本
身
吸
毒
∣
检
测
为
阳
性
)
=
P
(
吸
毒
)
P
(
检
测
为
阳
性
∣
吸
毒
)
P
(
检
测
为
阳
性
)
P(本身吸毒|检测为阳性) = \frac{P(吸毒)P(检测为阳性|吸毒)}{P(检测为阳性)}
P(本身吸毒∣检测为阳性)=P(检测为阳性)P(吸毒)P(检测为阳性∣吸毒)
那么:
P(吸毒)= 0.5% = 0.005
P(检测为阳性|吸毒) = 99% = 0.99
P(检测为阳性) = P(吸毒且被检测为阳性)+ P(不吸毒且被检测为阳性)= (99% * 0.5%) + (1% * 99.5%) = 0.0149
带入公式可得:
P
(
本
身
吸
毒
∣
检
测
为
阳
性
)
=
P
(
吸
毒
)
P
(
检
测
为
阳
性
∣
吸
毒
)
P
(
检
测
为
阳
性
)
=
0.005
∗
0.99
0.0149
=
0.3322
P(本身吸毒|检测为阳性) = \frac{P(吸毒)P(检测为阳性|吸毒)}{P(检测为阳性)}=\frac{0.005 * 0.99}{0.0149}=0.3322
P(本身吸毒∣检测为阳性)=P(检测为阳性)P(吸毒)P(检测为阳性∣吸毒)=0.01490.005∗0.99=0.3322
分析一下这个问题:
尽管检测的结果可靠性已经很高,但是我们只能得到:
如果某人检测结果呈阳性,那么这个人吸毒的概率大概是33%,也就是说这个人不吸毒的可能性比吸毒更大。
在本题中,测试条件(雇员吸毒)越难发生,那么发生误判的可能性就越大,但如果让该名雇员继续复查,则本题中的测试条件将从0.5%变为33.22%,再使用贝叶斯定理计算一次的话,将会得到新的结果:此人的吸毒概率将为98.01%!但还不仅局限于这个结果,如果再次复查,且用贝叶斯定理计算的话,那么此人吸毒的概率将为99.98%!已经超过了检测的可靠度。
二、贝叶斯应用在什么地方呢?
朴素贝叶斯(Naive Bayes)是一个基于贝叶斯理论的分类器。它会单独考量每一唯独特征被分类的条件概率,进而综合这些概率并对其所在的特征向量做出分类预测。
因此,朴素贝叶斯的基本数据假设是:各个维度上的特征被分类的条件概率之间是相互独立的。它经常被应用在文本分类中,包括互联网新闻的分类,垃圾邮件的筛选。
实例: 新闻分类
在本例中将用到Python下的Sklearn库,并需要导入一个14MB的数据集:20new-sbydate.tar.gz,在引入该文件的时候将通过
from sklearn.datasets import fetch_20newsgroups
# 从sklearn.datasets里导入新闻数据抓取器 fetch_20newsgroups
但在这里,初次使用该抓取器的读者将会报错,详细解决方案请各位读者移步我的另一篇博文:关于sklearn.datasets.fetch_20newsgroups下载报错的问题
实验开始
第一步: 引入我们需要用到的所有库,并获取文件中数据的条数
from sklearn.datasets import fetch_20newsgroups # 从sklearn.datasets里导入新闻数据抓取器 fetch_20newsgroups
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer # 从sklearn.feature_extraction.text里导入文本特征向量化模块
from sklearn.naive_bayes import MultinomialNB # 从sklean.naive_bayes里导入朴素贝叶斯模型
from sklearn.metrics import classification_report
news = fetch_20newsgroups(subset='all')
print('数据的长度为:',len(news.data))
效果图
第二步: 数据预处理:将训练集和测试集分割,文本特征向量化
#将训练集和测试集分割
X_train, X_test, y_train, y_test = train_test_split(news.data, news.target,test_size=0.25,random_state=33) # 随机采样25%的数据样本作为测试集
print (X_train[0] ) #查看训练样本
print (y_train[0:100] ) #查看标签
#文本特征向量化
vec = CountVectorizer()
X_train = vec.fit_transform(X_train)
X_test = vec.transform(X_test)
效果图
第三步: 使用贝叶斯算法对取出的样本进行训练
MNB = MultinomialNB() # 使用默认配置初始化朴素贝叶斯
MNB.fit(X_train, y_train) # 利用训练数据对模型参数进行估计
y_predict = MNB.predict(X_test) # 对参数进行预测
第四步: 获取预测到的结果
print('朴素贝叶斯分类器的精度为:', MNB.score(X_test, y_test))
print(classification_report(y_test, y_predict, target_names=news.target_names))
预测结果:
至此,本次案例就已经结束,下面总结一下有关贝叶斯的知识点
特点分析
朴素贝叶斯模型被广泛应用于海量互联网文本分类任务。由于其较强的特征条件独立假设,使得模型预测所需要估计的参数规模从幂指数量级向线性量级减少,极大地节约了内存消耗和计算时间。但是,也正是受这种强假设的限制,模型训练时无法将各个特征之间的联系考量在内,使得该模型在其他数据特征关联性较强的分类任务上的性能表现不佳。
在这里就必须要提到一个新的知识点了——NLP | 文本特征向量化方法
三、文本特征向量化方法
想要使用朴素贝叶斯模型对文本分类的时候,最重要的一点就是要先对文本中的数据进行处理
常见处理方法如下:
1、分词 即按照特定的格式或者要求对一段文字进行拆分
2、统计特定词在句中是否出现(词集模型)
3、统计特定词在句中出现的次数(词袋模型)
4、统计特定词在文档中的TFIDF值(词袋模型+IDF值)
什么是词集模型?
one-hot 编码向量化文本
这里采用一个自定义函数实现:
# %load one-hot.py
import numpy as np
import pandas as pd
import jieba
def do_onehot():
# 读取待编码的文件
file_path = 'txt.txt'
with open(file_path, encoding="utf-8") as f:
docs = f.readlines()
# 将文件每行分词,分词后的词语放入words中
words = []
for i in range(len(docs)):
docs[i] = jieba.lcut(docs[i].strip("\n"))
words += docs[i]
# 找出分词后不重复的词语,作为词袋,是后续onehot编码的维度
vocab = sorted(set(words), key=words.index)
# 建立一个M行V列的全0矩阵,M是文档样本数,这里是行数,V为不重复词语数,即编码维度
V = len(vocab)
M = len(docs)
onehot = np.zeros((M, V))
for i, doc in enumerate(docs):
for word in doc:
if word in vocab:
pos = vocab.index(word)
onehot[i][pos] = 1
onehot = pd.DataFrame(onehot, columns=vocab)
print(onehot)
return onehot
doonthot()
效果图
由于Pycharm的输出结果不太好看 这里我们选择在jupyter notebook上运行
什么是词袋模型?
TF-IDF文本向量化
实例:
# %load TF-IDF.py
import numpy as np
import pandas as pd
import math
import jieba
def doc2tfidf_matrix():
# 读取待编码的文件
file_path = 'txt.txt'
with open(file_path, encoding="utf-8") as f:
docs = f.readlines()
# 将文件每行分词,分词后的词语放入words中
# print(docs)
words = []
for i in range(len(docs)):
docs[i] = jieba.lcut(docs[i].strip("\n"))
words += docs[i]
print(docs)
# 找出分词后不重复的词语,作为词袋
vocab = sorted(set(words), key=words.index)
# 建立一个M行V列的全0矩阵,M问文档样本数,这里是行数,V为不重复词语数,即编码维度
V = len(vocab)
M = len(docs)
onehot = np.zeros((M, V)) # 二维矩阵要使用双括号
tf = np.zeros((M, V))
for i, doc in enumerate(docs):
for word in doc:
if word in vocab:
pos = vocab.index(word)
onehot[i][pos] = 1
tf[i][pos] += 1 # tf,统计某词语在一条样本中出现的次数
row_sum = tf.sum(axis=1) # 行相加,得到每个样本出现的词语数
# 计算TF(t,d)
tf = tf / row_sum[:, np.newaxis] # 分母表示各样本出现的词语数,tf为单词在样本中出现的次数,[:,np.newaxis]作用类似于行列转置
# 计算DF(t,D),IDF
df = onehot.sum(axis=0) # 列相加,表示有多少样本包含词袋某词
idf = list(map(lambda x: math.log10((M + 1) / (x + 1)), df))
# 计算TFIDF
tfidf = tf * np.array(idf)
tfidf = pd.DataFrame(tfidf, columns=vocab)
print(tfidf)
return tfidf
doc2tfidf_matrix()
效果图:
什么是哈希向量化文本?
哈希技巧是无固定状态的,它把任意的数据块映射到固定数目(n_features)的位置,并且保证相同的输入一定产生相同的输出,不同的输入尽可能产生不同的输出。它可以用并行,线上,流式传输创建特征向量,因为它初始化是不需要文集输入的。
哈希向量化可以缓解TfidfVectorizer在处理高维文本时内存消耗过大的问题。
TfidfVectorizer在执行时,需要先将词袋矩阵放入内存,再计算各位置单词的TFIDF值,如果词袋维度大,将占用过多内存,效率低,此时可以使用哈希向量化。
四、总结
朴素贝叶斯分类器与其他方法相比最大的优势或许就在于,它在接受大数据量训练和查询时所具备的的高速度。即使选用超大规模的训练集,针对每个项目通常也只会有相对较少的特征数,并且对项目的训练和分类页仅仅是针对特征概率的数学运算而已。
尤其当训练量逐渐递增时则更加如此。在不借助任何旧有训练数据的前提下,每一组新的训练数据都有可能会引起概率值变化。对于一个如垃圾邮件过滤这样的应用程序而言,支持增量式训练的能力是非常重要的,因为过滤程序时常要对新到的邮件进行训练,然后必须即可进行相应的调整;更何况,过滤程序也未必有权限访问已经收到的所有邮件信息。
朴素贝叶斯分类器的另一大优势是,对分类器实际学习状况的解释还是相对简单的。由于每个特征的概率值都被保存了起来,因此我们可以在任何时候查看数据库,找到最合适的特征来区分垃圾邮件和非垃圾邮件,或是编程语言和蛇。保存在数据库中的这些信息都很有价值,它们有可能被用于其他的应用程序,或者作为构筑这些应用程序的一个良好基础。
朴素贝叶斯分类器的最大缺陷就是,它无法处理基于特征组合所产生的变化结果。假设有如下这样一个场景,我们正在尝试从非垃圾邮件中鉴别出垃圾邮件来:假设我们构建的是一个Web应用程序,因为单词“online”市场会出现在你的工作邮件中。而你的好友则在一家药店工作,并且喜欢给你发一些他碰巧在工作中遇到的奇闻趣事。同时,和大多数不善于严密保护自己邮件地址的人一样,偶尔你也会收到一封包含单词”online pharmacy“的垃圾邮件。