機器學習:自己動手實現樸素貝葉斯和使用skearn中的樸素貝葉斯

一、樸素貝葉斯(Naive Bayes)簡介

1.原理

    相信大家都學過貝葉斯公式,見下:

    P(X|Y)=\frac{P(Y|X)P(X)}{P(Y)}

    而我們正是用這條公式來實現樸素貝葉斯。現要實現這樣一個分類任務:對於一條數據x =(x_1,x_2,...,x_n),我們希望預測它屬於m個類別中的哪個類別 \gamma = (c_1, c_2, ...c_m)。 所以我們就是要求這些條件概率 P(c_1|x), P(c_2|x),..., P(c_m|x), 並比較哪一個最大,那麼x就屬於概率最大那一類。根據貝葉斯公式,有:(式中用c代表某一類別)

    P(c|x) = \frac{P(c)P(x|c)}{P(x)}

    該算法之所以被稱爲樸素貝葉斯,是因爲它採用了屬性條件獨立性假設,也就是假設x中的所有屬性x1,x2,x3...xn它們之間是獨立的,不相互影響的,都獨立的對分類結果產生影響,基於這樣的假設,上式變爲:

    P(c|x)=\frac{P(c)P(x|c)}{P(x)} =\frac{P(c)P(x_1,x_2,...,x_n|c)}{P(x)} = \frac{P(c)}{P(x)}\prod _{i=1}^np(x_i|c)

    在這裏有一個值得思考的問題。即在上式當中,我們可不可以根據x的各個屬性的取值,直接統計x出現的個數,進而得到P(x|c), 而不需要將所有的屬性分開考慮呢?實際上這往往是不行的。由於訓練集是有限的,而且往往不大,很多樣本取值在訓練集中根本沒有出現,但這不代表它的出現概率爲零,所以直接使用頻率來估計P(x|c)是不可行的。

    接下來,由於對於所有類別來說,P(x)相同,根據上面的式子,我們只需要計算、比較分子即可:

    h_{nb}(x)=arg \max_{c\in\gamma} P(c)\prod_{i=1}^nP(x_i|c)

    令Dc表示訓練集D中第c類樣本組成的集合,若有充足的獨立同分布樣本,則可容易地估算出該類的先驗概率:

    P(c)=\frac{\vert D_c\vert }{\vert D\vert}

    對於離散屬性而言,令D_{c,x_i}表示Dc中在第i個屬性上取值爲x_i的樣本組成的集合,則:

    P(x_i|c)= \frac{\vert D_{c,x_i}\vert }{\vert D_c \vert }

    對於連續屬性可以考慮概率密度函數,假定p(x_i|c)\sim N(\mu _{c,i}, \sigma _{c,i}^2),  即服從正態分佈,那麼有:

    p(x_i|c) = \frac{1}{\sqrt {2\pi }\sigma _{c,i}} exp(-\frac{(x_i-\mu _{c,i})^2}{2\sigma _{c,i}^2})

    有人可能發現如果用於連乘的條件概率P(x_i|c)爲零的話, 整個結果就會變爲零。所以在估計概率值的時候通常要進行smoothing,常用拉普拉斯修正。具體來說,令N表示訓練D中可能的類別數,Ni表示第i個屬性可能的取值數,則上面的式子可以修正爲:

    \hat P(c) = \frac{\vert D_c \vert +1 }{\vert D\vert +N}

    \hat P(x_i|c) = \frac{\vert D_{c,x_i}\vert +1}{\vert D_c \vert +N_i}

    另外,當我們進行連乘操作,往往由於概率的值小而且連乘的概率值很多,或造成數值下溢,所以通常通過取對數的方式,將連乘轉化成連加,下面自己實現的版本就採用的就是連加操作。

2.優點

    1. 儘管假設簡單,naive Bayes分類器在很多真實的分類問題,例如文檔分類、垃圾郵件過濾,效果很理想。

    2.在數據較少的情況下仍然有效,它只需要少量的訓練數據去估計必要的參數,可以處理多類別問題。

    3.相比其它複雜的方法, naive Bayes分類器執行非常快,類條件特徵分佈的可分解性意味着,每一個分佈都可以作爲一維分佈獨立地估計。

3.缺點

    儘管naive Bayes是一個適宜的分類器,但是它的估計量並不好,因此,來自predict_proba的概率輸出結果建議只作爲參考。

    在現實中,屬性條件獨立性假設往往不成立,屬性和屬性之間往往有一定的關係,所以樸素貝葉斯過於理想,在某些屬性獨立性很弱的數據集上效果往往不夠好。因此也催生了半樸素貝葉斯和貝葉斯網絡算法來解決這個問題。

二、代碼實現

    下面是一個用於文檔分類的貝葉斯分類器的實現。

