朴素贝叶斯算法
1.基本概念
朴素贝叶斯算法是基于统计学概率的分类方法,通过计算样本发生的概率来实现分类。这里必须知道贝叶斯公式
p
(
X
Y
)
=
p
(
X
∣
Y
)
∗
p
(
Y
)
=
p
(
Y
∣
X
)
∗
p
(
X
)
(
1
)
p(XY)=p(X|Y)*p(Y)=p(Y|X)*p(X)\quad\quad\quad(1)
p(XY)=p(X∣Y)∗p(Y)=p(Y∣X)∗p(X)(1)
有些时候在直接计算P(Y|X)的条件概率时显得很困难,但是使用朴素贝叶斯公式就可以间接求解。当然使用贝叶斯公式求解问题的前提是,各个事件相互独立。举个例子吧
在这份数据集中,事件年龄和有工作是互不影响的,两者没有任何关系,他们之间就是相互独立的,当然在现实生活中肯定会有一些影响。记事件年龄为0的情况下同意贷款的概率为P(Y|x1),直接求这个概率就显得很棘手,但是使用贝叶斯公式后,就很简单了
p
(
Y
∣
x
1
)
=
p
(
X
1
∣
Y
)
∗
p
(
Y
)
p
(
x
1
)
=
2
9
∗
9
15
5
15
(
2
)
p(Y|x_1)=\frac{p(X_1|Y)*p(Y)}{p(x_1)}=\frac{\frac{2}{9}*\frac{9}{15}}{\frac{5}{15}}\quad\quad\quad(2)
p(Y∣x1)=p(x1)p(X1∣Y)∗p(Y)=15592∗159(2)
2.朴素贝叶斯分类器
朴素贝叶斯分类器就是分别计算在该数据样本中的情况下是类别A的概率
p
(
A
∣
X
)
=
p
(
X
∣
A
)
∗
p
(
A
)
p
(
X
)
(
3
)
p(A|X)=\frac{p(X|A)*p(A)}{p(X)}\quad\quad\quad(3)
p(A∣X)=p(X)p(X∣A)∗p(A)(3)
是该数据样本中的情况下是类别B的概率
p
(
B
∣
X
)
=
p
(
X
∣
B
)
∗
p
(
B
)
p
(
X
)
(
4
)
p(B|X)=\frac{p(X|B)*p(B)}{p(X)}\quad\quad\quad(4)
p(B∣X)=p(X)p(X∣B)∗p(B)(4)
然后比较两种大小,取概率大的作为该数据样本的类别。当然这是一个二分类问题,多分类问题也是类似比较各个分类的条件概率,然后取概率大作为数据样本的分类。
对于多个特征维度X的情况下,P(X|A)计算时需要拆分成P(x1|A)*P(x1|A)*P(x1|A),但是如果其中一项概率为零,会影响整个概率计算结果,为了解决这个问题,初始化过程中设为1,不要设为0.同时还存在另外一个问题就是如果各个概率过小,计算时可能会出现下溢出(很多小数相乘会四舍五入为0),为了解决这个问题,计算过程中取对数
l
n
(
p
(
X
∣
A
)
)
=
l
n
(
p
(
x
1
∣
A
)
)
+
l
n
(
p
(
x
2
∣
A
)
)
+
l
n
(
p
(
x
3
∣
A
)
)
+
.
.
.
(
5
)
ln(p(X|A))=ln(p(x_1|A))+ln(p(x_2|A))+ln(p(x_3|A))+...\quad(5)
ln(p(X∣A))=ln(p(x1∣A))+ln(p(x2∣A))+ln(p(x3∣A))+...(5)
在观察公式3,4我们发现分母都是一样的,所以这里可以简化计算,只比较分子的大小
3.使用朴素贝叶斯过滤垃圾邮件
#导入第三方科学计算包
import random
from numpy import *
#导入正则表达式模块
import re
#统计文档中所有单词
def createVocabList(dataSet):
#定义并初始化词汇列表
vocabSet=set([])
#对于数据集中每一句话迭代
for document in dataSet:
#将每一句话中的单词与词汇集合并并去重
vocabSet=vocabSet|set(document)
#返回词汇列表
return list(vocabSet)
#将词汇表转换为文档向量
def setOfWordsVec(vocabList,inputSet):
#定义并初始化返回的词汇向量
returnVec=[0]*len(vocabList)
#对于传入的话的每一个单词迭代
for word in inputSet:
#如果该单词在词汇列表中
if word in vocabList:
#对相应的词汇向量加一
returnVec[vocabList.index(word)]+=1
#返回词汇向量
return returnVec
#贝叶斯分类器
def trainNB(trainMatrix,trainCategory):
#获得数据集中有个样本数据(多少句话)
numTrainDocs=len(trainMatrix)
#获取词汇表的数据
numWords=len(trainMatrix[0])
#计算侮辱性样本的概率
pAbusive=sum(trainCategory)/float(numTrainDocs)
#定义并初始化非侮辱性样本数目
p0Num=ones(numWords)
# 定义并初始化侮辱性矩阵
p1Num=ones(numWords)
p0Denom=2.0
p1Denom=2.0
#对每个样本迭代(每一句话)
for i in range(numTrainDocs):
#如果样本是侮辱性
if trainCategory[i]==1:
#加上该侮辱性性矩阵
p1Num+=trainMatrix[i]
#计算样本单词的数目
p1Denom+=sum(trainMatrix[i])
else:
#加上非侮辱性矩阵
p0Num+=trainMatrix[i]
#计算样本中单词数目
p0Denom+=sum(trainMatrix[i])
#计算条件概率:在侮辱性的情况下,各个单词出现的概率
p1Vect=log(p1Num/p1Denom)
# 计算条件概率:在非侮辱性的情况下,各个单词出现的概率
p0Vect=log(p0Num/p0Denom)
#返回侮辱性概率,在侮辱性的情况下,各个单词出现的概率,在侮辱性的情况下,各个单词出现的概率
return p0Vect,p1Vect,pAbusive
#使用朴素贝叶斯分类器进行分类
def classifyNB(vecClassify,p0Vec,p1Vec,pClass1):
#计算条件概率:在该邮件的情况下,是垃圾邮件的概率(计算时由于两者分母相同,故省去了分母)
p1=sum(vecClassify*p1Vec)+log(pClass1)
#计算条件概率:在该邮件的情况下,是正常邮件的概率(计算时由于两者分母相同,故省去了分母)
p0=sum(vecClassify*p0Vec)+log(1.0-pClass1)
#比较两者概率,返回概率大的哪个类别
if p1>p0:
return 1
else:
return 0
#邮件文件解析
def textParse(bigString):
#使用正则表达式处理数据文件
regEx=re.compile(r'\W')
listOfTokens=regEx.split(bigString)
#筛选出字符长度大于2单词,并且改为小写
return [tok.lower() for tok in listOfTokens if len(tok)>2]
#垃圾邮件测试
def spamTest():
#定义邮件内容数据集
docList=[]
#定义邮件种类
classList=[]
#
fullText=[]
#对26封邮件迭代处理
for i in range(1,26):
#对邮件内容处理
wordList=textParse(open("D:/学习资料/机器学习实战/《机器学习实战》源代码/machinelearninginaction/Ch04/email/spam/%d.txt" % i).read())
#将每一封邮件作为一个数据样本存入邮件数据集
docList.append(wordList)
#
fullText.extend(wordList)
#定义邮件种类为垃圾邮件
classList.append(1)
#对邮件内容处理
wordList=textParse(open("D:/学习资料/机器学习实战/《机器学习实战》源代码/machinelearninginaction/Ch04/email/ham/%d.txt" %i).read())
# 将每一封邮件作为一个数据样本存入邮件数据集
docList.append(wordList)
fullText.extend(wordList)
# 定义邮件种类为垃圾邮件
classList.append(0)
#获取所有邮件的词汇表
vocabList=createVocabList(docList)
#定义并初始化训练集
trainingSet=list(range(50))
#定义并初始测试集
testSet=[]
#从0-9迭代
for i in range(10):
#在1-50间生成分布均匀的随机数
randIndex =int(random.uniform(0,len(trainingSet)))
# 在测试数据集中添加随机生成下标所对应的训练数据集
testSet.append(trainingSet[randIndex])
#在训练数据集中删除添加的测试数据集
del (trainingSet[randIndex])
# 初始化训练集词汇向量表
trainMat=[]
# 定义并初始化训练集标签
trainClasses=[]
#对训练集中的每一份邮件样本迭代
for docIndex in trainingSet:
#将样本邮件中的词汇向量添加到存储词汇向量表
trainMat.append(setOfWordsVec(vocabList,docList[docIndex]))
#将样本邮件类别添加到存储邮件类别的列表中
trainClasses.append(classList[docIndex])
#在非垃圾邮件概率的情况下,各个单词出现的概率,在垃圾邮件的情况下,各个单词出现的概率,获取垃圾邮件概率
p0v,p1v,pSpm=trainNB(array(trainMat),array(trainClasses))
#定义并初始话错误数目
errorCount=0.0
#对测试集中每一份邮件样本迭代
for docIndex in testSet:
#获取测试邮件样本的词汇向量表
wordVector=setOfWordsVec(vocabList,docList[docIndex])
#如果预测结果与实际不符
if classifyNB(array(wordVector),p0v,p1v,pSpm)!=classList[docIndex]:
errorCount+=1
print(f"错误率是{float(errorCount)/len(testSet)}")
#测试
if __name__=='__main__':
spamTest()
4.总结
朴素贝叶斯分类常用于垃圾邮件检测,污秽语言检测,它要求各个时间之间相互独立,互不影响.