概率分類之樸素貝葉斯分類(垃圾郵件分類python實現)

昕昕點燈發表於2020-10-05


什麼是概率分類?

        舉個最簡單的二分類例子:有兩類(w1, w2),有樣本 x ,現問:xv屬於w1,還是w2?
        即求:p(w1 / x)與p(w2 / x),

  • 若p(w1 / x)> p(w2 / x)則 x 屬於 w1;
  • 若p(w1 / x)< p(w2 / x)則 x 屬於 w2。

這就是利用概率進行分類!

        其中:
在這裡插入圖片描述
分子相同,故只用比較分母即可,其中:
        p(w1)與p(w2)為w的先驗概率;
        p(w1 / x)、 p(w2 / x)為x在w上的後驗概率
        p(x / w1)、 p(x / w2)為x在w上的條件概率

因此概率分類的關鍵是如何通過求條件概率!

樸素貝葉斯分類

        樸素貝葉斯分類器 (naÏve Bayes classifier)採用:“屬性條件獨立性假設”:對已知類別,假設所有屬性相互獨立。換言之,假設每個屬性獨立地對分類結果發生影響。
        故上式可寫成:
在這裡插入圖片描述
        其中 d 為屬性數目, x i x_i xi x x x 在第 i i i 個屬性上的取值。p(c)為樣本中標籤為c的樣本數佔總樣本數的比值。
        對離散屬性而言:
在這裡插入圖片描述
        D c , x i D_{c,x_i} Dc,xi, 表示 D c D_c Dc 中在第 i i i 個屬性上取值為 x i x_i xi 的樣本組成的集合。

        對連續屬性而言:
在這裡插入圖片描述

垃圾郵件分類

垃圾郵件分類問題:
        標籤 c i , i = 1 , 2 c_i,i={1,2} ci,i=1,2 c 1 c_1 c1 (正常郵件), c 2 c_2 c2(垃圾郵件),輸入郵件為 w w w,郵件有d個屬性值{x1, x2, x3, …xd}
        由上述可知求 P ( c 1 ∣ w ) P(c_1 | w) Pc1w P ( c 2 ∣ w ) P(c_2|w) Pc2w
        即求: P ( w ∣ c 1 ) P(w |c_1) Pwc1 P ( w ∣ c 2 ) P(w|c_2) Pwc2

        Python的sklearn包中有現成的樸素貝葉斯分類類可以呼叫(多項式的樸素貝葉斯分類):

from sklearn.naive_bayes import MultinomialNB

這是搜狗2020校招的一道演算法題:

        編寫Naïve Bayes分類模型對郵件文字進行分類,判斷該郵件是不是垃圾郵件(二分類)。我們已經通過資料預處理,將原始的郵件文字資料轉化為分類器可用的資料向量形式,具體:資料表示為整型數向量x=(x1,x2,…,xd)。d是資料特徵向量的維數,每個輸入資料樣本的格式為:
        Label x1 x2 … xd
        其中Label為0或者1的整型數字(0表示正常郵件,1表示垃圾郵件);
        x1 x2 … xd是離散化後的特徵,表示為從0開始的自然數;
        維度d小於20;
        如果Label=?,則表示希望輸出的預測類別值(需要預測的類別一定已在對應的訓練資料中已經出現過)。

這裡採用了 l o g log log,將連乘變為了連加,效果一樣:

import math

def trainNB(traindata, trainlabel):
    # 訓練集大小
    lenstrain = len(traindata)
    # 特徵屬性維度
    lensvec = len(traindata[0])
    # 訓練集中標籤為0的樣本概率
    p0 = sum(trainlabel)/lenstrain
    # 採用拉普拉斯修正
    p0d, p1d = [1]*lensvec, [1]*lensvec
    p0sum, p1sum = 2.0, 2.0
    for i in range(lenstrain):
        if trainlabel[i]==1:
            for j in range(lensvec):
                p1d[j] += traindata[i][j]
            p1sum += sum(traindata[i])
        else:
            for j in range(lensvec):
                p0d[j] += traindata[i][j]
            p0sum += sum(traindata[i])
    p0vecpro = [math.log(p0d[i]/p0sum) for i in range(lensvec)]
    p1vecpro = [math.log(p1d[i]/p1sum) for i in range(lensvec)]
    return p0vecpro, p1vecpro, p0

def test(testdata, p0vecpro, p1vecpro, p0):
    lenstest = len(testdata)
    p0test = 0
    p1test = 0
    for i in range(lenstest):
        p0test += p0vecpro[i]*testdata[i]
        p1test += p1vecpro[i]*testdata[i]
    p0test += math.log(p0)
    p1test += math.log(1-p0)
    if p0test>p1test:
        return 0
    else:
        return 1
    
traindata, trainlabel, testdata = [], [], []
M, N, d = map(int, input().split("\t"))
for i in range(M):
    line = [int(j) for j in input().split("\t")]
    traindata.append(line[1:])
    trainlabel.append(line[0])
for i in range(N):
    line = [int(j) if j!="?" else j for j in input().split("\t")]
    testdata.append(line[1:])
p0vecpro, p1vecpro, p0 = trainNB(traindata, trainlabel)
for i in range(N):
    print(test(testdata[i], p0vecpro, p1vecpro, p0))

相關文章