今天在图书馆学习了一天,把朴素贝叶斯的基本原理给弄明白了。故写篇文章加深印象。
当然还是要推荐一发博客的:ACdreamers-朴素贝叶斯分类
一、贝叶斯定理
条件概率:
P(c|x⃗ )=P(x⃗ c)P(x⃗ )
贝叶斯定理:
P(c|x⃗ )=P(c)P(x⃗ |c)P(x⃗ )
证明如下:
P(c|x⃗ )=P(x⃗ c)P(x⃗ )
P(x⃗ |c)=P(x⃗ c)P(c)
联立两式,消去
P(x⃗ c) 得到贝叶斯定理。
二、朴素贝叶斯分类器
不难发现,基于贝叶斯定理来估计概率 P(c|x⃗ ) 的主要困难在于,类条件概率 P(x⃗ |c) 是所有属性上的联合概率,难以从有限的训练样本直接估计而得。为了避开这个障碍,朴素贝叶斯分类器采用了“属性条件独立性假设”:对已知类别,假设所有属性相互独立。
基于属性条件独立性假设,贝叶斯定理可重写为:
P(c|x⃗ )=P(c)P(x⃗ |c)P(x⃗ )=P(c)P(x⃗ )∏i=1dP(xi|c)
其中 d 是属性数目, xi 为 x 在第 i 个属性上的取值。
朴素贝叶斯分类原理
对于给定待分类项,求解在此项出现的条件下,各个类别出现的概率,待分类项属于概率最大的那一类
三、使用朴素贝叶斯过滤垃圾邮件
定义事件:
– c0 : 不是垃圾邮件
– c1 : 是垃圾邮件
– w⃗ : 词向量,表示一篇邮件中出现的词汇
– P(w⃗ |ci) : 在出现事件 ci 的情况下,出现的是 w⃗ 的概率
– P(ci|w⃗ ) : 在出现的是词向量 w⃗ 的情况下,出现的是事件 ci 的概率
由上可知,我们需要求得所有的 P(ci|w⃗ ) 并找出最大的,其事件 ci 即是分类结果
训练算法
根据朴素贝叶斯分类器:
P(ci|w⃗ )=P(ci)P(w⃗ |ci)P(w⃗ )=P(ci)P(w⃗ )∏j=1dP(wj|ci)
我们可以对于每个待分类的数据,已知
P(ci) 和
P(w⃗ ) 。我们需通过训练算法得到得是
∏dj=1P(wj|ci)
测试算法
计算 P(ci|w⃗ ) 的值,取其中最大的,得到分类结果。与测试样本集中的结果进行比对。最后求出错误率
代码
import numpy as np
import re, os, random
# 将inputset转换为一个长度为len(vocabList)的列表
def setOfWord2Vec(vocabList, inputset):
returnVec = [0] * len(vocabList)
for word in inputset:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
return returnVec
# 读取文件filename中的所有单词,并以全部小写返回
def file2vec(filename):
returnVec = []
fr = open(filename, 'r')
regEx = re.compile("\W+")
text = fr.read()
returnVec.extend(regEx.split(text))
fr.close()
return [word.lower() for word in returnVec if len(word) > 2]
# 创建词汇表
def createVocabList(dataSet):
returnVec = set([])
for word in dataSet:
returnVec |= set(word)
return list(returnVec)
# 训练数据
def trainNB0(trainMatrix, trainCategory):
numTrainMatrix = len(trainMatrix)
numTrainWord = len(trainMatrix[0])
pAbvious = sum(trainCategory) / float(numTrainMatrix)
p0Num, p1Num = np.ones(numTrainWord), np.ones(numTrainWord)
p0Denom, p1Denom = 2.0, 2.0
for i in range(numTrainMatrix):
# 如果是侮辱性文档,则增加p1Num
if trainCategory[i] == 1:
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p0Vect, p1Vect = np.log(p0Num / p0Denom), np.log(p1Num / p1Denom)
return p0Vect, p1Vect, pAbvious
# 验证
def classifyNB(testSet, p0Vec, p1Vec, pAbvious):
p0 = sum(testSet * p0Vec) + np.log(1.0 - pAbvious)
p1 = sum(testSet * p1Vec) + np.log(pAbvious)
if p0 > p1:
return 0
else:
return 1
def spamTest():
# 所有词汇集合,结果集合,
docList, classList = [], []
for i in range(1, 26):
wordList = file2vec("spam/%d.txt" % i)
docList.append(wordList)
classList.append(1)
wordList = file2vec("ham/%d.txt" % i)
docList.append(wordList)
classList.append(0)
vocabList = createVocabList(docList)
trainingSet, testSet = list(range(50)), []
# 随机构造测试集
for i in range(10):
randIndex = int(random.uniform(0, len(trainingSet)))
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat, trainClasses = [], []
for docIndex in trainingSet:
trainMat.append(setOfWord2Vec(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V, p1V, pSpam = trainNB0(np.array(trainMat), trainClasses)
errorCount = 0
for index in testSet:
word = setOfWord2Vec(vocabList, docList[index])
if classifyNB(word, p0V, p1V, pSpam) != classList[index]:
errorCount += 1
print ("the error rate is:", float(errorCount) / len(docList))
if __name__ == '__main__':
spamTest()
计算的时候考虑上溢出和下溢出的问题,分别做了相应的处理。具体参加《机器学习实战》4.5.3节
根据这个算法,进行了多次测试(相同数据集),因为数据集划分的是随机的,随机结果最大错误率是4%(不一定准确)
总结
优点: 在数据较少的情况下仍然有效,可以处理多类别问题
缺点: 对于输入数据的准备方式较为敏感
使用数据类型: 标称型数据
说句题外话
为什么CSDN的markdown代码块在预览的时候感觉就挺好看的…为什么发表出来之后就感觉挺难看的了….加载的样式表不一样么….