K-近邻算法和决策树算法容易理解,但从朴素贝叶斯开始,机器学习书中关于算法来历及证明几乎没有,很多都涉及到统计概论的知识,推荐李航老师的《统计学习方法》,清华大学出版社,里面有关于机器学习的很多算法原理,建议基础薄弱的同学把整本书看完,也不是很厚,200多页。
```
'''基于概率论的分类方法:朴素贝叶斯
朴素的概念:整个形式化过程只做最原始最简单的假设
优点:在数据较少的情况下仍然有效,可以处理多类别的问题
缺点:对于输入数据的准备方式较为敏感
适用数据类型:标称型数据
'''
'''
贝叶斯决策理论的核心思想:选择高概率对应的类别
'''
import operator
from numpy import *
import re
#词表到向量的转换函数
def loadDataSet(): #创建实验样本
postingList=[['my','dog','has','flea','problems'],\
['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 postingList,classVec #返回的第一个变量是进行词条切分后的文档集合
def createVocabList(dataSet): #传入词条切分后的列表
vocabSet=set([]) #创建空的集合,而且创建空集合必须用set()
for document in dataSet:
vocabSet=vocabSet|set(document) #创建两个集合的并集,其中没有重复元素,这样生成的集合内的元素是无序的
return list(vocabSet) #返回的是被列表化的集合
def setOfWords2Vec(vocabList,inputSet): #传入一个集合和被检测列表
returnVec=[0]*len(vocabList) #初始化向量列表全为0,其中个数为单词(集合)列表元素数(len)
for word in inputSet: #遍历被检测列表
if word in vocabList: #检测被检测的列表中是否有给定样本中的词汇
returnVec[vocabList.index(word)]=1#将该词出现的第一个位置的特征值标记为1
else: print("the word:%s is not in my Vocabulary!"%word)
return returnVec #返回被检测列表的某个词是否在该列表中
#朴素贝叶斯分类器训练函数
def trainNB0(trainMatrix,trainCategory): #参数意义:文档矩阵和由每篇文档类别标签所构成的向量
numTrainDocs=len(trainMatrix) #得到文本个数
numWords=len(trainMatrix[0]) #得到第一个文本的长度
pAbusive=sum(trainCategory)/float(numTrainDocs) #计算了侮辱性言论总的比例
p0Num=ones(numWords) #传入一个参数,初始化一个长度为numWords的列表
p1Num=ones(numWords)
p0Denom=2.0
p1Denom=2.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=log(p1Num/p1Denom) #p1Vect的意义是:计算了每个单词在被判断为侮辱性言论中出现的概率
p0Vect=log(p0Num/p0Denom) #基本同上(用对数的形式是为了避免结果下溢)
return p0Vect,p1Vect,pAbusive #返回的是概率
'''
计算出来的p0Vect和p1Vect中对应的比例是该位置单词在对应类别标签中的出现比例,而比例最高的是该类别最具有代表性的单词
实际上是在算条件概率:p(A|B)=P(AB)/P(B)
'''
##listOPosts,listClasses=loadDataSet()
##myVocabList=createVocabList(listOPosts)
##trainMat=[]
##for postinDoc in listOPosts:
## trainMat.append(setOfWords2Vec(myVocabList,postinDoc))
##print(trainMat)
##p1d,p0d,p1,p0=trainNB0(trainMat,listClasses)
##print(p1d)
##print(p0d)
##print(p1)
##print(p0)
##print(p1/p1d)
#以下两个函数是朴素贝叶斯分类函数
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
p1=sum(vec2Classify*p1Vec)+log(pClass1) #实际上是在计算靠近值:单词出现在集合中的0/1情况*概率再全部求和+类别对数概率
p0=sum(vec2Classify*p0Vec)+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(array(trainMat),array(listClasses))
testEntry=['love','my','dalmation']
thisDoc=array(setOfWords2Vec(myVocabList,testEntry))#标记testEntry文本中单词在结合中的出现
print(testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb))#p0V、p1V、pAb是训练好的的分类器传入thisDoc进行测试
testEntry=['stupid','garbage']
thisDoc=array(setOfWords2Vec(myVocabList,testEntry))
print(testEntry,'classified as:',classifyNB(thisDoc,p0V,p1V,pAb))
#朴素贝叶斯词袋模型
def bagOfWords2VecMN(vocabList,inputSet):
returnVec=[0]*len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)]+=1#每个单词在集合中的出现次数
return returnVec
##mySent='This book is the best book on Python or M.L. I have ever laid eyes upon.'
##mySent.split()
##regEx=re.compile('\\W') #书上有*号,但是结果不对,应该是版本问题
##listOfTokens=regEx.split(mySent)
##a=[tok.lower() for tok in listOfTokens if len(tok)>0]
##emailText=open('email/ham/6.txt').read()
##listOfTokens=regEx.split(emailText)
#文件解析及完整的垃圾邮件测试函数
def textParse(bigString):
#import re
listOfTokens=re.split(r'\W',bigString) #接受一个大字符串并将其解析为字符串列表(这里原文是:listOfTokens=re.split(r'\W*',bigString) )
return [tok.lower() for tok in listOfTokens if len(tok)>0] #所有字符转换为小写
def spamTest():
docList=[]
classList=[]
fullText=[]
for i in range(1,26):
wordList=textParse(open('email/spam/%d.txt'% i).read())#导入文本并将其解析为词列表
docList.append(wordList) #每个文件为一个元素
fullText.extend(wordList) #是所有的词构成的一个列表
classList.append(1) #classList末尾添加1
wordList=textParse(open('email/ham/%d.txt'% i).read())#这里在根目录ham中第23个文本中找到SciFinance?,把?替换成空格即可
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList=createVocabList(docList) #创建词汇的集合(无重复项)
trainingSet=list(range(50)) #一共有50个文本
testSet=[] #初始化样本列表
for i in range(10):
randIndex=int(random.uniform(0,len(trainingSet)))#在[0,len(trainingSet))生成一个随机数
testSet.append(trainingSet[randIndex]) #把抽取的每个样本附加在testSet的末尾作为其中一个元素
del(trainingSet[randIndex]) #添加完以后删除这个元素,后面的元素顶上来填空位置
trainMat=[] #初始化词汇出现情况(每个元素代表一个文本中词汇在集合中的出现情况)
trainClasses=[] #初始化0/1分类情况
for docIndex in trainingSet: #此时的trainingSet已经是range(40)了
trainMat.append(setOfWords2Vec(vocabList,docList[docIndex]))#循环结束得到全部文本中前40个中词汇在集合中出现的情况
trainClasses.append(classList[docIndex]) #循环结束得到的是(前40个)classList列表中第docList个文档的0/1归类情况
p0V,p1V,pSpam=trainNB0(array(trainMat),array(trainClasses)) #得到每个词的出现次数与其所在句所有词0/1之和的比例以及训练样本中侮辱性言论文本比例
errorCount=0 #判断错误数量初始化
for docIndex in testSet: #遍历测试样本下标
wordVector=setOfWords2Vec(vocabList,docList[docIndex]) #得到测试样本的单词集合(无重复项)
if classifyNB(array(wordVector),p0V,p1V,pSpam)!=classList[docIndex]:#判断机器分类与实际分类是否相同
errorCount+=1 #不同则判断错误数量加一
print('classification error ',docList[docIndex])
print('the error rate is: ',float(errorCount)/len(testSet)) #输出判断错误率
'''
1.留存交叉验证:随机选择数据的一部分作为训练集,剩余部分作为测试集的过程
2.UnicodeDecodeError: 'gbk' codec can't decode byte 0xae in position 199: illegal multibyte sequence该报错是由于存在未能编码的字符
3.python2与python3中range()的区别:python2中的range()返回一个列表,而python3中返回一个迭代值就是range(0,10)(如果输入的是range(10))
'''
#RSS源分类器及高频词去除函数
def calcMostFreq(vocabList,fullText): #参数意义:单词集合(无重复)、文本所有单词构成的列表(有重复)
freDict={} #声明计数字典
for token in vocabList: #遍历集合中的每一个不重复元素
freqDict[token]=fullText.count(token)#a.count(sub)函数是对a中sub个数的统计字典的
sortedFreq=sorted(freqDict.items(),key=operator.itemgetter(1),reverse=True)#对字典迭代并按第1个元素排序(从零开始),顺序为倒序(从大到小)
return sortedFreq[:30] #取出现频率最高的30个单词(0-29)
```