朴素贝叶斯(NBM)|机器学习方法(李航)

什么是朴素贝叶斯

今天阴天,天气预报说有60%的概率下雨,有20%的概率打雷,下雨后发生打雷的概率只有10%。根据朴素贝叶斯模型就可以求出,打雷后下雨的概率。

朴素贝叶斯模型是一种简单但非常强大的分类器,在垃圾邮件过滤、疾病诊断、文本分类等方面都显示出了巨大的成效。这里的”朴素“是指:设特征之间是相互独立

朴素贝叶斯模型

我们收到一封邮件出现“中奖”等词,需要判断它是否垃圾邮件。首先根据以往的邮件统计出在垃圾邮件中出现词“中奖”的概率 P ( 中奖 ∣ 垃圾 ) P(中奖|垃圾) P(中奖垃圾),所有邮件中出现垃圾邮件的概论 P ( 垃圾 ) P(垃圾) P(垃圾),“中奖”在所有邮件中出现的概论 P ( 中奖 ) P(中奖) P(中奖),根据朴素贝叶斯模型就求出,邮件中出现“中奖”这个词,是垃圾邮件的概率:
P ( 垃圾 ∣ 中奖 ) = P ( 中奖 , 垃圾 ) P ( 中奖 ) = P ( 中奖 ∣ 垃圾 ) P ( 垃圾 ) P ( 中奖 ) P(垃圾|中奖)=\frac{P(中奖,垃圾)}{P(中奖)}=\frac{P(中奖|垃圾)P(垃圾)}{P(中奖)} P(垃圾中奖)=P(中奖)P(中奖,垃圾)=P(中奖)P(中奖垃圾)P(垃圾)
**联合概率:**指包含多个条件且所有条件同时成立的概率。比如上式中的 P ( 中奖 , 垃圾 ) P(中奖,垃圾) P(中奖,垃圾),包含“中奖”这个词有是垃圾邮件的概率。

**边缘概率:**仅考虑单一事件发生的可能性,而不考虑其它事件发生的可能性。比如上式中的 P ( 中奖 ) P(中奖) P(中奖) P ( 垃圾 ) P(垃圾) P(垃圾)

**条件概率:**在某一事件发生的情况下,另一事件发生的概率。比如上式中的 P ( 中奖 ∣ 垃圾 ) P(中奖|垃圾) P(中奖垃圾)

**先验概率:**根据以往的经验,不用采样就可以得到的概率。比如上式中的 P ( 中奖 ∣ 垃圾 ) P ( 垃圾 ) P ( 中奖 ) P(中奖|垃圾)P(垃圾)P(中奖) P(中奖垃圾)P(垃圾)P(中奖)

**后验概率:**指某件事已经发生,依据这件事发生的事实来推断某一个事件在该事件前发生的概率。比如上式中的 P ( 垃圾 ∣ 中奖 ) P(垃圾|中奖) P(垃圾中奖)

公式的推导

由上面我们得到贝叶斯公式的一般形式
P ( B ∣ A ) = P ( A , B ) P ( A ) = P ( A ∣ B ) P ( B ) P ( A ) P(B|A)=\frac{P(A,B)}{P(A)}=\frac{P(A|B)P(B)}{P(A)} P(BA)=P(A)P(A,B)=P(A)P(AB)P(B)
由公式我们将问题转换为求以下先验概率分布及条件概率分布。

先验概率分布:
P ( Y = c k ) , k = 1 , 2... , k P(Y=c_k),k=1,2...,k P(Y=ck),k=1,2...,k
其中 c k c_k ck代表各个类, K K K为类的总数量,P(Y=c_k)代表在训练集中某一类出现的概率。

条件概率分布:
P ( X = x ∣ Y = c k ) = P ( X ( 1 ) = x ( 1 ) , . . . , X ( n ) = x ( n ) ∣ Y = c k ) , k = 1 , 2 , . . . , K P(X=x|Y=c_k)=P(X^{(1)}=x^{(1)},...,X^{(n)}=x^{(n)}|Y=c_k),k=1,2,...,K P(X=xY=ck)=P(X(1)=x(1),...,X(n)=x(n)Y=ck),k=1,2,...,K
这里指 Y = c k Y=c_k Y=ck的情况下各个特征分别为其某一可取值的概率,上式既有条件也有联合,因此也可称为条件联合概率。

通过条件概率和先验概率(边缘概率)可得到联合概率分布 P ( X , Y ) P(X,Y) P(X,Y)

我们可以对条件概率分布作条件独立性的假设,虽然该假设可能并不符合实际情况,但在该假设下的一些模型的预测准确率足够用以做出决策,因此可以忽略条件独立性假设造成的误差。

