1. 程式人生 > >python樸素貝葉斯實現-2

python樸素貝葉斯實現-2

本文主要內容:

1. 樸素貝葉斯為何需要特徵條件獨立

2. 樸素貝葉斯三種模型:

  • 特徵是離散的時候,使用多項式模型

  • 特徵是連續變數的時候,應該採用高斯模型

  • 特徵的取值只能是1和0伯努利模型)

3. 多項式模型的python實現

樸素貝葉斯 (naive Bayes)
法是基於貝葉斯定理特徵條件獨立假設的分類方法。對於給定的訓練資料集,首先基於特徵條件獨立假設學習輸入/輸出的聯合概率分佈;然後基於此模型,對給定的輸入x,利用貝葉斯定理求出後驗概率最大的輸出Y。

貝葉斯分類是一類分類演算法的總稱,這類演算法均以貝葉斯定理為基礎,故統稱為貝葉斯分類。而樸素樸素貝葉斯分類是貝葉斯分類中最簡單,也是常見的一種分類方法。

理解樸素貝葉斯 (naive Bayes)主要分為兩個部分:

1. 貝葉斯定理

2. 特徵條件獨立

貝葉斯定理上篇blog已經做了回顧,本文首先,說明特徵條件獨立的意義

1. 樸素貝葉斯為何需要特徵條件獨立

這裡寫圖片描述

這裡寫圖片描述

樸素貝葉斯法對條件概率分佈作了條件獨立性的假設。由於這是一個較強的假設,樸素貝葉斯法也由此得名。具體地,條件獨立性假設是:

這裡寫圖片描述

光看定義,還是不能很好的理解為何需要條件獨立,現在給出知乎上面別人的解釋:

假設根據一個男生四個特徵(帥, 性格好,身高,上進)來判斷女生是否嫁還是不嫁。首先給出下表(以及省略很多資料)

這裡寫圖片描述

現在給我們的問題是,如果一對男女朋友,男生想女生求婚,男生的四個特點分別是不帥,性格不好,身高矮,不上進,請你判斷一下女生是嫁還是不嫁?

轉為數學問題就是比較p(嫁|(不帥、性格不好、身高矮、不上進))p(不嫁|(不帥、性格不好、身高矮、不上進))的概率,誰的概率大,我就能給出嫁或者不嫁的答案!

沒有假設特徵之間相互獨立,那麼我們統計的時候,就需要在整個特徵空間中去找,比如統計p(不帥、性格不好、身高矮、不上進|嫁),我們就需要在嫁的條件下,去找四種特徵全滿足分別是不帥,性格不好,身高矮,不上進的人的個數,這樣的話,由於資料的稀疏性,很容易統計到0的情況

我們這個例子有4個特徵,其中帥包括{帥,不帥},性格包括{不好,好,爆好},身高包括{高,矮,中},上進包括{不上進,上進},那麼四個特徵的聯合概率分佈總共是4維空間,總個數為2*3*3*2=36個

假設特徵之間相互獨立,根據樸素貝葉斯公式:

這裡寫圖片描述

樸素貝葉斯法對條件概率分佈做了條件獨立性的假設,由於這是一個較強的假設,樸素貝葉斯也由此得名!這一假設使得樸素貝葉斯法變得簡單,但有時會犧牲一定的分類準確率。

2. 樸素貝葉斯三種模型:

特徵是離散的時候,使用多項式模型

這裡寫圖片描述

下面給出實際示例:

這裡寫圖片描述

這裡寫圖片描述

特徵是連續變數的時候,應該採用高斯模型

特徵的取值只能是1和0伯努利模型)

3. 多項式模型的python實現

資料來自於李航書上的示例, S, M, L改為了 4, 5, 6

def get_multi_data():
    x = np.array([
        [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3],
        [4, 5, 5, 4, 4, 4, 5, 5, 6, 6, 6, 5, 5, 6, 6]
    ])

    x = x.T

    y = np.array([-1, -1, 1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1])
    return x, y

