在《貝葉斯之朴素理解》比較詳細地總結了一個朴素貝葉斯。這里再對非朴素貝葉斯做一個小結,以了結貝葉斯分類。
1、非朴素貝葉斯公式
1.1 高維高斯分布
在此之前,我們同樣先需准備一些數學知識,高維高斯概率分布,或者也叫做聯合高斯概率分布,它有如下公式
注:如果特征屬性是以列向量的形式表示的,那么上式(1-1)應表示為
\[p(\mathbf{x})=\frac{1}{\sqrt{(2\pi)^n|\Sigma|}}\exp\left(-\frac{1}{2}(\mathbf{x}-\boldsymbol{\mu})^T\Sigma^{-1}(\mathbf{x}-\boldsymbol{\mu})\right) \]
上式中,\(\boldsymbol{\mu}=(\mu_1,\mu_2,\cdots,\mu_n)\)表示特征\(\mathbf{x}=(x_1,x_2,\cdots,x_n)\)的均值向量,即有
注:其中\(n\)表示特征的個數,\(m\)表示樣本數。
\(\Sigma\)表示協方差矩陣,\(|\Sigma|\)表示協方差矩陣的行列式,協方差陣可以表示為
其中\(\mathbf{x}_j\)表示第\(j\)個樣本的特征行向量。
1.2 聯合貝葉斯公式
與《貝葉斯之朴素理解》第2小節中的貝葉斯公式類似,可以表達為如下公式
同樣我們可以假設其中的似然概率\(p(\mathbf{x}|c_k)\)服從高斯分布,那么由式(1-1)可得似然概率的表達式為
由於對所有的\(p(c_k|\mathbf{x})\),\(p(\mathbf{x})\)都是一樣的,所以我們只需要對式(1-4)的分母比較大小,因此我們可以推出如下判別式
注:取對數,可以簡化我們的計算,並不影響我們對大小的判斷。更進一步地,我們可以將上式的表達式中的常數項去掉。
最終,我們只需要對式(1-6)進行計算,即可分出類別。
2、非朴素貝葉斯實現
2.1 准備數據
與《貝葉斯之朴素理解》第4小節類似,我們先准備好我們的工作環境:jupyter+python3.6,這是我目前用的環境,如果大家沒有用過jupyter,我建議大家用一下,相信你會愛上它的。關於jupyter的安裝和下載以及使用,我在這里就不說了,聰明的你自會百度或google。其次,我們再准備一下數據集:CIFAR-10圖像數據,我將其放入了我的百度網盤,鏈接: https://pan.baidu.com/s/1yIkiL7xXHsqlXS53gxMkEg 提取碼: wcc4。原始的CIFAR-10圖像是一個用於普世物體識別的數據集,分為airplane、automobile、bird、cat、deer、dog、frog、horse、ship、truck共10類,但是這里為了簡單起見,我用了其中3類。
注:由於在《貝葉斯之朴素理解》一文中詳細地說明了關於數據的讀取,這里就不多說了,直接貼出代碼,相信機智的你也能看懂。
下面代碼為讀取數據(請保證數據集在當前文件路徑下的data文件夾下)
import numpy as np
import pandas as pd
from scipy.io import loadmat
train_data_mat = loadmat("./data/train_data.mat")
test_data_mat = loadmat("./data/test_data.mat")
labels_name_mat = loadmat("./data/labels_name.mat")
# 訓練數據和標簽
train_data = train_data_mat["Data"]
train_data_label = train_data_mat["Label"]
# 測試數據和標簽
test_data = test_data_mat["Data"]
test_data_label = test_data_mat["Label"]
# 標簽的實際名字
label_names = labels_name_mat["label_names"]
# 因為標簽名字有誤,我這里把它手動改一下
label_names[:, 0] = ['automobile', 'bird', 'cat', 'deer', 'dog']
col_name_lst = [0]*3072
for i in range(1, 3073):
col_name_lst[i-1] = "x" + str(i)
# 結構化訓練集數據
train_data = pd.DataFrame(train_data, columns=col_name_lst)
train_data_label = pd.DataFrame(train_data_label, columns=['class_no'])
train_dataFrm = train_data.join(train_data_label)
# 結構化測試集數據
test_data = pd.DataFrame(test_data, columns=col_name_lst)
test_data_label = pd.DataFrame(test_data_label, columns=['class_no'])
test_dataFrm = test_data.join(test_data_label)
# 上面所得到的數據是全部5類的數據,下面只取出前3類數據
train_dataFrm = train_dataFrm[train_dataFrm["class_no"] <= 3]
train_data = train_dataFrm.drop(columns=["class_no"], axis=1)
train_data_label = train_dataFrm["class_no"].copy()
test_dataFrm = test_dataFrm[test_dataFrm["class_no"] <= 3]
test_data = test_dataFrm.drop(columns=["class_no"], axis=1)
test_data_label = test_dataFrm["class_no"].copy()
# 查看取出3類后的基本的數據結構信息
# print(train_data_label.shape)
# print(train_data.shape)
# print(test_data_label.shape)
# print(test_data.shape)
2.2 實現貝葉斯
據式(1-6)實現如下貝葉斯分類器。
計算均值向量和協方差矩陣
from sklearn.decomposition import PCA
# 利用PCA對原始數據進行降維
pca = PCA(n_components=21)
pca.fit(train_data)
train_data_pca = pca.transform(train_data)
test_data_pca = pca.transform(test_data)
train_data_pca = pd.DataFrame(train_data_pca, index=train_dataFrm.index)
test_data_pca = pd.DataFrame(test_data_pca, index=test_dataFrm.index)
# 求出每個類的均值向量和協方差矩陣
train_cls_cov = []# 協方差矩陣
train_cls_cov_inv = []#協方差矩陣的逆
train_cls_cov_det = []#協方差矩陣的行列式
train_cls_mean = []#均值向量
for i in range(0,3):
train_cls_cov.append(np.cov(train_data_pca[train_dataFrm["class_no"]==1+i].T))
train_cls_cov_inv.append(np.linalg.inv(train_cls_cov[i]))
train_cls_cov_det.append(np.linalg.det(train_cls_cov[i]))
train_cls_mean.append(train_data_pca[train_dataFrm["class_no"]==1+i].mean())
注:上面的代碼中利了PCA對數據進行降維,關於PCA的知識,后面有時間再討論。這里之所以要進行降維,有兩原因,一是因為原始數據維度過高,求出它的協方差矩陣后,對其求行列式,行列式會變成0(其實此時不是0,是一個非常非常小的數,計算機無法存放,所以為0),二是因為原始數據的數據內容並不純凈,PCA可以起到一個去除噪聲的作用。
對測試集數據進行預測
for img_index in range(0, test_data.shape[0]):
determine_clf = [0]*3
ftr_data = test_data_pca.iloc[img_index]
for i in range(0, 3):
class_mean = train_cls_mean[i]
class_cov = train_cls_cov[i]
class_inv = train_cls_cov_inv[i]
class_det = train_cls_cov_det[i]
prob_temp = -(np.log(class_det)*0.5+0.5 * \
np.dot(np.dot((ftr_data-class_mean), class_inv), (ftr_data-class_mean).T))
prob_temp = prob_temp + np.log(prior_series[i+1])
determine_clf[i] = prob_temp
# 取出其中最大值的索引,即為我們的預測值
pred_label[img_index] = np.argmax(determine_clf) + 1
accu = sum(pred_label == test_data_label)/len(pred_label)
print("dimn:{0:3}-->accu:{1:.3f}".format(test_data_pca.shape[1], accu))
輸出
dimn: 21-->accu:0.722
可以看到,降到21維后,准確率為72.2%。
