- ['love', 'my', 'dalmation'] classified as: 0
- ['stupid', 'garbage'] classified as: 1
-
- 3.4 文档词袋模型
-
- 我们将每个词的出现与否作为一个特征,这可以被描述为词集模型,上面就是词集模型。
- 如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法被称为词袋模型。
- 词集和词袋的区别:在词袋中,每个单词可以出现多次 ,而在词集中,每个词只能出现一次。
-
- 为适应词袋模型 ,需要对函数setOfWords2Vec稍加修改,修改后的函数为bagOfWords2Vec,代码如下:
- def bagOfWords2VecMN(vocabList, inputSet):
- returnVec = [0]*len(vocabList)
- for word in inputSet:
- if word in vocabList:
- returnVec[vocabList.index(word)] += 1
- return returnVec
这个返回的列表表现的是单词出现的次数,还不再是是否出现-
4. 使用朴素贝叶斯过滤垃圾邮件
4.1 准备数据:切分文本
前面介绍的词向量是直接给定的,下面来介绍如何从文本中构建自己的词列表
先从一个文本字符串介绍
- mySent = ' This book is the best book on python or M.L. I have ever laid eyes upon.'
可以看到, 切分的结果不错, 但是标点符号也被当成了词的一部分.
解决方法:可以使用正则表示式来切分句子 , 其中分隔符是除单词、数字外的任意字符串
- regEx = re.compile('\\W*')
- listOfTokens = regEx.split(mySent)
可以看到里面的标点没有了,但剩下一些空字符,还要进行一步,去掉这些空字符。
- [tok for tok in listOfTokens if len(tok) >0]
空字符消掉了,我们可以看到,有的词首字母是大写的,这对句子查找很有用,但我们是构建词袋模型,所以还是希望格式统一,还要处理一下
- [tok.lower() for tok in listOfTokens if len(tok) >0]
可以看到大写全部变成了小写,如果是想从小写变成大写,只需将 tok.lower() 改成 top.upper() 即可
我们构建一个 testParse 函数,来切分文本,代码如下
- 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]
4.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
第一个循环是对垃圾邮件和非垃圾邮件进行切分,然后生成词列表和类标签
第二个循环是 0 到 50 个数中随机生成 10 个序号
第三个循环是将第二个循环得到的序号映射到词列表,得到训练集和相应的类别,然后进行训练算法
第四个循环是进行错误率计算,分类出的类别与实际类别相比较,累计错误的样本数,最后除以总数,得到错误率
- the error rate is: 0.0
每次运行得出的结果可能不太一样,因为是随机选的序号
5. 使用朴素贝叶斯分类器从个人广告中获取区域倾向
在这个最后的例子当中, 我们将分别从美国的两个城市中选取一些人, 通过分析这些人发布的征婚广告信息, 来比较这两个城市的人们在广告用词上是否不同。如果结论确实是不同, 那么他们各自常用的词是哪些? 从人们的用词当中, 我们能否对不同城市的人所关心的内容有所了解?
下面将使用来自不同城市的广告训练一个分类器, 然后观察分类器的效果。我们的目的并不是使用该分类器进行分类, 而是通过观察单词和条件概率值来发现与特定城市相关的内容。
5.1 收集数据:导入 RSS 源
接下来要做的第一件事是使用 python 下载文本, 而利用 RSS,这很容易得到,而 Universal Feed Parser 是 python 最常用的 RSS 程序库。
由于 python 默认不会安装 feedparser,所以需要自己手动安装,这里附上 ubuntu 下的安装方法
第一步:wget http://pypi.python.org/packages/source/f/feedparser/feedparser-5.1.3.tar.gz#md5=f2253de78085a1d5738f626fcc1d8f71
第二步:tar zxf feedparser-5.1.3.tar.gz
第三步:cd feedparser-5.1.3
第四步:python setup.py install
具体可以看到这个链接:blog.csdn.net/tinkle181129/article/details/45343267
相关文档:http://code.google.com/p/feedparser/
- ny = feedparser.parse('http://newyork.craigslist.org/stp/index.rss')
上面是打开了 Craigslist 上的 RSS 源,要访问所有条目的列表,输入以下代码
Out:25
可以构建一个类似 spamTest 的函数来对测试过程自动化
- def calcMostFreq(vocabList,fullText):
- import operator
- freqDict = {}
- for token in vocabList:
- freqDict[token]=fullText.count(token)
- sortedFreq = sorted(freqDict.iteritems(), 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
localWords 函数与之前介绍的 spamTest 函数类似,不同的是它是使用两个 RSS 作为参数。
上面还新增了一个辅助函数 calcMostFreq, 该函数遍历词汇表中的每个词并统计它在文本中出现的次数, 然后根据出现次数从高到低对词典进行排序 , 最后返回排序最高的 30 个单词
下面来测试一下
- cd桌面 / machinelearninginaction / Ch04
- /home/fangyang / 桌面 / machinelearninginaction / Ch04
- ny = feedparser.parse('http://newyork.craigslist.org/stp/index.rss')
- sf = feedparser.parse('http://sfbay.craigslist.org/stp/index.rss')
- vocabList,pSF,pNY = bayes.localWords(ny,sf)
- vocabList,pSF,pNY = bayes.localWords(ny,sf)
我们会发现这里的错误率要远高于垃圾邮件中的错误率,这是因为这里关注的是单词概率而不是实际分类,可以通过 calcMostFreq 函数改变移除单词数,降低错误率,因为次数最多的前 30 个单词涵盖了所有用词的 30%,产生这种现象的原因是语言中大部分都是冗余和结构辅助性内容。
5.2 分析数据:显示地域相关的用词
将 pSF 和 pNY 进行排序,然后按照顺序将词打印出来,这里用 getTopWords 函数表示这个功能
- 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]
输入是两个 RSS 源,然后训练并测试朴素贝叶斯分类器,返回使用的概率值然后创建两个列表用于元组的存储。与之前返回排名最高的 x 个单词不同, 这里可以返回大于某个阈值的所有词。这些元组会按照它们的条件概率进行排序。
- bayes.getTopWords(ny, sf)
值得注意的现象是, 程序输出了大量的停用词。移除固定的停用词(比如 there 等等)看看结果会如何变化,依本书作者的经验来看, 这样会使分类错误率降低。
小结
(1)对于分类而言, 使用概率有时要比使用硬规则更为有效
(2)贝叶斯概率及贝叶斯准则提供了一种利用已知值来估计未知概率的有效方法
(3)独立性假设是指一个词的出现概率并不依赖于文档中的其他词,这个假设过于简单。这就是之所以称为朴素贝叶斯的原因。
(4)下溢出就是其中一个问题, 它可以通过对概率取对数来解决
(5)词袋模型在解决文档分类问题上比词集模型有所提高
(6)移除停用词,可降低错误率
(7)花大量时间对切分器进行优化
百度云链接:https://pan.baidu.com/s/1jIj4FDs 密码: 3yqh
-
-
-