目录
一、贝叶斯算法概述
1、贝叶斯要解决的问题:
正向概率:假设袋子里面有N个白球,M个黑球,你伸手进去摸一把,摸出黑球的概率是多大?
逆向概率:如果我们事先并不知道袋子里面黑白球的比例,而是闭着眼睛摸出一个(或几个)球,观察这些取出来的球的颜色之后,那么我们可以就此对袋子里面的黑白球的比例做出什么样的推测?
2、贝叶斯公式推导
用一个实例进行推导:
一个学校男生占60%,女生占40%,男生总是穿长裤,女生一半穿长裤一半穿裙子。
·正向概率:随机选取一个学生,ta穿长裤的概率和穿裙子的概率。
·逆向概率:你看到一位穿长裤的学生,你只看见ta穿的是长裤,而无法确定ta的性别,推断ta是女生的概率。
假设学校里面的人总数是U个,则穿长裤的男生:
,
穿长裤的女生:
,
所以穿长裤的总人数:
,
所以穿长裤的人是女生的概率为:
所以贝叶斯公式为:
二、拼写纠正实例
问题:用户输入一个不在字典里的单词,我们需要去猜测他真正想输入的单词是什么。
即求:P(猜测的他想输入的单词|他实际输入的单词),判断哪个猜测成功的概率更大。
假设用户实际输入的单词记为D(观测数据),则猜测1:P(h1|D),猜测2:P(h2|D),猜测3:P(h3|D)······统一为:P(h|D)
所以
而对于不同的猜测,P(D)都是一样的,所以在比较P(h1|D)和P(h2|D)时可以忽略这个常数。
所以
可以看出,对于给定的观测数据,一个猜测是好是坏,取决于这个猜测本身独立的可能性大小,即先验概率(Prior),和这个猜测生成我们观测到的数据的可能性大小。
总之,贝叶斯方法计算:P(h)P(D|h),P(h)是特定猜测的先验概率,P(D|h)可以由自己指定的规则得出。
三、垃圾邮件过滤实例
1、分析
问题:给定一封邮件,判定它是否属于垃圾邮件。
我们用D来表示这封邮件(D由n个单词组成),用h+表示垃圾邮件,h-表示正常邮件。
由贝叶斯公式得:
先验概率:P(h+)和P(h-)这两个先验概率很容易求出来,只需计算一个邮件库里面垃圾邮件和正常邮件的比例就好了。
D里面含有n个单词d1、d2、d3······,,而P(d1,d2,...dn|h+)可以扩展为:
假设di与di-1是完全条件无关的(朴素贝叶斯假设特征之间是独立的,互不影响),则简化为:
而对于简化后的式子只要统计di这个单词在垃圾邮件中出现的频率即可。
2、朴素贝叶斯算法
(1)邮件数据读取
import numpy as np
import re
import random
#进行预处理
def textParse(input_string):
listofTokens = re.split(r'\W+',input_string)
return [tok.lower() for tok in listofTokens if len(listofTokens)>2]
#入口函数
def spam():
doclist=[]
classlist=[]
for i in range (1,26):
wordlist = textParse(open('email/spam/%d.txt'%i,'r').read())
doclist.append(wordlist)
classlist.append(1) #1表示垃圾邮件
wordlist = textParse(open('email/ham/%d.txt'%i,'r').read())
doclist.append(wordlist)
classlist.append(0) #0表示正常邮件
(2)预料表与特征向量构建
创建预料表
def creatVocablist(doclist):
vocabSet = set([])
for document in doclist:
vocaSet = vocabSet|set(document)
return list(vocabSet)
得到预料表的返回值
vocablist = creatVocablist(doclist)
切分训练集和测试集
trainSet = list(range(50))
testSet = []
for i in range(10):
ranIndex = int(random.uniform(0,len(trainSet)))
testSet.append(trainSet[randIndex])
del(trainSet[randIndex])
trainMat=[]
trainClass=[]
for docIndex in trainSet:
trainMat.append(setOfWord2Vec(vocablist,doclist[docIndex]))
trainClass.append(classlist[docIndex])
其中setOfWord2Vec函数定义如下:
def setOfWord2Vec(vocablist,inputSet):
returnVec = [0]*len(vocablist)
for word in inputSet:
if word in vocablist:
returnVec[vocablist.index(word)] = 1
return returnVec
(3)分类别统计词频
def trainNB( trainMat , trainClass):
numTrainDocs = len(trainMat)
numWords = 1en(trainMat[0])
p1 = sum(trainClass )/float (numTrainDocs)
p0Num = np. ones((numWords)) #做了一个平滑处理
p1Num = np. ones((numWords)) #拉普拉斯平滑
p0Denom = 2
p1Denom = 2 #通常情况下都是设置成类别个数
for i in range( numTrainDocs) :
if trainClass[i] ==1 #垃圾邮件
p1Num += trainMat [i]
p1Denom += sum(trainMat [i])
else:
p0Num += trainMat [i]
p0Denom += sum( trainMat [i] )
(4)贝叶斯公式对数变换
p1Vec = np.log(p1Num/ p1Denom)
p0Vec = np. log(p0Num/ p0Denom)
得到返回值
p0Vec,p1Vec,p1 = trainNB(np.array(trainMat),np.array(trainClass))
(5)完成预测模块
errorCount = 0
for docIndex in testSet:
wordVec = set0fWord2Vec(vocablist,doclist [docIndex])
if classifyNB(np. array( wordVec ) , p0Vec ,p1Vec,p1) != classlist [docIndex]:
errorCount+= 1
print ('当前10个样本,错误个数:' , errorCount)
其中classifyNB函数为:
def classifyNB( wordVec , p0Vec, p1Vec,p1_ class):
p1 = np.log(p1_ class) + sum( wordVec*p1Vec)
p0 = np.log(1.0 - p1_ class) + sum( wordVec*p0Vec)
if p0>p1 :
return 0
else:
return 1
测试结果: