天天看點

樸素貝葉斯分類器簡單分析

1.貝葉斯公式

我們先來看看貝葉斯公式:

樸素貝葉斯分類器簡單分析

其實這個公式并不難推導,隻需要利用到高中所學的條件機率對其進行相應的變行即可。下面對于這個公式,先驗機率,後驗機率等,引用了知乎裡一段很通俗易懂的段落進行解釋。https://www.zhihu.com/answer/241988854

我的朋友小鹿說,他的女神每次看到他的時候都沖他笑,他想知道女神是不是喜歡他呢?下面我們一起用貝葉斯幫小鹿預測下女神喜歡他的機率有多大,這樣小鹿就可以根據機率的大小來決定是否要表白女神。

首先,我分析了給定的已知資訊和未知資訊:

1)要求解的問題:女神喜歡你,記為A事件

2)已知條件:女神經常沖你笑,記為B事件

是以說,P(A|B)是女神經常沖你笑這個事件(B)發生後,女神喜歡你(A)的機率。

樸素貝葉斯分類器簡單分析

從公式來看,我們需要知道這麼3個事情:

1)先驗機率

我們把P(A)稱為"先驗機率"(Prior probability),即在不知道B事件的前提下,我們對A事件機率的一個主觀判斷。這個例子裡就是在不知道女神經常對你笑的前提下,來主觀判斷出女神喜歡一個人的機率,這裡我們假設是50%,也就是不能喜歡你,可能不喜歡還你的機率都是一半。

2)可能性函數

P(B|A)/P(B)稱為"可能性函數"(Likelyhood),這是一個調整因子,即新資訊B帶來的調整,作用是使得先驗機率更接近真實機率。

可能性函數你可以了解為新資訊過來後,對先驗機率的一個調整。比如我們剛開始看到“人工智能”這個資訊,你有自己的了解(先驗機率/主觀判斷),但是當你學習了一些資料分析,或者看了些這方面的書後(新的資訊),然後你根據掌握的最新資訊優化了自己之前的了解(可能性函數/調整因子),最後重新了解了“人工智能”這個資訊(後驗機率)

如果"可能性函數"P(B|A)/P(B)>1,意味着"先驗機率"被增強,事件A的發生的可能性變大;

如果"可能性函數"=1,意味着B事件無助于判斷事件A的可能性;

如果"可能性函數"<1,意味着"先驗機率"被削弱,事件A的可能性變小。

還是剛才的例子,根據女神經常沖你笑這個新的資訊,我調查走訪了女神的閨蜜,最後發現女神平日比較高冷,很少對人笑。是以我估計出"可能性函數"P(B|A)/P(B)=1.5(具體如何估計,省去1萬字,後面會有更詳細科學的例子)

3)後驗機率

P(A|B)稱為"後驗機率"(Posterior probability),即在B事件發生之後,我們對A事件機率的重新評估。這個例子裡就是在女神沖你笑後,對女神喜歡你的機率重新預測。

帶入貝葉斯公式計算出P(A|B)=P(A)* P(B|A)/P(B)=50% *1.5=75%

樸素貝葉斯分類器簡單分析

是以,女神經常沖你笑,喜歡上你的機率是75%。這說明,女神經常沖你笑這個新資訊的推斷能力很強,将50%的"先驗機率"一下子提高到了75%的"後驗機率"。

在得到預測機率後,小鹿自信滿滿的發了下面的表白微網誌

稍後,果然收到了女神的回複。預測成功。

現在我們再來看一遍貝葉斯公式,你現在就能明白這個公式背後的最關鍵思想了:我們先根據以往的經驗預估一個"先驗機率"P(A),然後加入新的資訊(實驗結果B),這樣有了新的資訊後,我們對事件A的預測就更加準确。

是以,貝葉斯定理可以了解成下面的式子:

後驗機率(新資訊出現後A發生的機率) = 先驗機率(A發生的機率) x 可能性函數(新資訊帶出現來的調整)

貝葉斯的底層思想就是:

如果我能掌握一個事情的全部資訊,我當然能計算出一個客觀機率(古典機率、正向機率)。可是生活中絕大多數決策面臨的資訊都是不全的,我們手中隻有有限的資訊。既然無法得到全面的資訊,我們就在資訊有限的情況下,盡可能做出一個好的預測。也就是,在主觀判斷的基礎上,可以先估計一個值(先驗機率),然後根據觀察的新資訊不斷修正(可能性函數)。

上面已經對這個公式有了一定的了解。在生活中貝葉斯也經常會用到,比如要預測明天的是否下雨,我們不可能去統計每個明天是否下雨(即通過完整的資訊)來得到下雨的機率,這時就可以用貝葉斯的思想,借助以往的天氣情況的經驗來預測天氣。

2.樸素貝葉斯分類模型

