机器学习实战+第四章_朴素贝叶斯

相比较与kNN,计算量已经大大下降。因为kNN每次进行预测,都要将预测的instance与训练集中所有例子进行计算。而bayes只需要记录词向量中每个词的权重即可。

假设文本里每个词的出现概率是独立的,与文本中的其它词无关。

对于极小数的相乘,采用了取对数的方法保留精度。

可以采用停用词表或remove高频词来进行优化。


'''
p(A|B)=p(B|A)P(A)/P(B)	条件概率
P(Ci|W)=p(W|Ci)p(Ci)/p(W)		、
如果Ci是label,W看作将一个事物抽象而成的若干特征。那么P(Ci|W)就是这个有若干特征指代的事物属于Ci的概率。
那么预测事物W属于哪个标签,只需求出事物W在所有标签中的概率,将概率最高的那个作为标签即可。
因为P(C1|W)与P(C2|W)的比较等价于P(C1|W)/p(W)与P(C2|W)/p(W)的比较。故只需知道p(W|Ci)p(Ci)的大小在C1和C2中谁大即可预测w属于C1还是C2.
'''
from numpy import *
import feedparser

def loadDataSet():
	dataset=[['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 dataset,classVec

#将dataset中出现的所有词转换成集合,并返回字符串列表
def createVocabList(dataset):
	vocabSet=set([])		#创建集合
	for line in dataset:
		vocabSet=vocabSet|set(line)		#| 集合并操作。注意,这里不能直接set(dataset),那样集合的元素不是词而是列表
	return list(vocabSet)

#根据字符串列表将input转换成词向量,input中词若也在字符串列表中,则对应位置的0变为1.
def wordsVec(vocabList,inputline):
	returnVec=[0]*len(vocabList)		#创建一个长度和vocablist一致的一维全0列表
	for vocabulary in inputline:
		if vocabulary in vocabList:
			#returnVec[vocabList.index(vocabulary)]=1	#这个词在vocabList出现过,那么向量对应的位置就改为1.
			returnVec[vocabList.index(vocabulary)]+=1	#由词集模型(出现与否)转化为词袋模型(出现几次)
	return returnVec

#计算p1Vector,p0Vector和pAbusive,前两个分别是在分类1和0中各个词出现的权重
#参数1为一个由dataset转化而来的矩阵,矩阵的每一行是一个词向量,参数2即labels(classVec),表示trainMat的每一行是否为侮辱性
def trainNB(trainMat,trainCategory):	#NB naive basyes	#trainMat和trainCategory均为array类型,而不是list类型
	numLine=len(trainMat)
	numVoca=len(trainMat[0])
	pAbusive=sum(trainCategory)/numLine		#侮辱性句子在所有句子中的频率
	p0Num=ones(numVoca)
	p1Num=ones(numVoca)	
	count1=2
	count0=2	 
	for i in range(numLine):
		if trainCategory[i]:
			p1Num+=trainMat[i]
			count1+=sum(trainMat[i])
		else:
			p0Num+=trainMat[i]
			count0+=sum(trainMat[i])		
	p1Vector=array(log(p1Num/count1))	#一个向量,向量的每列都代表一个词,其值为该词的频率(该词在label中出现频数/count1)
	p0Vector=array(log(p0Num/count0))	#list不可以和num或list做除法,array可以和array或num做除法
	return 	p0Vector,p1Vector,pAbusive
'''
p1Vector和p0Vector再取对数的操作,是为了避免多个很小的数相乘导致下溢或四舍五入的错误。
且In(a*b)=In(a)+In(b),这对于概率常见的连乘计算很有用
但是为了避免出现In(0),p0Num和p1Num使用ones函数而非zeros函数。count0和count1也相应的由0改为2.
'''


#计算词向量出现在各分类中的概率,概率最大的分类作为返回
#参数1为待分类的词向量,参数2到4是trainNB函数返回的结果。
def classifyNB(vectorClassify,p0Vector,p1Vector,pClass1):
	p1=sum(vectorClassify*p1Vector)+pClass1		
	#sum()的结果为p(W|Ci)的对数,p(W|Ci)=p(W0,W1,W2...Wn|Ci)=每个词在分类Ci中出现的权重*这个词是否在W中出现。pClass1即为p(Ci)。
	#所以p1其实并不是p(Ci|W)的对数,而是p(CiW)的对数.即p1是Ci和W同时发生的概率的对数。当然,不除p(w)对比大小没有影响
	p0=sum(vectorClassify*p0Vector)+(1-pClass1)
	if p1>p0:
		return 1
	else:
		return 0


def testingNB():
	dataset,classVec=loadDataSet()
	vocabList=createVocabList(dataset)
	trainMat=[]
	for line in dataset:
		trainMat.append(wordsVec(vocabList,line))
	p1v,p0v,pAb=trainNB(trainMat,classVec)
	testline=['love','my','dalmation']
	testVec=wordsVec(vocabList,testline)
	print(classifyNB(testVec,p0v,p1v,pAb))
	testline=['garbage','dalmation','stupid']
	testVec=wordsVec(vocabList,testline)
	print(classifyNB(testVec,p0v,p1v,pAb))
	return

#testingNB()


#输入是一封邮件的文本内容,输出是一个字符列串表
def textParse(bigString):   	#parse 解析 
	import re
	stringList=re.split(r'\W*',bigString)		#split与正则,加r则\不用再转义。\W与\w含义相反,表示所有不是字母数字下划线的字符。*为数量符
	return [string.lower() for string in stringList if len(string)>2]		#列表表达式
	#每个字符串都要大于2的原因。第一,字符长度小于等于2的单词没有什么含义,如is,am。第二,类似由'M.L.'分割成'M','L',这种词同样没有价值。
	#最后,split可能会切出部分空字符。如'M.L.'返回['M','L',''],但是,书中的'M.L. I'返回是['M','L','I']而不是['M','L','','I']


def spamTest():
	docList=[]		#二维矩阵,每一行记录一条邮件的字符串列表
	classList=[]	#记录分类信息
	fullList=[]		#记录邮件中出现的所有词
	from os import listdir
	hamemails=listdir('D:\ADA\save\python\MachineLearninginAction\machinelearninginaction\Ch04\email\ham')
	spamemails=listdir('D:\ADA\save\python\MachineLearninginAction\machinelearninginaction\Ch04\email\spam')	#spam为垃圾邮件
	for url in hamemails:
		vocabList=textParse(open('D:\ADA\save\python\MachineLearninginAction\machinelearninginaction\Ch04\email\ham\{}'.format(url)).read())
		docList.append(vocabList)		#append与extend区别
		fullList.extend(vocabList)
		classList.append(0)
	for url in spamemails:
		vocabList=textParse(open('D:\ADA\save\python\MachineLearninginAction\machinelearninginaction\Ch04\email\spam\{}'.format(url)).read())
		docList.append(vocabList)
		fullList.extend(vocabList)
		classList.append(1)	
	vocabSet=createVocabList(docList)	#遍历所有字符串,返回字符串的集合的列表
	#构造训练集和测试集
	trainingSetIndex=list(range(len(docList)))	#注意,一定要转换为list,range类型无法继续del操作。
	testSetIndex=[]			#trainingSetIndex和testSetIndex记录的是要选哪几个词做训练集和测试集,记录的是词在词列表中的下标。
	for i in range(int(len(docList)/5)):
		index=int(random.uniform(0,len(trainingSetIndex)))	#random.uniform(start,end) 产生一个范围在start到end之间的随机数
		testSetIndex.append(trainingSetIndex[index])
		del(trainingSetIndex[index])
	trainMat=[] ;trainClass=[]
	for i in trainingSetIndex:
		trainMat.append(wordsVec(vocabSet,docList[i]))
		trainClass.append(classList[i])
	p0v,p1v,pSpam=trainNB(trainMat,trainClass)
	errorCount=0
	for  i in testSetIndex:
		testVector=wordsVec(vocabSet,docList[i])
		if classifyNB(testVector,p0v,p1v,pSpam)!=classList[i]:
			errorCount+=1
	print(errorCount/len(testSetIndex))		
	return
#spamTest()

#从个人广告获取区域倾向
#统计最高频的三十个词
def calcMostFreq(vocabList,fullText):
	from operator import itemgetter
	worddict={}
	for word in fullText:
		worddict[word]=fullText.count(word)		#list的count(element)
	sortedList=sorted(worddict.items(),key=itemgetter(1),reverse=True)	#将词列表(不是词向量)按词频排序.
	#itemgetter(1)在这里表示worddict.items()返回的第二个值,即为字典的值
	return sortedList[:30]

#输入为两个RSS源,分别为两个地区的人发布的征婚广告。输出为分类的错误率统计
def localWord(feed1,feed0):
	docList=[];classList=[];fullText=[]
	#fullText用一个一维列表来记录所有词,便于后面calcMostFreq函数里统计高频词
	minlen=min(len(feed1['entries']),len(feed0['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)
	#去掉高频词
	top30words=calcMostFreq(vocabList,fullText)
	for word in top30words:		#若top30words是字典,那么这里的word就是键。但是注意,在calcMostFreq中,worddict的items()是view类型,已经不再是字典。
		vocabList.remove(word[0])
	#划分训练集和测试集
	trainingSetIndex=list(range(2*minlen));testSetIndex=[]
	for i in range(int(len(docList)/5)):
		index=int(random.uniform(0,len(trainingSetIndex)))	#random.uniform(start,end) 产生一个范围在start到end之间的随机数
		testSetIndex.append(trainingSetIndex[index])
		del(trainingSetIndex[index])
	trainMat=[] ;trainClass=[]
	for i in trainingSetIndex:
		trainMat.append(wordsVec(vocabList,docList[i]))
		trainClass.append(classList[i])
	p0v,p1v,pSpam=trainNB(trainMat,trainClass)		#这里pSpam代指feed1占标签的总数
	errorCount=0
	for  i in testSetIndex:
		testVector=wordsVec(vocabList,docList[i])
		if classifyNB(testVector,p0v,p1v,pSpam)!=classList[i]:
			errorCount+=1
	print(errorCount/len(testSetIndex))	
	return vocabList,p0v,p1v

#显示具有代表性的词汇显示
def getTopWord(ny,sf):
	from operator import itemgetter
	vocabList,p0v,p1v=localWord(ny,sf)
	topNY=[];topSF=[]
	#p>-6时词特别多,其实可以再加一层,对排序后的词只打印10个。这样特征词汇少一点。
	for i in range(len(p0v)):
		if p0v[i]>-4.8:
			topSF.append((vocabList[i],p0v[i]))		#每次插入一个元组,元组由这个词和该词的权重组成
		if p1v[i]>-4.8:
			topNY.append((vocabList[i],p1v[i]))		
	print ("SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**")
	sortedSF=sorted(topSF,key=itemgetter(1))
	for item in sortedSF:
		print(item[0])	
	print ("NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**")	
	sortedNY=sorted(topNY,key=itemgetter(1))
	for item in sortedNY:
		print(item[0])	
	return	

ny=feedparser.parse('http://newyork.craigslist.org/stp/index/rss')
sf=feedparser.parse('http://sfbay.craigslist.org/stp/index/rss')
#vocabList,p0v,p1v=localWord(ny,sf)
getTopWord(ny,sf)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值