【机器学习实践】高斯朴素贝叶斯分类器

高斯朴素贝叶斯分类器的原理

网上资料很多,主要原理有以下几点:

中心极限定理(模型的训练过程)

认为任何自然界中的现象,观测趋近于无穷次时某测量值满足高斯分布,因此可以通过对特征值序列取得平方差和均值的方法直接得出其表达式参数。
如果一个序列
x_1, x_2, ...x_n
的标准差为\sigma ,均值为\nu
则其分布满足
P=\frac{1}{\sigma\sqrt{2\pi}} e^\frac{-(x-\nu)^2}{2\sigma^2}
在训练模型时即计算出各个特征对应的均值和标准差,存储在模型中

贝叶斯公式(模型的预测过程)

P(feature|label)=\frac{P(label)*P(label|feature)}{P(feature)}

其中feature包含多个特征,朴素贝叶斯的意思就是,认为各个特征相互独立,则
P(feature|label) = P(feature_1|label)*P(feature_2|label)*...
P(feature) = P(feature_1)*P(feature_2)*...
每个概率均由中心极限定理部分给出的高斯分布形式可以得到
P(label)是先验概率,由原始数据易得。

本文主要参考对象:
李小文:高斯朴素贝叶斯的原理及Python实现

python实现

个人主要使用numpy对类进行了实现,如以下代码所示

#naive_bayes.py

from functools import reduce
import numpy as np

class Gaussian_naive_bayes:
    def __init__(self):
        pass

    def fit(self, X:np.array, y:np.array)->None:
        #data preparation
        X_ndarray_2d = np.array(X)
        y_ndarray_1d = np.array(y)
        self.point_num = np.shape(y_ndarray_1d)[0]
        self.KL = y_ndarray_1d.max() + 1    # kinds of label
        self.KF = np.shape(X_ndarray_2d)[1] # kinds of feature
        self.gaussian_param_mat_3d = np.ndarray((self.KL, self.KF, 2), dtype=np.float64) #element: (var, avg)
        self.feature_prob_vec_2d = np.ndarray((self.KF, 2), dtype=np.float64) #element: (var, avg)
        self.label_prob_vec_1d = np.ndarray((self.KL,), dtype=np.float64) #element: prob
        #fill data structures
        #P(i|F) = P(i)*P(F|i)/P(F)
        #P(i)
        for k in range(self.KL):
            is_label_k_tfarray_1d = (k == y_ndarray_1d)
            self.label_prob_vec_1d[k] = np.sum(is_label_k_tfarray_1d)/self.point_num
        #P(F)
        var_feature = np.var(X_ndarray_2d, axis=0)
        avg_feature = np.average(X_ndarray_2d, axis=0)
        self.feature_prob_vec_2d = np.vstack([var_feature, avg_feature]).T
        #P(F|i)
        for kl in range(self.KL):
            data_idx = (y_ndarray_1d == kl)
            var_feature_from_label = np.var(X_ndarray_2d[data_idx], axis=0)
            avg_feature_from_label = np.average(X_ndarray_2d[data_idx], axis=0)
            self.gaussian_param_mat_3d[kl] = np.vstack([var_feature_from_label, avg_feature_from_label]).T

    def print_params(self):
        if self.gaussian_param_mat_3d is None : print("[-] model not trained yet");return
        else:
            print("[+] gaussian parameters for P(label|feature)")
            print(self.gaussian_param_mat_3d)
            print("[+] gaussian parameters for P(feature)")
            print(self.feature_prob_vec_2d)
            print("[+] prior probabilities as P(label)")
            print(self.label_prob_vec_1d)

    def predict_prob(self, X:np.ndarray)->np.ndarray:
        if self.gaussian_param_mat_3d is None : print("[-] model not trained yet");return
        else:
            ret_prob = np.ndarray((self.KL), dtype=np.float64)
            for kl in range(self.KL):
                P_feature_from_label = 1.
                P_label = self.label_prob_vec_1d[kl]
                P_feature = 1.
                for kf in range(self.KF):
                    P_feature_from_label *= np.exp(-(X[kf] - self.gaussian_param_mat_3d[kl, kf, 1])**2/(2*self.gaussian_param_mat_3d[kl, kf, 0]))/(np.sqrt(2*np.pi))/np.sqrt(self.gaussian_param_mat_3d[kl, kf, 0])
                    P_feature *= np.exp(-(X[kf] - self.feature_prob_vec_2d[kf, 1])**2/(2*self.feature_prob_vec_2d[kf, 0]))/np.sqrt(2*np.pi)/np.sqrt(self.feature_prob_vec_2d[kf, 0])
                ret_prob[kl] = P_feature_from_label * P_label * P_feature
        return ret_prob

    def predict_label(self, X:np.ndarray)->int:
        ret_prob = self.predict_prob(X)
        norm_prob = ret_prob/np.linalg.norm(ret_prob, ord=2)
        max_prob_index = norm_prob.argmax()
        print("[+] predict result:", max_prob_index, "probability:", norm_prob[max_prob_index], sep=' ')
        return max_prob_index

并通过实例化对象对分类器进行检验。
我采用了知名的sklearn鸢尾花(iris)数据集,对数据进行加载,放入分类器中训练,得到模型。之后使用模型对训练数据本身做出判断。求出准确率。

#main.py

import naive_bayes
import numpy as np
from sklearn import datasets

data = datasets.load_iris()

print("features", data['feature_names'], sep='\n')
print("data", data['data'], sep='\n')
print("target", data['target'], sep='\n')

yyz_gnb_classify_machine = naive_bayes.Gaussian_naive_bayes()
yyz_gnb_classify_machine.fit(data.data, data.target)
yyz_gnb_classify_machine.print_params()
# print(yyz_gnb_classify_machine.predict_prob([4.3, 3., 1.1, 0.1]))
datasize = np.shape(data.target)[0]
result_list = list()
for i in range(datasize):
    result_list.append(yyz_gnb_classify_machine.predict_label(data.data[i]))

print("[+] rate of correct:", np.sum(data['target'] == np.array(result_list))/datasize)

以下是程序的输出结果,需要注意的是,由于篇幅限制我手动对结果进行了压缩,太长的部分用...进行替代。

features
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
data
[[5.1 3.5 1.4 0.2]
 [4.9 3.  1.4 0.2]
 [4.7 3.2 1.3 0.2]
 [4.6 3.1 1.5 0.2]
 ...
 [6.7 3.3 5.7 2.5]
 [6.7 3.  5.2 2.3]
 [6.3 2.5 5.  1.9]
 [6.5 3.  5.2 2. ]
 [6.2 3.4 5.4 2.3]
 [5.9 3.  5.1 1.8]]
target
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2]
[+] gaussian parameters for P(label|feature)
[[[0.121764 5.006   ]
  [0.140816 3.428   ]
  [0.029556 1.462   ]
  [0.010884 0.246   ]]

 [[0.261104 5.936   ]
  [0.0965   2.77    ]
  [0.2164   4.26    ]
  [0.038324 1.326   ]]

 [[0.396256 6.588   ]
  [0.101924 2.974   ]
  [0.298496 5.552   ]
  [0.073924 2.026   ]]]
[+] gaussian parameters for P(feature)
[[0.68112222 5.84333333]
 [0.18871289 3.05733333]
 [3.09550267 3.758     ]
 [0.57713289 1.19933333]]
[+] prior probabilities as P(label)
[0.33333333 0.33333333 0.33333333]
[+] predict result: 0 probability: 1.0
[+] predict result: 0 probability: 1.0
[+] predict result: 0 probability: 1.0
[+] predict result: 0 probability: 1.0
...
[+] predict result: 2 probability: 0.9996593043019856
[+] predict result: 2 probability: 0.9999999314375727
[+] predict result: 2 probability: 0.9999999999999697
[+] predict result: 2 probability: 0.9982447476614136
[+] rate of correct: 0.96

可以看出,对于训练时使用的原数据而言每次判断准确率都接近1,但是具有判断错误的情况发生,准确率在0.96左右。总体而言满足了机器学习的分类要求。

总结

  1. 高斯朴素贝叶斯分类器是一种有效的分类器。对于已经量化特征的自然数据具有很好的分类作用。
  2. 本次训练过程中,没有采用将训练集和测试集分开的方法,日后关于这个问题我会单独做一次研究。
  3. 本次只研究了高斯朴素贝叶斯分类器,还有多项式朴素贝叶斯分类器、伯努利朴素贝叶斯分类器等,适用于不同的数据集形式(多项式:文本分类)。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容