1.概述
knn算法 决策树都是给出具体某个分类,但有时候并不能确定给出某个分类,而只能给出最优分类及其概率,这种情况可以使用基于概率论的分离算法,比如朴素的贝叶斯算法
截图自《机器学习实战》:
贝叶斯决策理论的核心,选择出现概率最高的分类,选择最高概率的决策。
2,应用
文档分类,通过观察文档实例中特征词出现或者不出现作为一个判断特征,这样会出现很多判断特征,不适合专家系统的决策树,也不适合大量计算的KNN,可以看出每种算法都有其适用的场景,就像厨房里不同的刀具一样,有专门用来切肉的刀,也有切水果的水果刀。所以, 需要做到两方面:1.熟悉算法原理 2.熟悉应用场景
要理解朴素贝叶斯决策,需要先理解条件概率及贝叶斯准则,考虑下面一个例子:
现在有一些石头,3个灰色,4个黑色
问:
1)、取到灰色石头的概率 ? 很简单,灰色石头个数/总的石头个数=3/7
2) 取到黑色石头的概率? 4/7
现在将问题升级,分别放到两个桶里A,B,如下图:
问:
1) 从B桶中取灰色石头的概率? p(gray|bucketB)=p(gray,bucketB)/p(bucketB)=1/7/3/7=1/3 =>可以看出,计算方法为,直接锁定B桶(已知条件),然后取灰色球的个数除以B桶中球的总数即可=>如果将A B想象成分类,可以看出,可以计算出特定分类下,某种特征(颜色)出现的条件概率,不过需要假设不同特征之间是相互独立的。计算方法为,统计固定分类下,某个特征值的个数(比如,统计指定分类的文档中单词出现的次数),然后除以“固定分类”下总的特征个数(比如,统计某个分类的文档中所有单词的个数),这样就可以得到这个分类下,某个(或者多个,向量)单词的出现概率
2)反过来问,取到灰色石头落在B桶的概率??很有意思吧,嘿嘿
根据贝叶斯准则,p(bucketB|gray)=p(gray|bucketB)p(bucketB)/p(gray)=(1/3 * 3/7)/3/7=1/3 =>可以看出,计算方法为,先根据贝叶斯准则建立起跟上面求出的p(gray|bucketB)的联系,可以看出贝叶斯准则表达的是,“可以根据已知的先验经验,然后加推理,可以做出预测”,通俗的举个例子,比如:一个好人很可能会做一些好事,那么通过一些好事他是否做过,可以判断一个人是否为好人,当然这是个概率问题。=>对于文档分类也一样,前面根据样本数据可以计算出,固定分类下,文档中的单词出现的概率,反过来,根据这些经验数据,可以去预测一篇文档落在哪个分类里。
引自《机器学习实战》一段话,也是利用条件概率进行分类算法的理论基础:
代码实现:
使用朴素贝叶斯算法实现文档分类,使用文档中的单词是否出现作为特征,这样得到的特征可能会非常多。注意我们假设各个特征是独立的,这样不一定符合实际使用场景,但是可以简化我们的计算,减少样本数据量。在这里就是假设单词之间没有相互关系,彼此出现的概率相同,这也是朴素贝叶斯算法的朴素的意思。第二个假设是,每个特征同等重要。引自《机器学习实战》,朴素贝叶斯的一般过程:
bayes.py:
from numpy import * import re ''' 朴素贝叶斯算法实现分类,该算法有时候不是为了准备分类,而是想通过算法发现数据背后相关的内容。学习通过修正从样本中获取的数据, 比如去掉最值,移除辅助词,观察正确率变化,提高算法的精确度。 ''' # 加载数据 def loadDataSet(): postingList = [['my', 'dog', 'has', 'flea', 'problem', 'help', 'please'], ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'], ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'], ['stop', 'posting', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'], ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'], ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']] classVec = [0, 1, 0, 1, 0, 1] return postingList, classVec # 创建词汇表 # 提取数据集每个元素,去重返回列表 def createCabList(dataset): vocabSet = set() for item in dataset: vocabSet = vocabSet | set(item) return list(vocabSet) # 检查输入的词汇列表是否在词汇表中出现,如果出现就置对应位置为1 # 将输入的一组单词转换成数字向量 #这个是“词集模型”,每个单词在文档中特征为,是否出现 def setofWords2Vec(vocabList, inputlist): resultVec = [0] * len(vocabList) for item in inputlist: if item in vocabList: resultVec[vocabList.index(item)] = 1 return resultVec #如果一个单词出现多次,为了记录这个特征的信息,需要“词袋模型” def bagofWords2VecMN(vocabList, inputlist): resultVec = [0] * len(vocabList) for item in inputlist: if item in vocabList: resultVec[vocabList.index(item)] += 1 return resultVec # 获取训练矩阵 def getTrainMatrix(vocabList, postingList): trainMatirx = [] for posting in postingList: trainMatirx.append(setofWords2Vec(vocabList, posting)) return trainMatirx # 计算固定分类下的条件概率 # 输入:特征值矩阵 分类向量 def trainNB0(trainMatrix, classVec): # 文档个数 numTrainDocs = len(trainMatrix) # 特征维度-单词个数 numWords = len(trainMatrix[0]) # 计算侮辱性分类的文档出现的概率p(c1) pAbusive = (sum(classVec)) / float(len(classVec)) # 初始化单词在不同分类下的概率向量和出现总数 # 注意这里需要进行矩阵运算,不能直接使用list,需要使用numpy中的array进行计算 # p0num=[0]*numWords # p0num=zeros(numWords) # 防止多个特征值概率相乘,其中一个为0,导致整体为0 p0num = ones(numWords) # p1num=[0]*numWords # p1num=zeros(numWords) p1num = ones(numWords) # p0totalnum=0.0 p0totalnum = 2.0 # p1totalnum=0.0 p1totalnum = 2.0 # 遍历特征值矩阵,按照分类向量中指定文档类型,进行分类,然后计算条件概率p(w|ci) for i in range(numTrainDocs): # 如果i行是侮辱性文档 if classVec[i] == 1: # 进行单词出现个数的向量累积,想象下那个灰色或者黑色的球吧,嘿嘿 p1num += trainMatrix[i] # 统计这个分类下的单词总数,想象下那个B桶中的所有球 p1totalnum += sum(trainMatrix[i]) # 如果是非侮辱性文档 else: p0num += trainMatrix[i] p0totalnum += sum(trainMatrix[i]) # 计算条件概率,p(w|ci) w是单词出现次数的向量矩阵 # p0Vect=p0num/p0totalnum # p1Vect=p1num/p1totalnum # 防止很多小的数相乘导致下溢出ln(ab)=lna+lnb p0Vect = log(p0num / p0totalnum) p1Vect = log(p1num / p1totalnum) return p0Vect, p1Vect, pAbusive # 使用朴素贝叶斯进行分类 # 计算哪个出现的概率更大,sum(vec2Classify*p(w|ci))+logci -->vec2Classify是特征值向量->出现1,不出现0 #p(ci|w)=p(w|ci)p(ci)/p(w) p(w)相同,无需比较,所以只需要比较p(w|ci)p(ci)=>ln(p(w|ci)p(ci))=ln(p(w|ci))+ln(ci) #=ln(p(w1|ci)p(w2|ci)...p(wn|ci))+ln(ci)=sum(ln(p(wi|ci))+ln(ci)=>sum(vec2Classify*piVect)+ln(ci) #问题?如果特征值取值不光0或者1,而是更多值该如何计算??? def trainNB(vec2Classify, p0Vect, p1Vect, pAbusive): p1 = sum(vec2Classify * p1Vect) + log(pAbusive) p0 = sum(vec2Classify * p0Vect) + log(1.0 - pAbusive) if p1 > p0: return 1 else: return 0 #将输入的一段字符串拆分成单词列表 def text2words(text): return [word.lower() for word in re.compile(r'\W+').split(text) if len(word) >2 ] ''' 测试贝叶斯算法 ''' def testBayes(postingList, classVec,vec2ClassifyList,factClassVec,desc): print('输入文档列表:\n', array(postingList)) print('分类列表:\n', classVec) vocabList = createCabList(postingList) print('所有单词的列表:\n', vocabList) trainMatrix = getTrainMatrix(vocabList, postingList) print('单词是否出现的矩阵:\n', array(trainMatrix)) p0Vect, p1Vect, pAbusive = trainNB0(array(trainMatrix), array(classVec)) #print('非侮辱性文档中的单词出现的概率:\n', p0Vect) #print('侮辱性文档中的单词出现的概率:\n', p1Vect) #print('侮辱性文档出现的概率:\n', pAbusive) #vec2ClassifyList = ['dog', 'stupid'] index=0 trueCount=0 totalCount=len(vec2ClassifyList) for vec2ClassifyElem in vec2ClassifyList: vec2Classify = setofWords2Vec(vocabList, vec2ClassifyElem) classResult = trainNB(array(vec2Classify), p0Vect, p1Vect, pAbusive) print('%s是否是%s的?0:不是 1:是\n' % (vec2ClassifyElem,desc), classResult) if classResult ==factClassVec[index] : trueCount+=1 index +=1 print('正确率:\n',trueCount/float(totalCount)) def readDataSet(filepaths,start,end): postingList = [] classVec = [] for i in range(start, end): with open(filepaths[0] + '\%d.txt' % i, 'r') as fd: classVec.append(1) postingList.append(text2words(fd.read())) with open(filepaths[1] + '\%d.txt' % i, 'r') as fd: classVec.append(0) postingList.append(text2words(fd.read())) return postingList,classVec #测试敏感词 vec2ClassifyList=[['stupid','uu']] postingList, classVec = loadDataSet() factClassVec=[1] testBayes(postingList, classVec,vec2ClassifyList,factClassVec,'侮辱性') #测试分类邮件 #垃圾邮件目录 spamPath='D:\software\python\sourcecode_and_data\MLiA_SourceCode\machinelearninginaction\Ch04\email\spam' #非垃圾邮件目录 hamPath='D:\software\python\sourcecode_and_data\MLiA_SourceCode\machinelearninginaction\Ch04\email\ham' filepaths=[spamPath,hamPath] #读入文件,构造数据集和分类向量 postingList,classVec=readDataSet(filepaths,1,15) #print("邮件数据集:\n",array(postingList)) #print("邮件分类向量\n",classVec) #读入文件,构造测试数据,最好使用随机,这里只是简单测试 testPostingList,testClassVec=readDataSet(filepaths,15,26) testBayes(postingList, classVec,testPostingList,testClassVec,'垃圾邮件')