机器学习算法解析—朴素贝叶斯分类

朴素贝叶斯分类算法是一组监督学习算法,基于贝叶斯公式并假设各个特征独立分布。


公式推导:

给定类别变量 y 以及特征向量( x1,...,xn ),

  1. 联合概率公式:
    P(yx1,...,xn)=P(y|x1,...,xn)P(x1,...,xn)=P(x1,...,xn|y)P(y)

  2. 推导出贝叶斯公式:
    P(y|x1,...,xn) = P(y)P(x1,...,xn|y)P(x1,...,xn)

  3. 朴素独立性假设:
    P(xi|y,x1,...,xi1,xi+1,...,xn)=P(xi|y)

  4. 代入贝叶斯公式:
    P(y|x1,...,xn) = P(y)ni=1P(xi|y)P(x1,...,xn)

  5. 对于给定的特征向量( x1,...,xn ),P( x1,...,xn )为常量,因此上式消去分母,等式变为等比关系式:
    P(y|x1,...,xn)P(y)ni=1P(xi|y)

  6. 类别变量 y 应该取使条件概率P(y | x1,...,xn )最大的 y 值,根据上述等比公式,得出分类规则如下:
    ŷ =argmaxyP(y)ni=1P(xi|y)


算法分解:

  1. 给定训练数据

    import numpy as np
    X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
    Y = np.array([1, 1, 1, 2, 2, 2])
  2. 计算类别先验概率:P(y) = 类别为 y 的样本数/总样本数

    class_prior_y1 = X[Y==1, :].shape[0] / float(X.shape[0])   # 0.5
    class_prior_y2 = X[Y==2, :].shape[0] / float(X.shape[0])   # 0.5
  3. 类条件概率密度函数 P( xi | y)参数估计 :用最大似然(MLE)估计分布的参数(也即最大后验估计MAP),下面分别描述高斯分布、多项式分布、伯努利分布的参数估计步骤:

  4. 高斯分布参数估计(适用于连续特征值)
    P(xi|y) = 12πσ2y,iexp((xiμy,i)22σ2y,i)
    -用最大似然估计分布参数 μy,i σ2y,i
    -计算参数 μy,i :样本中各个类别的各个特征的均值

    mu_y1_x1 = np.mean( X[Y==1,  :1], axis=0)  # -2
    mu_y1_x2 = np.mean( X[Y==1, 1:2], axis=0)  # -1.33333333
    mu_y2_x1 = np.mean( X[Y==2,  :1], axis=0)  # 2
    mu_y2_x2 = np.mean( X[Y==2, 1:2], axis=0)  # 1.33333333

    -计算参数 σ2y,i :样本中各个类别的各个特征的方差(偏移量平方的均值,开方为标准差 σy,i

    sigma_y1_x1 = np.var( X[Y==1,  :1], axis=0) + epsilon  # 0.66666667
    sigma_y1_x2 = np.var( X[Y==1, 1:2], axis=0) + epsilon  # 0.22222222
    sigma_y2_x1 = np.var( X[Y==2,  :1], axis=0) + epsilon  # 0.66666667
    sigma_y2_x2 = np.var( X[Y==2, 1:2], axis=0) + epsilon  # 0.22222222

    -epsilon:附加的极小值,防止任意特征的方差为零导致的计算错误,通常取最大方差(基于全体样本)的一个很小的分数

    epsilon = 1e-9 * np.var(X, axis=0).max()   #4.6666666666666671e-09

    -分类判别:为便于计算,对分类得分公式取自然对数(自然对数为单调递增函数)
    log(P(y))+ni=1(12log(2πσ2y,i)12(xiμy,i)2σ2y,i)
    给定测试数据,分别计算各个分类的得分,取得分最大的类别为判定类别

    NewX = [-0.8, -1]
    log_likelihood_y1 = np.log(class_prior_y1)  \
        - 0.5 * (np.log(2. * np.pi * sigma_y1_x1) + np.log(2. * np.pi * sigma_y1_x2)) \
        - 0.5 * ((NewX[0] - mu_y1_x1) ** 2 / sigma_y1_x1 + (NewX[1] \
        -mu_y1_x2) ** 2 / sigma_y1_x2)  # -2.906253
    
    log_likelihood_y2 = np.log(class_prior_y2)  \
        - 0.5 * (np.log(2. * np.pi * sigma_y2_x1) + np.log(2. * np.pi * sigma_y2_x2)) \
        - 0.5 * ((NewX[0] - mu_y2_x1) ** 2 / sigma_y2_x1 + (NewX[1] \
        - mu_y2_x2) ** 2 / sigma_y2_x2) # -19.70625271
    
    NewL = ('1' if log_likelihood_y1 >= log_likelihood_y1 else '2')    #"1"
  5. 多项式分布参数估计(适用于频次计数类特征值,如文本分类)
    log(P(xi|y))=xilog(θy,i)
    θy,i Ny,i+αNy+αn
    log(θy,i)=log(Ny,i+α)log(Ny+αn)

    -a 为先验平滑因子(>=0),保证样本中没有出现的特征不会出现0值,a=1 称为 Laplace 平滑,a <1 称为Lidstone平滑

    alpha = 1

    -由于特征值代表特征出现的计数,因此所有特征的值应该具有相同的量纲(频次或权重),且为非负数,给定有效的训练数据:

    X = np.array([[10, 1], [9, 2], [8, 3], [1, 10], [2, 9], [3, 8]])   
    Y = np.array([1, 1, 1, 2, 2, 2])

    -n = 特征的数量

    features_num = X.shape[1]  #2

    θy,i 为特征 xi 出现在类别为 y 的样本中的概率(类条件特征概率),用最大似然估计计算(人话:频次占比), log(θy,i) =取对数。

    • Ny,i = 类别为 y 的所有样本中特征 xi 值的和(即加权频次和)
    • Ny = 类别为 y 的所有样本的所有特征值的总和(即加权频次总和)
    log_theta_y1_x1 = np.log(np.sum(X[Y==1, :1]) + alpha) - \      
        np.log(np.sum(X[Y==1, :]) + features_num*alpha)  # -0.223143551314   
    log_theta_y1_x2 = np.log(np.sum(X[Y==1, 1:2]) + alpha) - \     
        np.log(np.sum(X[Y==1, :]) + features_num*alpha)  # -1.60943791243    
    log_theta_y2_x1 = np.log(np.sum(X[Y==2, :1]) + alpha) - \      
        np.log(np.sum(X[Y==2, :]) + features_num*alpha)  # -1.60943791243
    log_theta_y2_x2 = np.log(np.sum(X[Y==2, 1:2]) + alpha) - \     
        np.log(np.sum(X[Y==2, :]) + features_num*alpha)  # -0.223143551314   

    -类别先验概率P(y) =类别为 y 的样本数/总样本数,对数形式=分子的对数-分母的对数:

    class_log_prior_1 = np.log(X[Y==1, :].shape[0]) - np.log(X.shape[0])  # -0.69314718056
    class_log_prior_2 = np.log(X[Y==2, :].shape[0]) - np.log(X.shape[0])  # -0.69314718056

    -分类判别:分类得分公式取自然对数,得到线性分类器的形式(截距+参数矢量)
    log(P(y))+ni=1(xilog(θy,i))

    log_pro_y1 = class_log_prior_1 + \
        NewX[0] * log_theta_y1_x1 + NewX[1] * log_theta_y1_x2 # -6.8603222257475043
    log_pro_y2 = class_log_prior_2 + \
        NewX[0] * log_theta_y2_x1 + NewX[1] * log_theta_y2_x2 # -11.019205309107175
    NewL = ("1" if log_pro_y1 > log_pro_y2 else2") #  “1”
  6. 伯努利分布参数估计(适用于布尔特征值,即:表示是否出现某特征)
    log(P(xi|y))=xilog(θy,i)+(1xi)log(1θy,i)
    θy,i Ny,i+αNy+2α
    log(θy,i)=log(Ny,i+α)log(Ny+2α)
    log(1θy,i)=log(1exp(log(θy,i))

    -给定有效的训练数据、以及平滑因子a(见上节说明):

    X = np.array([[1, 0], [1, 0], [1, 0], [0, 1], [0, 1], [1, 1]])
    Y = np.array([1, 1, 1, 2, 2, 2])
    alpha = 1 #Laplace

    log(P(xi|y)) =类条件特征概率对数形式,与上述多项式密度函数情形主要的不同是对值为0的特征做了负向的惩罚(即:加号后面部分)

    θy,i 为特征 xi 出现在类别为 y 的样本中的概率,用最大似然估计计算, log(θy,i) =取对数。

    • Ny,i = 类别为 y 的所有样本中特征 xi =1的样本数量(即特征值总和)
    • Ny = 类别为 y 的所有样本的数量
    log_theta_y1_x1 = np.log(np.sum(X[Y==1,   :1]) + alpha) \
         - np.log(X[Y==1, :].shape[0] + 2*alpha)  # -0.223143551314
    log_theta_y1_x2 = np.log(np.sum(X[Y==1, 1:2]) + alpha) \
         - np.log(X[Y==1, :].shape[0] + 2*alpha)  # -1.60943791243
    log_theta_y2_x1 = np.log(np.sum(X[Y==2,   :0]) + alpha) \
         - np.log(X[Y==2, :].shape[0] + 2*alpha)  # -0.916290731874
    log_theta_y2_x2 = np.log(np.sum(X[Y==2, 1:2]) + alpha) \
         - np.log(X[Y==2, :].shape[0] + 2*alpha)  # -0.223143551314

    -分类先验概率P(y) =类别为 y 的样本数/总样本数,对数形式=分子的对数-分母的对数:

    class_log_prior_1 = np.log(X[Y==1, :].shape[0]) - np.log(X.shape[0])  #  -0.69314718056
    class_log_prior_2 = np.log(X[Y==2, :].shape[0]) - np.log(X.shape[0])  # -0.69314718056

    -分类判别公式-对数形式:
    log(P(y)ni=1P(xi|y))
    =log(P(y))+ni=1(log(P(xi|y)))
    =log(P(y))+ni=1(xilog(θy,i)+(1xi)(log(1θy,i)))

    log_pro_y1 = class_log_prior_1 + \
           NewX[0] * log_theta_y1_x1 + (1-NewX[0]) * np.log(1-np.exp(log_theta_y1_x1)) + \
           NewX[1] * log_theta_y1_x2 + (1-NewX[1]) * np.log(1-np.exp(log_theta_y1_x2))  # -2.5257286443082556
    log_pro_y2 = class_log_prior_2 + \
           NewX[0] * log_theta_y2_x1 + (1-NewX[0]) * np.log(1-np.exp(log_theta_y2_x1)) + \
           NewX[1] * log_theta_y2_x2 + (1-NewX[1]) * np.log(1-np.exp(log_theta_y2_x2))  # -2.8134107167600364
    NewL = ("1" if log_pro_y1 > log_pro_y2 else "2")  # “1”
  7. 概率估计:将[分类得分]归一化计算出各分类的条件概率(实际上NB模型估计的概率并不精确),由于分类得分为条件概率的对数量,因此先对分类得分做指数运算,然后计算占比:
    P(yi) = exp(yi)cn=1exp(yn)
    =exp(yilog(cn=1exp(yn)))
    =exp(yimax(y)log(cn=1exp(ynmax(y)))

    对分母做exp-log变换后将exp提到最外层,从而将除法化为减法运算(分母部分变为log-sum-exp公式),避免了小数乘除的溢出问题。对分类得分做简单的线性规范化(统一减去最大值)可以减小计算的成本,加快计算的速度。

    import math
    
    log_likelihood_max = max(log_likelihood_y1,log_likelihood_y2)   # -2.906253,-19.70625271
    log_likelihood_acc = math.log(math.exp(log_likelihood_y1 - log_likelihood_max) +  \
                    math.exp(log_likelihood_y2 - log_likelihood_max))
    
    prob_y1 = math.exp(log_likelihood_y1 - log_likelihood_max - log_likelihood_acc)    # 0.9999999494346746
    prob_y2 = math.exp(log_likelihood_y2 - log_likelihood_max - log_likelihood_acc)  # 0.0000000505653254

补充说明:

  1. 只需要少量的训练样本来估计模型参数

  2. 在文本分类及垃圾过滤方面表现良好

  3. 相比其他复杂的分类算法速度极快,得益于特征独立性假设,使得类条件特征分布的估计都是一维的

  4. 相对于好的分类效果,其概率估计值并不准确,算是差的Estimator(概率估计算法)

  5. 可以设置样本的权重参数、可以指定类别先验概率(不用从样本中学习)或采用均分概率

  6. 可以分批次增量的方式训练模型,从而支持大样本、在线学习、以及分布式并行计算,通过将新增批量样本(或并行计算的各个分区样本)的学习结果与现有计算结果合并的方式实现。

  7. 上述示例代码采用python编写,目的是以分解的方式分步讲述算法的执行过程,非面向工程实现。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值