本文部分總結內容摘自李航老師的《統計學習方法》及其配套課件
原文代碼作者:https://github.com/wzyonggege/statistical-learning-method
在開始樸素貝葉斯的學習之前,先弄清楚幾個概念:
先驗概率:
事件發生前的預判概率。可以是基於歷史數據的統計,可以由背景常識得出,也可以是人的主觀觀點給出。一般都是單獨事件概率,如P(x),P(y)。
後驗概率:
事件發生後求的反向條件概率;或者說,基於先驗概率求得的反向條件概率。概率形式與條件概率相同。
條件概率:
一個事件發生後另一個事件發生的概率。一般的形式爲P(x|y)表示y發生的條件下x發生的概率。
基本假設
樸素貝葉斯,英文叫Naive Bayes。
Naive?那是有原因的,樸素貝葉斯對輸入變量做了一個很強的假設——條件獨立
條件獨立
輸入變量之間是相互獨立的,沒有概率依存關係。(若相互依存,那叫貝葉斯網絡)
即,用於分類的特徵(xj)在類(y=ck)確定的條件下,都是相互獨立的,即
P(X=x|Y=ck)=P(X1=x2,X2=x2...Xn=xn|Y=ck)
=P(X1=x1|Y=ck)P(X2=x2|Y=ck)...P(Xn=xn|Y=ck)
訓練數據集:由X和Y的聯合概率分佈P(X,Y)獨立同分布產生
樸素貝葉斯通過訓練數據集學習聯合概率分佈P(X,Y) ,
即先驗概率分佈:
及條件概率分佈:
條件獨立性假設:
“樸素” 貝葉斯名字由來, 犧牲分類準確性。
如下:
-
後驗概率最大化的含義:
樸素貝葉斯法將實例分到後驗概率最大的類中。 這等價於期望風險最小化。 假設選擇0-1損失函數:
式中f(X)是分類決策函數。 這時, 期望風險函數爲
期望是對聯合分佈P(X,Y)取的。 由此取條件期望
注意:
爲了使期望風險最小化, 只需對X=x逐個極小化, 由此得到:
這樣一來, 根據期望風險最小化準則就得到了後驗概率最大化準則:
即樸素貝葉斯法所採用的原理。
-
極大似然估計
在樸素貝葉斯法中, 學習意味着估計P(Y=)和P(=|Y=)。 可以應用極大似然估計法估計相應的概率。 先驗概率P(Y=)的極大似然估計是
設第j個特徵可能取值的集合爲{,,…,}, 條件概率P(=|Y=)的極大似然估計
是
式中, 是第i個樣本的第j個特徵; 是第j個特徵可能取的第l個值; I爲指示函數。
-
樸素貝葉斯法的學習與分類算法
-
貝葉斯估計
用極大似然估計可能會出現所要估計的概率值爲0的情況。 這時會影響到後驗概率的計算結果, 使分類產生偏差。 解決這一問題的方法是採用貝葉斯估計。 具體地, 條件概率的貝葉斯估計是
式中 ≥0。 等價於在隨機變量各個取值的頻數上賦予一個正數 >0。 當 =0時就是極大似然估計。 常取 =1, 這時稱爲拉普拉斯平滑(Laplace smoothing) 。 顯然, 對任何l=1,2,…,Sj, K=1,2,…,K, 有
表明式(4.10) 確爲一種概率分佈。 同樣, 先驗概率的貝葉斯估計是
-
樸素貝葉斯的優缺點
優點:
-
測試數據集的類預測簡單又快速,在多類預測表現良好;
-
當變量假設獨立成立時,相比Logistic迴歸等其他分類方法,樸素貝葉斯分類器性能更優,需要的訓練數據較少;
-
相較於數值變量,樸素貝葉斯分類器在多個分類變量的情況下表現更好。若是數值變量,需要正態分佈假設。
缺點:
-
如果分類變量的類別(在測試數據集中)沒有在訓練數據集總被觀察到,那這個模型會分配一個0(零)概率給它,同時也會無法進行預測。這通常被稱爲“零頻率”。
-
樸素貝葉斯的另一個限制是獨立預測的假設。在現實生活中,這幾乎是不可能的,各變量間或多或少都會存在相互影響。
基於貝葉斯定理與特徵條件獨立假設的分類方法。
模型:
- 高斯模型
- 多項式模型
- 伯努利模型
GaussianNB 高斯樸素貝葉斯
GaussianNB假設特徵的先驗概率爲正態分佈,即如下式:
其中Ck爲Y的第k類類別。μ和σ^2爲需要從訓練集估計的值。
GaussianNB會根據訓練集求出μ和σ^2。 μ爲在樣本類別Ck中,所有Xj的平均值。σ^2爲在樣本類別Ck中,所有Xj的方差。
- 數據準備
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from collections import Counter
import math
# data
def create_data():
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
data = np.array(df.iloc[:100, :])
# print(data)
return data[:,:-1], data[:,-1]
X, y = create_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
- 函數:
class NaiveBayes:
def __init__(self):
self.model = None
# 數學期望
@staticmethod
def mean(X):
#此處算的是平均
return sum(X) / float(len(X))
# 標準差(方差)
def stdev(self, X):
avg = self.mean(X)
return math.sqrt(sum([pow(x-avg, 2) for x in X]) / float(len(X)))
# 概率密度函數
def gaussian_probability(self, x, mean, stdev):
exponent = math.exp(-(math.pow(x-mean,2)/(2*math.pow(stdev,2))))
return (1 / (math.sqrt(2*math.pi) * stdev)) * exponent
# 處理X_train
def summarize(self, train_data):
#zip(*x),解壓,保證解壓後參數長度一致,這裏得到的是x的每一列作爲一組
summaries = [(self.mean(i), self.stdev(i)) for i in zip(*train_data)]
#所以這裏得到四組,每一組是每個特徵的期望和方差
return summaries
# 分類別求出數學期望和標準差
def fit(self, X, y):
labels = list(set(y)) #構建一個不重複的集合
# labels = [0.0, 1.0]
#創建字典eg: {label :[]},此處應該只有{0:[], 1:[]}
data = {label:[] for label in labels}
#data ={0.0: [], 1.0: []}
#zip() 多個參數壓縮到一起,該函數返回一個以元組爲元素的列表
for f, label in zip(X, y):
data[label].append(f) #添加數據字典
#{0.0: [array1,array2……],1.0:....]
#求出每個類對應的數學期望和標準差
#這裏的summarize函數將X的每一列特徵計算了期望和標準差
self.model = {label: self.summarize(value) for label, value in data.items()}
return 'gaussianNB train done!'
# 計算概率
def calculate_probabilities(self, input_data):
# summaries:{0.0: [(5.0, 0.37),(3.42, 0.40)], 1.0: [(5.8, 0.449),(2.7, 0.27)]}
# input_data:[1.1, 2.2]
probabilities = {}
#每一類
for label, value in self.model.items():
probabilities[label] = 1 #保證非零
for i in range(len(value)): #每個value[i]中存放期望與方差
mean, stdev = value[i]
#累乘
probabilities[label] *= self.gaussian_probability(input_data[i], mean, stdev)
return probabilities
# 類別
def predict(self, X_test):
#probabilities ={0.0: 1.5639826885594645, 1.0: 2.3090969013303393e-17}
#排序sorted(d.items(), key=lambda x: x[-1]) 按照前面對象的第-1維數據(即value)的值進行排序
label = sorted(self.calculate_probabilities(X_test).items(), key=lambda x: x[-1])[-1][0]
return label
def score(self, X_test, y_test):
right = 0
for X, y in zip(X_test, y_test):
label = self.predict(X)
if label == y:
right += 1
return right / float(len(X_test))
- 模型計算:
model = NaiveBayes()
model.fit(X_train, y_train)
'gaussianNB train done!'
print(model.predict([4.4, 3.2, 1.3, 0.2]))
# 0.0
model.score(X_test, y_test)
#1.0
個人有個疑惑好像在上述代碼中沒有計算對應的各個類別的先驗概率 P(Y=Ck)P(Y=Ck),累乘中的計算似乎沒有算上這個不知道爲什麼。。不過採用的數據iris數據集中的各個類別的數量是相等的。如果是其他的訓練數據應該要再乘上去吧。
-
scikit-learn實例
在scikit-learn中,一共有3個樸素貝葉斯的分類算法類。分別是GaussianNB,MultinomialNB和BernoulliNB。其中GaussianNB就是先驗爲高斯分佈的樸素貝葉斯,MultinomialNB就是先驗爲多項式分佈的樸素貝葉斯,而BernoulliNB就是先驗爲伯努利分佈的樸素貝葉斯。
這三個類適用的分類場景各不相同,一般來說,如果樣本特徵的分佈大部分是連續值,使用GaussianNB會比較好。如果如果樣本特徵的分大部分是多元離散值,使用MultinomialNB比較合適。而如果樣本特徵是二元離散值或者很稀疏的多元離散值,應該使用BernoulliNB。
GaussianNB類的主要參數僅有一個,即先驗概率priors ,對應Y的各個類別的先驗概率P(Y=Ck)P(Y=Ck)。這個值默認不給出,如果不給出此時P(Y=Ck)=mk/m。其中m爲訓練集樣本總數量,mk爲輸出爲第k類別的訓練集樣本數。如果給出的話就以priors 爲準。
在使用GaussianNB的fit方法擬合數據後,我們可以進行預測。此時預測有三種方法,包括predict,predict_log_proba和predict_proba。
predict方法就是我們最常用的預測方法,直接給出測試集的預測類別輸出。
predict_proba則不同,它會給出測試集樣本在各個類別上預測的概率。容易理解,predict_proba預測出的各個類別概率裏的最大值對應的類別,也就是predict方法得到類別。
predict_log_proba和predict_proba類似,它會給出測試集樣本在各個類別上預測的概率的一個對數轉化。轉化後predict_log_proba預測出的各個類別對數概率裏的最大值對應的類別,也就是predict方法得到類別。
- sklearn.naive_bayes
from sklearn.naive_bayes import GaussianNB
clf = GaussianNB() #注:建立一個高斯樸素貝葉斯分類器的實例,這裏默認沒有預設先驗概率,根據訓練集自動設置。
#注:分類器的fit方法,輸入數據集的特徵x和分類標籤y,進行分類器的訓練。然後用predict方法,輸入特徵x,輸出分類器對樣本的預測標籤。
clf.fit(X_train, y_train)
clf.score(X_test, y_test)
clf.predict([[4.4, 3.2, 1.3, 0.2]])
from sklearn.naive_bayes import BernoulliNB, MultinomialNB
# 伯努利模型和多項式模型