1. 程式人生 > >學習筆記——Kaggle_Digit Recognizer (樸素貝葉斯 Python實現)

學習筆記——Kaggle_Digit Recognizer (樸素貝葉斯 Python實現)

本文是個人學習筆記,該篇主要學習樸素貝葉斯演算法概念,並應用sklearn.naive_bayes演算法包解決Kaggle入門級Digit Recognizer。

貝葉斯定理

對於貝葉斯定理的瞭解和學習大部分都是從概率論開始的,但實際貝葉斯定理應用範圍極其廣泛,在我們學習貝葉斯定理在演算法中的應用前,在複習下其基本概念。

貝葉斯公式:

P(A,B)=P(A)P(B|A)=P(B)P(A|B)

簡單轉換後,即可得到:

P(A|B)=P(A)P(B|A)/P(B)

應用到分類演算法中時:這裡可以認為A為類別B為特徵

P(A|B)是已知B發生後A的條件概率,也稱作A的後驗概率

(根據樣本分佈和未知引數的先驗概率分佈求得的條件概率分佈)。

P(A)是A的先驗概率或邊緣概率(基於主觀判斷而非樣本分佈的概率分佈),稱作”先驗”是因為它不考慮B因素。

P(B|A)是已知A發生後B的條件概率,也稱作B的後驗概率,這裡稱作似然度

P(B)是B的先驗概率或邊緣概率,這裡稱作標準化常量

P(B|A)/P(B)稱作標準似然度

樸素貝葉斯

當然,實際分類問題往往都是多類別多特徵的判斷,則貝葉斯公式將轉化為如下: 類別Ai,特徵 B1...Bn

P(Ai|B1,...,Bn)=P(Ai)P(B1,...,Bn|Ai)P(B1,...,Bn)=P(Ai)P(
B1|Ai)P(B2,...,Bn|AiB1)
P(B1,...,Bn)
=...
=P(Ai)P(B1|Ai)P(B2|AiB1)...P(Bn|AiB1...Bn1)P(B1,...,Bn)

從公式上不難發現存在許多的似然值,相互關係複雜計算量大。為了解決該問題且簡化計算,就衍生了樸素貝葉斯假設:特徵之間相互獨立。雖然該假設看上去與實際符合度低,但經過大量實踐證明其應用效果其實相當出色。

基於樸素貝葉斯假設,公式將可以簡化為:

P(Ai|B1,...,Bn)=P(B1|Ai)P(B2|Ai)...P(Bn|Ai)P(B1,...,Bn)

樸素貝葉斯的分類原理就是根據新樣本的特徵計算分別其在各類的別概率大小,選取概率最大的類別為其最終類別。因為每個類別計算的分母都是相同的,所以實際可以省略分母的概率計算。

樸素貝葉斯的流程可以通過下圖表示:
流程圖

總體上,樸素貝葉斯分類實現經歷三個階段

第一階段——準備工作階段,根據具體情況確定特徵屬性,並對每個特徵屬性進行適當劃分,然後由人工對部分待分類項進行分類,形成訓練樣本集。這一階段的輸入是待分類資料,輸出是特徵屬性和訓練樣本。分類器的質量很大程度上由特徵屬性、特徵屬性劃分及訓練樣本質量決定。

第二階段——分類器訓練階段,主要工作是計算每個類別在訓練樣本中的出現頻率及每個特徵屬性劃分對每個類別的條件概率估計,並將結果記錄。其輸入是特徵屬性和訓練樣本,輸出是分類器。

其中需要注意的是,計算條件概率是非常重要的階段,當特徵屬性為離散值時,只要統計訓練樣本中各個劃分在每個類別中出現的頻率即可進行推算, 而當特徵屬性為連續值時,通常假定其值服從高斯分佈。
另外,為了避免條件概率為0的情況(某個類別下某個特徵項劃分沒有出現),在這裡引入了Laplace校準,即對沒類別下所有劃分的計數加1,這樣如果訓練樣本集數量充分大時,並不會對結果產生影響,並且解決了上述頻率為0的情況。

第三階段——應用階段。這個階段的任務是使用分類器對待分類項進行分類,其輸入是分類器和待分類項,輸出是待分類項與類別的對映關係。

Python 程式碼

Python的sklearn中有已整合的樸素貝葉斯演算法包naive_bayes,根據特徵值的分佈可以選擇對應的分類計算方法(MultinomialNB,GaussianNB等)

import pandas as pd
import numpy as np
import time
from sklearn import naive_bayes

def data_load():

    # 利用pandas讀取csv檔案內容
    train_ttl=pd.read_csv('D:\Program files\JetBrains\digit recognizer\Raw data\\train.csv')
    train_label=pd.DataFrame(train_ttl['label'])
    train_data=pd.DataFrame(train_ttl.ix[:,1:])
    test_data=pd.read_csv('D:\Program files\JetBrains\digit recognizer\Raw data\\test.csv')

    # dataframe歸整化
    test_data[test_data!=0]=1

    # train_data[train_data!=0]=1
    m,n=train_data.shape#這裡似乎因為dataframe太大,用bool判斷更改時總會異常跳出,所以選擇迴圈更改
    for i in range(m):
        for j in range(n):
            if train_data.ix[i,j]!=0:
                train_data.ix[i,j]=1

    return train_data,train_label,test_data

#利用Python Sklearn包,進行test樣本集分類判別
def bayes_classify(traindata,trainlabel,testdata):

    bayes_clf = naive_bayes.MultinomialNB()#設定函式和引數
    bayes_clf.fit(traindata,trainlabel.values.ravel())#訓練Train樣本
    bayes_result=bayes_clf.predict(testdata)#預測Test樣本

    return bayes_result

if __name__=='__main__':
    start = time.clock()
    traindata,trainlabel,testdata=data_load()#載入raw data

    m,n=testdata.shape
    result_labels=bayes_classify(traindata,trainlabel,testdata)

    #將結果轉化成Dataframe結構
    result={}
    ImageId=np.arange(m)+1
    result['Label']=result_labels
    result_frame=pd.DataFrame(result,index=ImageId)

    #匯出結果
    result_frame.to_csv('D:\Program files\JetBrains\digit recognizer\Raw data\\result_bayes.csv')
    end = time.clock()
    print('總耗時:', (end - start)/3600.0)#接近1.6小時,效果不理想只有0.81