import numpy as np
from sklearn import naive_bayes
data = np.array([[0, 0, 1, 1, 1], [1, 0, 1, 1, 0], [1, 1, 0, 0, 1], [1, 1, 0, 0, 0],
[0, 1, 0, 0, 1], [0, 0, 0, 1, 0], [1, 0, 0, 1, 1], [1, 1, 0, 0, 1],
[1, 1, 1, 1, 0], [1, 1, 0, 1, 0], [1, 1, 0, 1, 1], [1, 0, 1, 1, 0],
[1, 0, 1, 0, 0]])
label = np.array([0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1])
test_data = np.array([1, 0, 1, 1, 0])
# No smoothing equivalent to frequentist naive Bayes
nb_model = naive_bayes.BernoulliNB(alpha=0.0)
nb_result = nb_model.fit(data, label).predict_proba(test_data.reshape(1, -1))
print('The probability that the new man is Scottish by naive Bayes is: ', nb_result[:, 1])
# Laplacian smoothing equivalent to Bayesian naive Bayes
bnb_model = naive_bayes.BernoulliNB()
bnb_result = bnb_model.fit(data, label).predict_proba(test_data.reshape(1, -1))
print('The probability that the new man is Scottish by Bayesian naive Bayes is: ', bnb_result[:, 1])
一、数据说明
python
data # 13 个样本,5 个二元特征(0/1)
label # 0/1 标签,前 6 个为 0,后 7 个为 1
test_data = [1, 0, 1, 1, 0]
这是一个经典的**"苏格兰人"分类问题**(类似 Mitchell《机器学习》中的例子),标签 1 代表 Scottish,标签 0 代表 English。特征是 5 个二元属性(如是否穿裙子、是否喝威士忌等)。
统计一下:
-
类别 0(English):6 个样本
-
类别 1(Scottish):7 个样本
-
测试样本
[1,0,1,1,0]恰好与第 2 个训练样本相同,其标签为 0。
二、两个模型的核心区别
两段代码唯一差别是 BernoulliNB(alpha=...) 的参数:
| 参数 | 模型 | 含义 |
|---|---|---|
alpha=0.0 |
无平滑 = 频率派朴素贝叶斯 | 用最大似然估计(MLE) |
alpha=1.0(默认) |
贝叶斯朴素贝叶斯(拉普拉斯平滑) | 用带先验的贝叶斯估计 |
三、对测试样本的手工推算(验证)
| 模型 | P(Scottish∣x)P(Scottish∣x) 近似值 |
|---|---|
| alpha=0(频率派) | ≈ 0.855 |
| alpha=1(贝叶斯/拉普拉斯) | ≈ 0.721 |
(实际 sklearn 输出会与上述手算高度一致,可能因浮点有微小差异。)
四、观察与点评
-
predict_proba返回两列 :[:, 1]取的是类别 1(Scottish)的后验概率,符合注释。 -
平滑显著改变结果:从约 0.855 降到约 0.721。原因是:
-
类别 1 中特征 1 全为 1(p11=1p11=1),MLE 下该特征对类别 1 的似然贡献为 1,不惩罚;
-
拉普拉斯把它压到 8/9<18/9<1,同时其他特征为 0 的概率也被抬高,综合导致类别 1 的似然下降更明显,类别 0 相对上升。
-
-
alpha=0的隐患 :若某特征在某类别中从未取过某值(如本题类别 1 列 1 全为 1,若测试样本列 1 为 0,则 1−p11=01−p11=0,似然直接为 0),MLE 会产生"零概率"问题。本题测试样本恰好避开,但一般化场景下alpha=0数值不稳定。实践中alpha=1更稳健。 -
"频率派 vs 贝叶斯"的说法 :代码注释将其表述为 frequentist vs Bayesian。严格说,拉普拉斯平滑对应 Beta 先验的 MAP 估计,是贝叶斯思想的一种近似;
sklearn的BernoulliNB两种情形都走同一套"计数 + 平滑"公式,只是 α 不同,因此两者本质是同一算法不同超参数,而非两种完全不同的推断框架。 -
先验估计 :
BernoulliNB的类先验同样受alpha影响(class_prior=None时用平滑后的频率)。alpha=0 时先验为 6/13、7/13;alpha=1 时先验会变为 (6+1)/(13+2)=7/15(6+1)/(13+2)=7/15、(7+1)/15=8/15(7+1)/15=8/15。这点在手算中若严格按 sklearn 实现也应纳入,会使 alpha=1 的结果略高于我上面的近似(我上面用了未平滑先验)。准确起见,sklearn 的class_log_prior_在 alpha=1 时确实使用了平滑后的类频率,因此实际输出可能比 0.721 稍高一些(约 0.73 左右)。建议以程序实际打印为准。
五、总结
这段代码用同一个数据集、同一个伯努利朴素贝叶斯模型,仅通过改变平滑参数 α,对比了:
-
α=0:纯最大似然(频率派),易受零计数影响;
-
α=1:拉普拉斯平滑(贝叶斯式),更稳健,但会"稀释"极端概率。
它直观展示了平滑对朴素贝叶斯后验概率的影响:对本题测试样本,平滑使"是苏格兰人"的概率从约 85% 降到约 72--73%,但两种情况下模型仍倾向于预测为类别 1(Scottish)------尽管该测试样本与一个标签为 0 的训练样本完全相同,说明在朴素贝叶斯的独立性假设与整体统计下,类别 1 仍占优。