美文网首页机器学习与数据挖掘机器学习机器学习
小白学习深度学习之朴素贝叶斯分类器

小白学习深度学习之朴素贝叶斯分类器

作者: Vophan | 来源:发表于2019-01-29 21:06 被阅读6次

    继续我们的学习,今天我们记录的是朴素贝叶斯分类器

    什么是朴素贝叶斯

    学过概率论的同学应该知道贝叶斯公式,就是那个全概率公式的逆形式,通过前验概率来求后验概率。


    贝叶斯公式

    我们这里只简单的理解一下贝叶斯公式,因为这些基础内容不是我们这篇文章重点介绍的内容

    相比全概率公式是知道原因求结果概率,
    贝叶斯公式是已知结果来求得原因概率

    这是我们概率老师当时讲的。

    什么是朴素贝叶斯?朴素贝叶斯怎么分类?

    举个例子:

    例子:
    现在我们有5个训练数据(2维特征向量),X = {x1, x2, x3, x4, x5},这五个数据分别属于两个类型Y,Y = {y1, y2}
    现在,我随便给你一个特征向量Xr,问你他属于那个类别?
    实际上,这个问题可以转化为这样的一个问题:

    比较属于那种类别的概率更大
    那么前面我们用到的贝叶斯公式就排上了用场:
    贝叶斯公式
    根据贝叶斯公式以及链式法则,我们得到了这个公式,但是我们需要注意,这仅仅是二维的特征空间,就已经很难算了,如果维度达到三十维呢?

    所以,朴素贝叶斯就被提出来了,其实朴素贝叶斯的naive不应该翻译为朴素,还应该是“天真”(个人看法),为什么这么说呢?我们接着看这个例子:
    因为上面的式子很难算,于是在20世纪50年代有人提出了这个假设(from wiki):

    特征向量的各个特征独立同分布

    这意味着什么?


    朴素贝叶斯

    这样就好算了很多,这个假设会使我们的分类模型损失一些精度,但是却降低了特别多的计算难度。


    接着,我们继续计算这个条件概率:
    首先,我们看分母,这是一个常数,对于比较概率大小没有影响,所以将他忽略(不信可以全概率公式展开)。
    然后,我们需要计算的就是:
    p(y)先验概率以及两个条件概率,可能悟性好的朋友就已经知道了,这个可以通过训练数据中的频率来估计概率,这是没错的。
    但是,这只有在数据样本特别大的时候,才能这样。那我们怎么做呢?

    极大似然估计

    这也是概率论里面的参数估计的方法,首先,我们先来理解一下什么是似然:

    “似然性”与“或然性”或“概率”意思相近,都是指某种事件发生的可能性,但是在统计学中,“似然性”和“概率”(或然性)又有明确的区分。概率用于在已知一些参数的情况下,预测接下来的观测所得到的结果,而似然性则是用于在已知某些观测所得到的结果时,对有关事物的性质的参数进行估计。在这种意义上,似然函数可以理解为条件概率的逆反。在已知某个参数B时,事件A会发生的概率写作:

    似然

    摘自wiki
    我个人感觉:
    这个似然函数的引入有些许tricky的味道,我现在对于他有两种解释:
    1、频率学派认为世界是确定的,有一个本体,这个本体的真值是不变的,我们的目标就是要找到这个真值或真值所在的范围;所以对于一个未知的对象,我们要做的就是将他参数化,于是就将条件概率转化为符合某种分布的联合密度函数,我们要做的就是求解参数theta。
    2、我们就是假设数据符合某种分布,就像中心极限定理说的一样。然后这组数据的联合条件密度分布就一定可以参数化,所以,我们就引入了极大似然的方法来估计参数值。
    以上为个人看法,欢迎大家与我讨论。

    极大后验估计

    前面说到了频率学派,当然不可避免的就得说到贝叶斯学派,极大后验估计(MAP)是贝叶斯学派的参数估计方法。
    在这里就不展开说了,如果想要深入了解,给大家推荐一篇文章,
    这里

    tricky

    我想说一下,我在学习朴素贝叶斯过程中遇到的一些,感觉很tricky的地方。
    我们先了解一下,极大似然估计的过程是什么样的:
    1、首先,假设数据成某一种分布,如果不确定可以根据中心极限定理,假设他为高斯分布。
    2、然后,写出他的似然函数,对于离散量,似然函数就是联合分布函数,对于连续变量,似然函数就是联合密度函数,然后根据不同的分布形式,会有不同的参数。
    3、然后进行NLL优化,其实就是用对数函数将累乘变为累加,然后对参数求偏导等于零,然后就算出了参数值

    但是我们再看一些文章时,包括sklearn的源码,我们都可以发现一个地方,与我们所说的步骤不同,就是,他们都是直接算出了训练数据集的均值以及方差,直接带到了似然函数中,然后就直接比较了,得到了类别。

    开始我十分纳闷,为什么可以这样?这和我们认知的不一样,直到我动手推导了一下这个过程:

    符合高斯分布的似然函数
    NLL优化
    注:这里使用e为底,是为了消去式子里的e方便计算,用2为底一样
    对mu求偏导等于零

    我们可以发现,解得:mu就等于均值

    对sigma求偏导等于零
    很明显,结果就是训练数据集的方差

    正是这样,我们才可以将均值方差直接带进去。
    p(y)也是一个道理。

    代码实现:

    import numpy as np
    import math
    
    
    class NaiveBayes:
        """
        a step to archieve my dream
        """
        def __init__(self, data_dim, data_num, class_num = 2):
            super(NaiveBayes, self).__init__()
            self.data_dim = data_dim
            self.data_num = data_num
            self.class_num = class_num
            self.data = self.data_make(data_dim, data_num)
            self.mean = np.zeros((class_num, data_dim)).reshape((class_num, data_dim))
            self.var = np.zeros((class_num, data_dim)).reshape((class_num, data_dim))
            self.p_y1 = 0
            self.p_y2 = 0
    
        def data_make(self, data_dim, data_num):
            """
            make train data
            :param data_dim: dimension of the feature
            :param data_num: the number of train data
            :return: a list of data whose type is dict
            """
            train_data = []
            for _ in range(data_num):
    
                tmp_data = {}
                tmp_data["data"] = np.random.randn(1, data_dim)
                if _ % 2 == 0:
                    flag = -1
                else:
                    flag = 1
                tmp_data["flag"] = flag
    
                train_data.append(tmp_data)
            return train_data
    
        def fit(self):
            """
            calculate the mean and var of the data
            :return: NULL
            """
            data_list_1 = [data["data"] for data in self.data if data["flag"] == 1]
            data_list_2 = [data["data"] for data in self.data if data["flag"] == -1]
            self.p_y1 = len(data_list_1)/self.data_num
            self.p_y2 = len(data_list_2)/self.data_num
            self.mean[0] = np.mean(data_list_1, axis=0, keepdims=True).reshape(self.data_dim)
            self.mean[1] = np.mean(data_list_2, axis=0, keepdims=True).reshape(self.data_dim)
            self.var[0] = np.std(data_list_1, axis=0, keepdims=True, ddof=1).reshape(self.data_dim)
            self.var[1] = np.std(data_list_1, axis=0, keepdims=True, ddof=1).reshape(self.data_dim)
    
        def predict(self, data):
            """
            to classify the data
            :param data: the data u wanna classify
            :return: int
            """
            p1 = (1/(pow(2*math.pi, 0.5)*self.var[0][0]))*pow(math.e, -(pow((data[0] - self.mean[0][0]), 2)/2*pow(self.var[0][0], 2)))*(1/(pow(2*math.pi, 0.5)*self.var[0][1]))*pow(math.e, -(pow((data[1] - self.mean[0][1]), 2)/2*pow(self.var[0][1], 2)))
            p2 = (1/(pow(2*math.pi, 0.5)*self.var[1][0]))*pow(math.e, -(pow((data[0] - self.mean[1][0]), 2)/2*pow(self.var[1][0], 2)))*(1/(pow(2*math.pi, 0.5)*self.var[1][1]))*pow(math.e, -(pow((data[1] - self.mean[1][1]), 2)/2*pow(self.var[1][1], 2)))
    
    
            print(p1,',',p2)
    
            if p2 > p1:
                print("[*]: the data belongs to the second class")
            else:
                print("[*]: the data belongs to the first class")
    
    
    加油

    相关文章

      网友评论

        本文标题:小白学习深度学习之朴素贝叶斯分类器

        本文链接:https://www.haomeiwen.com/subject/acaxsqtx.html