当“概率直觉“遇见机器学习:手把手实现朴素贝叶斯算法
假设你正在查看邮箱,突然收到一封标题为"紧急!账户验证通知"的邮件。你的大脑瞬间开始快速计算:这个标题包含敏感词的概率有多大?发件人地址是否可疑?这些特征共同作用下,判断它是垃圾邮件的概率是多少?这就是朴素贝叶斯算法的核心思想。
目录
一、从生活直觉到数学公式:贝叶斯法则的智慧
1.1 贝叶斯定理:概率世界的黄金法则
我们先来看一个经典案例:某种疾病的发病率为0.1%,检测准确率为99%。当某人检测结果为阳性时,实际患病的概率是多少?会很高吗?
根据贝叶斯定理:
P(患病|阳性) = (P(阳性|患病)*P(患病)) / P(阳性)
= (0.99*0.001) / (0.99*0.001 + 0.01*0.999)
≈ 9%
这个反直觉的结果展示了贝叶斯定理的强大:它通过整合先验知识和新证据来修正概率判断。
1.2 朴素之处:特征条件的独立性假设
朴素贝叶斯算法基于贝叶斯定理,通过计算各类别的后验概率,选择具有最大后验概率的类别作为预测结果。其“朴素”之处在于假设各特征之间是条件独立的,即在已知类别的条件下,特征之间相互独立。尽管这一假设在实际问题中往往不成立,但朴素贝叶斯算法在很多场景下依然表现良好,尤其在文本分类、垃圾邮件检测等领域有着广泛应用。
二、理论基础
2.1 贝叶斯定理
贝叶斯定理公式如下:
其中:
-
:即后验概率,即在给定特征 X 后,类别为 Y 的概率;
-
:似然函数,即在类别 Y 下,观测到特征 X 的概率;
-
:先验概率,即类别 Y 出现的概率;
-
:观测到特征 X 的概率(对所有类别来说是常数)。
2.2 朴素假设
朴素贝叶斯的核心假设是特征条件独立,即:
这使得计算大规模特征时大大简化,不必考虑各特征之间的复杂联合分布。
2.3 常见变种
根据特征分布的不同,朴素贝叶斯主要有以下几种变体:
-
高斯朴素贝叶斯:假设特征服从高斯分布,常用于连续型数据;
例如鸢尾花数据集
from sklearn.datasets import load_iris
iris = load_iris()
X, y = iris.data, iris.target
条件概率计算
-
多项式朴素贝叶斯:适用于离散的计数特征,例如文本中的词频;
from sklearn.feature_extraction.text import CountVectorizer
docs = ["优惠 特价 促销", "会议 报告 总结"]
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(docs)
-
伯努利朴素贝叶斯:适用于二值特征,判断某个词是否出现等场景。
from sklearn.preprocessing import Binarizer
binarizer = Binarizer(threshold=0.5)
X_binary = binarizer.fit_transform(X)
三、算法流程
3.1 训练阶段
计算各类别的先验概率 P(Y)。
对于每个类别,估计特征的条件概率 (根据不同的分布假设,估计方式不同)。
3.2 预测阶段
给定一个新的样本 X,计算每个类别的后验概率:
选择后验概率最大的类别作为预测结果。
四、实战演练:手写数字识别
4.1 数据准备与可视化
from sklearn.datasets import load_digits#导入手写数字数据集,包含1797个手写数字图片
import matplotlib.pyplot as plt#绘图函数
digits = load_digits()#加载数字数据集 这里image被展平为一维数组(原本是二维)
plt.figure(figsize=(10,4))#创建绘图窗口,宽高单位 英寸
for index, (image, label) in enumerate(zip(digits.data[:5], digits.target[:5])):
#zip样本与标签配对,enumerate 每一个对象加上索引
plt.subplot(1,5,index+1)#构建子图网格,窗口分为1行5列,当前是第 index+1 个图像
plt.imshow(image.reshape(8,8), cmap=plt.cm.gray_r)#显示图像,将1维数组变回二维8×8数组,cmap灰度反转
plt.title(f'Label: {label}')#加上标签
plt.tight_layout()#自动调整子图布局

为了方便理解,这里再补充一个点
上面代码第三行 load_digits() 函数,将返回的数据集对象赋值给变量 digits。这个数据集对象包含多个属性,其中常用的有:
digits.data:一个二维数组,形状为(n_samples, n_features),其中n_samples是样本数量(这里是 1797),n_features是特征数量(这里是 8x8 = 64,因为每个图像被展平为一个一维向量)。每一行代表一个手写数字图像的特征向量。digits.target:一个一维数组,包含每个样本对应的真实标签(0 - 9 的数字)。
4.2 模型训练与评估
from sklearn.naive_bayes import GaussianNB#从sklearn朴素贝叶斯模块导入高斯朴素贝叶斯分类器
from sklearn.model_selection import train_test_split#用于将数据集划分为训练集和测试集,方便评估模型泛化能力。
from sklearn.metrics import confusion_matrix#计算混淆矩阵,用于直观展示分类模型的预测结果(真实标签与预测标签的匹配情况)。
import seaborn as sns#导入 Seaborn 库,用于更美观的可视化(这里用于绘制混淆矩阵热图)
X_train, X_test, y_train, y_test = train_test_split(
digits.data, digits.target, test_size=0.3)#30%数据作为测试集
#digits.data 手写数字数据集的特征(图像像素展平后的向量)。
#digits.target 手写数字数据集的真实标签(0-9 的数字)
model = GaussianNB()#创建高斯朴素贝叶斯分类器
model.fit(X_train, y_train)#用训练数据及标签进行模型训练
# 可视化混淆矩阵
plt.figure(figsize=(10,8))#创建绘图窗口
sns.heatmap(confusion_matrix(y_test, model.predict(X_test)),
annot=True, fmt='d', cmap='Blues')
plt.xlabel('Predicted')
plt.ylabel('True')
同样这里再解释一下倒数第三行代码(其余直接看注释)
confusion_matrix(y_test, model.predict(X_test)):计算测试集的混淆矩阵。model.predict(X_test) 用训练好的模型预测测试集特征,得到预测标签,再与真实标签 y_test 对比。(即用模型预测出的标签与真实标签对比)
sns.heatmap(...):用 Seaborn 的热图展示混淆矩阵:
annot=True:在每个格子中显示具体数值。fmt='d':数值显示为整数。cmap='Blues':使用蓝色系颜色映射,颜色深浅表示数值大小(如深蓝色代表数值大)。

上面的图像中:
对角线元素表示预测正确的样本数,比如真实标签为 0 ,且预测为 0 的有 56 个
非对角线元素则表示预测错误的样本数
颜色越深,表示数值越大,颜色浅表示数值小
通过混淆矩阵,可直观分析模型在哪些数字上表现好(如 0、1、4 等对角线数值高的类别),以及哪些数字容易被误判(如非对角线数值高的组合),进而指导模型优化(如调整参数、增加特定类别样本等)。
4.3 核心代码解析
高斯朴树贝叶斯分类器代码如下
class GaussianNB:
def fit(self, X, y):
self.classes = np.unique(y) # 获取数据中的所有类别
self.means = {} # 存储每个类别下特征的均值
self.vars = {} # 存储每个类别下特征的方差
self.priors = {} # 存储每个类别的先验概率
for c in self.classes:
X_c = X[y == c] # 筛选出类别为 c 的所有样本特征
self.means[c] = X_c.mean(axis=0) # 计算类别 c 中每个特征的均值
self.vars[c] = X_c.var(axis=0) # 计算类别 c 中每个特征的方差
self.priors[c] = X_c.shape[0] / X.shape[0] # 计算类别 c 的先验概率(样本数占比)
def predict(self, X):
posteriors = [] # 存储每个类别对输入样本的后验概率(对数形式)
for c in self.classes:
# 计算先验概率的对数
prior = np.log(self.priors[c])
# 计算高斯分布似然概率的对数(拆分公式便于计算)
likelihood = -0.5 * np.sum(np.log(2 * np.pi * self.vars[c])) # 固定项
likelihood -= 0.5 * np.sum(((X - self.means[c])**2) / self.vars[c], axis=1) # 指数项
posteriors.append(prior + likelihood) # 后验概率 = 先验对数 + 似然对数
# 返回后验概率最大的类别
return self.classes[np.argmax(posteriors, axis=0)]
五、总结
朴素贝叶斯算法虽然依赖于特征独立性的假设,但其实现简单、速度快、效果良好,使其在实际应用中十分常见。本文从贝叶斯定理出发,阐述了朴素贝叶斯算法的基本理论和假设,并通过详细的Python案例实战,帮助大家理解如何在真实问题中应用该算法。
希望这篇博客文章能让你对朴素贝叶斯算法有更深入的认识,并在后续的机器学习实践中游刃有余。如果你有任何问题或改进建议,欢迎在评论区留言讨论!
如果这篇文章对你有所启发,期待你的点赞关注!

更多推荐

所有评论(0)