貝葉斯分類算法是利用統計學裡的機率統計知識進行分類的算法。這裡設Y = {C1,C2,C3,…,Cm}是有m個不同類别的集合,X(x1,x2…,xn)表示一個n維的特征向量

樸素貝葉斯分類器簡單分析

貝葉斯決策準則是根據給定特征X求出該特征屬于每個類别(C1,C2,C3,…,Cm)的機率,X被判為機率最大這個類别,這時也可以使得預測所産生的損失(預測錯誤)最小化了。對于每個要預測類别的X,P(X)都是一樣的,是以隻要求

樸素貝葉斯分類器簡單分析

樸素貝葉斯有一個很嚴格的假設:在類别Ci情況下特征屬性之間互相獨立(這裡是特征屬性條件獨立假設而不是特征屬性獨立假設!),這樣一來P( X | Ci )可以由之前的求解聯合機率轉變為單個屬性相關機率的乘積,可以更好地估算P( X | Ci )(但實際往往無法做到完全互相獨立)。這裡其實還有一個假設,每個特征屬性等重要,不然還要在每個P( Xk | Ci )上乘個機率。

樸素貝葉斯分類器簡單分析

是以最終的的表達式是

樸素貝葉斯分類器簡單分析

接下來進行求解:

P( Ci ) = Di / D , Di 表示類别Ci的個數,D表示樣本總個數

  1. 若P (xk | Ci)中的特征屬性為離散型,P (xk | Ci)=Ski / Si, Ski表示類别Ci樣本中該特征屬性取值為xk 的個數,Si表示類别Ci樣本個數;
  2. 若P (xk | Ci)中的特征屬性為連續型,則一般假設該特征屬性服從高斯分布,先得到類别Ci樣本中該特征屬性的方差與均值,利用機率密度函數求解。
    樸素貝葉斯分類器簡單分析

這裡還可能出現一個問題,倘若P (xk | Ci)=Ski / Si中有一個 為0時,也就是在類别Ci樣本中沒有所求特征屬性值為xk的情況,整個式子都會等于0将會出現很大的偏差。這時可以進行平滑處理,(這裡叫拉普拉斯修正)其實就是加個相關的常數,下面的N表示類别數,Ni表示屬性xi的可能取值的個數。當然這個問題隻會在屬性是離散的情況下

樸素貝葉斯分類器簡單分析

樸素貝葉斯有高斯模型、多項式模型、伯努利模型這三個模型,下面就對高斯模型、多項式模型進行簡單實作

3.樸素貝葉斯–高斯模型

當特征屬性是連續型的時候,就可以用高斯模型進行分類了,實際就是利用上面的機率密度函數

下面簡單地實作一下:

#導入包
from sklearn import datasets
import random
import numpy as np
           

這是一個用于資料集分割的函數

def train_test_split(x,y,rate=0.25):
    indexs = np.array(range(0,len(x)))
    random.shuffle(indexs) #打亂索引
    n = len(x) - int(rate*len(x))#train的資料量
    x_train, x_test, y_train, y_test = x[indexs[:n]], x[indexs[n:]], y[indexs[:n]], y[indexs[n:]]
    return x_train, x_test, y_train, y_test
           

下面是高斯模型

#p(c/x) = p(c)*p(x/c)
class GaussianNB:
    def __init__(self):
        self._means = None#儲存每個類别下每個屬性的均值的一個數組
        self._stds = None#儲存每個類别下每個屬性的方差的一個數組
        self._Pc = None
        self._n = None#全部的類别
        self.prob_ =None
        
    def fit(self,x,y): 
        n = list(set(y))#得到全部的類别
        y = y.reshape(-1,1)#轉化為二維矩陣便于ndarray合并
        #得到每個類别資料樣本對應的std和mean
        X = np.hstack([x,y])#合并,便于按類分割資料
        stds = np.ones((len(n),np.shape(x)[1]))
        means = np.ones((len(n),np.shape(x)[1]))
        for i in n:
            inde = X[X[:,np.shape(X)[1]-1]==i]
            inde = inde[:,:np.shape(X)[1]-1]#得到每個類型的資料樣本
            a = np.mean(inde[:,:np.shape(inde)[1]],axis=0)#每列
            b = np.std(inde[:,:np.shape(inde)[1]],axis=0)   
            means[i] = a
            stds[i] = b        
        #求一個關于P(c)的向量 便于後邊運算
        Pc = np.array([sum(y==i)/len(y) for i in n]).reshape((len(n),))
        self.means = means
        self.stds = stds
        self.Pc = Pc
        self.n = n
        return self
    
    def predict(self,x_test):
        #預測
        prob = []
        for k in x_test:
            pro = []
            for i in range(0,len(self.n)): #每個類别       
                mult = 1
                for j in range(0,np.shape(x_test)[1]):  #特征數
                    mult *= np.exp((-1)*(k[j] - self.means[i,j])**2/(self.stds[i,j])*2)/(np.sqrt(2*np.pi*self.stds[i,j]))
                pro.append(mult)
            prob.append(pro)            
        prob = [self.Pc*prob[i] for i in range(len(prob))]
        pre = np.argmax(prob,axis=1)
        prob = np.array(prob)
        self.prob_ = prob
        return pre
    
    def accuracy_score(self,x_test,y_test):
        accuracy_score = sum(self.predict(x_test)==y_test)/len(y_test)
        return accuracy_score
           

