機器學習-樸素貝葉斯分類器比較香港和新加坡熱點信息

系統環境

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])

總結

貝葉斯概率以及貝葉斯準則提供了一種利用已知值來估計未知概率的有效方法

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