朴素贝叶斯
输入数据创造词汇表 代码实现:
# coding:UTF-8
def loadDataSet():
"""
创建了一下实验样本
:return:词条且分的文档集合,类别标签的集合,自动检测侮辱性的语言
"""
postingList = [['my', 'dog', 'has', 'flea', \
'problem', '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]
return postingList, classVec
def createVocabList(dataSet):
"""
创建一个包含在所有文档中出现的不重复词的列表
一个不重读此表set返回
求并集
:param dataSet:
:return:
"""
vocabSet = set([])
for document in dataSet:
vocabSet = vocabSet | set(document)
return list(vocabSet)
def setOfWords2Vec(vocabList, inputSet):
"""
函数首先创建一个和词汇表登场的向量 全部设置为0,遍历文档中的所有单词,如果出现了词汇表中的单词,则将其元素设置为1
:param vocabList: 词汇表以及某个文档
:param inputSet:
:return: 输出文档向量 向量的每一元素为1或者0,分别表示词汇表中单词在输入文档中是否出现
"""
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
def main():
listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
print('词汇表:',myVocabList)
print('对照组:', listOPosts[0])
print(setOfWords2Vec(myVocabList, listOPosts[0]))
print(setOfWords2Vec(myVocabList, listOPosts[3]))
if __name__ == '__main__':
main()
执行截图:
训练算法:从词向量计算概率:
前面介绍了如何将一组单词转换为一组数字,接下来如何使用这些数据计算概率,
程序4-2 朴素贝叶斯分类器训练函数
def trainNB0(trainMatrix, trainCategory):
"""
:param trainMatrix: 文档矩阵
:param trainCategory: 每篇文档类别标签所构成的向量
:return:
"""
numTrainDocs = len(trainMatrix) # 总文档的长度
numWords = len(trainMatrix[0]) # 第一个文档关键词长度
pAbusive = sum(trainCategory) / float(numTrainDocs) # 侮辱性词汇占的概率
p0Num = zeros(numWords) # 第一个词组对应的0矩阵
p1Num = zeros(numWords) # 第一个词组对应的0矩阵
p0Denom = 0.0
p1Denom = 0.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 = p1Num / p1Denom
p0Vect = p0Num / p0Denom
return p0Vect, p1Vect, pAbusive
执行代码:
myVocabList = createVocabList(listOPosts)
trainMat = []
for postinDoc in listOPosts: # for循环使用词向量来填充trainMat列表
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
print(trainMat)
print("----------")
print(listClasses)
p0V, p1V, pAb = trainNB0(trainMat, listClasses)
print('pAb 显示的概率是: %f ' % pAb)
print(p0V)
print("-----------------------")
print(p1V)
运行算法概率的截图:
4.5.3 根据显示状况修改分类器:
贝叶斯分类器对文档进行划分 需要计算多个概率的乘积以获得文档属于某个类别的改了,避免其中一个概率值出现0,最后的乘积为0
p0Num = ones(numWords)
p1Num = ones(numWords)
p0Denom = 2.0
p1Denom = 2.0
由于产生下溢出,可以对其使用对数乘积
def classifyNB(vec2classify, p0Vec, p1Vec, pClass1):
"""
使用数组来计算两个向量相乘的结果,这里的相乘指的是对象元素相乘
:param vec2classify: 需要分类的向量
:param p0Vec: 三个概率
:param p1Vec:
:param pClass1:
:return:
"""
p1 = sum(vec2classify * p1Vec) + log(pClass1)
p0 = sum(vec2classify * p0Vec) + log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
def testingNB():
"""
便利函数,封装所有操作
:return:
"""
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, 'classfied as:', classifyNB(thisDoc, p0V, p1V, pAb))
实现截图:
4.5.4文档词袋模型
每个词是否出现作为一个特征,这个可以被描述为词集模型,如果一个词在文档中不止出现过一次,者可能意味着包含盖茨是否出现在文档中所表达的某种信息,词袋模型,每个单词可以出现多次,但是在词集中,每一个词只能出现一袭。需要setOfWord2Vec稍加修改,修改后的函数为bagOfWords2Vec
def bagOfWords2VecMN(vocabList, inputSet):
"""
每遇到一个单词
:param vocabList:
:param inputSet:
:return:
"""
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return returnVec
4.6 使用朴素贝叶斯过滤垃圾邮件
使用朴素贝叶斯解决一些显示生活中的问题时,需要从文本内容获取字符串列表,然后生成词向量
收集数据;
准备数据: 将文本文件解析成词条向量
分析数据:检查词条确保解析的正确性
训练算法 使用trainNB0函数
测试算法 classifyNB 构建一个新的测试函数来计算文档集的错误率
使用算法
4 .6.1切分文本
切分数据截图:
Demo
import re
if __name__ == '__main__':
mySent = 'This book is the the best book on Python or M.L. I have ever laid eyes upon.'
mySent.split()
regEx = re.compile('\W+') # 获取一长串的的字母 单词形式
listOfTokens = regEx.split(mySent)
print(listOfTokens)
print([tok.lower() for tok in listOfTokens if len(tok) > 0])
emailText = open(r'email/ham/6.txt', encoding='ISO-8859-1').read() # 显示编码方便MAC读入数据
# print(emailText)
listOfTokens = regEx.split(emailText)
print(listOfTokens)
代码实现截图:
URL切分 需要实现过滤掉小于字符长度3的数据 实际的解析过程中,需要用更高级的过滤去来对诸如HTML和URI的对象的进行处理,目前一个URI最终会解析成词汇表中的单词,文本解析可能是一个相当复杂的过程
使用朴素贝叶斯进行交叉验证
def textParse(bigString):
"""
URL切分 需要实现过滤掉小于字符长度3的数据 实际的解析过程中,
需要用更高级的过滤去来对诸如HTML和URI的对象的进行处理,目前一个URI最终会解析成词汇表中的单词,文本解析可能是一个相当复杂的过程
j接受一个大字符并将其解析为字符串列表,该函数去掉少于两个字符的字符串,并将所有的字符串转换为小写
:param bigString:
:return:
"""
listOfToken = re.split(r'\W+', bigString)
return [tok.lower() for tok in listOfToken if len(tok) > 2]
def spamTest():
"""
实现对贝叶斯垃圾邮件分类器进行自动化处理,导入文件家spam ham中的文本文件,并将他们解析为词列表
构建一个测试集与训练集
两个集合中的邮件都是随机选出的
10测试 trainSet是一个整数列表0-49 随机选择其中10个文件 将文档添加到测试机,从训练集剔除
随机选择数据的一部分作为训练
需要多次迭代计算出求出平均错误率
需要使用setOfWords2Vec函数来构建词向量 traindNB0()函数中用于计算分类所需的概率,然后遍历测试集
:return:
"""
docList = []
classList = []
fullText = []
for i in range(1, 26):
wordList = textParse(open(r'email/spam/%d.txt' % i, encoding='ISO-8859-1').read())
# print('wordList:', wordList)
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(open(r'email/ham/%d.txt' % i, encoding='ISO-8859-1').read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
# print('classList:', classList)
vocabList = createVocabList(docList)
# print('vocabList',vocabList)
trainingSet = list(range(50))
# print('trainingSet', trainingSet)
testSet = []
for i in range(10):
randIndex = int(random.uniform(0, len(trainingSet)))
testSet.append(trainingSet[randIndex])
del (trainingSet[randIndex])
# print('trainingSet', trainingSet)
# print('docList', docList)
trainMat = []
trainClasses = []
for docIndex in trainingSet:
trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
# print('trainClasses', trainClasses)
p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))
errrorCount = 0
for docIndex in testSet:
wordVector = setOfWords2Vec(vocabList, docList[docIndex])
if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
errrorCount += 1
print('the error rate is :', float(errrorCount) / len(testSet))
执行截图:
4.7 使用朴素贝叶斯分类器从个人广告中获取区域倾向
朴素贝叶斯应用:1,过滤网站的恶意留言 2,过滤垃圾邮件
使用朴素贝叶斯来发现地域相关的用词
收集数据:从RSS源收集内容,这里需要对RSS源构建一个接口
准备数据:将文本文件解析成词条向量
分析数据:检查词条确保解析的正确性
训练算法:使用trainNB0
测试算法:观察错误率,确保分类器可用,可以修改切分程序,降低错误率,提高分类结果
使用算法:构建一个完整的程序,封装所有内容,给定两个RSS源,该程序会显示最常用的公共词
4.7.1 导入RSS源
Universal Feed Parser 是python常用的RSS程序库
def calcMostFreq(vocabList, fullText):
"""
获取排序中最高的30个单词并随后将他们移除
:param vocabList:
:param fullText:
:return: 需要返回之后会
"""
freqDict = {}
for token in vocabList:
freqDict[token] = fullText.count(token)
sortedFreq = sorted(freqDict.items(), key=operator.itemgetter(1), reverse=True)
return sortedFreq[:30]
def localWords(feed1, feed0):
docList = []
classList = []
fullText = []
minLen = min(len(feed1['entries']), len(feed0['entries']))
print(len(feed1['entries']))
print(len(feed0['entries']))
print(feed0['entries'])
print(feed1['entries'])
for i in range(minLen):
wordList = textParse(feed1['entries'][i]['summary'])
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(feed0['entries'][i]['summary'])
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList)
print("^^^^^^^^^^^^^")
print(len(vocabList))
top30Words = calcMostFreq(vocabList, fullText)
for pairW in top30Words:
if pairW[0] in vocabList:
vocabList.remove((pairW[0]))
trainingSet = list(range(2 * minLen))
print('trainingSet',trainingSet)
print('trainingSetlen', size(trainingSet))
testSet = []
for i in range(minLen):
print('----------------')
print((trainingSet))
randIndex = int(random.uniform(0, len(trainingSet)))
print('randIndex', randIndex)
print('i', i)
print('testSet', testSet)
# print('randIndex:%d : %s'%(randIndex, trainingSet[randIndex]))
testSet.append(trainingSet[randIndex])
del (trainingSet[randIndex])
trainMat = []
trainClasses = []
for docIndex in trainingSet:
trainMat.append(bagOfWords2VecMN(vocabList,docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))
errorCount = 0
for docIndex in testSet:
wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
errorCount += 1
print('the error rate is :', float(errorCount) / len(testSet))
return vocabList, p0V, p1V
4.7.2 显示地域相关的用词
可以用向量pSF与pNY进行排序,然后按照顺序将顺序将词打印出来。
def getTopWords(ny, sf):
"""
使用两个RSS源做为输入,然后训练并测试朴素贝叶斯分类器,返回使用的概率值,然后创建两个列表用于元组的存储。与之前返回排名最高的X
个单词不同,这里可以返回大于某个阈值的所有词。这些元组会按照他们的条件概率进行排序。
:param ny:
:param sf:
:return:
"""
vocabList, p0V, p1V = localWords(ny, sf)
topNY = []
topSF = []
for i in range(len(p0V)):
if(p0V[i]) > -6.0: topSF.append((vocabList[i],p0V[i]))
if(p1V[i]) > -6.0: topNY.append((vocabList[i],p0V[i]))
sortedSF = sorted(topSF, key=lambda pair : pair[1], reverse=True)
print('SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**')
for item in sortedSF:
print(item[0])
sortedNY = sorted(topNY, key=lambda pair:pair[1], reverse=True)
print("NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**")
for item in sortedNY:
print(item[0])
执行返回排名最该的X个单词
4.8本章小结
使用概率有时要比硬规则更为有效,贝叶斯概率以及贝叶斯准则提供了一个利用已知值来估计未知概率的有效方法
可以通过特征之间的条件独立性假设,降低对数据量的需求,独立性假设是指一个词的出现概率并不依赖文档中的其他词,条件独立性并不正确。