還是用包裡的鸢尾花資料集進行模型實作

iris =datasets.load_iris()
x = iris.data
y = iris.target
x_train, x_test, y_train, y_test = train_test_split(x,y)
model = GaussianNB()
model.fit(x_train,y_train)
pred = model.predict(x_test)
prob_ = model.prob_  #這裡必須要先進行預測
print('accuracy_score: {}'.format(model.accuracy_score(x_test,y_test)))
           

進行多次預測效果都比較好,當然與這份資料太幹淨也有一定關系!

accuracy_score: 1.0
accuracy_score: 0.972972972972973
accuracy_score: 0.9459459459459459
           

哒哒哒哒哒哒

4.樸素貝葉斯–多項式模型

當特征屬性是離散型的時候,這時可以建立多項式模型。樸素貝葉斯–多項式模型用來進行文本分類比較好,(順便說句,伯努利也可以用來文本分類)根據一篇文章裡的單詞情況判斷這篇文章是科學篇,還是文學篇等等。

接下來進行一個簡單的文本分類,這裡用到的公式和上面特征屬性所講到的公式有點不一樣。

在多項式模型中, 設某文檔d=(t1, t2, …, tk),tk是該文檔中出現過的單詞,允許重複,則:

  1. 先驗機率P(c)= 類c下單詞總數/整個訓練樣本的單詞總數
  2. 類條件機率P (tk | c) =(類c下單詞tk在各個文檔中出現過的次數之和+1)/(類c下單詞總數+|V|)

    (V是訓練樣本的單詞表(即抽取單詞,單詞出現多次,隻算一個))

額 由于沒找到相關資料,也比較懶 就還是用鸢尾花資料集進行模型實作 這的确有點不太适合 湊合下吧

import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split

#這裡就預測一個樣本了,要多個直接套個循環就可了  上面的高斯模型可以預測多個
def Multi(x_train,y_train,x_test,y_test):
    
    yn = list(set(y_train))#利用集合唯一性得到有多少的個類别,再轉化為list
    #總單詞數
    N = np.sum([len(x_train[i]) for i in range(len(x_train))])
        
    #求P(c) 
    y_train = y_train.reshape(-1,1)
    X = np.hstack([x_train,y_train])
    Nc = np.ones(len(yn))
    j = 0
    for i in yn:
        indes = X[X[:,np.shape(X)[1]-1]==i]#每個類别的樣本
        indes = indes[:,:np.shape(X)[1]-1]#除去label
        sum = np.sum([len(indes[i]) for i in range(len(indes))])
        Nc[j] = sum
        j +=1
    Pc = Nc/N
        
    #求P(tk|c)
    prob = np.ones(len(yn))
    j = 0
    for i in yn: #周遊每個類别
        multi = 1
        for k in x_test: #周遊文檔的每個單詞
            indes = X[X[:,np.shape(X)[1]-1]==i]#每個類别的樣本
            indes = indes[:,:np.shape(X)[1]-1]#除去label
            sum = np.sum(k==indes)#統計單詞在C類下出現的次數
            multi *= (sum+1)/(Nc[i] + len(set(x_train.flatten())))
        prob[j] = Pc[j]*multi
        j += 1
    pre = np.argmax(prob,axis=0)
    print('屬于類别: ',yn[pre],pre==y_test)


iris = datasets.load_iris()
x = iris.data
y = iris.target
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.3)


for i in range(10):  #進行了10次預測
    Multi(x_train,y_train,x_test[i],y_test[i])
           

跑了兩遍程式

屬于類别:  2 True
屬于類别:  1 True
屬于類别:  1 True
屬于類别:  2 True
屬于類别:  0 False
屬于類别:  2 True
屬于類别:  0 True
屬于類别:  0 True
屬于類别:  0 True
屬于類别:  2 True
           
屬于類别:  2 True
屬于類别:  2 True
屬于類别:  0 True
屬于類别:  2 False
屬于類别:  0 True
屬于類别:  2 True
屬于類别:  1 False
屬于類别:  0 True
屬于類别:  1 True
屬于類别:  0 True
           

資料不太恰當,效果還好 這還得歸于這麼資料比較簡單、幹淨 但讓算法也是功勞 這裡就可以看出,有時資料表面上雖然和算法不太友好、不太适合,但效果卻不一定 。