您的位置:首页 > 编程语言 > Python开发

写程序学ML:朴素贝叶斯算法原理及实现(三)

2017-10-01 21:39 519 查看
[题外话]近期申请了一个微信公众号:平凡程式人生。有兴趣的朋友可以关注,那里将会涉及更多更新机器学习、OpenCL+OpenCV以及图像处理方面的文章。

2.2   使用朴素贝叶斯算法对文本进行分类

使用朴素贝叶斯算法对文本进行分类实现过程如下:

创建文件doctest.py;

调用函数loadDataSet()创建6个文本样例,并标记每个样本的类别。

调用函数Bayes.createVocabList()创建6个样本对应的词汇表。

调用函数Bayes.bagOfWords2Vec()获取各个样本中单词出现次数的列表,并记录到变量trainMat中。

调用函数Bayes.trainNB0()使用6个样本进行训练,返回两个类别中各个单词出现的概率及样本中类别1的概率。

调用函苏Bayes.classifyNB()对测试样本进行分类测试。

具体代码实现如下:
def testingNB():
listOPosts, listClasses = loadDataSet()
myVocabList = Bayes.createVocabList(listOPosts)
trainMat = []
for postinDoc in listOPosts:
#trainMat.append(Bayes.setOfWords2Vec(myVocabList, postinDoc))
trainMat.append(Bayes.bagOfWords2Vec(myVocabList, postinDoc))
p0V, p1V, pAb = Bayes.trainNB0(trainMat, listClasses)
testEntry = ['love', 'my', 'dalmation']
#thisDoc = array(Bayes.setOfWords2Vec(myVocabList, testEntry))
thisDoc = array(Bayes.bagOfWords2Vec(myVocabList, testEntry))
print testEntry, 'classified as: ', Bayes.classifyNB(thisDoc, p0V, p1V, pAb)
testEntry = ['stupid', 'garbage']
#thisDoc = array(Bayes.setOfWords2Vec(myVocabList, testEntry))
thisDoc = array(Bayes.bagOfWords2Vec(myVocabList, testEntry))
print testEntry, 'classified as: ', Bayes.classifyNB(thisDoc, p0V, p1V, pAb)
         函数loadDataSet

         函数定义为:def loadDataSet()

         该函数用来产生6个训练样本及其类别信息。

具体代码实现如下:

def loadDataSet():
#定义6个文档样例
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

2.3  使用朴素贝叶斯算法对email进行分类

使用朴素贝叶斯算法对email进行分类实现过程如下:

创建文件emailtest.py;

使用事先准备好的训练样本spam和ham,分别为25个文本文件。调用函数Bayes.textParse()对样本进行解析,获取样本中长度大于2的单词。Spam作为类别1,ham作为类别0。

调用函数Bayes.createVocabList()获取样本中所有出现过的单词的词汇表。

从50个训练样本中,随机抽取10个样本作为测试样本。调用函数Bayes.setOfWords2Vec()获取训练样本中单词在词汇表中出现情况。

调用函数Bayes.trainNB0()使用训练样本对朴素贝叶斯分类器进行训练。

调用函数Bayes.classifyNB(),使用测试样本对之前训练过的分类器进行测试,检测该分类器的错误率。

