美文网首页
0#03连续型朴素贝叶斯

0#03连续型朴素贝叶斯

作者: dogo_L1L | 来源:发表于2018-08-16 23:49 被阅读0次

0x00 数据准备

以下我我生成的随机数
前两列是向量,最后一列是标签

[[ -2.40190838  -9.46793749   0.        ]
 [  0.48501661  -1.64782819   1.        ]
 [ -1.13857628  -1.52379494   1.        ]
 [  0.03676704  -3.07497058   1.        ]
 [  0.58966412   2.05349804   1.        ]
 [  1.74603537  -2.5598727    1.        ]
 [  0.9932642   -0.48002329   1.        ]
 [ -3.97025533 -10.74409641   0.        ]
 [ -1.21779287 -11.15836353   0.        ]
 [ -0.52577983 -11.34940749   0.        ]
 [  0.52298726  -0.13703454   1.        ]
 [ -1.80888642   1.30322485   1.        ]
 [ -0.45292089  -6.04316334   0.        ]
 [ -2.65890181  -1.12446239   1.        ]
 [  1.54891636   0.74589865   1.        ]
 [ -1.3087977   -7.71897353   0.        ]
 [ -0.47151448 -10.37571491   0.        ]
 [ -0.89524628 -10.96464394   0.        ]
 [ -2.86703029 -10.84498679   0.        ]
 [ -2.5972638   -9.71612662   0.        ]]

在文末尾,会解释数据产生的来源。

0x01 笔算连续型朴素贝叶斯

连续型贝叶斯中每个向量的特征值是一个连续型数字,所以无法计算其概率,我们假设特征值服从高斯分布(正态分布,Gaussian),虽然我们无法计算具体的的概率,但是可以用概率密度替代。
回忆一下,计算正太分布需要哪些东西。

[1] 公式准备

正态分布

[1.1] 计算数学期望(平均值mu)
按标签分离后
标签:0的数学期望为

[-1.67085098, -9.83834141]

标签:1的数学期望为

[ 0.03162864, -0.64453651]

[1.2] 计算标准差(sigma)
标签:0的标准差为

[1.1524603 , 1.62677798]

标签:1的标准差为

[1.37551258, 1.57723297]

[1.3] 返回一个概率密度(gaussian(x,mu,sigma))
就套用上面的公式,计算每个维度的概率密度
所以在这里就和离散的朴素贝叶斯不一样,离散型朴素贝叶斯在能计算出确切的条件概率,然后根据输入去查找,而连续型只能给一个公式,然后根据输入去带入。

[2] 计算先验算概率

统计 y(标签)=0和y(标签)=1的概率

[0.5,0.5]

[3] 预测,利用MAP估计进行评估

还是拿第一个数据来尝试
[ -2.40190838 -9.46793749 0. ]
第1个维度的值为-2.40190838
第2个维度的值为 -9.46793749
标签为 0

利用我们有的数据
先估算标签为0:
对于第1个维度的值为
x = -2.40190838
mu=-1.67085098      数学期望
sigma=1.1524603     标准差
exp(-(x-mu)**2/(2*sigma**2))/(sqrt_pi*sigma) = 0.28307756002538126
对于第2个维度的值为
x = -9.46793749
mu=-9.83834141      数学期望
sigma=1.62677798     标准差
exp(-(x-mu)**2/(2*sigma**2))/(sqrt_pi*sigma)=0.2389593899107375
P(标签为0的估算)=0.2389593899107375*0.28307756002538126*0.5
=0.03382202052054264
同理进行估计标签为1:
对于第1个维度的值为
x = -2.40190838
mu=0.03162864      数学期望
sigma=1.37551258     标准差
np.exp(-(x-mu)**2/(2*sigma**2))/(sqrt_pi*sigma)=0.060641622215243135
对于第2个维度的值为
x = -9.46793749
mu = -0.64453651
sigma = 1.57723297
np.exp(-(x-mu)**2/(2*sigma**2))/(sqrt_pi*sigma)= 4.048620642274784e-08
P(标签为1的估算)=0.060641622215243135*4.048620642274784e-08*0.5
=4.048620642274784e-08
所以最后的结果为标签为0,而且很明显差距很大。
可以说明可行。

0x02 python伪代码

其实这里的伪代码和离散型朴素贝叶斯相似。就不一一过于详细的解释。

[0] 数据导入

[0.1] 文本导入
现在数据导入不需要制作特征值字典,因为数据已经是数值化了,但是有个小问题,数据是文本,我们要转为对应的浮点型。
[0.2] x,y数据分离
但是数据分离还是要滴
x为

In [130]: x
Out[130]: 
array([[ -2.40190838,  -9.46793749],
       [  0.48501661,  -1.64782819],
       [ -1.13857628,  -1.52379494],
       [  0.03676704,  -3.07497058],
       [  0.58966412,   2.05349804],
       [  1.74603537,  -2.5598727 ],
       [  0.9932642 ,  -0.48002329],
       [ -3.97025533, -10.74409641],
       [ -1.21779287, -11.15836353],
       [ -0.52577983, -11.34940749],
       [  0.52298726,  -0.13703454],
       [ -1.80888642,   1.30322485],
       [ -0.45292089,  -6.04316334],
       [ -2.65890181,  -1.12446239],
       [  1.54891636,   0.74589865],
       [ -1.3087977 ,  -7.71897353],
       [ -0.47151448, -10.37571491],
       [ -0.89524628, -10.96464394],
       [ -2.86703029, -10.84498679],
       [ -2.5972638 ,  -9.71612662]])

y为

In [132]: y
Out[132]: 
array([0., 1., 1., 1., 1., 1., 1., 0., 0., 0., 1., 1., 0., 1., 1., 0., 0.,
       0., 0., 0.])

[0.3] 制作字典
对于y(标签)我们还是要制作字典,对其转化。
label_dic

{"第0类":0,"第1类":1}

[0.4] 统计标签(y)的出现频次
为了后面计算先验概率
cat_counter

[10,10]

[0.5] 按标签分类数据
为了计算数学期望(mu)和标准差(sigma),带入公式更加方便
labelled_x

In [136]: labelled_x
Out[136]: 
[array([[ -2.40190838,  -3.97025533,  -1.21779287,  -0.52577983,
          -0.45292089,  -1.3087977 ,  -0.47151448,  -0.89524628,
          -2.86703029,  -2.5972638 ],
        [ -9.46793749, -10.74409641, -11.15836353, -11.34940749,
          -6.04316334,  -7.71897353, -10.37571491, -10.96464394,
         -10.84498679,  -9.71612662]]),
 array([[ 0.48501661, -1.13857628,  0.03676704,  0.58966412,  1.74603537,
          0.9932642 ,  0.52298726, -1.80888642, -2.65890181,  1.54891636],
        [-1.64782819, -1.52379494, -3.07497058,  2.05349804, -2.5598727 ,
         -0.48002329, -0.13703454,  1.30322485, -1.12446239,  0.74589865]])]

第一部数据准备也就算完成了

[1] 计算公式

[1.1] 计算平均值
使用np.mean函数
mu = [np.mean(labelled_x[c][dim])for c in range(n_category)]

            标签为(y=0)             标签为(y=1)
第一个维度[-1.670850984534065, 0.03162864419482627]
第二个维度[-9.838341405638564, -0.6445365102599536]

[1.2] 计算标准差
使用np.std函数
sigma = [np.std(labelled_x[c][dim]) for c in range(n_category)]

            标签为(y=0)             标签为(y=1)
第一个维度[1.1524603038120147, 1.3755125846542104]
第二个维度[1.6267779834169904, 1.5772329705692298]

[1.3] 最后的公式为
gaussian_maximum_likelihood

np.exp(-(x - mu) ** 2 / (2 * sigma ** 2) / (sqrt_pi * sigma))
其中sqrt_pi = (2 *np. pi) ** 0.5

[2] 计算先验算概率

使用上面准备好的cat_counter来计算
p_category

[0.5,0.5]

[3] 预测,利用MAP估计进行评估

给我们一个数据,带入我们

-2.40190838  -9.46793749
我们要带入上面的公式
但是我们想像离散那样随拿随取 就像离散型朴素贝叶斯一样。
那么我们在data中储存函数吧。
        data = [
            gaussian_maximum_likelihood(self._labelled_x, n_category, dim) \
            for dim in range(len(x))
        ]

到这里伪代码写的差不多了。发现连续型与离散型有好多的重复代码,毕竟大家都是贝叶斯家族的。就会有个小想法
我们扔出来一个类吧,让连续型和离散型都继承自那个类。

0x03python的实现

那个被扔出来的基类

先考虑一下那些是重复的

  1. __init__初始化
  2. 计算先验概率的函数
  3. 训练的框架
  4. 预测的算法
    额,这部就是贝叶斯算法的框架吗?
    可能你没懂不过没事
    看代码
class NaiveBayes:
    def __init__(self):
        # 记录训练集的变量
        self._x = None
       
        # 记录训练集结果的变量
        self._y = None

        # 存储实际使用的条件概率的相关信息
        self._data = None
        
        # 决策函数
        self._func = None
        # 记录各个维度特征取值个数的数组
        self._n_possibilities = None
    
        # 记录按类别分开后的输入数据的数组
        self._labelled_x = None
     
        # 记录按类别相关信息的数组,定义有所不同
        self._label_zip = None

        # 第i类数据的个数
        self._cat_counter = None

        # 记录数据条件概率的原始极大似然法
        self._con_counter = None

        # 记录数值话类别时的转换关系
        self._label_dic = None

        # 数值化各个唯独特征时的转换关系
        self._feat_dic = None

    # 为了避免定义大量的property
    def __getitem__(self, item):
        if isinstance(item, str):
            return getattr(self, '_' + item)

    # 留下抽象方法让子类定义
    def feed_data(self, x, y, sample_weight=None):
        pass

    def feed_sample_weight(self, sample_weight=None):
        pass

    # TODO:计算先验概率的函数,lb就是估计中的平滑项
    # lb为1表示拉普拉斯平滑
    def get_prior_probability(self, lb=1):
        return [(_c_num + lb) / (len(self._y) + lb * len(self._cat_counter)) \
                for _c_num in self._cat_counter]

    # TODO:进行训练
    def fit(self, x=None, y=None, sample_weight=None, lb=1):
        if x is not None and y is not None:
            self.feed_data(x, y, sample_weight)

        self._func = self._fit(lb)

    # 重写了
    def _fit(self, lb):
        pass

    @staticmethod
    def _transfer_x(x):
        pass

    # TODO:预测单一样本
    def predict_one(self, x, get_raw_result=False):
        if isinstance(x, np.ndarray):
            x = x.tolist()
        else:
            x = x[:]
        # 相关数值化方法进行数值话
        x = self._transfer_x(x)
        m_arg = 0
        m_probability = 0
        for i in range(len(self._cat_counter)):
            p = self._func(x, i)
            logging.debug("p"+str(i))
            logging.debug(p)
            if p > m_probability:
                m_arg = i
                m_probability = p
        if not get_raw_result:
            return self._label_dic[m_arg]
        return m_probability

    # TODO:多样本预测
    def predict(self, x, get_raw_result=False):
        return np.array([self.predict_one(xx, get_raw_result) for xx in x])

    # TODO:对预测进行评估
    def evaluate(self, x, y):
        y_pred = self.predict(x)
        print("正确率:{:12.6}%".format(100 * np.sum(y_pred == y) / len(y)))

引用大佬的github
接下来就是填空题了

from ml.bys.b_NaiveBayes.Basic import *


