1. 程式人生 > >機器學習實戰(三)樸素貝葉斯NB(Naive Bayes)

機器學習實戰(三)樸素貝葉斯NB(Naive Bayes)

目錄

0. 前言

1. 條件概率

2. 樸素貝葉斯(Naive Bayes)

3. 樸素貝葉斯應用於文字分類

4. 實戰案例

4.1. 垃圾郵件分類案例


學習完機器學習實戰的樸素貝葉斯,簡單的做個筆記。文中部分描述屬於個人消化後的理解,僅供參考。

所有程式碼和資料可以訪問 我的 github

如果這篇文章對你有一點小小的幫助,請給個關注喔~我會非常開心的~

0. 前言

貝葉斯演算法,是一類基於概率論的分類方法。樸素貝葉斯(Naive Bayes),是採取了貝葉斯最原始、最簡單的假設的演算法。

樸素貝葉斯演算法給出例項屬於各個類別的概率,然後選擇概率最大的一類。貝葉斯決策理論的核心思想是選擇具有最高概率的決策。

  • 優點:在資料較少的情況下,仍然十分有效果
  • 缺點:對於輸入資料的準備方式比較敏感
  • 適用資料型別:標稱型資料

樸素貝葉斯假設:

  1. 所有特徵都是獨立的(即每個特徵出現的可能性與其他特徵無關)
  2. 每個特徵都是同等重要的

1. 條件概率

P(A) 稱為事件 A 的概率。P(A|B) 稱為在事件 B 發生的情況下,事件 A 的概率,這就是條件概率。根據概率論,給出以下的定義:

P(B|A)=\frac{P(AB)}{P(A)}=\frac{P(A|B)P(B)}{P(A)}

為了計算 P(B|A) ,我們可以設法通過計算 P(A|B)\ P(B)}\ {P(A) 來得到。

2. 樸素貝葉斯(Naive Bayes)

假設類別為 c_i ,測試樣本為 x ,要計算 P(c_i|x) ,可以通過計算 P(c_i|x)=\frac{P(x|c_i)P(c_i)}{P(x)} ,因為假設各個特徵獨立,所以有:

P(x|c_i)=P(x_1,...,x_n|c_i)=P(x_1|c_i)...P(x_n|c_i)

其概率,等於各個特徵在此類別前提下的概率之積。

  • P(x) :屬於不同類別的計算公式中,P(x) 為分母相同,可以不計算
  • P(c_i) :為訓練集中,第 i 個類別的概率,即第 i 個類別的頻率(出現的樣本數/總樣本數)
  • P(x_1|c_i) :為訓練集屬於第 i 個類別的樣本中,第  i 個特徵的概率,即第  i 個特徵的頻率(特徵出現的次數/總特徵數)

通過以上可得出樸素貝葉斯分類器,對於測試樣本 x ,只需將每個特徵的特徵值與對應特徵的概率相乘,就可得到 P(c_i|x) ,特徵值相當於一個權重。

3. 樸素貝葉斯應用於文字分類

在文字分類中,特徵值的選取可有兩種:

  • 詞集模型(set-of-words model):只判斷這個詞彙是否出現,即只有 0 和 1
  • 詞袋模型(bag-of-words model):判斷這個詞彙出現的次數

演算法可根據以下步驟進行,其他應用場合也可參考:

  1. 將訓練集中所有文字的單詞拆分出來,去重,獲得詞彙表
  2. 根據詞彙表(特徵),對每個文字提取特徵值,將文字轉換為向量
  3. 計算 P(c_i) 和 P(x|c_i)
  4. 將測試文字轉換為向量
  5. 將向量的每一個元素與對應的概率相乘,即第 j 個元素的特徵值乘以 P(x_j|c_i)
  6. 計算出每一個類別的 P(c_i|x) ,選擇最大概率的類別,作為分類結果

4. 實戰案例

以下將展示書中案例的程式碼段,所有程式碼和資料可以在github中下載:

4.1. 垃圾郵件分類案例

# coding:utf-8
from numpy import *
import re

"""
垃圾郵件分類案例
"""


# 根據所有樣本集合建立詞彙表
def createVocabList(dataSet):
    vocabSet = set()
    for document in dataSet:
        # 集合求並集
        vocabSet = vocabSet | set(document)
    return list(vocabSet)


# 通過詞彙表,文字轉換為向量
# 詞集模型(每個詞只記錄是否有出現)
def setOfWords2Vec(vocabList, inputSet):
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] = 1
    return returnVec