所以在事件独立的情况下, P ( A , B ) = P ( A ) ∗ P ( B ) P(A,B)=P(A)*P(B) P(A,B)=P(A)P(B),因此原来的条件概率可转换为多个独立事件的条件概率的连乘:
P ( X = x ∣ Y = c k ) = P ( X ( 1 ) = x ( 1 ) , . . . , X ( n ) = x ( n ) ∣ Y = c k ) = ∏ j = 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) P(X=x|Y=c_k)=P(X^{(1)}=x^{(1)},...,X^{(n)}=x^{(n)}|Y=c_k)=\prod_{j=1}^nP(X^{(j)}=x^{(j)}|Y=c_k) P(X=xY=ck)=P(X(1)=x(1),...,X(n)=x(n)Y=ck)=j=1nP(X(j)=x(j)Y=ck)
在此假设下,后验概率的计算用到贝叶斯定理:
P ( Y = c k ∣ X = x ) = P ( X = x , Y = c k ) P ( X = x ) = P ( X = x ∣ Y = c k ) P ( Y = c k ) P ( X = x ) = P ( Y = c k ) ∏ j − 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) P ( X = x ) P(Y=c_k|X=x)=\frac{P(X=x,Y=c_k)}{P(X=x)}=\frac{P(X=x|Y=c_k)P(Y=c_k)}{P(X=x)}=\frac{P(Y=c_k)\prod_{j-1}^nP(X^{(j)}=x^{(j)}|Y=c_k)}{P(X=x)} P(Y=ckX=x)=P(X=x)P(X=x,Y=ck)=P(X=x)P(X=xY=ck)P(Y=ck)=P(X=x)P(Y=ck)j1nP(X(j)=x(j)Y=ck)
因为x为新输入实例的特征向量,因此对于Y取任意值,分母不变。我们将后验概率最大的类作为x的类输出,在对比大小时,相同的分母可以忽略,所以最后朴素贝叶斯公式可转换为:
y = arg ⁡ max ⁡ P ( Y = c k ) ∏ j − 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) y=\arg \max P(Y=c_k)\prod_{j-1}^n P(X^{(j)}=x^{(j)}|Y=c_k) y=argmaxP(Y=ck)j1nP(X(j)=x(j)Y=ck)
其中, P ( Y = c k ) P(Y=c_k) P(Y=ck)即先验概率(边缘概率), P ( X ( j ) = x ( j ) ∣ Y = c k ) P(X^{(j)}=x^{(j)}|Y=c_k) P(X(j)=x(j)Y=ck)即条件概率,可以依据训练集得出。

上式代表计算在某一特征向量的情况下计算该实例为某一类的概率,将最大的概率所对应的类认为是该点的类。

那么接下来的问题就转变为先验概率和条件概率的计算,这两个概率分布的计算都很简单。

先验概率的计算方法:
P ( Y = c k ) = ∑ j − 1 n I ( y i = c k ) N P(Y=c_k)=\frac{\sum_{j-1}^n I(y_i=c_k)}{N} P(Y=ck)=Nj1nI(yi=ck)
其中 N N N为训练集中实例的总数, I I I为指示函数,表示若括号中条件成立则取1,否则取0。比如训练集一共有100个实例,其中分类为A的实例有50个,则 P ( Y = A ) = 50 / 100 = 1 / 2 P(Y=A)=50/100=1/2 P(Y=A)=50/100=1/2

条件概率的计算方法,原理同上:
P ( X ( j ) = a ( j ) ∣ Y = c k ) = ∑ i − 1 n I ( x i ( j ) = a ( j ) ∣ y i = c k ) ∑ i − 1 n I ( y i = c k ) P(X^{(j)}=a^{(j)}|Y=c_k)=\frac{\sum_{i-1}^n I(x_i^{(j)}=a^{(j)}|y_i=c_k)}{\sum_{i-1}^nI(y_i=c_k)} P(X(j)=a(j)Y=ck)=i1nI(yi=ck)i1nI(xi(j)=a(j)yi=ck)

贝叶斯估计

如果新出现的实例中有一个特征的取值在训练集中从来没有出现过,根据朴素贝叶斯公式中的 ∏ j − 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) \prod_{j-1}^n P(X^{(j)}=x^{(j)}|Y=c_k) j1nP(X(j)=x(j)Y=ck)这一连乘项中的一个值为0,即 ∏ j − 1 n P ( X ( j ) = x ( j ) ∣ Y = c k ) = 0 \prod_{j-1}^n P(X^{(j)}=x^{(j)}|Y=c_k)=0 j1nP(X(j)=x(j)Y=ck)=0 ,最终导致 Y Y Y取任意情况下后验概率皆为0,无法对新输入实例进行分类。

为了解决这一问题引入了贝叶斯估计

贝叶斯估计的先验概率为:
P λ ( Y = c k ) = ∑ j − 1 n I ( y i = c k ) + λ N + K λ P_\lambda(Y=c_k)=\frac{\sum_{j-1}^n I(y_i=c_k)+\lambda}{N+K\lambda} Pλ(Y=ck)=N+Kλj1nI(yi=ck)+λ
条件概率为:
P λ ( X ( j ) = a ( j ) ∣ Y = c k ) = ∑ i − 1 n I ( x i ( j ) = a ( j ) ∣ y i = c k ) + λ ∑ i − 1 n I ( y i = c k ) + S j λ P_\lambda(X^{(j)}=a^{(j)}|Y=c_k)=\frac{\sum_{i-1}^n I(x_i^{(j)}=a^{(j)}|y_i=c_k)+_\lambda}{\sum_{i-1}^nI(y_i=c_k)+S_j\lambda} Pλ(X(j)=a(j)Y=ck)=i1nI(yi=ck)+Sjλi1nI(xi(j)=a(j)yi=ck)+λ
式中 λ ≥ 0 \lambda≥0 λ0,当 λ = 1 \lambda=1 λ=1,则称为拉普拉斯平滑(一般都使 λ = 1 \lambda=1 λ=1)。 K K K为训练集中Y的类的数量, S j S_j Sj为特征 x ( j ) x^{(j)} x(j)可取值的数量。

也就是说,对于先验概率,拉普拉斯平滑在其分子加1,在分母加上类别的数量。对于条件概率,则是在分子加1,在分母加上该特征的可取值的数量。

尽管 λ \lambda λ会让后验概率与未加入 λ \lambda λ之前有所变化,但是在数据量非常大的情况下, λ \lambda λ的加入所引起的后验概率的变化是在可接受范围的,对最终的分类结果影响可以忽略不计。

代码实现

import pandas as pd
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from numpy import var
import math

# 加载鸢尾花数据集
iris = datasets.load_iris()
df = pd.DataFrame(iris.data)
df['label'] = iris.target
df.columns = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'label']
# 加载鸢尾花数据集
x = iris.data
y = iris.target

# 划分训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=100)

class Model:
    def __init__(self):
        self.y= None
        self.classes=None
        self.classes_num=None
        self.parameters =[]

    def _calculate_prior(self, c):
        '''
        先验函数,也就是求先验概率
        利用极大似然估计的结果得到
        '''
        frequency = np.mean(self.y == c)
        return frequency
        # 贝叶斯估计的先验概率
        frequency = (np.sum(self.y == c) + 1) / (len(X) + self.classes_num)

    def _calculate_likelihood(self, mean, var, X):
        """
        似然函数
        """
        # 高斯概率
        eps = 1e-4 # 防止除数为0
        coeff = 1.0 / math.sqrt(2.0 * math.pi * var + eps)
        exponent = math.exp(-(math.pow(X - mean, 2) / (2 * var + eps)))
        return coeff * exponent

    def _calculate_probabilities(self, X):
        posteriors = [] # 后验概率
        for i,c in enumerate(self.classes):
            # p(y)
            posterior = self._calculate_prior(c)
            # p(x | y)
            for feature_value, params in zip(X, self.parameters[i]):
                # 独立性假设
                # P(x1,x2|Y) = P(x1|Y)*P(x2|Y)
                likelihood = self._calculate_likelihood(params["mean"], params["var"], feature_value)
                posterior *= likelihood
            posteriors.append(posterior)
        # 返回具有最大后验概率的类别
        return self.classes[np.argmax(posteriors)]

    def fit(self, train_data, train_label):
        self.y = train_label
        self.classes = np.unique(y) # 类别 
        self.classes_num = len(self.classes)
        # 计算每个特征针对每个类的均值和方差
        for i, c in enumerate(self.classes):
            # 选择类别为c的X
            X_where_c = train_data[np.where(self.y == c)]
            self.parameters.append([])
            # 添加均值与方差
            for col in X_where_c.T:
                parameters = {"mean": col.mean(), "var": col.var()}
                self.parameters[i].append(parameters)
        
    def predict(self, X):
        y_pred = [self._calculate_probabilities(sample) for sample in X]
        return y_pred

    def score(self, X, y):
        y_pred = self.predict(X)
        accuracy = np.sum(y == y_pred, axis=0) / len(y)
        return accuracy
        

model = Model()
model.fit(x_train, y_train)
# 测试数据
print(model.score(x_test, y_test))

  • 25
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值