贝叶斯公式推导
提到贝叶斯公式,首先需要从条件概率说起,因为它是基于条件概率发展而来的。那么什么是条件概率呢?
举个例子:现有A,B两个事件,A发生的概率为P(A),B发生的概率为P(B),条件概率是指A在B发生的条件下发生的概率,表示为P(A|B),因为这里只有两个事件,因此:
P
(
A
∣
B
)
=
P
(
A
B
)
P
(
B
)
P(A|B)=\frac{P(AB)}{P(B)}
P(A∣B)=P(B)P(AB)
相反的,有:
P
(
B
∣
A
)
=
P
(
A
B
)
P
(
A
)
P(B|A)=\frac{P(AB)}{P(A)}
P(B∣A)=P(A)P(AB)
该公式也可以推广到任意有穷多个事件。由此我们来推导贝叶斯公式,设B1,B2,……,Bn是一一组完备事件组,则对任一事件A,P(A)> 0,有
P
(
B
i
∣
A
)
=
P
(
A
B
i
)
P
(
A
)
=
P
(
B
i
)
P
(
A
∣
B
i
)
∑
P
(
B
i
)
P
(
A
∣
B
i
)
P(Bi|A)=\frac{P(AB_{i})}{P(A)}=\frac{P(B_{i})P(A|B_{i})}{\sum P(B_{i})P(A|B_{i})}
P(Bi∣A)=P(A)P(ABi)=∑P(Bi)P(A∣Bi)P(Bi)P(A∣Bi)
至此,我们得出了贝叶斯公式。
朴素贝叶斯的一般过程
- 收集数据:任何方法。
- 准备数据:需要数值型或者布尔型数据。
- 分析数据:有大量特征时,绘制特征作用性不大,此时使用直方图效果更好。
- 训练算法:计算不同的独立特征的条件概率。
- 测试算法:计算错误率
- 使用算法:可以在任意的分类场景中使用朴素贝叶斯分类器。
朴素贝叶斯进行文本分类
机器学习的一个重要应用就是文档的自动分类。在文档分类中,整个文档是实例,而其中的某些元素则构成特征。我们可以观察文档中出现的词,并把每个词出现或不出现作为一个特征,这样得到的特征数目就会跟词汇表里的词目一样多。
函数实现
def loadDataSet(): # 文档集合,类别标签
postingList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
['stop', 'posting', 'stupid', 'worthless', 'garbage'],
['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
classVec = [0, 1, 0, 1, 0, 1] # 1表示侮辱性, 0非侮辱
return postingList, classVec
def createVocabList(dataSet): # 文档中出现的不重复词的词汇表
vocabSet = set([]) # set数据类型
for ducument in dataSet:
vocabSet = vocabSet | set(ducument) # 取并集
return list(vocabSet)
def setOfWords2Vec(vocabList, inputSet): # 输入词汇表和文档,检查输入文档中的词是否存在于词汇表中
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else:
print("the word: %s is not in my vocabulary" % word)
return returnVec # 返回文档向量,若输入文档内某单词存在于词汇表里,则其下表所在的值为1
def trainNB0(trainMatrix, trainCategory): # 输入样本矩阵,类别标签
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory) / float(numTrainDocs) # 计算侮辱性文档出现的概率
p0Num = ones(numWords)
p1Num = ones(numWords)
p0Denom = 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 testingNB0(): # 测试文档分类
listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
trainMat = []
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0v, p1v, pAb = trainNB0(array(trainMat), array(listClasses)) # 计算先验概率
testEntry = ["love", "my", "dalmation"]
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry, "classified as:", classifyNB(thisDoc, p0v, p1v, pAb))
testEntry = ["stupid", "garbage"]
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry, "classified as: ", classifyNB(thisDoc, p0v, p1v, pAb))
朴素贝叶斯过滤垃圾邮件
过滤垃圾邮件也是通文本分类一般,需要先从;文本内容中得到字符串列表,然后生成词向量。
函数实现
def textParse(bigString): # 处理文本为小写且只包含数字和字母
listOfTokens = re.split("\W+", bigString)
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
测试代码
def spamTest():
docList = []
classList = []
fullText = []
for i in range(1, 26):
wordList = textParse(
open("D://python//PyCode//machinelearninginaction//Ch04//email//spam//%d.txt" % i).read()) # 逐个读取spam里的文本文件
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(
open("D://python//PyCode//machinelearninginaction//Ch04\email//ham//%d.txt" % i).read()) # 逐个读取ham里的文本文件
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList)
trainingSet = list(range(50)) # 0-49的列表
testSet = []
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(setOfWords2Vec(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0v, p1v, pSpam = trainNB0(array(trainMat), array(trainClasses))
errorCount = 0
for docIndex in testSet:
wordVector = setOfWords2Vec(vocabList, docList[docIndex])
if classifyNB(array(wordVector), p0v, p1v, pSpam) != classList[docIndex]:
errorCount += 1
print("the error rate is: ", float(errorCount / len(testSet)))
def spamTest_times(): # 多次测试
for i in range(10):
spamTest()
第一个函数textParse()接受一个大字符串并将其解析为字符串列表。该函数就去掉少于两个字符的字符串,并将所有字符串转换成小写。
第二个函数spamTest()对贝叶斯垃圾邮件分类器进行自动化处理。导入spam(非垃圾邮件)和ham(垃圾邮件),并将它们解析为此列表。从50个样本中抽取10个作为测试集,剩余部分作为训练集,这个过程被称为留存交叉验证。接下来用for循环遍历训练集的内容,并用文档分类时写的setOfWords2Vec()函数来构建词条向量,将其传入trainNB0()函数中用于计算分类所需高i率。然后遍历测试集,对其中每封电子邮件进行分类。最后计算触错误的百分比。
测试结果
训练集数:10
训练集数:25
训练集数:40