# 通過詞彙表,文字轉換為向量
# 詞袋模型(每個詞記錄出現的次數)
def bagOfWords2VecMN(vocabList, inputSet):
    returnVec = [0] * len(vocabList)
    for word in inputSet:
        if word in vocabList:
            returnVec[vocabList.index(word)] += 1
    return returnVec


# 訓練NB分類器
def trainNB0(trainMatrix, trainCategory):
    # 文件數量
    numTrainDocs = len(trainMatrix)
    # 向量中特徵(單詞)的數量
    numWords = len(trainMatrix[0])
    # 類別1佔總文件的比例
    pAbusive = sum(trainCategory) / float(numTrainDocs)
    # 為防止再之後計算過程中某一個特徵的概率為0,導致總的概率為0,不採用以下
    # p0Num = zeros(numWords)
    # p1Num = zeros(numWords)
    # p0Denom = 0.0
    # p1Denom = 0.0
    p0Num = ones(numWords)
    p1Num = ones(numWords)
    p0Denom = 2.0
    p1Denom = 2.0
    # 遍歷每一個文字向量
    for i in range(numTrainDocs):
        # 如果向量屬於類別1
        if trainCategory[i] == 1:
            # 通過向量,計算文件每個詞彙的出現次數
            p1Num += trainMatrix[i]
            # 計算類別1中,單詞的總數量
            p1Denom += sum(trainMatrix[i])
        else:
            p0Num += trainMatrix[i]
            p0Denom += sum(trainMatrix[i])
    # 類別1中,每個單詞出現的次數/總單詞數=每個單詞的出現比例
    # 為防止數值太小,對其取Log
    p1Vect = log(p1Num / p1Denom)
    p0Vect = log(p0Num / p0Denom)
    return p0Vect, p1Vect, pAbusive


# 分類演算法
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    # 因為 p1Vec 取過對數,log(x1)+...+log(xn)=log(x1*...*xn) 等於乘積
    p1 = sum(vec2Classify * p1Vec) + log(pClass1)
    p0 = sum(vec2Classify * p0Vec) + log(1 - pClass1)
    if p1 > p0:
        return 1
    else:
        return 0


# 檔案解析
def textParse(bigString):
    listOfTokens = re.split('\W+', bigString)
    return [tok.lower() for tok in listOfTokens if len(tok) > 2]


# 獲取高頻的前30個詞彙
def calcMostFreq(vocabList, fullText):
    freqDict = {}
    # 遍歷詞彙表中的每一個詞
    for token in vocabList:
        freqDict[token] = fullText.count(token)
    # 排序
    sortedFreq = sorted(freqDict.items(), key=lambda item: item[1], reverse=True)
    return sortedFreq[:30]


# 垃圾郵件測試
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)
    # 獲取高頻詞彙
    top30Words = calcMostFreq(vocabList, fullText)
    # 去除高頻詞彙,因高頻詞彙很可能是冗餘詞彙
    for pairW in top30Words:
        if pairW[0] in vocabList:
            vocabList.remove(pairW[0])
    trainingSet = list(range(50))
    testSet = []
    # 選擇測試向量
    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:
        # 將文字集合轉換為文字向量矩陣
        trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
        trainClasses.append(classList[docIndex])
    # p0V: 類別0中,每個詞彙出現的比例
    # p1V: 類別1中,每個詞彙出現的比例
    # pSpam: 類別1的文字數量佔總文字數量的比例
    p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))
    correctCount = 0
    for docIndex in testSet:
        wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
        if classifyNB(array(wordVector), p0V, p1V, pSpam) == classList[docIndex]:
            correctCount += 1
    print('the correct rate is: ', float(correctCount) / len(testSet))
    return vocabList, p0V, p1V


# 獲取最具表徵性的詞彙
def getTopWords(vocabList, p0V, p1V):
    top0 = []
    top1 = []
    for i in range(len(p0V)):
        if p0V[i] > -6.0:
            top0.append((vocabList[i], p0V[i]))
        if p1V[i] > -6.0:
            top1.append((vocabList[i], p1V[i]))
    sorted0 = sorted(top0, key=lambda pair: pair[1], reverse=True)
    print("***** 0 *****")
    for item in sorted0:
        print(item[0])
    sorted1 = sorted(top1, key=lambda pair: pair[1], reverse=True)
    print('***** 1 *****')
    for item in sorted1:
        print(item[0])


if __name__ == '__main__':
    vocabList, p0V, p1V = spamTest()
    # getTopWords(vocabList, p0V, p1V)

如果這篇文章對你有一點小小的幫助,請給個關注喔~我會非常開心的~