程式碼實現

class MultinomialNB(object):
    def __init__(self, alpha=1.0):
        self.alpha = alpha

        self._dic_class_prior = {}
        self._cd_prob = {}

    def fit(self, x, y):
        # calculate class prior probabilities: P(y=ck)
        self._cal_y_prob(y)

        # calculate Conditional Probability: P( xj | y=ck )
        self._cal_x_prob(x, y)

    def _cal_y_prob(self, y):
        """
        calculate class prior probability
        like: {class_1: prob_1, class_2:prob_2, ...}
        for example two class 1, 2 with probability 0.4 and 0.6
        {1: 0.4, 2: 0.6}
        """
        sample_num = len(y) * 1.0
        if sample_num < 1:
            raise ValueError

        unique_class, class_count = np.unique(y, return_counts=True)

        # calculate class prior probability
        for c, num in zip(unique_class, class_count):
            self._dic_class_prior[c] = num / sample_num

    def _cal_x_prob(self, x, y):
        """
        calculate Conditional Probability: P( xj | y=ck )
        like { c0:{ x0:{ value0:0.2, value1:0.8 }, x1:{} }, c1:{...} }

        for example the below ,as to class 1 feature 0 has 3 values "1, 2 , 3"
        the corresponding probability 0.22, 0.33, 0.44 
        p( x1 = 1 | y = 1 ) = 0.22
        p( x1 = 2 | y = 1 ) = 0.33
        p( x1 = 3 | y = 1 ) = 0.44

        { 1: {0: {1: 0.22, 2: 0.33, 3: 0.44}, 1: {4: 0.11, 5: 0.44, 6: 0.44}},
         -1: {0: {1: 0.50, 2: 0.33, 3: 0.16}, 1: {4: 0.50, 5: 0.33, 6: 0.16}}
        }
        """

        unique_class = np.unique(y)

        for c in unique_class:
            self._cd_prob[c] = {}

            c_idxs = np.where(y==c)[0]

            for i, col_feature in enumerate(x.T):
                dic_f_prob = {}
                self._cd_prob[c][i] = dic_f_prob

                for idx in c_idxs:
                    if col_feature[idx] in dic_f_prob:
                        dic_f_prob[col_feature[idx]] += 1
                    else:
                        dic_f_prob[col_feature[idx]] = 1

                for k in dic_f_prob:
                    dic_f_prob[k] = dic_f_prob[k] * 1.0 / len(c_idxs)

    def _pred_once(self, x):
        dic_ret = {}

        for y in self._dic_class_prior:
            y_prob = self._dic_class_prior[y]
            for i, v in enumerate(x):
                y_prob = y_prob * self._cd_prob[y][i][v]

            dic_ret[y] = y_prob

        return dic_ret

    def predict(self, x):
        if x.ndim == 1:
            return self._pred_once(x)
        else:
            labels = []
            for i in xrange(x.shape[0]):
                labels.append(self._pred_once(x[i]))

        return labels

    def get_class_prior(self):
        return self._dic_class_prior

    def get_cd_prob(self):
        return self._cd_prob

執行的結果:

if __name__ == '__main__':

    x, y = get_multi_data()
    print x.ndim, y.ndim
    # 2 1

    mnb = MultinomialNB()
    mnb.fit(x, y)

    print "class prior probability: %s" % mnb.get_class_prior()
    # {1: 0.599, -1: 0.40}

    print "feature condition probability: %s" % mnb.get_cd_prob()
    # { 1: {0: {1: 0.22, 2: 0.33, 3: 0.44}, 1: {4: 0.11, 5: 0.44, 6: 0.44}},
    #  -1: {0: {1: 0.50, 2: 0.33, 3: 0.16}, 1: {4: 0.50, 5: 0.33, 6: 0.16}}
    # }

    item = np.array([2, 4])
    print mnb.predict(item)
    # {1: 0.02222, -1: 0.06666}

參考: