Python3:《机器学习实战》之朴素贝叶斯(4)从个人广告中获取区域倾向
前言:
书中的最后一个例子,是分别从美国的两个城市中选取一些人,通过分析这些人发布的征婚广告信息,来比较这两个城市的人们在广告用词上是否不同。如果结论确实是不同,那么他们各自的常用词是哪些?从人们的用词当中,我们能否对不同城市的人所关心的内容有所了解?
示例:使用朴素贝叶斯来发现地域相关的用词。
- 收集数据:从RSS源收集内容,这里需要对RSS创建一个接口。
- 准备数据:将文本文件解析成词条向量。
- 分析数据:检查词条确保解析的正确性。
- 训练算法:使用我们之间建立的trainNB0()函数。
- 测试算法:观察错误率,确保分类器可用,可以修改切分程序,以降低错误率,提高分类结果。
- 使用算法:构建一个完整的程序,封装所有内容,给定两个RSS源,该程序会显示最常用的公共词。
下面我们将使用来自不同城市的广告训练一个分类器,然后观察分类器的效果。注意,我们并不是要使用该分类器进行分类,而是通过观察单词和条件概率值来发现与特定城市相关的内容。
第一件事当然就是要准备数据,我们可以Python下载文本。利用RSS,这些文本很容易得到。现在所需要的是一个RSS阅读器。Universal Feed Parser是Python中最常用的RSS程序库。你可以在http://code.google.com/p/feedparser/下浏览相关文档,然后和其他Python包一样来安装feedparser,即 pip3 install feedparse。
书中使用的是Craigslist上的个人广告,当然希望是在服务条款允许的条件下。
我们还是先来构建一个类似于spamTest()的函数来对测试过程自动化吧。
代码实现:
def calcMostFreq(vocabList, fullText):
"""
Function: 计算出现频率
Args: vocabList:词汇表
fullText:全部词汇
Returns: sortedFreq[:30]:出现频率最高的30个词
"""
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):
"""
Function: RSS源分类器
Args: feed1:RSS源
feed0:RSS源
Returns: vocabList:词汇表
p0V:类别概率向量
p1V:类别概率向量
"""
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)
wordList = textParse(feed0['entries'][i]['summary'])
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList)
top30Words = calcMostFreq(vocabList, fullText)
for pairW in top30Words:
if pairW[0] in vocabList: vocabList.remove(pairW[0])
trainingSet = range(2*minLen); testSet = []
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:
trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))
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 erroe rate is: ', float(errorCount)/len(testSet))
return vocabList, p0V, p1V
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
上述代码类似于spamTest(),只不过添加了新的功能,引入calcMostFreq()来返回出现频率最高的30个单词,再狠心将他们剔除。当我们注释掉移除高频词的三行代码,会发现错误率提高了百分之二十左右。有趣的是,vocalList的大小约为3000个词,前30个高频词却占了30%,也就是说词汇表中的这一小部分单词,占据了所有文本用词的一大部分。产生这种现象的原因是语言中大部分都是冗余和结构辅助性内容,另一个常用的方法不仅移除高频词,同时从某个预定词表中移除结构上的辅助词。该词表为停用词表(stop word list),网上可以找到很多,例如http://www.ranks.nl/resources/stopwords.html。
输出结果:
>>> reload(bayes)
<module 'bayes' from 'E:\\机器学习实战\\mycode\\Ch04\\bayes.py'>
>>> ny = feedparser.parse('http://newyork.craiglist.org/stp/index.rss')
>>> sf = feedparser.parse('http://sfbay.craiglist.org/stp/index.rss')
>>> vocabList, pSF, pNY = bayes.localWords(ny, sf)
the erroe rate is: 0.5
>>> vocabList, pSF, pNY = bayes.localWords(ny, sf)
the erroe rate is: 0.35
>>> vocabList, pSF, pNY = bayes.localWords(ny, sf)
the erroe rate is: 0.5
需要注意的是RSS源要在函数外导入,因为网站上的RSS源会随着时间而改变。
为了得到错误率的精确估计,应该多次进行上述实验,然后取平均值。这里的错误率很高,但是我们关注的是单词概率而不是实际分类,因此这个问题倒不严重,可以改变要移除的单词数目,然后观察错误率的变化。
分析数据:显示地域性相关单词
可以先对pSF和pNY进行排序,然后按照顺序将词打印出来。
代码实现:
def getTopWords(ny, sf):
"""
Function: 最具表征性的词汇显示函数
Args: ny:RSS源
sf:RSS源
Returns: 打印信息
"""
import operator
vocabList, p0V, p1V=localWords(ny, sf)
topNY = []; topSF = []
for i in range(len(p0V)):
if p0V[i] > -4.5 : topSF.append((vocabList[i], p0V[i]))
if p1V[i] > -4.5 : 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])
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
可以通过调节阈值的大小来改变输出量的多少,记住这里的阈值时取自然对数之后的概率哦,所以是负数。
输出结果:
>>> reload(bayes)
<module 'bayes' from 'E:\\机器学习实战\\mycode\\Ch04\\bayes.py'>
>>> bayes.getTopWords(ny,sf)
the erroe rate is: 0.45
SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**
very
let
hello
email
sure
was
enjoy
new
well
dates
walk
NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**
down
them
girl
times
great
what
feet
most
been
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
值得注意的是程序输出了大量的停用词,有兴趣的同学可以试着把停用词去掉,然后看看分类错误率会不会降低。
总结:
对于分类而言,使用概率有时要比使用硬规则更为有效,贝叶斯概率及贝叶斯准则提供了一种利用已知值来估计未知概率的有效方法。尽管条件独立性假设并不正确,但是朴素贝叶斯仍然是一种有效的分类器。
朴素贝叶斯:
- 优点:在数据较少的情况下仍然有效,可以处理多类别问题。
- 缺点:对于输入数据的准备方式较为敏感。
- 适用数据类型:标称型数据。
系列教程持续发布中,欢迎订阅、关注、收藏、评论、点赞哦~~( ̄▽ ̄~)~
完的汪(∪。∪)。。。zzz