import numpy as np
def load_dataset():
    #返回一些自己構造的已經進行了分詞的句子(這裏稱它們爲document)
    documents = [['my','dog','has','flea',\
                    'problems', 'help','please'],
                    ['maybe','not','take','him',\
                    'to','dog','park','stupid'],
                    ['my','dalmation','is','so','cute',\
                    'I','love','him'],
                    ['stop','posting','stupid','worthless','garbage'],
                    ['mr','licks','ate','my','steak','how',\
                        'to','stop','him'],
                    ['quit','buying','worthless','dog','food','stupid']]
    labels = [0,1,0,1,0,1]
    return documents, labels


def create_vocabulary(dataset):
    #創建詞彙表
    vocab_set = set([])
    for document in dataset:
        vocab_set = vocab_set |set(document)
    return list(vocab_set)


def words2vect(vocabulary, input_list):
    #這裏使用的是獨熱編碼的方式
    return_vector = [0]*len(vocabulary)
    for word in input_list:
        if word in vocabulary:
            return_vector[vocabulary.index(word)] = 1
        else:
            print("the word %s is not in my vocabulary"%word)
    return return_vector


def trainNB0(train_matrix, train_category):
    #訓練分類器
    num_traindocs = len(train_matrix)
    num_words = len(train_matrix[0])
    # num_abusive代表帶有侮辱性的文檔的數目
    num_abusive = np.sum(train_category)
    # 這裏使用了拉普拉斯平滑
    prob_abusive = (num_abusive+1)/(float(num_traindocs)+2)
    p0_num = np.ones(num_words)
    p1_num = np.ones(num_words)
    for i in range(num_traindocs):
        if train_category[i] == 1:
            p1_num += train_matrix[i]
        else:
            p0_num += train_matrix[i]
    p1_vect = p1_num/(num_abusive+2)
    p0_vect = p0_num/(num_traindocs-num_abusive+2)
    return p0_vect , p1_vect, prob_abusive


def classifyNB(test_vector, p0_vect, p1_vect, p_class1):
    #進行分類
    p1 = np.log(p_class1)
    p0 = np.log(1.0-p_class1)
    for i in range(len(test_vector)):
        if test_vector[i] == 1:
            p1 += np.log(p1_vect[i])
            p0 += np.log(p0_vect[i])
        else:
            p1 += np.log(1-p1_vect[i])
            p0 += np.log(1-p0_vect[i])
    if p1 > p0:
        return 1
    else:
        return 0


def testingNB(data_test):
    #進行測試
    documents, labels = load_dataset()
    vocabulary = create_vocabulary(documents)
    train_matrix = []
    for doc in documents:
        train_matrix.append(words2vect(vocabulary,doc))
    p0_vect, p1_vect, prob_abusive = trainNB0(np.array(train_matrix),\
        np.array(labels))
    for entry in data_test:
        entry_vect = np.array(words2vect(vocabulary, entry))
        entry_class = classifyNB(entry_vect, p0_vect, p1_vect, prob_abusive)
        print("{} classify as {}".format(entry, entry_class))

data_test = [['please,my'],['garbage, trash']]
testingNB(data_test)
======OUTPUT========
[0,1]

三、使用sklearn中的貝葉斯分類器

    當我們需要使用貝葉斯分類器時,我們可以直接調用sklearn中的分類器,而不需要自己實現。在sklearn中總共有三種貝葉斯分類器, 分別是BernoulliNB,MultinomialNB和GaussianNB。

1. 三種貝葉斯分類器的區別:

    BernoulliNB假設數據的各個屬性都是服從伯努利分佈的,也就是二項分佈,上面的數據就是這樣的分佈。

    MultinomialNB假設數據的各個屬性是離散的,就是取有限多個的值。

    GaussianNB假設數據各個屬性是連續的。

2.三種貝葉斯分類器的應用場景:

    如果樣本特徵是二元離散值,應該使用BernoulliNB

    如果如果樣本特徵的分大部分是多元離散值,使用MultinomialNB比較合適。

    一般來說,如果樣本特徵的分佈大部分是連續值,使用GaussianNB會比較好。

    貝葉斯分類器一般會對不滿足假設條件的數據進行預處理,使其滿足假設條件。但處理之後效果不一定很好,多元離散值類型的iris數據用BernoulliNB進行分類,準確率極差。

3.代碼

from sklearn.naive_bayes import BernoulliNB, MultinomialNB, GaussianNB
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report

iris = datasets.load_iris()
trainX, testX, trainY, testY = train_test_split(iris.data, iris.target,test_size=0.2)

model1 = BernoulliNB().fit(trainX, trainY)
y_pred = model1.predict(testX)
print(classification_report(testY, y_pred))

model2 = MultinomialNB().fit(trainX, trainY)
y_pred = model2.predict(testX)
print(classification_report(testY, y_pred))

model3 = GaussianNB().fit(trainX, trainY)
y_pred = model3.predict(testX)
print(classification_report(testY, y_pred))

下面是三個模型的分類報告: 

 

四、參考資料 

【1】《機器學習實戰》Peter+Harrington

【2】《機器學習》周志華

【3】https://blog.csdn.net/Trance95/article/details/104877399/

【4】https://blog.csdn.net/wong2016/article/details/80719885

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