目录
一、朴素贝叶斯算法概述
朴素贝叶斯(Naïve Bayes, NB)算法,是一种基于贝叶斯定理与特征条件独立假设的分类方法。朴素:特征条件独立;贝叶斯:基于贝叶斯定理。
属于监督学习的生成模型,实现简单,并有坚实的数学理论(即贝叶斯定理)作为支撑。在大量样本下会有较好的表现,不适用于输入向量的特征条件有关联的场景。
二、朴素贝叶斯算法原理
1. 贝叶斯定理
定义
贝叶斯理论是以18世纪的一位神学家托马斯.贝叶斯(Thomas Bayes)命名。通常,事件A在事件B(发生)的条件下的概率,与事件B在事件A(发生)的条件下的概率是不一样的。然而,这两者是有确定的关系的,贝叶斯定理就是这种关系的陈述。
(1)条件概率
定义
就是事件 A 在另外一个事件 B 已经发生条件下的发生概率。条件概率表示为P(A|B),读作“在 B 发生的条件下 A 发生的概率”。
条件概率公式
设A,B是两个事件,且P(B)>0,则在事件B发生的条件下,事件A发生的条件概率为:
P(A|B)=P(AB)/P(B)
(2)联合概率
定义
表示两个事件共同发生(数学概念上的交集)的概率。A 与 B 的联合概率表示为联合概率。
联合概率公式
由条件概率公式得出乘法公式:
P(AB)=P(A|B)P(B)=P(B|A)P(A)
(3)贝叶斯公式
P(A|B) = P(AB)/P(B)可变形为:
即为贝叶斯公式。
先验概率——即P(A),在B事件发生前根据以往经验就可求得的A概率,如收到一封邮件,是垃圾邮件。
后验概率——即P(A|B),在B发生后对A发生概率进行的重新评估,如收到一封邮件,如果含有某个词这个邮件是垃圾邮件。
(4)全概率公式
即:
2. 朴素贝叶斯公式
朴素贝叶斯方法是基于贝叶斯定理和特征条件独立假设的分类方法。对于给定的训练数据集,首先基于特征条件独立假设学习输入/输出的联合概率分布;然后基于此模型,对给定的输入x,利用贝叶斯定理求出后验概率最大的输出y。
朴素贝叶斯与贝叶斯不同的点为——朴素贝叶斯采用了属性条件独立性假设,即每个属性对结果产生独立的影响。
公式
令 x 表示训练集 X 中可能的类别数,表示第i个属性可能的取值数,则贝叶斯公式可修正为
因为可能有数据收集不全可能的情况即有的数据为0导致直接为另一个结果,所以可以利用拉普拉斯修正,公式为:
Ni为第i个属性可能的取值数
因为所有结果值都为小数所以可能在最后累乘时产生下溢出情况,所以可采用对数化:
防止下溢出。
三、代码
1. 数据集
本次实验中,所采用的数据集来源:
数据集分为spam(垃圾)和ham(正常)
spam邮件:
ham邮件:
2. 代码实现:
1、创建词汇表
#创建词汇表
def createwordList(dataSet):
#dataSet:包含多个文档的数据集
vocabSet = set([])#去重词汇表
for document in dataSet:
vocabSet=vocabSet|set(document)#取并集
return list(vocabSet)
2、建立词袋、词集模型
词集模型:单词构成的集合,集合自然每个元素都只有一个,也即词集中的每个单词都只有一个。
词袋模型:在词集的基础上如果一个单词在文档中出现不止一次,统计其出现的次数(频数)。
不同:词袋在词集有没有某词的基础上更关注有几个某词。
#词集模型
def setOfWords(vocabList,inputSet):
#vocabList:去重词汇表
#inputSet:输入文档
returnVec=[0]*len(vocabList) #建立和去重词汇表相同长度的全0向量
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)]=1 #遍历所有单词,如果存在使相应向量=1
else:print("妹这个单词哦~")
return returnVec
#词袋模型
def bagOfWords(vocabList,inputSet):
returnVec=[0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)]+=1
return returnVec
3、贝叶斯公式
#训练朴素贝叶斯
def trainBYS(trainMatrix,trainClasses):
#trainMatrix:每个returnVec组成的矩阵
#trainClasses:每个reVec对应的类别,1侮辱类 0正常类
numTrainDocs=len(trainMatrix) #总文档数
numWords=len(trainMatrix[0]) #每个文档总字数
p_1=sum(trainClasses)/float(numTrainDocs) #文档属于侮辱类
#拉普拉斯修正_防止下溢出
p0Num=np.ones(numWords) #分子各单词出现数初始化为1
p1Num=np.ones(numWords)
p0Denom=2.0 #分母总单词数初始化为类别数 2
p1Denom=2.0
for i in range(numTrainDocs): #遍历训练样本
if trainClasses[i]==1:
p1Num+=trainMatrix[i] #侮辱类各个单词数量
p1Denom+=sum(trainMatrix[i]) #侮辱类总单词数量
else:
p0Num+=trainMatrix[i]
p0Denom+=sum(trainMatrix[i])
p1Vect=np.log(p1Num/p1Denom) #取对数
p0Vect=np.log(p0Num/p0Denom)
#print(p0Vect)
return p0Vect,p1Vect,p_1
4、分类文档
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
'''
对测试文档进行分类
Parameter:
vec2Classify:测试文档向量
p0Vec:正常类中每一个词的条件概率
p1Vec:侮辱类中每一个词的条件概率
pClass1:侮辱类占总样本概率
'''
p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)
p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
5、所有单词转化为小写
#将字符串转换为小写字符列表
def textParse(bigString):
'''
Parameter:
bigString:输入字符串
Return:
tok.lower():小写字符列表
'''
#将特殊符号作为切分标志进行字符串切分,即非字母、非数字
listOfTokens = re.split(r'\W+', bigString)
#除了单个字母,其它单词变成小写
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
6、测试分类器
#测试朴素bys分类器
def spamTest(method='bag'):
#method有bag词袋和set词集两中
if method == 'bag': #判断使用词袋模型还是词集模型
words2Vec = bagOfWords
elif method == 'set':
words2Vec = setOfWords
docList=[]
classList=[]
#遍历文件夹
for i in range(1,26):
#读取垃圾邮件 转化乘字符串列表
wordList=textParse(open('C:/Users/kid/Desktop/ML/machine-learning-master/Naive Bayes/email/spam/%d.txt' % i,'r').read())
#将列表记录加入文档列表并分类为1 侮辱类
docList.append(wordList)
classList.append(1)
#读取正常文件
wordList=textParse(open('C:/Users/kid/Desktop/ML/machine-learning-master/Naive Bayes/email/ham/%d.txt' % i,'r').read())
docList.append(wordList)
classList.append(0)
#创建去重词汇表
vocabList=createwordList(docList)
#print(vocabList)
#创建索引
trainSet=list(range(50))
testSet=[]
#分割测试急
for i in range(10):
#从索引中随机抽取十个并从索引中删除十个
randIndex=int(random.uniform(0,len(trainSet)))
testSet.append(trainSet[randIndex])
del(trainSet[randIndex])
#创建训练集矩阵和类别向量
trainMat=[]
trainClasses=[]
for docIndex in trainSet:
#将生成模型添加到训练矩阵并记录类别
trainMat.append(words2Vec(vocabList,docList[docIndex]))
trainClasses.append(classList[docIndex])
#训练bys
p0V,p1V,pSpam=trainBYS(np.array(trainMat),np.array(trainClasses))
#错误分类器
error=0
for docIndex in testSet:
wordVec=words2Vec(vocabList,docList[docIndex])
#测试
if classifyNB(np.array(wordVec),p0V,p1V,pSpam)!=classList[docIndex]:
error+=1
#print("分类错误的:",docList[docIndex])
#print("错误率:%.2f%%"%(float(error)/len(testSet)*100))
errorRate=float(error)/len(testSet)#分类错误率
return errorRate
7、调用进行实验
if __name__ == "__main__":
total = 100
print('使用词袋模型训练:')
sum_bag_error = 0
for i in range(total):
sum_bag_error += spamTest(method = 'bag')
print('使用词袋模型训练' + str(total) + '次得到的平均错误率为: ' + str((sum_bag_error / total)))
结果为:
四、总结
朴素贝叶斯优缺点:
优点:在数据较少的情况下仍然有效,可以处理多类别问题
缺点:对于输入数据的准备方式较为敏感,由于朴素贝叶斯的“特征条件独立”特点,所以会带来一些准确率上的损失