1. 程式人生 > >帶你徹徹底底搞懂樸素貝葉斯公式

帶你徹徹底底搞懂樸素貝葉斯公式

本文參考了該部落格的例項,但該部落格中的樸素貝葉斯公式計算錯誤,評論中的也不對,所以,重新寫一篇。

一. 樸素貝葉斯

      樸素貝葉斯中的樸素一詞的來源就是假設各特徵之間相互獨立。這一假設使得樸素貝葉斯演算法變得簡單,但有時會犧牲一定的分類準確率

    首先給出貝葉斯公式:
    換成分類任務的表示式:
     我們最終求的p(類別|特徵)即可!就相當於完成了我們的任務。
     則,樸素貝特斯公式為:
. 例項解析

首先,給出資料如下:


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

這是典型的二分類問題,按照樸素貝葉斯的求解,轉換為

P(嫁|不帥、性格不好、矮、不上進)和P(不嫁|不帥、性格不好、矮、不上進)的概率,最終選擇嫁與不嫁的答案。

這裡我們根據貝特斯公式:

由此,我們將(嫁|不帥、性格不好、矮、不上進)轉換成三個可求的P(嫁)、P(不帥、性格不好、矮、不上進|嫁)、P(不帥、性格不好、矮、不上進)。進一步分解可以得:
P(不帥、性格不好、矮、不上進)=P()P(不帥|嫁)P(性格不好|嫁)P(矮|嫁)P(不上進|嫁)+P(不)P(不帥|不嫁)P(性格不好|不嫁)P(矮|不嫁)P(不上進|不嫁)。

P(不帥、性格不好、矮、不上進|嫁)=P(不帥|嫁)P(性格不好|嫁)P(矮|嫁)P(不上進|嫁)

將上面的公式整理一下可得:


 P()=1/2、P(不帥|嫁)=1/2、P(性格不好|嫁)=1/6、P(矮|嫁)=1/6、P(不上進|嫁)=1/6。
 P(不嫁)=1/2、P(不帥|不嫁)=1/3、P(性格不好|不嫁)=1/2、P(矮|不嫁)=1、P(不上進|不嫁)=2/3
 但是由貝葉斯公式可得:對於目標求解為不同的類別,貝葉斯公式的分母總是相同的。所以,只求解分子即可:

於是,對於類別“嫁”的貝葉斯分子為:P(嫁)P(不帥|嫁)P(性格不好|嫁)P(矮|嫁)P(不上進|嫁)=1/2 * 1/2 * 1/6 * 1/6 * 1/6=1/864     
對於類別“不嫁”的貝葉斯分子為:P(不)P(不帥|不嫁)P(性格不好|不嫁)P(矮|不嫁)P(不上進|不嫁
)=1/2 * 1/3 * 1/2 * 1* 2/3=1/18。
經代入貝葉斯公式可得:P(嫁|不帥、性格不好、矮、不上進)=(1/864) / (1/864+1/18)=1/49=2.04%
P(不嫁|不帥、性格不好、矮、不上進)=(1/18) / (1/864+1/18)=48/49=97.96%
P(不嫁|不帥、性格不好、矮、不上進) > P(嫁|不帥、性格不好、矮、不上進),則該女子選擇不嫁!

. 樸素貝葉斯的優缺點

優點:
  (1) 演算法邏輯簡單,易於實現(演算法思路很簡單,只要使用貝葉斯公式轉化即可!
(2)分類過程中時空開銷小(假設特徵相互獨立,只會涉及到二維儲存
缺點:
      樸素貝葉斯假設屬性之間相互獨立,這種假設在實際過程中往往是不成立的。在屬性之間相關性越大,分類誤差也就越大。

四. 樸素貝葉斯實戰

sklearn中有3種不同型別的樸素貝葉斯:

  • 高斯分佈型:用於classification問題,假定屬性/特徵服從正態分佈的。
  • 多項式型:用於離散值模型裡。比如文字分類問題裡面我們提到過,我們不光看詞語是否在文字中出現,也得看出現次數。如果總詞數為n,出現詞數為m的話,有點像擲骰子n次出現m次這個詞的場景。
  • 伯努利型:最後得到的特徵只有0(沒出現)和1(出現過)。
  4.1  我們使用iris資料集進行分類
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import cross_val_score
from sklearn import datasets
iris = datasets.load_iris()
gnb = GaussianNB()
scores=cross_val_score(gnb, iris.data, iris.target, cv=10)
print("Accuracy:%.3f"%scores.mean())
    輸出: Accuracy:0.953
 

4.2 Kaggle比賽之“舊金山犯罪分類預測”

       題目資料:第一種獲取方式:Kaggle網站上;第二種獲取方式:百度網盤

        題目背景:『水深火熱』的大米國,在舊金山這個地方,一度犯罪率還挺高的,然後很多人都經歷過大到暴力案件,小到東西被偷,車被劃的事情。當地警方也是努力地去總結和想辦法降低犯罪率,一個挑戰是在給出犯罪的地點和時間的之後,要第一時間確定這可能是一個什麼樣的犯罪型別,以確定警力等等。後來乾脆一不做二不休,直接把12年內舊金山城內的犯罪報告都丟帶Kaggle上,說『大家折騰折騰吧,看看誰能幫忙第一時間預測一下犯罪型別』。犯罪報告裡面包括日期描述星期幾所屬警區處理結果地址GPS定位等資訊。當然,分類問題有很多分類器可以選擇,我們既然剛講過樸素貝葉斯,剛好就拿來練練手好了。

     (1) 首先我們來看一下資料

import pandas as pd  
import numpy as np  
from sklearn import preprocessing  
from sklearn.metrics import log_loss  
from sklearn.cross_validation import train_test_split
train = pd.read_csv('/Users/liuming/projects/Python/ML資料/Kaggle舊金山犯罪型別分類/train.csv', parse_dates = ['Dates'])  
test = pd.read_csv('/Users/liuming/projects/Python/ML資料/Kaggle舊金山犯罪型別分類/test.csv', parse_dates = ['Dates'])  
train  

我們依次解釋一下每一列的含義:

  • Date: 日期
  • Category: 犯罪型別,比如 Larceny/盜竊罪 等.
  • Descript: 對於犯罪更詳細的描述
  • DayOfWeek: 星期幾
  • PdDistrict: 所屬警區
  • Resolution: 處理結果,比如說『逮捕』『逃了』
  • Address: 發生街區位置
  • X and Y: GPS座標

        train.csv中的資料時間跨度為12年,包含了將近90w的記錄。另外,這部分資料,大家從上圖上也可以看出來,大部分都是『類別』型,比如犯罪型別,比如星期幾。

    (2)特徵預處理

       sklearn.preprocessing模組中的 LabelEncoder函式可以對類別做編號,我們用它對犯罪型別做編號;pandas中的get_dummies( )可以將變數進行二值化01向量,我們用它對”街區“、”星期幾“、”時間點“進行因子化。

#對犯罪類別:Category; 用LabelEncoder進行編號  
leCrime = preprocessing.LabelEncoder()  
crime = leCrime.fit_transform(train.Category)   #39種犯罪型別  
#用get_dummies因子化星期幾、街區、小時等特徵  
days=pd.get_dummies(train.DayOfWeek)  
district = pd.get_dummies(train.PdDistrict)  
hour = train.Dates.dt.hour  
hour = pd.get_dummies(hour)  
#組合特徵  
trainData = pd.concat([hour, days, district], axis = 1)  #將特徵進行橫向組合  
trainData['crime'] = crime   #追加'crime'列  
days = pd.get_dummies(test.DayOfWeek)  
district = pd.get_dummies(test.PdDistrict)  
hour = test.Dates.dt.hour  
hour = pd.get_dummies(hour)  
testData = pd.concat([hour, days, district], axis=1)  
trainData 

    特徵預處理後,訓練集feature,如下圖所示:

   (3) 建模

from sklearn.naive_bayes import BernoulliNB
import time
features=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday', 'BAYVIEW', 'CENTRAL', 'INGLESIDE', 'MISSION',  
 'NORTHERN', 'PARK', 'RICHMOND', 'SOUTHERN', 'TARAVAL', 'TENDERLOIN']  
X_train, X_test, y_train, y_test = train_test_split(trainData[features], trainData['crime'], train_size=0.6)  
NB = BernoulliNB()  
nbStart = time.time()  
NB.fit(X_train, y_train)  
nbCostTime = time.time() - nbStart  
#print(X_test.shape)  
propa = NB.predict_proba(X_test)   #X_test為263415*17; 那麼該行就是將263415分到39種犯罪型別中,每個樣本被分到每一種的概率  
print("樸素貝葉斯建模%.2f秒"%(nbCostTime))  
predicted = np.array(propa)  
logLoss=log_loss(y_test, predicted)  
print("樸素貝葉斯的log損失為:%.6f"%logLoss)  
輸出:
樸素貝葉斯建模0.55秒
樸素貝葉斯的log損失為:2.582561