具体代码实现如下:
def spamTest():
docList = []
classList = []
fullText = []
for i in range(1, 26): #将文件夹spam和ham下所有文本文件解析出来
#从对应文本文件中读出字符串,将其解析为单词列表
wordList = Bayes.textParse(open('email/spam/%d.txt' % i).read())
docList.append(wordList) #将当前文本的词汇列表添加到docList变量中
fullText.extend(wordList) #将当前文本的所有单词追加到fullText变量中
classList.append(1) #分类列表变量classList中增加一个1类信息
wordList = Bayes.textParse(open('email/ham/%d.txt' % i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0) #分类列表变量classList中增加一个0类信息
vocabList = Bayes.createVocabList(docList) #获取docList中所有出现过的单词的词汇表
trainingSet = range(50) #创建拥有50个元素的list变量,存储0-49个数字,对应spam与ham目录下所有文本
testSet = []
for i in range(10): #从0-9循环,产生10个测试样本id
#uniform() 方法将随机生成下一个实数,它在 [x, y) 范围内。
#在[0, 50)之间产生一个随机整数
randIndex = int(random.uniform(0, len(trainingSet)))
print randIndex
#将trainingSet中对应训练样本id添加到测试集testSet中,并从trainingSet中删除该id
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat = []
trainClasses = []
#在40个训练样本中,逐个文本循环处理,获取1类和0类各个单词出现的概率及1类样本的概率
for docIndex in trainingSet:
#获取当前文档中单词在词汇表vocabList是否出现的列表,添加到列表变量trainMat中
trainMat.append(Bayes.setOfWords2Vec(vocabList, docList[docIndex]))
#将对应文档的分类信息添加到trainClasses中
trainClasses.append(classList[docIndex])
#获取训练样本中1类和0类各个词汇的出现概率,及所有样本中1类样本所占概率
p0V, p1V, pSpam = Bayes.trainNB0(array(trainMat), array(trainClasses))
print classList
errorCount = 0
#使用10个测试样本,对贝叶斯分类效果进行检测
for docIndex in testSet:
#获取当前测试样本中单词在词汇表vocabList是否出现的列表
wordVector = Bayes.setOfWords2Vec(vocabList, docList[docIndex])
#使用贝叶斯分类器对当前测试样本进行分类,判断分类结果是否正确
if Bayes.classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
errorCount += 1
print 'the error rate is: ', float(errorCount) / len(testSet) #打印出分类错误率

2.4   使用朴素贝叶斯算法对RSS进行分类

使用朴素贝叶斯算法对RSS进行分类实现过程如下:

使用RSS需要安装feedparser。

创建文件rsstest.py;

调用feedparser.parse()分别获取两个RSS源。

调用函数localWords()对两个RSS源进行处理,获取两个RSS源中词汇表及各自词汇出现概率信息。

最后调用函数getTopWords(),获取两个RSS源中单词出现概率大于某个阈值的单词,按降序排序将其打印出来。

具体代码实现如下:
ny = feedparser.parse('http://newyork.craigslist.org/stp/index.rss')
sf = feedparser.parse('http://sfbay.craigslist.org/stp/index.rss')
vocabList, pSF, pNY = localWords(ny, sf)
vocabList, pSF, pNY = localWords(ny, sf)
getTopWords(ny, sf)

         函数getTopWords ()

         函数定义为:def getTopWords(ny, sf)

         该函数用来获取ny, sf中单词出现概率大于某个阈值的单词,按降序排序将其打印出来。

         该函数实现过程如下:

         调用函数localWords(),获取两个RSS源ny, sf中词汇表及各自词汇出现概率信息。

对两个类别对应的单词概率进行判断,如果概率大于-5.0,将对应词汇和概率都存储起来。按照单词出现的概率值进行降序排序,并打印出来。

具体代码实现如下:

#获取ny, sf中单词出现概率大于某个阈值的单词,按降序排序将其打印出来
def getTopWords(ny, sf):
#获取两个RSS源ny, sf中词汇表及各自词汇出现概率信息
vocabList, p0V, p1V = localWords(ny, sf)
topNY = []
topSF = []
#对两个类别对应的单词概率进行判断,如果概率大于-5.0,将对应词汇和概率都存储起来
for i in range(len(p0V)):
if p0V[i] > -5.0:
topSF.append((vocabList[i], p0V[i]))
if p1V[i] > -5.0:
topNY.append((vocabList[i], p1V[i]))
#lambda作为一个表达式,定义了一个匿名函数
#g = lambda x:x+1  x为入口参数,x+1为函数体
#topSF为二维列表,pair[1]指向词汇对应的概率,即按照概率值进行降序排序
sortedSF = sorted(topSF, key = lambda pair: pair[1], reverse = True)
print "SF**SF**SF**"
for item in sortedSF:#打印出各项单词
print item[0]
sortedNY = sorted(topNY, key = lambda pair: pair[1], reverse = True)
print "NY**NY**NY**"
for item in sortedNY:
print item[0]

         函数localWords()

         函数定义为:deflocalWords(feed1, feed0)

         该函数用来获取两个RSS源中词汇表及各自词汇出现概率信息。

         该函数实现过程如下:

         调用函数Bayes.textParse()对RSS源中文本信息进行解析,获取长度大于2的单词列表。

         调用函数Bayes.createVocabList()产生词汇表。

         调用函数calcMostFreq(),获取在文本中出现次数最多的30个词汇信息,并将这些单词从词汇表中删除。由于语言中大部分都是冗余和结构辅助性内容,导致词汇表中一小部分单词却占据了所有文本用词的一大部分。需要去除冗余词汇。另一个常用的方法是不仅移除高频词,同时从某个预定词表中移除结构上的辅助词。该词表称为停用词表(stop word list)。

         从训练样本中随机获取20个样本信息作为测试样本集,并从训练样本中去除这些样本。

调用函数Bayes.bagOfWords2Vec()获取相关文本样本的词汇出现次数信息。

调用函数Bayes.trainNB0()获取两个类别各自单词的出现频率,以及样本集的概率。

调用函数Bayes.classifyNB(),使用测试样本集对学习结果进行测试,计算分类错误率。

返回词汇表和各个词汇的出现概率。

具体代码实现如下:

#feed1/0为两个RSS源
#获取两个RSS源中词汇表及各自词汇出现概率信息
def localWords(feed1, feed0):
docList = [] #以二维数组形式存储所有样本的词汇表
classList = [] #存储所有样本的类别信息
fullText = [] #以一维数组形式存储所有样本的词汇表
minLen = min(len(feed1['entries']), len(feed0['entries'])) #获取两个RSS源的最小长度
for i in range(minLen):
#解析feed1['entries'][i]['summary'],将长度大于2的单词提取出来,并全转换为小写
wordList = Bayes.textParse(feed1['entries'][i]['summary'])
docList.append(wordList) #将该样本词汇添加到docList中
fullText.extend(wordList) #将该样本词汇追加到fullText中
classList.append(1) #将样本类别信息添加到classList
wordList = Bayes.textParse(feed0['entries'][i]['summary'])
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = Bayes.createVocabList(docList) #获取docList中所有不重复的单词列表
#由于语言中大部分都是冗余和结构辅助性内容,导致词汇表中一小部分单词却占据了所有文本用词的一大部分。需要去除冗余词汇。
#另一个常用的方法是不仅移除高频词,同时从某个预定词表中移除结构上的辅助词。该词表称为停用词表(stop word list)。
top30Words = calcMostFreq(vocabList, fullText) #获取在fullText中出现次数最多的30个词汇信息
for pairW in top30Words: #从词汇表vocabList中去除出现次数最多的30个单词
if pairW[0] in vocabList:
vocabList.remove(pairW[0])
trainingSet = range(2 * minLen); #定义列表变量存储训练样本id
print 'minLen : %d' % minLen
if minLen < 20:
print 'the len is too small.'
testSet = [] #用于存储测试样本id
for i in range(20): #从训练样本中随机获取20个样本信息作为测试样本集,并从训练样本中去除这些样本
randIndex = int(random.uniform(0, len(trainingSet)))
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat = []
trainClasses = []
#从文本样本集中获取训练样本集,将相关文本样本的词汇出现次数信息存储到矩阵trainMat中,样本分类信息存储到trainClasses中
for docIndex in trainingSet:
#获取样本docList[docIndex]在词汇表vocabList中各个单词出现次数情况
trainMat.append(Bayes.bagOfWords2Vec(vocabList, docList[docIndex]))
#获取当前样本的分类信息classList[docIndex]
trainClasses.append(classList[docIndex])
#通过贝叶斯分类器对训练样本进行学习
#获取两个类别各自单词的出现频率,以及样本集的概率
p0V, p1V, pSpam = Bayes.trainNB0(array(trainMat), array(trainClasses))
errorCount = 0
#使用测试样本集对学习结果进行测试
for docIndex in testSet:
#获取样本docList[docIndex]在词汇表vocabList中各个单词出现次数情况
wordVector = Bayes.bagOfWords2Vec(vocabList, docList[docIndex])
#对当前测试样本进行分类,判断是否与已知类型相同
if Bayes.classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
errorCount += 1
print 'the error rate is: ', float(errorCount) / len(testSet) #打印出错误率
return vocabList, p0V, p1V #返回词汇表和各个词汇的出现概率

3、小结

对于分类而言,使用概率有时要比使用硬规则更为有效。贝叶斯概率及贝叶斯准则提供了一种利用已知值来估计未知概率的有效方法。

可以通过特征之间的条件独立性假设,降低对数据量的需求。当然我们也知道这个假设过于简单。这就是之所以称为朴素贝叶斯的原因。尽管条件独立性假设并不正确,但是朴素贝叶斯仍然是一种有效的分类器。

本文中涉及的所有code可以访问如下目录获取:

https://github.com/stevewang0/MLInAction
(完)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息