一、朴素贝叶斯算法简述

朴素贝叶斯(Naive Bayes) 是经典的机器学习算法之一,也是为数不多的基于概率论的分类算法。由于朴素贝叶斯计算联合概率,所以朴素贝叶斯模型属于生成式模型。经典应用案例包括:文本分类、垃圾邮件过滤等。

贝叶斯算法贝叶斯定理为基础,使用概率统计的知识对样本数据集进行分类。由于其有着坚实的数学基础,贝叶斯分类算法的误判率是很低的。贝叶斯方法的特点是结合先验概率和后验概率,即避免了只使用先验概率的主观偏见,也避免了单独使用样本信息的过拟合现象。贝叶斯分类算法在数据集较大的情况下表现出较高的准确率,同时算法本身也比较简单。

1. 贝叶斯定理

1.1 贝叶斯定理的内容

贝叶斯定理(Bayes Theorem),通常称为贝叶斯公式(Bayes Formula)。其内容为:若存在事件 A A A B B B属于同一个样本空间,且 P ( A ) , P ( B ) ≠ 0 P(A), P(B) \not= 0 P(A),P(B)=0,则对于事件 A A A B B B的条件概率,存在:
P ( A ∣ B ) = P ( B ∣ A ) ⋅ P ( A ) P ( B ) P(A|B)=\frac{P(B|A)·P(A)}{P(B)} P(AB)=P(B)P(BA)P(A)

该定理的推导本文不予以展开,仅阐述公式相关概念及其意义。

1.2 先验概率和后验概率

先验概率(Prior Probability) 指根据以往的经验、在实验或采样开始前就可以获得的概率。也就是信息更新前就可获得的概率。
后验概率(Posterior Probability) 指在事件发生后得到的“修正”概率,即通过事件发生后得到的数据计算出的概率。也就是基于新信息更新后的概率。

以下为一个解释这两个概念的例子:

有两个盒子,分别为红色和蓝色。在红色盒子中放着2个苹果和6个橙子,在蓝色盒子中放着3个苹果和1个橙子。现在我们随机从某个盒子里取出一个水果,设随机变量 B B B表示选中的是哪个盒子, B B B的取值为 r e d red red b l u e blue blue,分别代表选中盒子的颜色为红色和蓝色;随机变量 F F F表示挑中的是哪种水果, F F F的取值为 a p p l e apple apple o r a n g e orange orange ,分别代表取出的水果是苹果和橙子。假设 P ( B = r e d ) = 0.6 P(B=red)=0.6 P(B=red)=0.6 P ( B = b l u e ) = 0.4 P(B=blue)=0.4 P(B=blue)=0.4

现已得知某次实验取出的水果是橙子,要求这个橙子是从红色盒子里取出来的概率,即 P ( B = r e d ∣ F = o r a n g e ) P(B=red|F=orange) P(B=redF=orange),由贝叶斯公式,得:
P ( B = r e d ∣ F = o r a n g e ) = P ( F = o r a n g e ∣ B = r e d ) ⋅ P ( B = r e d ) P ( F = o r a n g e ) P(B=red|F=orange)=\frac{P(F=orange|B=red)·P(B=red)}{P(F=orange)} P(B=redF=orange)=P(F=orange)P(F=orangeB=red)P(B=red)
在上面的计算过程中,我们 P ( B = r e d ) P(B=red) P(B=red)或者说 P ( B ) P(B) P(B)称为先验概率,因为我们在得到 F F F a p p l e apple apple或者 o r a n g e orange orange之前,就可以得到 P ( B ) P(B) P(B)的值。同样的,我们 P ( B = r e d ∣ F = o r a n g e ) P(B=red|F=orange) P(B=redF=orange)称为后验概率,因为我们在完整的一次实验之后,也就是得到了 F F F的具体取值之后才能得到这个概率。

将该例子代回贝叶斯定理中,即 P ( A ) P(A) P(A)为先验概率, P ( A ∣ B ) P(A|B) P(AB)为后验概率。

至于例子中的 P ( F = o r a n g e ∣ B = r e d ) 和 P ( F = o r a n g e ) P(F=orange|B=red)和P(F=orange) P(F=orangeB=red)P(F=orange),即贝叶斯定理中的 P ( B ∣ A ) P(B|A) P(BA) P ( B ) P(B) P(B),它们既不是先验概率也不是后验概率。
其中 P ( B ∣ A ) P(B|A) P(BA)似然概率(Likelihood Probability),是指在某个参数或假设成立的前提下,观察到的当前数据的概率。它在贝叶斯公式中用于结合先验概率从而得到联合概率(Joint Probability)(也就是 P ( A B ) P(AB) P(AB)),进而结合随后提到的边缘概率得到后验概率
P ( B ) P(B) P(B)边缘概率(Marginal Probability),即在联合概率分布中,忽略其他随机变量后某个特定变量的概率分布。简单来说,它是通过“边缘化”其他变量得到的概率。它确保了后验概率的总和为1,即对后验概率进行了归一化。

1.3 贝叶斯定理的意义

由先验概率和后验概率的定义可知,贝叶斯定理是二者之间相互转换的过程。

贝叶斯定理将先验概率转化为后验概率,即使用所谓“以往的经验”去计算“新数据更新后的结果”。也就是说,当你不能准确知悉一个事物的本质时,你可以依靠与事物特定本质相关的事件出现的多少去判断其本质属性的概率。用数学语言表达就是:支持某项属性的事件发生得愈多,则该属性成立的可能性就愈大

2. 朴素贝叶斯算法

朴素贝叶斯算法以贝叶斯定理为基础,使用概率统计的知识对样本进行分类。它在贝叶斯算法的基础上进行了相应的简化,即假定给定目标值时属性之间相互条件独立。也就是说没有哪个属性变量对于决策结果来说占有着较大的比重,也没有哪个属性变量对于决策结果占有着较小的比重。虽然这个简化方式在一定程度上降低了贝叶斯分类算法的分类效果,但是在实际的应用场景中,极大地简化了贝叶斯方法的复杂性。这也是其名“朴素”的来源。

朴素贝叶斯算法分类的大致流程如下:

  1. 计算给定数据集的先验概率
  2. 为测试集中给定的属性计算条件概率,即似然概率。对于离散型数据,正常计算其概率即可。对于连续型数据,一般假设同一类别中的属性值服从正态分布,计算当前类别与所在属性下的属性值分布的均值和方差,然后计算当前属性值在这个分布中的概率密度,随后利用正态分布的概率计算公式得出给定值的分布概率。
  3. 计算出结果在各分类取值上的分布概率,选取概率最大者作为分类结果。

二、应用实例

1. 问题引入

给出下列训练数据 ( X , Y ) (X,Y) (X,Y),共计17条数据。 X X X是属性集合(色泽、根蒂、敲声、纹理、脐部、触感、密度、含糖率), Y Y Y是类别标记(是否是好瓜)。现有一新的样本“test1”,我们想要预测它的类别 y y y(是否是好瓜)。

训练数据如下表所示:

编号 色泽 根蒂 敲声 纹理 脐部 触感 密度 含糖率 好瓜
1 青绿 蜷缩 浊响 清晰 凹陷 硬滑 0.697 0.460
2 乌黑 蜷缩 沉闷 清晰 凹陷 硬滑 0.774 0.376
3 乌黑 蜷缩 浊响 清晰 凹陷 硬滑 0.634 0.264
4 青绿 蜷缩 沉闷 清晰 凹陷 硬滑 0.608 0.318
5 浅白 蜷缩 浊响 清晰 凹陷 硬滑 0.556 0.215
6 青绿 稍蜷 浊响 清晰 稍凹 软粘 0.403 0.237
7 乌黑 稍蜷 浊响 稍糊 稍凹 软粘 0.481 0.149
8 乌黑 稍蜷 浊响 清晰 稍凹 硬滑 0.437 0.211
9 乌黑 稍蜷 沉闷 稍糊 稍凹 硬滑 0.666 0.091
10 青绿 硬挺 清脆 清晰 平坦 软粘 0.243 0.267
11 浅白 硬挺 清脆 模糊 平坦 硬滑 0.245 0.057
12 浅白 蜷缩 浊响 模糊 平坦 软粘 0.343 0.099
13 青绿 稍蜷 浊响 稍糊 凹陷 硬滑 0.639 0.161
14 浅白 稍蜷 沉闷 稍糊 凹陷 硬滑 0.657 0.198
15 乌黑 稍蜷 浊响 清晰 稍凹 软粘 0.360 0.370
16 浅白 蜷缩 浊响 模糊 平坦 硬滑 0.593 0.042
17 青绿 蜷缩 沉闷 稍糊 稍凹 硬滑 0.719 0.103

新样本如下表所示:

编号 色泽 根蒂 敲声 纹理 脐部 触感 密度 含糖率 好瓜
test1 青绿 蜷缩 浊响 清晰 凹陷 硬滑 0.697 0.460

2. 代码实现

以下为朴素贝叶斯分类器的代码实现:

import numpy as np

class NaiveBayesClassifier:
    def __init__(self):
        self.class_probs = {}		# 先验概率P(y)的值
        self.feature_probs = {}		# 似然概率P(x|y)的值
        self.classes = None

    def fit(self, X, y):
    	'''训练分类器,计算先验概率和似然概率'''
        self.classes = np.unique(y)
        n_samples = len(y)

		'''计算先验概率'''
        for c in self.classes:
            self.class_probs[c] = np.sum(y == c) / n_samples

		'''计算条件概率,即似然概率'''
        for feature_idx in range(X.shape[1]):
            self.feature_probs[feature_idx] = {}

			# 如果该特征为字符串型,即离散特征
            if isinstance(X[0, feature_idx], str):
                for c in self.classes:
					# 获取当前类别下的所有样本
                    class_mask = (y == c)
					# 获取当前类别下所有特征的取值
                    feature_values = X[class_mask, feature_idx]

					# 计算每个特征取值的概率
                    unique, counts = np.unique(feature_values, return_counts=True)
                    total = np.sum(counts)
                    self.feature_probs[feature_idx][c] = {
                        val: (count + 1) / (total + len(unique))
                        for val, count in zip(unique, counts)
                    }
					# 存储所有可能的取值
                    self.feature_probs[feature_idx]['_values_'] = unique
			# 非字符串型,即连续特征
            else:
                for c in self.classes:
                    class_mask = (y == c)
                    feature_values = X[class_mask, feature_idx].astype(float)
                    mean = np.mean(feature_values)
                    std = np.std(feature_values)
					# 防止标准差为0
                    if std < 1e-6:
                        std = 1e-6
                    self.feature_probs[feature_idx][c] = {'mean': mean, 'std': std}

    def predict(self, X):
    	'''使用分类器对样本进行分类''
        predictions = []
        for sample in X:
            max_prob = -1
            best_class = None

            for c in self.classes:
                prob = self.class_probs[c]

                for feature_idx in range(len(sample)):
                    feature_val = sample[feature_idx]
					# 离散特征
                    if isinstance(feature_val, str):
                        if feature_val in self.feature_probs[feature_idx][c]:
                            prob *= self.feature_probs[feature_idx][c][feature_val]
                        else:
                            unique_values = self.feature_probs[feature_idx]['_values_']
                            prob *= 1 / (np.sum(y == c) + len(unique_values))
					#连续特征
                    else:
                        params = self.feature_probs[feature_idx][c]
                        mean, std = params['mean'], params['std']

                        exponent = np.exp(-((float(feature_val) - mean) ** 2) / (2 * std ** 2))
                        gauss_prob = (1 / (np.sqrt(2 * np.pi) * std)) * exponent
                        prob *= gauss_prob

                if prob > max_prob:
                    max_prob = prob
                    best_class = c

            predictions.append(best_class)
        return np.array(predictions)

调用分类器对给定样本进行预测。

# data为数据集, test1为训练集
X = np.array([row[:-1] for row in data])
y = np.array([row[-1] for row in data])


classifier = NaiveBayesClassifier()
classifier.fit(X, y)

result = classifier.predict(test1)

由分类结果可得,样本“test1”的类别为“好瓜”,即其标签“好瓜”的取值为“是”。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