机器学习-朴素贝叶斯分类器比较香港和新加坡热点信息

系统环境

Window 7 , Anaconda3,Python 3.6 , spyder IDE

条件概率

定理4(贝叶斯公式)
设B1,B2,…Bn…是一完备事件组,则对任一事件A,P(A)>0,有
这里写图片描述
后面我将会用到这个公式,进行概率求解。

收集数据

通过craigslist广告分类网站的rss源,收集到新加坡和香港两个城市的热点信息。采用python的第三方库 feedparser (Universal Feed Parser是Python中最常用的RSS程序库)

第三方库安装

我采用的是Anaconda3 可以通过打开 Anaconda Prompt 进行下载,进入终端后,输入 conda install feedparser 就能自动下载该库了

相关代码

下载好库之后,import feedparser

sg=feedparser.parse('http://singapore.craigslist.com.sg/search/stp?format=rss')
  hk=feedparser.parse('http://hongkong.craigslist.hk/search/stp?format=rss')

这就收集到相关数据,可以通过 sg[‘entries’] 查看相关rss源的内容

准备数据

整理rss源信息

feed1,feed0参数分别对应着上面的sg,hk

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)
        wordList = textParse(feed0['entries'][i]['summary'])
        docList.append(wordList)
        fullText.extend(wordList)
        classList.append(0)

因为作对比,我们要保证作比较的数据长度一直,所以选取两个rss信息短的量作为比较长度。
这段代码,收集到的每一段rss信息都存在docList中,fullText则汇总所有信息,classList则是收集每一段信息的类别

合并相关单词

这一步是通过集合的形式,统计整个docList出现的单词数量

  vocabList = createVocabList(docList) #生成单词集合
'''
函数功能:通过集合,将收集到的内容整理成集合的形式
'''
def createVocabList(dataSet):
    vocabSet = set()     #创建一个空集
    for document in dataSet: 
        vocabSet = vocabSet | set(document) #创建两个集合的并集
    return list(vocabSet)

找出频率最高的前30个单词

因为一句话中会有很多停顿词,过渡词,对于我们来说这些词的参考价值不大,所以选择出前30个单词方便后面,我们过滤掉这些无用词汇

 top30Words = calcMostFreq(vocabList, fullText) #选取出 出现频率最高的30个词
 for pairW in top30Words: #去掉30个出现频率最高的单词
        if pairW[0] in vocabList:
            vocabList.remove(pairW[0])
'''
函数功能:统计出出现频率最高的前30个单词
'''
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]

分析数据

数据的分类

随机选取rss信息中的20个数据作为测试数据,其余作为训练数据。
采用留存交叉验证(hold-out cross validation)的方法进行训练和测试

trainingSet = list(range(2*minLen))
    testSet = []
    for i in range(20): #选取数据集中的20个数据作为测试数据
        randIndex = int(random.uniform(0, len(trainingSet)))
        testSet.append(trainingSet[randIndex])
        del(trainingSet[randIndex])
    trainMat = []
    trainClasses = []
    for docIndex in trainingSet:
        trainMat.append(bagOfWord2VecMN(vocabList, docList[docIndex]))#采用词袋模型,将每一则rss数据转化为词向量
        trainClasses.append(classList[docIndex])
    p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses)) #计算出条件概率

词袋模型生成词向量

上述的代码中有个函数是bagOfWord2VecMN 是将我们的数据转化成向量的形式,词袋模型会统计每个单词出现的次数,此外有一种叫词集模式,在本程序中采用词袋模型更好

'''
函数功能:采用词袋模型,将每一则rss信息转换成词向量
'''
def bagOfWord2VecMN(vocabList, inputSet):
    returnVec = [0]*len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec

训练算法

朴素贝叶斯分类训练器

'''
函数功能:朴素贝叶斯分类器训练函数
'''
def trainNB0(trainMatrix, trainCategory):
    numTrainDocs = len(trainMatrix) #矩阵的长度
    numWords =  len(trainMatrix[0]) #矩阵的列长度
    pAbusive =  sum(trainCategory) / float(numTrainDocs) #计算属于侮辱性文档的概率
    p0Num = ones(numWords)
    p1Num = ones(numWords)
    p0Denom = 2.0
    p1Denom = 2.0
    for i in range(numTrainDocs):
        if trainCategory[i] == 1:
            p1Num += trainMatrix[i]
            p1Denom += sum(trainMatrix[i])
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    p1Vect = log(p1Num / p1Denom)
    p0Vect = log(p0Num / p0Denom)
    return p0Vect, p1Vect, pAbusive    

最后返回的是一个对数形式的列表,因为p1Num/p1Denom一般来说会是一个很小的值,而根据贝叶斯概率公式计算后面还需要将这些数相乘,,Python最后会四舍五入将得到这值为0,这也叫下溢出。
ln(a * b) = ln(a) + ln(b) 采用对数的形式可以很方便的来解决这个问题,并且不会对结果有任何影响

朴素贝叶斯分类器

'''
函数功能:朴素贝叶斯分类函数
'''    
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    p1 = sum(vec2Classify * p1Vec) + log(pClass1) #只需要对vec2Classify所对应的词概率进行求和
    p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)                                                 
    if p1 > p0:
        return 1
    else:
        return 0

测试算法

下面代码接着localWords函数

    errorCount = 0.0
    for docIndex in testSet: #测试数据来测试训练结果
        wordVector = bagOfWord2VecMN(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

使用算法

下面函数将统计出香港和新加坡在craigslist广告分类网站出现频率较高的词,并且可以分析这些词推断这两个地方的热点信息,并且可以进行比较

'''
函数功能:处理收到rss信息,统计出现频率较高的词
'''
def getTopWords(sg, hk):
    import operator
    vocabList, p0V, p1V = localWords(sg, hk)
    topSG = []
    topHK = []
    for i in range(len(p0V)):
        if p0V[i] > -4.5: #大于3.125%的词
            topSG.append((vocabList[i], p0V[i]))
        if p1V[i] > -4.5:
            topHK.append((vocabList[i], p1V[i]))
    sortedSG = sorted(topSG, key=lambda pair: pair[1], reverse=True) #按照列表中元组里的第二个元素进行排序
    print('SF**SF**SF**SF**SF**SF**SF**SF**SF**SF**')
    for item in sortedSG:
        print(item[0])
    sortedHK = sorted(topHK, key=lambda pair: pair[1], reverse=True)
    print('NY**NY**NY**NY**NY**NY**NY**NY**NY**NY**')
    for item in sortedHK:
        print(item[0])

总结

贝叶斯概率以及贝叶斯准则提供了一种利用已知值来估计未知概率的有效方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章