class DataUtil:
    # [0] 数据导入
    def get_dataset(path, train_num=None, tar_idx=None, shuffle=True):
        x = []
        # [0.1] 文本导入
        with open(path, "r", encoding="utf-8") as f:
            for sample in f:
                x.append(sample.strip().split(","))
        if shuffle:
            np.random.shuffle(x)
        tar_idx = -1 if tar_idx is None else tar_idx
        # [0.2] x, y数据分离
        y = np.array([xx.pop(tar_idx) for xx in x])
        x = np.array(x)
        if train_num is None:
            return x, y
        return (x[:train_num], y[:train_num]), (x[train_num:], y[train_num:])


sqrt_pi = (2 * np.pi) ** 0.5


# [1] 计算公式
class NBFunctions:

    @staticmethod
    def gaussian_maximum_likelihood(labelled_x, n_category, dim):
        # [1.1] 计算平均值
        mu = [np.mean(labelled_x[c][dim]) for c in range(n_category)]
        # [1.2] 计算标准差
        sigma = [np.std(labelled_x[c][dim]) for c in range(n_category)]

        # [1.3] 最后的公式为
        def func(_c):
            def sub(xx):
                return NBFunctions.gaussian(xx, mu[_c], sigma[_c])

            return sub

        return [func(_c=c) for c in range(n_category)]

    @staticmethod
    # [1.3] 最后的公式为
    def gaussian(x, mu, sigma):
        return np.exp(-(x - mu) ** 2 / (2 * sigma ** 2) / (sqrt_pi * sigma))


class GaussianNB(NaiveBayes):
    def feed_data(self, x, y, sample_weight=None):
        # []注意文本转浮点
        x = np.array([list(map(lambda c: float(c), sample)) for sample in x])
        # [0.3] 制作字典
        labels = list(set(y))
        label_dic = {label: i for i, label in enumerate(labels)}
        y = np.array([label_dic[yy] for yy in y])
        # [0.4] 统计标签(y)的出现频次
        cat_counter = np.bincount(y)
        labels = [y == value for value in range(len(cat_counter))]
        # [0.5] 按标签分类数据
        labelled_x = [x[label].T for label in labels]
        self._x = x.T
        self._y = y
        self._labelled_x = labelled_x
        self._label_zip = labels
        self._cat_counter = cat_counter
        self._label_dic = {i: _l for _l, i in label_dic.items()}
        self.feed_sample_weight(sample_weight)

    def feed_sample_weight(self, sample_weight=None):
        if sample_weight is not None:
            local_weight = sample_weight * len(sample_weight)
            for i, label in enumerate(self._label_zip):
                self._labelled_x[i] *= local_weight[label]

    def _fit(self, lb):
        n_category = len(self._cat_counter)
        # [2] 计算先验算概率
        p_category = self.get_prior_probability(lb)
        data = [
            NBFunctions.gaussian_maximum_likelihood(self._labelled_x, n_category, dim) \
            for dim in range(len(self._x))
        ]
        # data为一个函数,不信可以打印一下
        self._data = data

        def func(input_x, tar_category):
            rs = 1
            for d, xx in enumerate(input_x):
                rs *= data[d][tar_category](xx)
            return rs * p_category[tar_category]

        return func

    @staticmethod
    def _transfer_x(x):
        return x

0x04 数据是怎么来的

对了,还是要解释一下数字是怎么来的,其实这里是为了把连续型朴素贝叶斯的内部原理讲清楚才是这么复杂的.
如果可以调用库就不一样了
在sklearn.datasets中有一个 make_blobs函数可以生成随机的聚类数据

