爲什麼機器學習(四)—— 樸素貝葉斯的“訓練”爲什麼這麼輕鬆

續着上一篇說到的Iris數據集多分類問題,基於softmax的邏輯迴歸分類需要通過數據訓練一輪輪地降低損失函數,以獲得最佳的參數w和b。

而對於樸素貝葉斯算法來說,其核心源於貝葉斯公式,這個偉大的業餘(?)數學家貝葉斯的著名公式:

P(ab)=P(ba)P(a)P(b) \begin{aligned} P(a|b) = \frac{P(b|a)P(a)}{P(b)} \end{aligned}
其表現的是先驗概率P(a)和後驗概率P(a|b)之間的概率。

我們可以用一個形象的例子來說,比如相親:在不知道其他情況下,我們知道一個普通小夥子被姑娘看上的概率是0.4。根據後續的交流,我們發現這個小夥子爲人誠實,和藹可親,那麼根據常識,被看上的概率應該提高了;如果這個小夥子收入年入百萬,英俊瀟灑,身高185…那麼原來這個0.4的概率就會被不斷地修正,不斷地升高接近1.0。

我們剛剛這個例子中,一開始那個0.4的概率就是先驗概率,後面不斷地補充和小夥子相關的其他特徵,不斷修正得到的概率就是後驗概率。

利用貝葉斯公式,我們可以定性的發現先驗概率、後驗概率與這些其他相關特徵發生概率之間的關係。

回到樸素貝葉斯算法中,我們做多分類,也可以採用這種思想。一開始我們可以以這個類別在這個數據集中出現的比例爲先驗概率,在此之後,我們根據其他特徵發生的概率、在這個實體是這個類別條件下發生此特徵的概率來計算後驗概率,之後比較後驗概率(修正後的概率)的大小,選擇後驗概率最大的作爲一個實例的預測值。

對於離散型特徵,我們可以用每個特徵出現的次數來作爲概率:
P(xi=vy=c)=Nxi=v,y=cNy=c \begin{aligned} P(x_i=v|y=c) = \frac{N_{x_i=v,y=c}}{N_{y=c}} \end{aligned}
由於P(x)P(x)可以認爲是一個常數,所以最終我們需要比較
argmaxyP(y=c)inP(xi=vy=c) \begin{aligned} argmax_y P(y=c)\prod_{i}^{n}P(x_i=v|y=c) \end{aligned}

這裏xi=vx_i=v代表第i個特徵值爲v,y=c代表爲第c類

對於連續型特徵,我們可以任務特徵服從一維正態分佈,用其概率密度函數來表示概率:
f(xi=xy=c)=12πσexp((xμ)22σ2) \begin{aligned} f(x_i=x|y=c) = \frac{1}{\sqrt{2π}\sigma}exp(-\frac{(x-\mu)^2}{2\sigma^2}) \end{aligned}
同理最終我們要求:
argmaxyP(y=c)inf(xiy=c) \begin{aligned} argmax_y P(y=c)\prod_{i}^{n}f(x_i|y=c) \end{aligned}

我們可以發現,樸素貝葉斯不需要數據一輪輪的訓練來下降損失函數,只需要利用數據計算公式進行計算和比較即可。所以樸素貝葉斯比起softmax邏輯分類來說,“訓練”是輕鬆很多了。(我要是softmax算法我可能要罵娘了hhh)

下面上代碼:

import pandas as pd
from sklearn import datasets
import numpy as np

# 利用sklearn的datasets下載iris數據集
x_data = datasets.load_iris().data
y_data = datasets.load_iris().target

#打亂順序,分爲訓練集測試集
np.random.seed(100)
np.random.shuffle(x_data)
np.random.seed(100)
np.random.shuffle(y_data)
dataset_size = len(x_data)
trainData_size = int(dataset_size*0.7)
testData_size = dataset_size - trainData_size
x_train = x_data[:trainData_size]
y_train = y_data[:trainData_size]
x_test = x_data[trainData_size:]
y_test = y_data[trainData_size:]


# 轉爲dataframe
train = pd.DataFrame(x_train,columns=['SpealLength', 'SpealWidth', 'PetalLength', 'PetalWidth'])
train.insert(0,'target',y_train)

# 訓練過程(算公式過程)
# 開闢一個3*8的ndarray存3類4個特徵的均值和標準差
w = np.zeros((3,8))
# 存儲每類的數量
type_size = np.zeros(3)

# 分別對三類數據對應特徵求均值和標準差
for type in range(3):
    temp = train[train['target']==type]
    type_size[type] = len(temp)
    temp = temp[['SpealLength', 'SpealWidth', 'PetalLength', 'PetalWidth']]
    mean = temp.mean()
    std = temp.std()
    for feature in range(4):
        w[type][feature * 2] = mean.iloc[feature]
        w[type][feature * 2 + 1] = std.iloc[feature]


# 求正態概率密度
def normal_f(mean,std,x):
    x = np.array(x)
    return 1/(np.sqrt(2*np.pi)*std)*np.exp(-np.square(x-mean)/(2*np.square(std)))

acc_num = 0
for i in range(len(x_test)):
    # 後驗概率
    post_prob = np.zeros(3)
    for type in range(3):
        #先驗概率賦值給後驗概率
        post_prob[type] = type_size[type] / len(x_train)
        for feature in range(4):
            post_prob[type] *= normal_f(w[type][feature * 2],w[type][feature * 2 + 1],x_test[i][feature])
    ans = np.argmax(post_prob)
    print("guess:",ans,'\n',"right_ans:",y_test[i])
    if ans == y_test[i]:
        acc_num += 1
print("acc:",acc_num/len(x_test))

最終準確率100%
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章