朴素贝叶斯
import numpy as np
import matplotlib.pyplot as plt
from math import log
文本分类
词表到向量的转换函数
def loadDataSet():
"""
生成一个文本数据集和标签
参数:
无
返回:
postingList -- 文本列表
classVec -- 标签分类
"""
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']]
#0-非侮辱性,1-侮辱性
classVec = [0,1,0,1,0,1]
return postingList,classVec
In [3]:
def createVocabList(dataSet):
"""
建立词汇表,也就是所有文本的并集
参数:
dataSet -- 数据集
返回:
vocabSet -- 词汇表
"""
#首先建立一个空集,新建一个集合,避免直接修改原数据
vocabSet = set([])
#遍历数据集中的每个文档
for document in dataSet:
#每个文档和之前的词汇表求并集,保证没有重复
vocabSet = vocabSet | set(document)
return list(vocabSet)
def setOfWords2Vec(vocabList, inputSet):
"""
#初始化返回句子向量,长度为词汇表长度的全0向量
returnVec = [0]*len(vocabList)
#遍历输入集中的所有词汇
for word in inputSet:
#如果该词汇在词汇表中
if word in vocabList:
#对应序号元素置1
returnVec[vocabList.index(word)] = 1
#反之给出错误提示
else:
print("词汇“{}”不在词汇表中".format(word))
return returnVec
检验函数的执行效果
listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
print("myVocabList = {}".format(myVocabList))
结果:
myVocabList = ['how', 'has', 'him', 'stop', 'stupid', 'garbage', 'so', 'not', 'quit', 'flea', 'posting', 'to', 'love', 'steak', 'problems', 'food', 'worthless', 'mr', 'please', 'dalmation', 'maybe', 'take', 'buying', 'dog', 'my', 'I', 'licks', 'cute', 'ate', 'park', 'help', 'is']
从词向量计算概率
def trainNB0(trainMatrix,trainCategory):
#将数据转换为矩阵形式
trainData = np.array(trainMatrix)
trainLabels = np.array(trainCategory)
#训练文本数
numTrainDocs = trainData.shape[0]
#训练词条数
numWords = trainData.shape[1]
#侮辱性文本的概率为训练标签取均值
pAbusive = np.mean(trainLabels)
#初始化为全1
p0Num = np.ones((1, numWords)); p1Num = np.ones((1, numWords))
#分母初始化为2
#两项改动为了是避免连乘中一项为0,导致整体结果为0的情况
#Bayes分类只用比较概率的相对大小,所以这么做不会影响结果
p0Denom = 2.0; p1Denom = 2.0
#p1Num为统计每个侮辱性词条出现次数的向量,等于文本中标记1的子矩阵,沿axis = 0相加
p1Num += np.sum(trainData[trainLabels == 1, :], axis = 0, keepdims = True)
#p1Denom为统计侮辱性词条总数的标量,等于文本中标记1的子矩阵向量之和
p1Denom += np.sum(trainData[trainLabels == 1, :])
#p0同上
p0Num += np.sum(trainData[trainLabels == 0, :], axis = 0, keepdims = True)
p0Denom += np.sum(trainData[trainLabels == 0, :])
#对概率取对数,可以避免下溢出或者浮点数舍入误差,同样由于相对大小不变,不影响结果
p1Vect = np.log(p1Num/p1Denom)
p0Vect = np.log(p0Num/p0Denom)
return p0Vect,p1Vect,pAbusive
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
"""
#元素间乘法
p1 = np.sum(vec2Classify * p1Vec) + np.log(pClass1)
p0 = np.sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
def testingNB():
"""
测试朴素贝叶斯分类器
参数:
无
返回:
无 -- 直接打印结果
"""
#读取之前的数据
listOPosts,listClasses = loadDataSet()
#产生词汇表
myVocabList = createVocabList(listOPosts)
#初始化一个训练矩阵
trainMat=[]
#遍历文档
for postinDoc in listOPosts:
#转化为向量后,添加到训练矩阵中
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
#得到三个概率
p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses))
#待测试词组
testEntry = ['love', 'my', 'dalmation']
#词组转为矩阵
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
#打印结果
print("{} classified as: {}".format(testEntry, classifyNB(thisDoc,p0V,p1V,pAb)))
#待测试词组
testEntry = ['stupid', 'garbage']
#词组转为矩阵
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry))
#打印结果
print("{} classified as: {}".format(testEntry, classifyNB(thisDoc,p0V,p1V,pAb)))
测试
testingNB()
结果
['love', 'my', 'dalmation'] classified as: 0
['stupid', 'garbage'] classified as: 1
文档的词袋模型
def bagOfWords2VecMN(vocabList, inputSet):
"""
文档的词袋模型
参数:
vocabList -- 词汇列表
inputSet -- 输入词集
返回:
returnVec -- 返回向量
"""
returnVec = [0]*len(vocabList)
for word in inputSet:
if word in vocabList:
#如果在词库中,对应词条就++
returnVec[vocabList.index(word)] += 1
return returnVec
使用朴素贝叶斯过滤垃圾邮件
使用朴素贝叶斯进行交叉验证
def textParse(bigString):
"""
输入很长的字符串,转换为向量
参数:
bigString -- 长字符串
返回:
去掉少于两个字符,转换为小写的字符串
"""
import re
listOfTokens = re.split(r'\W*', bigString)
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
def spamTest():
"""
垃圾邮件测试
参数:
无
返回:
无
"""
#新建三个列表
docList=[]; classList = []; fullText =[]
#遍历垃圾邮件和正常邮件,各25个
for i in range(1,26):
#读取垃圾邮件
wordList = textParse(open("email/spam/{}.txt".format(i), errors = 'ignore').read())
#添加到列表
docList.append(wordList)
fullText.extend(wordList)
#添加到类
classList.append(1)
#读取正常邮件
#ham中的23.txt总是报错有不能解读的字节,选择忽略该错误
wordList = textParse(open("email/ham/{}.txt".format(i), errors = 'ignore').read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
#创建词汇表
vocabList = createVocabList(docList)
#训练集和测试集序号集
trainingSet = list(range(50)); testSet=[]
#随机抽取训练集中的10个序号,放入测试集
for i in range(10):
#生成随机序号
randIndex = np.int(np.random.uniform(0,len(trainingSet)))
#序号对应的元素由训练集移动到测试集中
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(np.array(trainMat),np.array(trainClasses))
#错误计数器初始化为0
errorCount = 0
#对于测试集
for docIndex in testSet:
#得到词袋向量
wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
#判断结果
if classifyNB(np.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]:
#统计错误
errorCount += 1
#打印错误信息
print("错误序号为:{}".format(docList[docIndex]))
print("总准确率为:{}".format(1 - np.float(errorCount)/len(testSet)))
测试结果
错误序号为:['oem', 'adobe', 'microsoft', 'softwares', 'fast', 'order', 'and', 'download', 'microsoft', 'office', 'professional', 'plus', '2007', '2010', '129', 'microsoft', 'windows', 'ultimate', '119', 'adobe', 'photoshop', 'cs5', 'extended', 'adobe', 'acrobat', 'pro', 'extended', 'windows', 'professional', 'thousand', 'more', 'titles']
总准确率为:0.9
参考
文章借鉴了同学的优秀作业