import numpy as np
if __name__ == '__main__':
    # 我们随机生成一些数据吧
    from sklearn.datasets import make_blobs
    import time
    """
    聚类数据生成器
        Parameters
    ----------
    n_samples : int, optional (default=100)
        The total number of points equally divided among clusters.
        待生成的样本的总数
    n_features : int, optional (default=2)
        The number of features for each sample.
        是每个样本的特征数。
    centers : int or array of shape [n_centers, n_features], optional
        (default=3)
        The number of centers to generate, or the fixed center locations.
        表示类别数
    cluster_std : float or sequence of floats, optional (default=1.0)
        The standard deviation of the clusters.
        表示每个类别的方差
    center_box : pair of floats (min, max), optional (default=(-10.0, 10.0))
        The bounding box for each cluster center when centers are
        generated at random.

    shuffle : boolean, optional (default=True)
        Shuffle the samples.

    random_state : int, RandomState instance or None, optional (default=None)
        If int, random_state is the seed used by the random number generator;
        If RandomState instance, random_state is the random number generator;
        If None, the random number generator is the RandomState instance used
        by `np.random`.
    """
    # 100个数据,2个向量,2个标签,随机数种子2(为了能够生成一样的数据),方差1.5
    x, y = make_blobs(20, 2, centers=2, random_state=2, cluster_std=1.5)
    from sklearn.naive_bayes import GaussianNB

    model = GaussianNB()
    model.fit(x, y)
    # 这一步偷偷把数据拿出来
    print(np.concatenate([x, np.array([y]).T], axis=1))

    y_new = model.predict(x)
    print("正确率:{:12.6}%".format(100 * np.sum(y_new == y) / len(y)))

当然啦,这两种方法的正确率为100%.

相关文章

  • 0#03连续型朴素贝叶斯

    0x00 数据准备 以下我我生成的随机数前两列是向量,最后一列是标签 在文末尾,会解释数据产生的来源。 0x01 ...

  • 算法笔记(7)-朴素贝叶斯算法及Python代码实现

    朴素贝叶斯算法有三种类型,分别是贝努利朴素贝叶斯、高斯贝叶斯、多项式朴素贝叶斯。 贝叶斯公式 贝努利朴素贝叶斯 适...

  • 朴素贝叶斯法

    朴素贝叶斯法 朴素贝叶斯法的学习与分类 朴素贝叶斯法的参数估计 朴素贝叶斯实现 高斯朴素贝叶斯实现 使用 skle...

  • 朴素贝叶斯(NBM)之后验概率最大化的含义 | 统计学习方法

    朴素贝叶斯 - 贝叶斯估计Python复现: 舟晓南:朴素贝叶斯(Bayes)模型python复现 - 贝叶斯估计...

  • 朴素贝叶斯算法介绍及优化

    朴素贝叶斯(Naive Bayes) 贝叶斯公式 朴素贝叶斯算法其实原理很简单,要理解朴素贝叶斯算法我们首先得知道...

  • 朴素贝叶斯法(NaiveBayes)

    朴素贝叶斯法(Naive Bayes) 朴素贝叶斯法是基于贝叶斯定力和特征条件独立假设的分类方法。 朴素贝叶斯法实...

  • 朴素贝叶斯算法

    问题 1. 什么是朴素贝叶斯 2. 怎么理解贝叶斯公式和朴素贝叶斯公式 3. 朴素贝叶斯算法流程是怎样...

  • 深度学习知识点汇总-机器学习基础(6)

    2.6 逻辑回归与朴素贝叶斯有什么区别? 逻辑回归是判别模型, 朴素贝叶斯是生成模型。 朴素贝叶斯属于贝叶斯,逻辑...

  • 朴素贝叶斯

    朴素贝叶斯 用处:朴素贝叶斯主要解决的是而分类的问题。 为什么叫朴素贝叶斯: 因为贝叶斯分类只做最原始,最简单的假...

  • sklearn-朴素贝叶斯

    朴素贝叶斯分为三种:高斯朴素贝叶斯、多项式朴素贝叶斯、伯努利朴素贝叶斯。这三种的不同之处在于求条件概率的公式不同。...

网友评论

      本文标题:0#03连续型朴素贝叶斯

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