太讚了!機器學習基礎核心算法:貝葉斯分類!(附西瓜書案例及代碼實現)
Datawhale
作者:尹曉丹 ,Datawhale優秀學習者
寄語:首先,簡單介紹了生成模型和判別模型,對條件概率、先驗概率和後驗概率進行了總結;其次,對樸素貝葉斯的原理及公式推導做了詳細解讀;再次,對三種可能遇到的問題進行了解析,給出了合理的解決辦法;最後,對樸素貝葉斯的sklearn參數和代碼進行了詳解。
貝葉斯分類是一類分類算法的總稱,這類算法均以貝葉 斯定理爲基礎,故統稱爲貝葉斯分類。而樸素貝葉斯分類是貝葉斯分類中最簡單,也是 應用最爲廣泛的分類算 法之一。 樸素貝葉斯方法是在 貝葉斯 算法的基礎上進行了相應的簡化,即假定給定目標值時屬性之間相互條件獨立。
知識框架
相關概念
生成模型
概率統計理論中, 生成模型是指能夠隨機生成觀測數據的模型,尤其是在給定某些隱含參數的條件下。它給觀測值和標註數據序列指定一個聯合概率分佈。
在機器學習中,生成模型可以用來直接對數據建模(例如根據某個變量的概率密度函數進行數據採樣),也可以用來建立變量間的條件概率分佈。條件概率分佈可以由生成模型根據貝葉斯定理形成。
常見的基於生成模型算法有高斯混合模型和其他混合模型、隱馬爾可夫模型、隨機上下文無關文法、樸素貝葉斯分類器、AODE分類器、潛在狄利克雷分配模型、受限玻爾茲曼機等。
舉個栗子: 要確定一個瓜是好瓜還是壞瓜,用判別模型的方法使從歷史數據中學習到模型,然後通過提取這個瓜的特徵來預測出這隻瓜是好瓜的概率,是壞瓜的概率。
判別模型
在機器學習領域判別模型是一種對未知數據 y 與已知數據 x 之間關係進行建模的方法。
判別模型是一種基於概率理論的方法。已知輸入變量 x ,判別模型通過構建條件概率分佈 P(y|x) 預測 y 。
常見的基於判別模型算法有邏輯迴歸、線性迴歸、支持向量機、提升方法、條件隨機場、人工神經網絡、隨機森林、感知器。
舉個栗子: 利用生成模型是根據好瓜的特徵首先學習出一個好瓜的模型,然後根據壞瓜的特徵學習得到一個壞瓜的模型,然後從需要預測的瓜中提取特徵,放到生成好的好瓜的模型中看概率是多少,在放到生產的壞瓜模型中看概率是多少,哪個概率大就預測其爲哪個。
生成模型與判別模型的區別
生成模型是所有變量的全概率模型,而判別模型是在給定觀測變量值前提下目標變量條件概率模型。
因此,生成模型能夠用於模擬(即生成)模型中任意變量的分佈情況,而判別模型只能根據觀測變量得到目標變量的採樣。判別模型不對觀測變量的分佈建模,因此它不能夠表達觀測變量與目標變量之間更復雜的關係。因此,生成模型更適用於無監督的任務,如分類和聚類。
先驗概率、條件概率
條件概率
就是事件A在事件B發生的條件下發生的概率。條件概率表示爲P(A|B),讀作“A在B發生的條件下發生的概率”。
先驗概率
在貝葉斯統計中,某一不確定量 p 的先驗概率分佈是在考慮"觀測數據"前,能表達 p 不確定性的概率分佈。它旨在描述這個不確定量的不確定程度,而不是這個不確定量的隨機性。這個不確定量可以是一個參數,或者是一個隱含變量。
後驗概率
在貝葉斯統計中,一個隨機事件或者一個不確定事件的後驗概率是在考慮和給出相關證據或數據後所得到的條件概率。
同樣,後驗概率分佈是一個未知量(視爲隨機變量)基於試驗和調查後得到的概率分佈。“後驗”在本文中代表考慮了被測試事件的相關證據。
貝葉斯決策理論
貝葉斯決策論是概率框架下實施決策的基本方法,對分類任務來說,在所有相關概率都已知的理想情形下,貝葉斯決策論考慮如何基於這些概率和誤判損失來選擇最優的類別標記。
假設有N種可能標記, 是將類 誤分類爲 所產生的損失,基於後驗概率 可以獲得樣本x分類爲 所產生的期望損失 ,即在樣本x上的條件風險:
我們的任務是尋找一個判定準則 以最小化總體風險
顯然,對每個樣本 ,若 能最小化條件風險 ,則總體風險 也將被最小化。這就產生了貝葉斯判定準則:最小化總體風險,只需要在每個樣本上選擇那個能使條件風險 最小的類別標記,即:
此時, 稱作貝葉斯最優分類器,與之對應的總體風險 稱爲貝葉斯風險, 反映了分類器能達到的最好性能,即機器學習所產生的模型精度的上限。 具體來說,若目標是最小化分類錯誤率(對應0/1損失),則 可以用 損失改寫,得到條件風險和最小化分類錯誤率的最優分類器分別爲:
即對每個樣本x,選擇能使後驗概率 P(c|x) 最 大的類別標識。
獲得後驗概率的兩種方法:
-
判別式模型 : 給定x, 可以通過直接建模P(c|x)來預測c。
-
生成模型 : 先對聯合分佈p(x,c)模,然後再有此獲得P(c|x)。
貝葉斯公式
對生成模型來說,必然考慮:
其中P(c)是“先驗概率”; P(x|c)是樣本x對於類標記c的類條件概率,或稱爲“似然”; P(x)是用於歸一化的“證據”因子。 上式即爲貝葉斯公式,可以將其看做:
對類條件概率P(x|c)來說,直接根據樣本出現的頻率來估計將會遇到嚴重的困難,所以引入了極大似然估計。
極大似然估計
估計類條件概率有一種常用的策略就是先假定其具有某種確定的概率分佈形式,再基於訓練樣本對概率分佈的參數進行估計。
假設P(x|c)具有某種確定的形式並且被參數 唯一確定,則我們的任務就是利用訓練結D估計參數 。爲了明確期間,我們將P(x|c)記爲 。
舉個通俗的例子:假設一個袋子裝有白球與紅球,比例未知,現在抽取10次(每次抽完都放回,保證事件獨立性),假設抽到了7次白球和3次紅球,在此數據樣本條件下,可以採用最大似然估計法求解袋子中白球的比例(最大似然估計是一種“模型已定,參數未知”的方法)。
當然,這種數據情況下很明顯,白球的比例是70%,但如何通過理論的方法得到這個答案呢?一些複雜的條件下,是很難通過直觀的方式獲得答案的,這時候理論分析就尤爲重要了,這也是學者們爲何要提出最大似然估計的原因。我們可以定義從袋子中抽取白球和紅球的概率如下:
x1爲第一次採樣,x2爲第二次採樣,f爲模型, theta爲模型參數。 其中θ是未知的,因此,我們定義似然L爲:
兩邊取ln,取ln是爲了將右邊的乘號變爲加號,方便求導。
兩邊取ln的結果,左邊的通常稱之爲對數似然。
這是平均對數似然。最大似然估計的過程,就是找一個合適的theta,使得平均對數似然的值爲最大。因此,可以得到以下公式:
最大似然估計的公式。 這裏討論的是2次採樣的情況,
當然也可以拓展到多次採樣的情況:最大似然估計的公式(n次採樣)。我們定義M爲模型(也就是之前公式中的f),表示抽到白球的概率爲θ,而抽到紅球的概率爲(1-θ),因此10次抽取抽到白球7次的概率可以表示爲:
將其描述爲平均似然可得:
10次抽取抽到白球7次的平均對數似然,抽球的情況比較簡單,可以直接用平均似然來求解。那麼最大似然就是找到一個合適的theta,獲得最大的平均似然。因此我們可以對平均似然的公式對theta求導,並另導數爲0。
求得,θ=0.7。求導過程 由此可得,當抽取白球的概率爲0.7時,最可能產生10次抽取抽到白球7次的事件。以上就用到了最大似然估計的思想。
令Dc表示訓練集D中第c類樣本組成的集合,假設這些集合是獨立同分布的,則對參數θc、對於數據集Dc的似然是:
對θc進行激發似然估計買就是去尋找能最大化似然函數的參數值θc直觀上,極大似然估計是在試圖在θc的所有可能的去職中,找到一個能使數據出現最大“可能性”的最大值上面的式子中的連乘操作容易造成下溢,通常使用對數似然:
此時,參數 的極大似然估計 爲
例如,在連續屬性的情形下,假設概率密度函數 ,則參數 和 。
也就是說通過極大似然發得到的額正態分佈均值就是樣本均值,方差就是 的均值。這顯然是一個符合只覺得結果,在離散屬性情形下,也可以通過類似的方法來估計類條件概率。
需要注意的是這種方法雖然能夠使類條件概率估計變得簡單,但是估計結果準確性嚴重依賴於所假設的概率分佈形式是否符合潛在的真實數據分佈。在顯示生活中往往需要應用任務本身的經驗知識,“猜測”則會導致誤導性的結果。
貝葉斯分類器的訓練過程就是參數估計。總結最大似然法估計參數的過程,一般分爲以下四個步驟:
-
寫出似然函數;
-
對似然函數取對數,並整理;
-
求導數,令偏導數爲0,得到似然方程組;
-
解似然方程組,得到所有參數即爲所求。
樸素貝葉斯分類器
基於貝葉斯公式來估計後驗概率P(c|x)主要困難 在於類條件概率P(x|c)是所有屬性上的聯合概率,難以從有限的 訓練樣本直接估計而得。
基於有限訓練樣本直接計算聯合概率,在計算上將會遭遇組合爆炸問題; 在數據上將會遭遇樣本稀疏問題; 屬性越多,問題越嚴重。
爲了避開這個障礙,樸素貝葉斯分類器採用了“屬性條件獨立性假設”:對已知類別,假設所有屬性相互獨立。 換言之,假設每個屬性獨立的對分類結果發生影響相互獨立。
回答西瓜的例子就可以認爲{色澤 根蒂 敲聲 紋理 臍部 觸感}這些屬性對西瓜是好還是壞的結果所產生的影響相互獨立。
基於條件獨立性假設,對於多個屬性的後驗概率可以寫成:
d爲屬性數目, 是 在第 個屬性上取值。 對於所有的類別來說 相同,基於極大似然的貝葉斯判定準則有樸素貝葉斯的表達式:
極值問題情況下每個類的分類概率
很多時候遇到求出各種目標函數(object function)的最值問題(最大值或者最小值)。關於函數最值問題,其實在高中的時候我們就已經瞭解不少,最經典的方法就是:直接求出極值點。
這些極值點的梯度爲0。若極值點唯一,則這個點就是代入函數得出的就是最值;若極值點不唯一,那麼這些點中,必定存在最小值或者最大值(去除函數的左右的最端點),所以把極值代入函數,經對比後可得到結果。
請注意: 並不一定所有函數的極值都可以通過設置導數爲0的方式求出。 也就是說,有些問題中當我們設定導數爲0時,未必能直接計算出滿足導數爲0的點(比如邏輯迴歸模型),這時候就需要利用數值計算相關的技術(最典型爲梯度下降法,牛頓法……)。
下溢問題如何解決
數值下溢問題:是指計算機浮點數計算的結果小於可以表示的最小數,因爲計算機的能力有限,當數值小於一定數時,其無法精確保存,會造成數值的精度丟失,由上述公式可以看到,求概率時多個概率值相乘,得到的結果往往非常小;因此通常採用取對數的方式,將連乘轉化爲連加,以避免數值下溢。
零概率問題如何解決?
零概率問題,就是在計算實例的概率時,如果某個量x,在觀察樣本庫(訓練集)中沒有出現過,會導致整個實例的概率結果是0。
在實際的模型訓練過程中,可能會出現零概率問題(因爲先驗概率和反條件概率是根據訓練樣本算的,但訓練樣本數量不是無限的,所以可能出現有的情況在實際中存在,但在訓練樣本中沒有,導致爲0的概率值,影響後面後驗概率的計算)。
即便可以繼續增加訓練數據量,但對於有些問題來說,數據怎麼增多也是不夠的。 這時我們說模型是不平滑的,我們要使之平滑,一種方法就是將訓練(學習)的方法換成貝葉斯估計。
現在看一個示例,及P(敲聲=清脆∣好瓜=是)=8/0=0。不論樣本的其他屬性如何,分類結果都會爲“好瓜=否”,這樣顯然不太合理。
樸素貝葉斯算法的先天缺陷
其他屬性攜帶的信息被訓練集中某個分類下未出現的屬性值“抹去”,造成預測出來的概率絕對爲0。爲了彌補這一缺陷,前輩們引入了拉普拉斯平滑的方法:對先驗概率的分子(劃分的計數)加1,分母加上類別數;對條件概率分子加1,分母加上對應特徵的可能取值數量。這樣在解決零概率問題的同時,也保證了概率和依然爲1:
其中,N表示數據集中分類標籤, 表示第 個屬性的取值類別數, 樣本容量, 表示類別 的記錄數量, 表示類別 中第 個屬性取值爲 的記錄數量。
將這兩個式子應用到上 面的計算過程中,就可以彌補樸素貝葉斯算法的這一缺陷問題。
用西瓜的數據來看,當我們計算 P(好瓜=是) 時,樣本有17個,所以|D| = 17,N,好瓜標籤可以分爲{是,否}兩類,所以N=2,(好瓜=是)的樣本個數有8個,所以這裏 。
綜上,根據拉普拉斯平滑後有
P(色澤=青綠|好瓜=是)時,色澤青綠的樣本有8個,所以|D_c| = 8,色澤標籤可以分爲{青綠,淺白,烏黑}三類,所以N=3,(好瓜=是)的樣本個數有3個,所以這裏 =3。綜上,根據拉普拉斯平滑後有
同理,分析可知,之前不合理的P(敲聲=清脆∣好瓜=是)=80=0P(敲聲=清脆|好瓜=是)=\frac{8}{0}=0P(敲聲=清脆∣好瓜=是)= 8/0 =0在進行拉普拉斯平滑後爲:
顯然結果不是0,使結果變得合理。
優缺點
優點
1. 樸素貝葉斯模型有穩定的分類效率。
2. 對小規模的數據表現很好,能處理多分類任務,適合增量式訓練,尤其是數據量超出內存時,可以一批批的去增量訓練。
3. 對缺失數據不太敏感,算法也比較簡單,常用於文本分類。
缺點
1. 理論上,樸素貝葉斯模型與其他分類方法相比具有最小的誤差率。但是實際上並非總是如此,這是因爲樸素貝葉斯模型給定輸出類別的情況下,假設屬性之間相互獨立,這個假設在實際應用中往往是不成立的,在屬性個數比較多或者屬性之間相關性較大時,分類效果不好。而在屬性相關性較小時,樸素貝葉斯性能最爲良好。對於這一點,有半樸素貝葉斯之類的算法通過考慮部分關聯性適度改進。
2. 需要知道先驗概率,且先驗概率很多時候取決於假設,假設的模型可以有很多種,因此在某些時候會由於假設的先驗模型的原因導致預測效果不佳。
3. 由於我們是通過先驗和數據來決定後驗的概率從而決定分類,所以分類決策存在一定的錯誤率。
4. 對輸入數據的表達形式很敏感。
sklearn參數詳解
高斯樸素貝葉斯算法是假設特徵的可能性(即概率)爲高斯分佈。
class sklearn.naive_bayes.GaussianNB(priors=None)
參數:
1. priors : 先驗概率大小,如果沒有給定,模型則根據樣本數據自己計算(利用極大似然法)。
2. var_smoothing :可選參數,所有特徵的最大方差
屬性:
3. class_prior_ : 每個樣本的概率
4. class_count : 每個類別的樣本數量
5. classes_ : 分類器已知的標籤類型
6. theta_ : 每個類別中每個特徵的均值
7. sigma_ : 每個類別中每個特徵的方差
8. epsilon_ : 方差的絕對加值方法
貝葉斯的方法和其他模型的方法一致
1. fit(X,Y) : 在數據集(X,Y)上擬合模型。
2. get_params() : 獲取模型參數。
3. predict(X) : 對數據集X進行預測。
4. predict_log_proba(X): 對數據集X預測,得到每個類別的概率對數值。
5. predict_proba(X) : 對數據集X預測,得到每個類別的概率。
6. score(X,Y) : 得到模型在數據集(X,Y)的得分情況。
構建樸素貝葉斯模型
這裏採用GaussianNB 高斯樸素貝葉斯,概率密度函數爲 :
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
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]
import math
class NaiveBayes:
def __init__(self):
self.model = None
# 數學期望
@staticmethod
def mean(X):
"""計算均值
Param: X : list or np.ndarray
Return:
avg : float
"""
avg = 0.0
# ========= show me your code ==================
avg = sum(X) / float(len(X))
# ========= show me your code ==================
return avg
# 標準差(方差)
def stdev(self, X):
"""計算標準差
Param: X : list or np.ndarray
Return:
res : float
"""
res = 0.0
# ========= show me your code ==================
avg = self.mean(X)
res = math.sqrt(sum([pow(x - avg, 2) for x in X]) / float(len(X)))
# ========= show me your code ==================
return res
# 概率密度函數
def gaussian_probability(self, x, mean, stdev):
"""根據均值和標註差計算x符號該高斯分佈的概率
Parameters:
----------
x : 輸入
mean : 均值
stdev : 標準差
Return:
res : float, x符合的概率值
"""
res = 0.0
# ========= show me your code ==================
exponent = math.exp(-(math.pow(x - mean, 2) /
(2 * math.pow(stdev, 2))))
res = (1 / (math.sqrt(2 * math.pi) * stdev)) * exponent
# ========= show me your code ==================
return res
# 處理X_train
def summarize(self, train_data):
"""計算每個類目下對應數據的均值和標準差
Param: train_data : list
Return : [mean, stdev]
"""
summaries = [0.0, 0.0]
# ========= show me your code ==================
summaries = [(self.mean(i), self.stdev(i)) for i in zip(*train_data)]
# ========= show me your code ==================
return summaries
# 分類別求出數學期望和標準差
def fit(self, X, y):
labels = list(set(y))
data = {label: [] for label in labels}
for f, label in zip(X, y):
data[label].append(f)
self.model = {
label: self.summarize(value) for label, value in data.items()
}
return 'gaussianNB train done!'
# 計算概率
def calculate_probabilities(self, input_data):
"""計算數據在各個高斯分佈下的概率
Paramter:
input_data : 輸入數據
Return:
probabilities : {label : p}
"""
# 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 = {}
# ========= show me your code ==================
for label, value in self.model.items():
probabilities[label] = 1
for i in range(len(value)):
mean, stdev = value[i]
probabilities[label] *= self.gaussian_probability(
input_data[i], mean, stdev)
# ========= show me your code ==================
return probabilities
# 類別
def predict(self, X_test):
# {0.0: 2.9680340789325763e-27, 1.0: 3.5749783019849535e-26}
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)
print(model.predict([3.4, 6.2, 2.0, 0.3]))
model.score(X_test, y_test)
“爲沉迷學習 點贊 ↓