☰
朴素贝叶斯参数估计:极大似然与贝叶斯估计对比及分类算法实现
2026/10/14 15:33:46 网站建设 项目流程

1. 从“数数”到“算概率”:朴素贝叶斯到底在算什么

很多人第一次翻开《统计学习方法》第四章,看到“极大似然估计”和“贝叶斯估计”这两个词并排出现时,脑子里冒出的第一个念头往往是:这俩到底有什么区别,为什么同一套模型要讲两种参数估计方法?我当初学到这里也卡了很久,后来把公式全部手推了一遍,又用代码从零实现了一次,才真正把这条线捋顺。这篇笔记就围绕朴素贝叶斯的参数估计和学习与分类算法展开,把极大似然估计和贝叶斯估计放在一起对比讲透,适合已经看过朴素贝叶斯基本假设、但被公式推导卡住的读者,也适合想从零手写一遍分类流程的开发者。

先说清楚这篇要解决的核心问题。朴素贝叶斯模型本身结构非常简单:给定输入特征向量,假设各个特征在类别条件下相互独立,然后利用贝叶斯定理反推后验概率,取最大的那个类别作为预测结果。模型形式定下来之后,剩下的关键问题就是——那些概率值从哪里来。先验概率怎么算,条件概率怎么算,这就是参数估计要干的事。极大似然估计的思路是“什么参数最能让当前数据出现”,说白了就是数数、做除法;贝叶斯估计则在此基础上引入一个先验分布,相当于在数数之前先给每个格子放几个“虚拟样本”,避免某些格子为空导致概率为零。

我个人的体会是,不理解这两种估计的区别,朴素贝叶斯就只是一个调包就能跑的黑盒;理解了之后,你才能解释为什么拉普拉斯平滑能解决零概率问题,为什么平滑参数取1还是取0.5会对结果产生影响,以及为什么在某些小样本场景下贝叶斯估计比极大似然估计更稳。下面我会按照“先讲估计方法、再讲学习与分类算法、最后做对比和踩坑总结”的顺序展开,每个部分都配上手推过程和可复现的代码片段。

2. 极大似然估计:最直白的“数数法”

2.1 极大似然估计的直觉与数学表达

极大似然估计的核心思想可以用一句话概括:既然这组数据已经出现了,那就找一组参数,使得这组数据出现的概率最大。放到朴素贝叶斯的场景里,我们要估计的参数有两类:先验概率和条件概率。

先验概率的极大似然估计非常直接,就是类别在训练集中出现的频率:

[ P(Y=c_k) = \frac{\sum_{i=1}^{N} I(y_i = c_k)}{N} ]

其中 N 是样本总数,I 是指示函数,条件成立取1否则取0。翻译成人话就是:某个类别出现了几次,除以总样本数,就是它的先验概率。

条件概率稍微复杂一点,因为要针对每个特征、每个特征取值、每个类别分别计算:

[ P(X^{(j)} = a_{jl} \mid Y = c_k) = \frac{\sum_{i=1}^{N} I(x_i^{(j)} = a_{jl}, y_i = c_k)}{\sum_{i=1}^{N} I(y_i = c_k)} ]

分母是类别 c_k 的样本数,分子是在类别 c_k 中第 j 个特征取值为 a_{jl} 的样本数。说白了就是:在某个类别里面,某个特征取某个值的情况占了多少比例。

我当初记这个公式的时候,用了一个很土但很有效的办法:把分母理解成“这个类别一共有多少人”,分子理解成“这个类别里符合条件的有多少人”,两者一除就是比例。这样记不容易把分子分母搞反。

2.2 手推一个极简例子

光看公式容易飘,拿一个具体的小数据集来推一遍最踏实。假设我们有一个关于天气和是否外出的数据集,特征有两个:天气(晴/阴/雨)、温度(热/凉),类别是“外出”和“不外出”。训练集如下:

编号天气温度是否外出
1晴热外出
2晴凉外出
3阴热不外出
4雨凉不外出
5雨热不外出

先算先验概率。总样本5个,外出2个,不外出3个:

  • P(外出) = 2/5 = 0.4
  • P(不外出) = 3/5 = 0.6

再算条件概率。以“天气=晴”为例:

  • P(天气=晴 | 外出) = 2/2 = 1.0
  • P(天气=晴 | 不外出) = 0/3 = 0.0

注意这里出现了一个0。这就是极大似然估计最典型的隐患——零概率问题。如果测试样本的天气是晴,那么在“不外出”这个类别下,天气为晴的条件概率是0,整个连乘结果就是0,模型会直接排除“不外出”这个类别,哪怕其他特征都强烈指向“不外出”。这个问题在后面讲贝叶斯估计时会专门解决。

继续算其他条件概率:

  • P(天气=阴 | 外出) = 0/2 = 0.0
  • P(天气=阴 | 不外出) = 1/3 ≈ 0.333
  • P(天气=雨 | 外出) = 0/2 = 0.0
  • P(天气=雨 | 不外出) = 2/3 ≈ 0.667
  • P(温度=热 | 外出) = 1/2 = 0.5
  • P(温度=热 | 不外出) = 2/3 ≈ 0.667
  • P(温度=凉 | 外出) = 1/2 = 0.5
  • P(温度=凉 | 不外出) = 1/3 ≈ 0.333

可以看到,由于样本量太小,很多条件概率直接是0。这不是代码写错了,而是极大似然估计在小样本下的固有缺陷。

2.3 极大似然估计的代码实现

把上面的过程写成代码,其实就是一个分组计数加除法的过程。下面这段代码不依赖任何机器学习库,纯手写,方便理解每一步在干什么:

import numpy as np from collections import defaultdict def train_mle(X, y): N = len(y) classes = np.unique(y) # 先验概率 prior = {} for c in classes: prior[c] = np.sum(y == c) / N # 条件概率 cond = defaultdict(dict) for c in classes: mask = (y == c) X_c = X[mask] for j in range(X.shape[1]): values, counts = np.unique(X_c[:, j], return_counts=True) for v, cnt in zip(values, counts): cond[c][(j, v)] = cnt / X_c.shape[0] return prior, cond def predict_mle(x, prior, cond, classes, n_features): best_c, best_p = None, -1 for c in classes: p = prior[c] for j in range(n_features): p *= cond[c].get((j, x[j]), 0) if p > best_p: best_p, best_c = p, c return best_c

这段代码里有一个关键点:cond[c].get((j, x[j]), 0)这一行,当某个条件概率在训练集中没有出现过时,直接返回0。这就是零概率问题的代码体现。实际跑的时候,如果测试样本包含训练集中未出现的特征取值,预测结果会非常不稳定。

注意:极大似然估计在样本量充足时是一致估计,会收敛到真实参数;但在小样本或特征取值稀疏的场景下,零概率问题会严重影响分类效果。这不是模型结构的问题,而是估计方法的问题。

3. 学习与分类算法:把估计出来的概率用起来

3.1 朴素贝叶斯算法的完整流程

参数估计只是第一步,真正做分类还需要一套完整的学习与分类流程。按照《统计学习方法》里的描述,朴素贝叶斯算法分为学习和分类两个阶段。

学习阶段的任务是估计先验概率和条件概率。输入是训练数据集,输出是这两组概率值。具体步骤是:先计算先验概率,再对每个特征、每个特征取值、每个类别计算条件概率。这一步用极大似然估计或者贝叶斯估计都可以,区别只在于要不要加平滑项。

分类阶段的任务是对于一个新的输入样本,计算它属于每个类别的后验概率,取最大的那个作为预测结果。后验概率的计算公式是:

[ P(Y=c_k \mid X=x) \propto P(Y=c_k) \prod_{j=1}^{n} P(X^{(j)}=x^{(j)} \mid Y=c_k) ]

因为分母 P(X=x) 对所有类别都一样,所以比较的时候只需要算分子部分。这也是为什么朴素贝叶斯分类时经常忽略分母,直接比较分子大小。

这里有一个实操中容易忽略的点:连乘容易下溢。当特征维度很高时,很多小于1的概率值连乘,结果会小到浮点数无法表示,变成0。解决办法是取对数,把连乘变成连加:

[ \log P(Y=c_k) + \sum_{j=1}^{n} \log P(X^{(j)}=x^{(j)} \mid Y=c_k) ]

取对数之后,比较大小关系不变,但数值稳定性大大提高。这个技巧在实际写代码时几乎是必须的,尤其是特征数超过几十个的时候。

3.2 后验概率最大化的等价理解

很多人学到分类阶段会有一个疑问:为什么取后验概率最大的类别就是最优的?这背后其实是期望风险最小化的逻辑。如果我们定义损失函数为0-1损失,即分类正确损失为0,分类错误损失为1,那么期望风险就是分类错误的概率。要让期望风险最小,就要让分类正确的概率最大,也就是后验概率最大。所以朴素贝叶斯分类器本质上是一个贝叶斯决策器,在0-1损失下是最优的。

这个理解很重要,因为它解释了为什么朴素贝叶斯虽然有一个很强的“特征条件独立”假设,但在很多实际任务上仍然表现不错。即使假设不成立,只要后验概率最大的类别判断没被带偏,分类结果就是对的。当然,如果假设严重违背,后验概率的估计会有偏差,分类效果就会下降。

3.3 分类算法的代码实现与对数技巧

把学习阶段和分类阶段串起来,加上对数技巧,代码大概长这样:

def train_nb(X, y, smooth=0): N = len(y) classes = np.unique(y) n_features = X.shape[1] prior = {} cond = defaultdict(lambda: defaultdict(float)) for c in classes: mask = (y == c) X_c = X[mask] prior[c] = np.log((np.sum(mask) + smooth) / (N + smooth * len(classes))) for j in range(n_features): values, counts = np.unique(X_c[:, j], return_counts=True) n_values = len(np.unique(X[:, j])) for v, cnt in zip(values, counts): cond[c][(j, v)] = np.log((cnt + smooth) / (X_c.shape[0] + smooth * n_values)) return prior, cond, classes, n_features def predict_nb(x, prior, cond, classes, n_features): best_c, best_score = None, -np.inf for c in classes: score = prior[c] for j in range(n_features): score += cond[c].get((j, x[j]), -np.inf) if score > best_score: best_score, best_c = score, c return best_c

这段代码里smooth参数就是平滑项。当smooth=0时等价于极大似然估计,当smooth=1时就是拉普拉斯平滑,也就是贝叶斯估计的一个特例。把两种估计方法统一到一个参数上,是我个人觉得最优雅的实现方式。

实操心得:取对数之后,原本的连乘变成连加,不仅避免了数值下溢,还让代码更容易调试。你可以单独打印每个特征的对数概率,看看哪个特征对最终分类贡献最大。这个技巧在排查分类错误时非常有用。

4. 贝叶斯估计:给“数数法”加一个先验

4.1 贝叶斯估计的核心思想

极大似然估计的问题在于,它完全依赖当前数据,数据里没出现的情况就被认为概率为零。贝叶斯估计的思路是:在观察数据之前,先给每个可能的参数值一个先验分布,然后根据数据更新这个分布,得到后验分布,再用后验分布的期望作为参数估计值。

具体到朴素贝叶斯,贝叶斯估计给条件概率加上一个正数 λ(通常取1),公式变成:

[ P_\lambda(X^{(j)}=a_{jl} \mid Y=c_k) = \frac{\sum_{i=1}^{N} I(x_i^{(j)}=a_{jl}, y_i=c_k) + \lambda}{\sum_{i=1}^{N} I(y_i=c_k) + S_j \lambda} ]

其中 S_j 是第 j 个特征可能取值的个数。先验概率也类似处理:

[ P_\lambda(Y=c_k) = \frac{\sum_{i=1}^{N} I(y_i=c_k) + \lambda}{N + K\lambda} ]

K 是类别总数。当 λ=1 时,这就是拉普拉斯平滑;当 λ=0 时,退化为极大似然估计。λ 的作用相当于在每个计数格子里面预先放了 λ 个“虚拟样本”,这样即使某个格子实际计数为0,加上 λ 之后也不会是0。

4.2 为什么加 λ 能解决问题

从贝叶斯的角度看,加 λ 等价于假设参数服从一个均匀的狄利克雷先验分布。狄利克雷分布是多项分布的共轭先验,所以后验分布仍然是狄利克雷分布,计算起来很方便。λ 越大,先验的影响越强,估计值越平滑;λ 越小,数据的影响越强,估计值越接近极大似然估计。

从实际效果看,加 λ 解决了两个问题。第一是零概率问题,某个条件概率不会因为训练集中没出现就变成0。第二是过拟合问题,当样本量很小时,极大似然估计会过度拟合训练数据,加 λ 相当于做了正则化,让估计值更保守。

我用一个具体数字来说明。还是前面那个天气例子,P(天气=晴 | 不外出) 在极大似然估计下是 0/3 = 0。如果取 λ=1,天气特征有3个取值(晴/阴/雨),那么:

P(天气=晴 | 不外出) = (0 + 1) / (3 + 3×1) = 1/6 ≈ 0.167

不再是0了。虽然0.167也不大,但至少不会让整个连乘结果归零。这就是平滑的作用。

4.3 λ 的取值选择与影响

λ 取多少合适,是一个需要根据具体问题调整的超参数。λ=1 是最常用的默认值,也就是拉普拉斯平滑。但在某些场景下,λ 取小于1的值效果更好,比如0.5、0.1甚至0.01。λ 越小,估计值越接近极大似然估计;λ 越大,估计值越平滑。

我做过一组对比实验,在文本分类任务上,λ 从0.01到10分别跑了一遍,发现λ=0.1到1之间效果比较稳定,λ太大(比如10)会导致所有条件概率都被拉平,模型区分能力下降;λ太小(比如0.001)又接近极大似然估计,零概率问题依然存在。所以实际使用时,建议在验证集上试几个值,不要盲目用默认值。

λ 取值特点适用场景
0极大似然估计,无平滑样本量极大、特征取值覆盖充分
0.01~0.1弱平滑样本量较大,但仍有稀疏取值
1拉普拉斯平滑,标准做法通用场景,小样本
>1强平滑样本量极小,或特征取值极多

注意:λ 的取值会影响先验概率和条件概率的估计,进而影响分类结果。如果发现模型在某些类别上总是预测错误,可以检查一下是不是 λ 太大导致先验概率被过度平滑了。

5. 两种估计方法的对比与选择

5.1 从公式到直觉的对比

把极大似然估计和贝叶斯估计放在一起看,区别其实就一句话:贝叶斯估计在分子和分母上各加了一个数。分子加 λ,分母加 S_j λ 或 Kλ。这个加法带来的效果是,所有概率值都被“拉”离0和1,变得更温和。

从偏差-方差权衡的角度看,极大似然估计是低偏差、高方差;贝叶斯估计通过引入先验,增加了偏差但降低了方差。在样本量充足时,极大似然估计的偏差小,效果更好;在样本量不足时,贝叶斯估计的方差小,更稳定。这跟机器学习里正则化的逻辑是一模一样的。

我用一个表格来总结两种方法的核心差异:

对比维度极大似然估计贝叶斯估计
核心思想找让数据出现概率最大的参数引入先验,用后验期望估计参数
零概率问题存在,未出现的取值概率为0不存在,加λ后至少为正值
小样本表现容易过拟合,不稳定更稳定,泛化更好
大样本表现一致估计,接近真实参数先验影响减弱,趋近极大似然
超参数无λ(平滑参数)
计算复杂度略低略高,但差异可忽略

5.2 实际任务中怎么选

选择哪种估计方法,主要看样本量和特征稀疏程度。如果训练集很大,特征取值覆盖充分,极大似然估计就够用了,没必要加平滑。如果训练集较小,或者特征取值很多(比如文本分类里的词表可能几万个),那就必须用贝叶斯估计,否则大量条件概率为0,模型基本没法用。

我个人的经验是,默认用拉普拉斯平滑(λ=1),然后在验证集上微调λ。这样既避免了零概率问题,又不会引入太多先验偏差。只有在样本量确实非常大、且确认没有稀疏问题时,才会考虑用极大似然估计。

还有一个细节:先验概率要不要平滑。如果类别分布本身很不均衡,比如正样本占1%,负样本占99%,那么先验概率的平滑要谨慎。λ 太大会把先验概率拉向均匀分布,反而丢失了类别不均衡的信息。这种情况下,可以先不平滑先验,只平滑条件概率。

5.3 一个容易踩的坑:平滑与特征取值数的关系

贝叶斯估计的分母是 S_j λ,其中 S_j 是第 j 个特征的可能取值数。这个 S_j 必须是训练集中出现过的取值数,还是理论上所有可能的取值数?这是一个很容易搞混的地方。

举个例子,某个特征在训练集中只出现了3个取值,但理论上可能有5个取值。如果按训练集算,S_j=3;如果按理论算,S_j=5。两种算法得到的分母不同,平滑后的概率也不同。实践中,通常按训练集中出现过的取值数来算,因为没出现过的取值在预测时也无法处理。但如果你明确知道特征的完整取值空间,按理论值算会更合理。

这个细节在教科书里往往一笔带过,但实际写代码时如果搞错了,平滑效果会打折扣。我的建议是,在数据预处理阶段就把每个特征的取值空间确定下来,训练和预测保持一致。

6. 常见问题与排查技巧实录

6.1 零概率问题排查

零概率问题是朴素贝叶斯最常见的坑。表现是:模型在测试集上准确率很低,或者某些类别的预测结果完全不对。排查方法是打印出每个条件概率,看看有没有0值。如果有,确认是不是训练集中该特征取值在该类别下没有出现。如果是,加平滑就能解决。

但有一种情况加平滑也解决不了:测试样本出现了训练集中从未出现过的特征取值。比如训练集里天气只有晴/阴/雨,测试集里出现了“雪”。这时候无论怎么平滑,模型都无法处理这个取值。解决办法是在数据预处理阶段做统一映射,把未见过的取值归到“其他”类,或者直接忽略该特征。

6.2 数值下溢问题排查

数值下溢的表现是:所有类别的后验概率都变成0,模型无法区分。排查方法是检查特征维度,如果超过20个,基本可以确定是下溢。解决办法就是取对数,把连乘变成连加。这个前面已经讲过,这里再强调一次:只要特征数超过10个,就建议直接取对数,不要等到出问题了再改。

6.3 类别不均衡问题排查

类别不均衡的表现是:模型总是预测多数类,少数类的召回率很低。排查方法是看混淆矩阵,如果多数类的样本几乎全部预测正确,少数类几乎全部预测错误,那就是不均衡问题。解决办法有两个:一是调整先验概率,给少数类更高的先验;二是对少数类样本过采样,或者对多数类样本欠采样。朴素贝叶斯本身对不均衡不算特别敏感,但如果先验概率严重偏斜,还是会影响结果。

6.4 特征独立性假设违背问题排查

朴素贝叶斯假设特征之间相互独立,但实际数据中特征往往有相关性。如果相关性很强,模型效果会下降。排查方法是计算特征之间的相关系数矩阵,如果有很多高相关性的特征对,说明独立性假设可能被违背。解决办法是做特征选择,去掉冗余特征,或者用更复杂的模型(比如贝叶斯网络)来建模特征之间的依赖关系。

不过在实际应用中,即使独立性假设不成立,朴素贝叶斯也经常能给出不错的分类结果。这是因为分类任务只关心后验概率最大的类别,不要求概率估计完全准确。所以除非效果确实很差,否则不必过于纠结独立性假设。

6.5 常见问题速查表

问题现象可能原因排查方法解决办法
准确率极低零概率问题打印条件概率,检查0值加平滑λ
所有概率为0数值下溢检查特征维度取对数
总是预测多数类类别不均衡看混淆矩阵调整先验或重采样
测试集无法处理未见特征取值检查取值空间统一映射到“其他”
效果不如预期特征相关性高算相关系数矩阵特征选择

实操心得:排查朴素贝叶斯问题时,第一步永远是打印概率值。先验概率、条件概率、后验概率,全部打印出来看一遍,大部分问题都能定位到。不要一上来就调参,先确认概率计算本身有没有问题。

7. 从零实现一个完整的朴素贝叶斯分类器

7.1 数据准备与预处理

把前面的代码片段整合起来,做一个完整的、可运行的朴素贝叶斯分类器。数据用sklearn自带的鸢尾花数据集,方便复现。虽然鸢尾花是连续特征,但我们可以做离散化处理,把它变成离散特征,更适合朴素贝叶斯的离散模型。

from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import KBinsDiscretizer import numpy as np # 加载数据 iris = load_iris() X, y = iris.data, iris.target # 离散化:每个特征分成3个区间 discretizer = KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='uniform') X_discrete = discretizer.fit_transform(X).astype(int) # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split( X_discrete, y, test_size=0.3, random_state=42 )

离散化这一步很关键。朴素贝叶斯的离散模型要求特征是离散的,连续特征要么做离散化,要么假设服从某个分布(比如高斯朴素贝叶斯)。这里用等宽离散化,把每个特征分成3个区间,简单直接。

7.2 训练与预测的完整代码

class NaiveBayes: def __init__(self, smooth=1.0): self.smooth = smooth self.prior = {} self.cond = {} self.classes = None self.n_features = None self.feature_values = None def fit(self, X, y): self.classes = np.unique(y) self.n_features = X.shape[1] N = len(y) K = len(self.classes) # 记录每个特征的取值空间 self.feature_values = [np.unique(X[:, j]) for j in range(self.n_features)] # 先验概率(对数) for c in self.classes: count = np.sum(y == c) self.prior[c] = np.log((count + self.smooth) / (N + self.smooth * K)) # 条件概率(对数) for c in self.classes: mask = (y == c) X_c = X[mask] n_c = X_c.shape[0] self.cond[c] = {} for j in range(self.n_features): S_j = len(self.feature_values[j]) for v in self.feature_values[j]: count = np.sum(X_c[:, j] == v) self.cond[c][(j, v)] = np.log( (count + self.smooth) / (n_c + self.smooth * S_j) ) def predict(self, X): predictions = [] for x in X: best_c, best_score = None, -np.inf for c in self.classes: score = self.prior[c] for j in range(self.n_features): score += self.cond[c].get((j, x[j]), -np.inf) if score > best_score: best_score, best_c = score, c predictions.append(best_c) return np.array(predictions) def score(self, X, y): pred = self.predict(X) return np.mean(pred == y)

这段代码把前面讲的所有要点都整合进去了:对数概率、平滑参数、特征取值空间记录、未见取值的处理。smooth=1.0就是拉普拉斯平滑,smooth=0就是极大似然估计。

7.3 对比两种估计方法的效果

# 极大似然估计 nb_mle = NaiveBayes(smooth=0) nb_mle.fit(X_train, y_train) acc_mle = nb_mle.score(X_test, y_test) # 贝叶斯估计(拉普拉斯平滑) nb_bayes = NaiveBayes(smooth=1.0) nb_bayes.fit(X_train, y_train) acc_bayes = nb_bayes.score(X_test, y_test) print(f"MLE 准确率: {acc_mle:.4f}") print(f"贝叶斯估计准确率: {acc_bayes:.4f}")

跑出来的结果,贝叶斯估计通常会比极大似然估计略高或者持平。在这个数据集上,因为样本量还算充足,两者差距不会特别大。但如果把训练集缩小到每类只有5个样本,差距就会明显拉开。你可以自己试一下,把train_test_split的test_size调大,或者手动抽样减少训练样本,观察两种方法的表现差异。

注意:smooth=0时,如果某个条件概率在训练集中没出现,对数概率会变成负无穷,预测时该类别直接被排除。这就是零概率问题在代码层面的表现。实际使用中不建议把smooth设为0,除非你确认数据没有稀疏问题。

8. 参数估计背后的贝叶斯哲学

8.1 频率派与贝叶斯派的视角差异

极大似然估计和贝叶斯估计的区别,往深了说,是频率派和贝叶斯派两种统计观念的差异。频率派认为参数是固定的未知常数,数据是随机的,所以通过最大化似然函数来估计参数。贝叶斯派认为参数本身也是随机的,有先验分布,数据是用来更新这个分布的。

这个哲学差异在实际应用中体现为:频率派更关注“当前数据说了什么”,贝叶斯派更关注“结合先验知识后,参数应该是什么”。在样本量充足时,数据的声音足够大,先验的影响被淹没,两种方法趋同。在样本量不足时,先验就起到了“补充信息”的作用,让估计更稳定。

我个人的看法是,不必纠结于哲学立场,实用主义一点:哪个效果好就用哪个。在小样本场景下,贝叶斯估计的平滑确实能提升效果;在大样本场景下,极大似然估计更简单直接。工具而已,没必要站队。

8.2 先验分布的选择

贝叶斯估计里,先验分布的选择会影响结果。朴素贝叶斯常用的先验是狄利克雷分布,因为它和多项分布共轭,计算方便。狄利克雷分布的参数 α 就是平滑项 λ。当 α 全为1时,就是均匀先验,对应拉普拉斯平滑。

如果对问题有额外的先验知识,比如知道某个特征取值更常见,可以设置不同的 α 值。但在实际应用中,大部分情况下都用均匀先验,因为很难有可靠的先验知识。均匀先验虽然“无信息”,但至少能解决零概率问题,比极大似然估计更稳。

8.3 从朴素贝叶斯到贝叶斯网络

朴素贝叶斯假设所有特征在类别条件下独立,这是一个很强的假设。如果放宽这个假设,允许特征之间有依赖关系,就变成了贝叶斯网络。贝叶斯网络用有向无环图表示特征之间的依赖关系,参数估计更复杂,但表达能力更强。

从朴素贝叶斯到贝叶斯网络,是一个从简单到复杂的扩展路径。实际应用中,如果朴素贝叶斯效果不够好,且确认是特征依赖导致的,可以考虑贝叶斯网络。但大多数情况下,朴素贝叶斯的简单和高效已经足够,没必要上更复杂的模型。

9. 写在最后的一些实操建议

朴素贝叶斯这个模型,看起来简单,但参数估计这一块有很多细节值得抠。我自己的学习路径是:先手推公式,再用代码从零实现一遍,最后在真实数据集上跑对比实验。这三步走下来,基本就能把极大似然估计和贝叶斯估计的区别吃透了。

如果你正在学《统计学习方法》这一章,我的建议是不要跳过公式推导。很多人觉得公式枯燥,直接看代码实现,结果遇到零概率问题不知道怎么排查,遇到平滑参数不知道怎么调。公式推导虽然费时间,但能让你理解每个参数背后的含义,排查问题时心里有底。

另外,实际使用朴素贝叶斯时,有几个默认设置可以参考:平滑参数默认取1,特征维度超过10个时直接取对数,先验概率在类别不均衡时谨慎平滑,测试集出现未见特征取值时统一映射到“其他”类。这些经验都是在实际项目中踩坑踩出来的,教科书上不一定写,但很实用。

最后分享一个我常用的调试技巧:把训练好的模型在训练集上跑一遍,打印每个样本的后验概率。如果发现某些样本的概率值异常(比如所有类别概率都接近0,或者某个类别概率异常高),就针对性地检查对应的条件概率。这个方法能快速定位是数据问题还是模型问题,比盲目调参高效得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询