优点:在数据较少的情况下任然有效(但是准确率也属于一言难尽),可以处理多类别问题。 缺点:对于输入数据的方式较为敏感。 适用数据类型:标称型数据。
前置知识:条件概率,贝叶斯决策理论,相互独立
相互独立:相互独立是设A,B是两事件,如果满足等式 P ( A B ) = P ( A ) P ( B ) P(AB)=P(A)P(B) P(AB)=P(A)P(B),则称事件A,B相互独立,简称A,B独立。
条件概率: P ( g r e y ∣ b u c k e t B ) P(grey|bucketB) P(grey∣bucketB)表示的是在该球取自B桶的条件下,该球为灰色的概率。
条件概率计算方法
P
(
B
∣
A
)
=
P
(
A
B
)
P
(
A
)
P(B|A)=\frac{P(AB)}{P(A)}
P(B∣A)=P(A)P(AB)
贝叶斯准则:
通过条件概率公式代换,得到
P
(
A
∣
B
)
P(A|B)
P(A∣B)和
P
(
B
∣
A
)
P(B|A)
P(B∣A)之间的转换方法:
p
(
A
∣
B
)
=
P
(
B
∣
A
)
P
(
A
)
P
(
B
)
p(A|B)=\frac{P(B|A)P(A)}{P(B)}
p(A∣B)=P(B)P(B∣A)P(A)
贝叶斯决策理论:
核心思想:选择概率最高的决策
如果 p 1 ( x , y ) > p 2 ( x , y ) p1(x,y)>p2(x,y) p1(x,y)>p2(x,y),则(x,y)属于1类
如果 p 1 ( x , y ) < p 2 ( x , y ) p1(x,y)<p2(x,y) p1(x,y)<p2(x,y),则(x,y)属于2类
综合以上的内容,最终的到贝叶斯分类准则:
样本数:
在进行分类是,特征数决定着样本数,由统计学知,如果每个特征需要N个样本,那么对于M个特征需要 N M N^M NM个样本,该数量是指数爆炸的。
假如特征之间相互独立,那么所需要的样本数可以从 N M N^M NM下降到 N ∗ M N*M N∗M。
使用朴素贝叶斯进行文档分类:
该方法中将单词作为特征,特征数巨大。
”朴素“:
单词的分布实际上是相互影响的,一些特定语法,一些固定搭配。该方法采用以特征之间相互独立为前提,对文档进行分类,故为朴素,目的也是为了使用更少的样本。
准备数据:从文本中构建词向量
训练算法:从词向量计算概率
根据贝叶斯分类准则,我们需要求得 P ( c 0 ∣ w ) P(c_0|w) P(c0∣w)和 P ( c 1 ∣ w ) P(c_1|w) P(c1∣w),其中w是由多个单词(特征)组成的词向量。
首先,由贝叶斯准则,的到:
P
(
c
0
∣
w
)
=
P
(
w
∣
c
0
)
P
(
C
0
)
P
(
w
)
P(c_0|w)=\frac{P(w|c_0)P(C_0)}{P(w)}
P(c0∣w)=P(w)P(w∣c0)P(C0)
其次,由”朴素“假设得
P
(
w
∣
c
0
)
=
P
(
w
1
,
w
2
,
…
,
w
n
∣
c
0
)
=
P
(
w
1
∣
c
0
)
P
(
w
2
∣
c
0
)
…
P
(
w
n
∣
c
0
)
P(w|c_0)=P(w_1,w_2,\dots,w_n|c_0)=P(w_1|c_0)P(w_2|c_0)\dots P(w_n|c_0)
P(w∣c0)=P(w1,w2,…,wn∣c0)=P(w1∣c0)P(w2∣c0)…P(wn∣c0)
便可以得到以下伪代码:
计算每个类别中的文档数目 对每篇训练文档: 对每个类别: 如果词条出现文档中->增加该词条的计数值 增加所有词条的计数值 对每个类别: 对每个词条: 将该词条的数目除以总词条数目得到条件概率 返回每个类别的条件概率
该代码在实现过程中运用了numpy的特性。
并且本书中的一些地方也没有讲清楚:
如:
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): p1 = sum(vec2Classify * p1Vec) + log(pClass1) # element-wise mult p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1) if p1 > p0: return 1 else: return 0
计算p1,p2时为何要加log(),为什么是概率相加,不应该是相乘吗?
CSDN大佬给出了答案,对其进行吸收和整理:
在解决下溢问题(多个极小数相乘时,会出现精度消失)时,所以我们可以使用 l o g ( x ∗ y ) = l o g ( x ) + l o g ( y ) log(x*y)=log(x)+log(y) log(x∗y)=log(x)+log(y)这个特性。
之所以可以使用,是因为log函数,和概率函数的分布形态近视:
那么以上的代码就被很好的解释了。
实例:使用朴素贝叶斯过滤垃圾邮件
收集数据:提供文本文件。 准备数据:将文本文件解析成词条向量。 分析数据:检查词条确保解析的正确性。 训练算法:使用我们之前建立的trainNB0()函数。 测试算法:使用classifyNB(),并且构建一个新的测试函数来计算文档集的错误率。 使用算法:构建一个完整的程序对一组文档进行分类,将错分的文档输出到屏幕上。
利用正则表达式切分文本
import re mySent='This book is the best book on python or M.L. i have ever laid eyes upon.' regEx = re.compile('\\W')#\W表示非单词数字的字符 listOfTkens = regEx.split(mySent)#split()表示以mySent作为分割标志切割字符 to=[tok.lower() for tok in listOfTkens if len(tok)>0]#去掉长度为0的空串,且大小写不影响侮辱性 print(listOfTkens) print(to)
留存交叉验证:从数据集中选择一定数量的作为测试集,剩余的用于训练。
实例:使用朴素贝叶斯分类器从个人广告获取区域倾向。
收集数据:从RSS源收集内容,这里需要对RSS源构建一个接口 准备数据:将文本文件解析成词条向量。 分析数据:检查词条确保解析的正确性。 训练算法:使用我们之前建立的trainNB0()函数。 测试算法:观察错误率,确保分类器可用。 使用算法:构建一个完整的程序对一组文档进行分类,将错分的文档输出到屏幕上。
使用了去除出现次数最多的30个词的方式降低错误率,这是由于语言中大部分的冗余造成的。
bayes.py
from numpy import * #-----------------------------词表到向量的转换------------------------------------ def loadDataSet(): postingList = [['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 postingList, classVec #---------------------------------建立词典----------------------------------- def createVocabList(dataSet): vocabSet = set([]) # 获取特征 for document in dataSet: vocabSet = vocabSet | set(document) # union of the two sets,集合取并 return list(vocabSet) #-------------------检查待分类的词向量的特征是否都出现在词典中------------------------ def setOfWords2Vec(vocabList, inputSet): returnVec = [0] * len(vocabList)#列表的复制操作 for word in inputSet: if word in vocabList: returnVec[vocabList.index(word)] = 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)#P(c1) p0Num = ones(numWords) p1Num = ones(numWords) # change to ones() p0Denom = 2.0 p1Denom = 2.0 # 优化,由于是概率相乘,但有一个为0时,整体概率会为0,防止出现该现象,故将所有词的出现次数初始化为1,分母初始化为2 for i in range(numTrainDocs): if trainCategory[i] == 1: p1Num += trainMatrix[i]#array类型,存放的是每个单词出现的次数,整体相加,方便 p1Denom += sum(trainMatrix[i]) else: p0Num += trainMatrix[i]#array类型,存放的是每个单词出现的次数,整体相加,方便 p0Denom += sum(trainMatrix[i]) p1Vect = log(p1Num / p1Denom) # 解决下溢问题,当多个较小的数相乘时,数值会很小,会出现精度丢失问题,利用激活函数调整分布 p0Vect = log(p0Num / p0Denom) # change to log(),log函数与概率密度函数特征类似,且不会出现下溢问题。 return p0Vect, p1Vect, pAbusive def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): p1 = sum(vec2Classify * p1Vec) + log(pClass1) # element-wise mult 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)) print(thisDoc) print(testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb)) 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 def textParse(bigString): # input is big string, #output is word list import re listOfTokens = re.split(r'\W*', bigString) return [tok.lower() for tok in listOfTokens if len(tok) > 2] 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) wordList = textParse(open('email/ham/%d.txt' % i).read()) docList.append(wordList) fullText.extend(wordList) classList.append(0) vocabList = createVocabList(docList) # create vocabulary trainingSet = range(50) testSet = [] # create test set for i in range(10): randIndex = int(random.uniform(0, len(trainingSet))) testSet.append(trainingSet[randIndex]) del (trainingSet[randIndex]) trainMat = [] trainClasses = [] for docIndex in trainingSet: # train the classifier (get probs) trainNB0 trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex])) trainClasses.append(classList[docIndex]) p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses)) errorCount = 0 for docIndex in testSet: # classify the remaining items wordVector = bagOfWords2VecMN(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)) # return vocabList,fullText def calcMostFreq(vocabList, fullText): import operator freqDict = {} for token in vocabList: freqDict[token] = fullText.count(token) sortedFreq = sorted(freqDict.items(), key=operator.itemgetter(1), reverse=True) return sortedFreq[:30] def localWords(feed1, feed0): import feedparser docList = [] classList = [] fullText = [] 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) # NY is class 1 wordList = textParse(feed0['entries'][i]['summary']) docList.append(wordList) fullText.extend(wordList) classList.append(0) vocabList = createVocabList(docList) # create vocabulary top30Words = calcMostFreq(vocabList, fullText) # remove top 30 words for pairW in top30Words: if pairW[0] in vocabList: vocabList.remove(pairW[0]) trainingSet = range(2 * minLen) testSet = [] # create test set for i in range(20): randIndex = int(random.uniform(0, len(trainingSet))) testSet.append(trainingSet[randIndex]) del (trainingSet[randIndex]) trainMat = [] trainClasses = [] for docIndex in trainingSet: # train the classifier (get probs) trainNB0 trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex])) trainClasses.append(classList[docIndex]) p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses)) errorCount = 0 for docIndex in testSet: # classify the remaining items wordVector = bagOfWords2VecMN(vocabList, docList[docIndex]) if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]: errorCount += 1 print('the error rate is: ', float(errorCount) / len(testSet)) return vocabList, p0V, p1V def getTopWords(ny, sf): import operator vocabList, p0V, p1V = localWords(ny, sf) topNY = [] topSF = [] for i in range(len(p0V)): if p0V[i] > -6.0: topSF.append((vocabList[i], p0V[i])) if p1V[i] > -6.0: topNY.append((vocabList[i], p1V[i])) sortedSF = sorted(topSF, key=lambda pair: pair[1], reverse=True) print("SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**") for item in sortedSF: print(item[0]) sortedNY = sorted(topNY, key=lambda pair: pair[1], reverse=True) print("NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**") for item in sortedNY: print(item[0])
main.py
import re import bayes listOPosts,listClasses=bayes.loadDataSet() myVocabList=bayes.createVocabList(listOPosts) print(myVocabList) print(bayes.setOfWords2Vec(myVocabList,listOPosts[0])) trainMat = [] for postinDoc in listOPosts: trainMat.append(bayes.setOfWords2Vec(myVocabList,postinDoc)) print(trainMat)#得到的是每一句话在字典中的分布 p0v,p1v,pAb=bayes.trainNB0(trainMat,listClasses) print(pAb) print(p0v) print(p1v) bayes.testingNB() mySent='This book is the best book on python or M.L. i have ever laid eyes upon.' regEx = re.compile('\\W')#\W表示非单词数字的字符 listOfTkens = regEx.split(mySent)#split()表示以mySent作为分割标志切割字符 to=[tok for tok in listOfTkens if len(tok)>0]#去掉长度为0的空串 print(listOfTkens) print(to)
结语: