帶你徹徹底底搞懂樸素貝葉斯公式
本文參考了該部落格的例項,但該部落格中的樸素貝葉斯公式計算錯誤,評論中的也不對,所以,重新寫一篇。
一. 樸素貝葉斯
樸素貝葉斯中的樸素一詞的來源就是假設各特徵之間相互獨立。這一假設使得樸素貝葉斯演算法變得簡單,但有時會犧牲一定的分類準確率。
首先給出貝葉斯公式:
換成分類任務的表示式:
我們最終求的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(出現過)。
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.9534.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