本文目的
本文将详细介绍用贝叶斯方法和交叉验证法实现垃圾邮件的分类,我将从设计算法框架,再到具体实现详细介绍。理论知识推荐张学工的模式识别(这里不再涉及),或者这篇blog这里写链接内容
算法知识介绍
贝叶斯的目的时利用先验知识和类概率密度去估计后验概率,
如公式所示,算法的目的是通过带标签的样本训练出得到先验概率:p(Ci),和类概率密度p(w/Ci),然后带入带测试数据集进行验证,如果验证集的厚颜概率密度有P(C1/w) > P(C2/w) 那么测试集样本属于C1类反之属于C2类。p(w)为归一化参数,不会影响分类结果。
算法处理流程
1-读入ham和spam文件中的文档,切分单词,建立一个无重复单词的单词库,且生成原始数据矩阵,和标签矩阵
2-利用交叉验证法进行训练,(这里为了符合实际会采用拉普拉斯修正),得到每个单词的类概率密度和先验概率
trainData = [] #原始数据
trainLabelInput = [] #标签矩阵
WordsLib = [] #单词库
for i in range(1,11):
wordlist = splitWords(open('E:/C_Code/python/py0527spam/spam/%d.txt'%i).read())
trainData.append(wordlist)
WordsLib.extend(wordlist) #为生成库做准备
trainLabelInput.append(0) #垃圾邮件标志为0
wordlist = splitWords(open('E:/C_Code/python/py0527spam/ham/%d.txt'%i).read())
trainData.append(wordlist)
WordsLib.extend(wordlist)
trainLabelInput.append(1) #非垃圾邮件标记为1
VocabList = createWordsLib(trainData)#生成了一个无重复单词的单词库
这里我们将得到一个没有重复单词的单词库(1350个不重复的单词),生成这个单词库的目的是为了对每一个样本都生成一个1*1350的0矩阵,然后对样本里的单词在单词库中进行检索,如果在单词库中,那么把对应位置的0置为1,即把单词样本转化为了一个代表单词样本的0,1矩阵。我们还把所有的样本加载到了trainData中,其对应的标签加载到了trainLabelInput中。
testLabels = []
trainIndex = list(range(20))
for i in range(5):
randomNum = int(np.random.uniform(0,len(trainIndex)))
testLabels.append(trainIndex[randomNum]) #挑选出了测视集
del(trainIndex[randomNum]) #删除测试集的索引号
trainMat = [] #训练矩阵
trainLabel = [] #训练矩阵标签
for indexNum in trainIndex:#生成了训练矩阵和训练标签
trainMat.append(createTrainMat(VocabList,trainData[indexNum]))
trainLabel.append(trainLabelInput[indexNum])
p0w,p1w,pVa = train(trainMat,trainLabel) #得到训练数据
这里我们采用交叉验证法,随机的挑选五个样本作为验证集,剩余的15个样本作为训练集。通过训练将得到类概率密度,和先验概率
errorNum = 0
for numIndex in testLabels:
testVec = createTrainMat(VocabList,trainData[numIndex])
count = predictionResult(testVec,p0w,p1w,pVa)
print("count:",count)
print("trainLabelInput[numIndex]:",trainLabelInput[numIndex])
if count != trainLabelInput[numIndex]:
errorNum += 1
print("numIndex:",numIndex)
print(trainData[numIndex])
print("error is",float(errorNum/len(testLabels)))
我们将对验证集的预测值和实际标签进行对比,得到最终的错误率,这里可能你得到的错误率会为0,或者为0.2,或者0.4,也就是说你的验证集的选择不同可能对你的预测是有影响的,因为你可能恰巧在五个验证集中选中了0,1,2个错分类的样本,我这里是在ham文档中人为的加了2个spam文件中的txt文档,所以每次由于选的验证集不同,可能会导致不同的错误率。
完整的代码如下:
import numpy as np
def splitWords(txtString): #读入txt文件,且对读入的txt文件生成一个单词的list
import re
wordList = []
listOfWords = re.split(r'\W*',txtString)
for word in listOfWords:
if(len(word) > 2):
wordList.append(word)
return wordList
def createWordsLib(wordslist):
WordsLib = set([])
for word in wordslist:
WordsLib = WordsLib | set(word) #set() word 是一个列表
return list(WordsLib)
def createTrainMat(vacabList,trainData):
Returntrain = np.zeros(len(vacabList))
for word in trainData:
if word in vacabList:
Returntrain[vacabList.index(word)] = 1 #存在某特征(单词),则某处置为1
else:
print(word,"is not in vacabList")
return Returntrain
def train(trainData,trainLabel):
sampleNum = len(trainData) #样本数
trainDataFeatures = len(trainData[0]) #特征数
pVa = sum(trainLabel) / len(trainLabel) #先验概率,不是垃圾邮件的先验概率
p0Num = np.ones(trainDataFeatures)
p1Num = np.ones(trainDataFeatures)
for i in range(sampleNum):
if trainLabel[i] == 0:#如果为垃圾邮件,则求垃圾邮件的类概率密度
p0Num += trainData[i]
p0NumSum = sum(trainData[i])
else:
p1Num += trainData[i]
p1NumSum = sum(trainData[i])
pw0 = np.log(p0Num/p0NumSum)
pw1 = np.log(p1Num/p1NumSum)
return pw0,pw1,pVa
def predictionResult(testMat,pw0,pw1,pVa):
p1 = sum(testMat * pw1) + np.log(pVa)
p0 = sum(testMat * pw0) + np.log(1-pVa)
if p1 > p0:
return 1
else:
return 0
def trainThread():
#原始数据的载入:读入数据,切分,生成一个总汇单词表;同时生成一个原始数据矩阵,和标签矩阵。
trainData = [] #原始数据
trainLabelInput = [] #标签矩阵
WordsLib = [] #单词库
for i in range(1,11):
wordlist = splitWords(open('E:/C_Code/python/py0527spam/spam/%d.txt'%i).read())
trainData.append(wordlist)
WordsLib.extend(wordlist) #为生成库做准备
trainLabelInput.append(0) #垃圾邮件标志为0
wordlist = splitWords(open('E:/C_Code/python/py0527spam/ham/%d.txt'%i).read())
trainData.append(wordlist)
WordsLib.extend(wordlist)
trainLabelInput.append(1) #非垃圾邮件标记为1
VocabList = createWordsLib(trainData)#生成了一个无重复单词的单词库
#利用留存法进行训练,被选的进行训练(随机),未被选的当作测试集,计算出类概率密度,和先验概率
testLabels = []
trainIndex = list(range(20))
for i in range(5):
randomNum = int(np.random.uniform(0,len(trainIndex)))
testLabels.append(trainIndex[randomNum]) #挑选出了测视集
del(trainIndex[randomNum]) #删除测试集的索引号
trainMat = [] #训练矩阵
trainLabel = [] #训练矩阵标签
for indexNum in trainIndex:#生成了训练矩阵和训练标签
trainMat.append(createTrainMat(VocabList,trainData[indexNum]))
trainLabel.append(trainLabelInput[indexNum])
p0w,p1w,pVa = train(trainMat,trainLabel) #得到训练数据
# testLabels = []
#被剔除的的样本进行验证
errorNum = 0
for numIndex in testLabels:
testVec = createTrainMat(VocabList,trainData[numIndex])
count = predictionResult(testVec,p0w,p1w,pVa)
print("count:",count)
print("trainLabelInput[numIndex]:",trainLabelInput[numIndex])
if count != trainLabelInput[numIndex]:
errorNum += 1
print("numIndex:",numIndex)
print(trainData[numIndex])
print("error is",float(errorNum/len(testLabels)))
#得出概率密度预测准确率