1. 程式人生 > >機器學習-*-樸素貝葉斯

機器學習-*-樸素貝葉斯

說明

貝葉斯定理

有事件A、事件B,其中事件B有 B 1 , B 2 , .

. . , B n {B_1,B_2,...,B_n} 個互不相交的事件組成。
若我們要估計 P
( B i A ) P(B_i|A)
發生的概率,那麼我們根據條件概率公式
P
( B i A ) = P ( B i , A ) P ( A ) P(B_i|A)=\frac{P(B_i,A)}{P(A)}

其中, P ( A ) = i = 1 n P ( A B i ) P ( B i ) P(A)=\sum_{i=1}^{n}P(A|B_i)P(B_i) P ( B i , A ) = P ( A B i ) P ( B i ) P(B_i,A)=P(A|B_i)P(B_i)
故有 P ( B i A ) = P ( A B i ) P ( B i ) i = 1 n P ( A B i ) P ( B i ) P(B_i|A)=\frac{P(A|B_i)P(B_i)}{\sum_{i=1}^{n}P(A|B_i)P(B_i)}
這就是所謂的貝葉斯公式

樸素貝葉斯

樸素貝葉斯是基於貝葉斯定理與特徵田間獨立假設而進行學習的模型。
樸素貝葉斯有一個較強的前提條件:各個特徵之間是獨立的。
他的思想是利用貝葉斯定理來學習到資料的分佈,屬於生成模型的一種。他根據期望風險最小化推匯出後驗概率最大化,故B可以用極大似然估計和貝葉斯估計來找到模型的引數。
具體來說:假設 Y Y 是我們的類別分佈, X X 是我們已有的資料。我們在預測新的樣本 x x 屬於哪個類別時,做的工作就是計算 P ( Y k x ) P(Y_k|x) ,其中 k _k 就是 Y Y 的類別個數。因此我們就需要學習聯合概率分佈 P ( Y , X ) P(Y,X) 。學習出來了聯合概率分佈,我們就可以給一個輸入的 x x 預測出其所屬的類別 y y

條件獨立性假設

P ( X = x Y = Y k ) = j = 1 m P ( x j Y = Y k ) P(X=x|Y=Y_k) = \prod_{j=1}^mP(x^j|Y=Y_k)
m是指的x的特徵數量

這裡寫的不是很好,對概率的知識瞭解的還是很淺,具體的推理可以看李航博士《統計學習方法》

Python實現程式碼

#!/usr/bin/python
# -*- coding:utf-8 -*-
"""
Author LiHao
Time 2018/11/2 16:17
"""
import os
import sys
import numpy as np
from dataset.dataUtils import load_mnist
from ml_learn.algorithm.base import Algorithm #自己寫的一個抽象類

class NativeBayes(Algorithm):
    def __init__(self,nb_lambda = 0):
        #每個引數的概率
        self._parameters_prop = {}
        #類別名稱及各個類別的概率
        self._labels_prop = {}
        #拉普拉斯平滑引數
        self._lambda = nb_lambda
        #類別字首
        self._label_prefix = "C"
        #引數及特徵字首
        self._parameters_predix = "P"
        #訓練資料總數
        self._data_num = 0
        #類別名稱列表
        self._label_name_list = None
        #特徵的取值集合,為map(set)巢狀
        self._features_set = {}

    def predict(self,x):
        """
        預測
        :param x:
        :return:
        """
        dx,fy = x.shape
        for i in range(dx):
            max_class_name = ""
            max_class_prop = 0.0
            for class_name,class_prop in self._labels_prop.items():
                likehood_prop = 1.0
                for j in range(fy):
                    current_data_name = self._parameters_predix + str(j) + "_" + str(x[i][j]) + "|" + class_name
                    if self._parameters_prop.get(current_data_name) is not None:
                        likehood_prop *= self._parameters_prop.get(current_data_name)
                    else:
                        likehood_prop *= 0.001
                current_prop = class_prop * likehood_prop
                if current_prop >= max_class_prop:
                    max_class_prop = current_prop
                    max_class_name = class_name
            print("The ",i,"'s data class is ",max_class_name," max prop is ",max_class_prop)

    def train(self,X,Y):
        """
        訓練-貝葉斯估計方法
        :param X:
        :param Y:
        :return:
        """
        data_num,feature_num = X.shape
        self._data_num = data_num
        label_list = []
        for i in range(data_num):
            #讀入類別標籤
            label_name = self._label_prefix+str(Y[i])
            if self._labels_prop.__contains__(label_name):
                self._labels_prop[label_name] += 1
            else:
                self._labels_prop[label_name] = 1
                label_list.append(label_name)
            for j in range(feature_num):
                feature_name = self._parameters_predix+str(j)
                if self._features_set.__contains__(feature_name):
                    self._features_set[feature_name].add(str(X[i][j]))
                else:
                    self._features_set[feature_name] = set([str(X[i][j])])
                current_data_name = self._parameters_predix+str(j)+"_"+str(X[i][j])+"|"+label_name
                if self._parameters_prop.__contains__(current_data_name):
                    self._parameters_prop[current_data_name] += 1
                else:
                    self._parameters_prop[current_data_name] = 1
        self._label_name_list = label_list
        #求引數的概率值
        for key,value in self._parameters_prop.items():
            belong_class = key.split("|")[-1]
            belong_feature_name = key.split("_")[0]
            #資料的每個特徵維度值的種類個數
            Sj = len(self._features_set[belong_feature_name])
            class_num = self._labels_prop[belong_class]
            self._parameters_prop[key] = (value*1.0+self._lambda)/(class_num+self._lambda*Sj)
        #求每個類別的概率值
        for key,value in self._labels_prop.items():
            self._labels_prop[key] = (value*1.0 + self._lambda)/(data_num + len(self._labels_prop)*self._lambda)
        print("LH -*- Train Done.")
        
def bayes_test():
    """
    這是用的李航博士書P50上的例子
    其中第二個維度取值是S M L 我把它們變成了1 2 3 
    得到結果和書上的結果是一致的
    nb_lambda = 0為極大似然估計  >0是貝葉斯估計
    """
    X = np.array([[1,1],[1,2],[1,2],[1,1],[1,1],[2,1],[2,2],[2,2],[2,3],[2,3],[3,3],[3,2],[3,2],[3,3],[3,3]])
    Y = np.array([[-1],[-1],[1],[1],[-1],[-1],[-1],[1],[1],[1],[1],[1],[1],[1],[-1]])
    T = np.array([[2,1]])
    import time
    start1 = time.time()
    clf = NativeBayes(nb_lambda=0)
    clf.train(X,Y)
    clf.predict(T)
    end1 = time.time()
    print(end1-start1)

if __name__ == '__main__':
    bayes_test()

這裡用了李航《統計學習方法》中的例子來測試,證明其準確性
在這裡插入圖片描述
load_mnist函式 見我的另一篇部落格中寫的KNN手寫數字分類
我也用上面得到的資料集做了樸素貝葉斯分類,但效果還不如KNN。部分原因是每一維資料是0-1之間離散的數值,沒有做標準化。將其變為0或1的取值應該會提升一些效果。
聽說,樸素貝葉斯用在文字分類中效果不錯