【机器学习实战二:朴素贝叶斯算法之过滤垃圾邮件】



一、部分说明

----------------------------------------------------------------

1、本文代码是《机器学习实战》这本书的例程。点击下载《机器学习实战》及原书中的源代码;

2、运行环境为:window10+python3.2+各种python机器学习库

     安装可参考:点击打开链接

3、本文注重代码的实现过程,其基本知识请参考《机器学习实战》和一些知名博客。

4、本人属于初学者,有些注释部分可能不对,还请指正。

5、(1)、本代码从原来的python2.x改为python3.x

      (2)、加入详细的注释,方便理解;

      (3)、还有加入自己想要实现的一些功能。

----------------------------------------------------------------

二、重点回顾

1、朴素贝叶斯算法原理:

2、朴素贝叶斯算法原理用于垃圾邮件过滤:


3、注意事项

     (1)、为了防止步骤四的小数相乘造成精度损失(及比较小的数相乘最后会被当作0),故通过相乘比较大小转化为对数相加,避免此类问题的出现,影响实验结果;

     (2)、分类时候还分为词集模式,知关心某个单词是否出现在邮件中;词袋模式,不仅关心单词是否出现还关心出现的次数;

     (3)、其他一些具体的注意事项,会在代码中标注,可以查看,方便理解;

三、源代码




<strong>'''***********************************************************
  Copyright (C), 2015-2020, cyp Tech. Co., Ltd.
  FileName:           朴素贝叶斯实例.cpp
  Author:             cyp     
  Version :           v1.0          
  Date:               2015-10-21 上午10:38
  Description:        利用朴素贝叶斯原理对邮件进行分类     
  Function List:      
    1.  createVocalList:计算输入各集的并集,且每个元素是unique
	2.  setofWords2Vec:  运用词集方式将数据预处理
	3.  bagofWords2Vec:  运用词袋方式将数据预处理
	4.  trainNB0:       训练数据
	5.  classifyNB:      对email进行分类
	6.  test             相当于主函数,训练模型并测试
***********************************************************'''


'''***********************************************************
需要导入的模块
***********************************************************'''
import random
import math
import numpy as np
import os

'''***********************************************************
  Function:        createVocalList
  Description:     计算输入各集的并集,且每个元素是unique
  Calls:           
  Called By:       test
  Input:           dataSet输入的数据集,及单词向量集
  Return:          返回输入数据集的并集,返回数据的数据类型是list
				   表示出现过的单词
************************************************************'''
def createVocalList(dataSet):
	vocalSet = set([])                  #创建一个空集
	for data in dataSet:
		vocalSet = vocalSet | set(data) #利用set的特性求两个集合的并集
	return list(vocalSet)               #以list的方式返回结果
	
	
'''***********************************************************
  Function:        setofWords2Vec
  Description:     判断的向量的单词是否在词库中是否出现,如果出现
				   则标记为1,否则标记为0
  Calls:           
  Called By:       test
  Input:
				   vocabList:
				   词库,曾经出现过的单词集合,即createVocalList
	               inputSet:
				   测试的单词向量
  Return:          returnVec是一个同vocalList同大小的数据,如果inputset单词
				   出现在vocalList则将returnVec对应的位置置1,否则为0
************************************************************'''
def setofWords2Vec(vocalList,inputset):
	returnVec = [0]*len(vocalList)       
	#同vocalList同大小的list,开始假设每个单词都没出现,即全为0
	for word in inputset:                                        
		if word in vocalList:        #如果输入的单词出现在词库中,则置1   
			returnVec[vocalList.index(word)] = 1
		else:                        #如果没出现在词库中,默认为0,并输出
			print("the word:%s is not in my Vocabulary!"%word)
	return returnVec				 #返回结果
'''***********************************************************
  Function:        bagofWords2Vec
  Description:     统计的单词是否在词库中出现频率
				 
  Calls:           
  Called By:       test
  Input:
				   vocabList:
				   词库,曾经出现过的单词集合,即createVocalList
	               inputSet:
				   测试的单词向量
  Return:          returnVec是一个同vocalList同大小的数据,对应位置
					表示该单词出现的个数
************************************************************'''
def bagofWords2Vec(vocalList,inputset):
	returnVec = [0]*len(vocalList)       
	#同vocalList同大小的list,开始假设每个单词都没出现,即全为0
	for word in inputset:                                        
		if word in vocalList:        #如果输入的单词出现在词库中,则加1   
			returnVec[vocalList.index(word)] += 1
	return returnVec				 #返回结果
'''***********************************************************
  Function:        trainNB0
  Description:     统计侮辱性email出现概率,侮辱性邮件各单词出现的概率,非
				   侮辱性email各单词出现概率
  Calls:           
  Called By:       test
  Input:
				   trainDataSet:参与训练的数据集,即setofWords2Vec返回数据的集合
	               trainLabels:训练数据的标签
  Return:          
					pShame:侮辱性email
					p0Vect:非侮辱性email中单词出现的概率
					p1Vect:侮辱性email中单词出现的概率
************************************************************'''
def trainNB0(trainDataSet,trainLabels):
	numTrains = len(trainDataSet)     #训练数据的组数
	numWords = len(trainDataSet[0])   #每组训练数据的大小
	pShame = sum(trainLabels)/float(numTrains)
	#标签中1表示侮辱,0表示非侮辱,故上述统计侮辱性email出现概率
	#zeros(numWords)表示1*numWords数组,非numWords*numWords数组
	#p0Num = np.zeros(numWords)         #存储统计非侮辱性邮件各单词出现频率
	#p1Num = np.zeros(numWords)         #存储统计侮辱性邮件各单词出现频率
	#p0SumWords = 0.0                  #非侮辱性邮件中单词总数
	#p1SumWords = 0.0                  #侮辱性邮件中单词总数
	#为了防止与0相乘,故初始化为1,因为两者的初始化一样,所以不影响结果
	p0Num = np.ones(numWords)           #存储统计非侮辱性邮件各单词出现频率
	p1Num = np.ones(numWords)           #存储统计侮辱性邮件各单词出现频率
	p0SumWords = 2.0                    #非侮辱性邮件中单词总数
	p1SumWords = 2.0                    #侮辱性邮件中单词总数
	for i in range(numTrains):
		if trainLabels[i]==1:         #如果为侮辱性email
			p1Num += trainDataSet[i]  #统计非侮辱性邮件各单词
		else:
			p0Num += trainDataSet[i]  #统计侮辱性邮件各单词
	p0SumWords = sum(p0Num)           #计算非侮辱性邮件中单词总数
	p1SumWords = sum(p1Num)           #计算侮辱性邮件中单词总数
	p0Vect = p0Num/p0SumWords         #非侮辱性邮件中单词出现的概率
	p1Vect = p1Num/p1SumWords         #侮辱性邮件中单词出现的概率
	return pShame,p0Vect,p1Vect
	
'''***********************************************************
  Function:        classifyNB
  Description:     对email进行分类
  Calls:           
  Called By:       test
  Input:
				   vec2Classify:要分类的数据
	               pShame:侮辱性email
				   p0Vect:非侮辱性email中单词出现的概率
				   p1Vect:侮辱性email中单词出现的概率
  Return:          
				   分类的结果,1表示侮辱性email,0表示非侮辱性email
************************************************************'''
def classifyNB(vec2Classify,p0Vect,p1Vect,pShame):
	#由于小数相乘,会有很大误差,故先求对数再相乘
	temp0 = vec2Classify*p0Vect 
	temp1 = vec2Classify*p1Vect
	temp00 = [] 
	temp11 = []
	#分步求对数,因为log不能处理array,list
	for x in temp0:
		if x>0:
			temp00.append(math.log(x)) 
		else:
			temp00.append(0)
	for x in temp1:
		if x>0:
			temp11.append(math.log(x)) 
		else:
			temp11.append(0)
	p0 = sum(temp00)+math.log(1-pShame)  #属于非侮辱性email概率
	p1 = sum(temp11)+math.log(pShame)    #属于侮辱性email概率
	if p1 > p0: #属于侮辱性email概率大于属于非侮辱性email概率
		return 1
	else:
		return 0

'''***********************************************************
  Function:        text2VecOfWords
  Description:     从email的string中提取单词
  Calls:           
  Called By:       test
  Input:
				   string: email字符串
  Return:          
				   单词向量
************************************************************'''
def text2VecOfWords(string):
	import re        #正则表达式工具
	listOfWolds = re.split(r"\W*",string)
	#分割数据,其分隔符是除单词,数字外任意的字符串 
	return [word.lower() for word in listOfWolds if len(word)>2]
	#单词不区分大小写,及全部转换为(小写),滤除没用短字符串

'''***********************************************************
  Function:        test
  Description:     将数据部分用来训练模型,部分用来测试
  Calls:           text2VecOfWords
				   createVocalList
				   trainNB0
				   classifyNB
  Called By:       main
  Input:
  Return:          
************************************************************'''
def test():
	emailList = []               #存放每个邮件的单词向量
	emailLabel = []              #存放邮件对应的标签
	cwd = "C:\\Users\\cyp\\Documents\\sourcecode\\machinelearning\\my__code\\chapter4\\"
	for i in range(1,26):
		#读取非侮辱邮件,并生成单词向量
		wordList = text2VecOfWords(open(cwd+r"email\ham\%d.txt"%i,encoding='Shift_JIS').read())
		emailList.append(wordList)#将单词向量存放到emailList中
		emailLabel.append(0)      #存放对应的标签
		wordList = text2VecOfWords(open(cwd+r"email\spam\%d.txt"%i,encoding='Shift_JIS').read())
		#读取侮辱邮件,并生成单词向量
		emailList.append(wordList)#将单词向量存放到emailList中
		emailLabel.append(1)      #存放对应的标签
	vocabList = createVocalList(emailList)#由所有的单词向量生成词库
	trainSet = [i for i in range(50)]     #产生0-49的50个数字
	testIndex = []                        #存放测试数据的下标
	for i in range(10):                   #从[0-49]中随机选取10个数
		randIndex = int(random.uniform(0,len(trainSet)))
										  #随机生成一个trainSet的下标
		testIndex.append(trainSet[randIndex])#提取对应的数据作为训练数据
		del(trainSet[randIndex])          #删除trainSet对应的值,以免下次再选中
	trainDataSet = []             #存放训练数据(用于词集方法)
	trainLabels = []              #存放训练数据标签(用于词集方法)
	trainDataSet1 = []             #存放训练数据(用于词袋方法)
	trainLabels1 = []              #存放训练数据标签(用于词袋方法)
	for index in trainSet:        #trainSet剩余值为训练数据的下标
		trainDataSet.append(setofWords2Vec(vocabList,emailList[index]))
		#提取训练数据
		trainLabels.append(emailLabel[index])
		#提取训练数据标签
		trainDataSet1.append(bagofWords2Vec(vocabList,emailList[index]))
		#提取训练数据
		trainLabels1.append(emailLabel[index])
		#提取训练数据标签
	pShame,p0Vect,p1Vect = trainNB0(trainDataSet,trainLabels)#开始训练
	pShame1,p0Vect1,p1Vect1 = trainNB0(trainDataSet1,trainLabels1)#开始训练
	errorCount = 0.0                #统计测试时分类错误的数据的个数
	errorCount1= 0.0                #统计测试时分类错误的数据的个数
	for index in testIndex:
		worldVec = setofWords2Vec(vocabList,emailList[index])#数据预处理
		#进行分类,如果分类不正确,错误个位数加1
		if classifyNB(worldVec,p0Vect,p1Vect,pShame) != emailLabel[index]:
			errorCount += 1
		worldVec = bagofWords2Vec(vocabList,emailList[index])#数据预处理
		#进行分类,如果分类不正确,错误个位数加1
		if classifyNB(worldVec,p0Vect1,p1Vect1,pShame1) != emailLabel[index]:
			errorCount1 += 1
	#输出分类错误率
	print("As to set,the error rate is :",float(errorCount)/len(testIndex))
	print("AS to bag,the error rate is :",float(errorCount1)/len(testIndex))
	
'''***********************************************************
  Function:       main
  Description:    运行test函数
  Calls:          test
  Called By:       
  Input:
  Return:          
************************************************************'''
if __name__=="__main__":
	test()</strong>

<span style="font-family: Microsoft YaHei; font-size: 18px;"><strong>四、结果展示</strong></span>

1、垃圾邮件过滤实验结果如下:


  注:本人菜鸟一枚,如有不对的地方还请指正,同时欢迎一起交流学习。




  • 5
    点赞
  • 90
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值