☰
贝叶斯分类器详解:从公式推导到Python实战
2026/9/26 9:39:39 网站建设 项目流程

简介:南大出品的《机器学习导论》第07章贝叶斯分类器课件,面向机器学习入门者与人工智能方向学生,系统讲解在概率框架下如何进行分类决策,帮助读者理解贝叶斯决策论与贝叶斯风险等核心概念。内容从贝叶斯定理与先验概率、似然、证据因子讲起,厘清判别式模型与生成式模型的差异;随后深入极大似然估计与朴素贝叶斯分类器,介绍拉普拉斯修正、连续属性概率密度估计等处理细节;再拓展到半朴素贝叶斯中的SPODE、TAN与AODE策略,以及贝叶斯网络的结构表达、道德图、条件独立性和推断方法,完整呈现从基础假设到复杂依赖建模的知识链路。资源为1个PDF文件,共23页,压缩包大小约1.05MB,排版清晰、公式完整,方便随时查阅、打印或作为课件补充。目前已有118人学习下载,适合在备考、课程复习或项目实战前快速构建贝叶斯分类器部分的系统认识。

1. 为什么说贝叶斯分类器是机器学习里最“反直觉”的一条路

很多入门机器学习的朋友,学完线性回归和决策树之后,拿到这份《机器学习导论》第07章贝叶斯分类器讲义,第一感觉是懵:前面那些算法全是盯着输入到输出的映射关系硬学,怎么到了贝叶斯这里,突然开始讲概率、讲先验、讲似然了?这份23页的PPT,恰恰是帮你从“判别式思维”切换到“生成式思维”的关键一章——它不直接学决策边界,而是先给每一类数据建模,回答“这堆数据是怎么产生的”,再反过来推断新样本该归哪一类。看懂这一章,你才算真正把机器学习从“找规律”升级到“做推断”。这篇笔记就是把23页讲义展开成能落地的方案:公式怎么理解、代码怎么写、参数怎么调、哪些地方新手容易翻车,一步步讲清楚。

2. 先看懂第07章的核心:贝叶斯决策论到底在干什么

2.1 从贝叶斯公式到后验概率:机器学习的“上帝视角”

贝叶斯分类器的一切推导,都建立在贝叶斯公式上。公式本身很简单:

P(ωᵢ|x) = P(x|ωᵢ) · P(ωᵢ) / P(x)

其中ωᵢ表示第i个类别,x是样本特征向量。这里每个符号在机器学习语境下都有明确含义,PPT里通常会花好几页来回解释它们之间的关系,我整理成一张速查表:

符号名字机器学习里的含义哪里来
P(ωᵢ)先验概率不看样本时,类别i本身出现的比例训练集里数一数
P(xωᵢ)类条件概率密度假设已知是类别i,x长成这样的可能性
P(x)证据因子所有类别下x出现的总概率全概率公式,通常不用真算
P(ωᵢx)后验概率看到样本x之后,它属于类别i的概率

初看这个公式,你可能会觉得这就是个条件概率的简单变形,没什么了不起。但放在机器学习里,它的思维方式是革命性的:判别式模型(比如逻辑回归)直接学 P(ωᵢ|x) 这个边界;生成式模型则是先学 P(x|ωᵢ) 和 P(ωᵢ),再通过贝叶斯公式反推后验。换句话说,贝叶斯分类器试图理解“每一类数据长什么样”,而不是“两类数据之间画在哪”。

这个区别在数据量少、噪声大的时候尤其重要。理解了生成式模型的思路,再看吴恩达、李宏毅这些课里的贝叶斯部分,你会发现大家讲的都是同一套概率语言,只是侧重不同。

2.2 最小错误率决策:为什么后验概率最大就是最优

分类问题最终要落到决策上:给定一个样本x,我该把它判给哪个类别?最朴素也最合理的准则就是最小化分类错误率。可以证明,把样本判给后验概率最大的那个类别,期望错误率最小。这就是最小错误率贝叶斯决策规则:

决策结果 = argmax over i P(ωᵢ|x)

这个结论看起来平淡,但它有一个隐藏前提经常被忽略:它默认所有分类错误的代价是相同的。换句话说,把A错判成B,和把B错判成A,对你来说损失一样。实际工程里根本不是这么回事——医疗诊断中漏诊的代价远大于误诊,风控场景里放走坏人的代价远大于误伤好人。

所以《机器学习导论》这门课在讲完最小错误率之后,一般会紧接着引入风险的概念。引入损失函数λ(αᵢ|ωⱼ)表示“真实类别是ωⱼ却做出决策αᵢ”的代价,于是条件风险变成:

R(αᵢ|x) = Σⱼ λ(αᵢ|ωⱼ) · P(ωⱼ|x)

这时候最优决策就变成了最小化条件风险,而不是单纯最大化后验概率。当损失函数是0-1损失时,最小风险决策恰好退化成最小错误率决策,前后逻辑就闭环了。这个“0-1损失退化成后验最大”的推导,是期末复习和面试八股里高频出现的考点,建议你亲手推一遍。

2.3 判别式与生成式的分岔口:贝叶斯分类器的定位

学这一章的时候,脑子里要有一张全局地图:机器学习算法可以分为两大类。判别式模型直接学习决策边界,典型代表是逻辑回归、支持向量机、决策树;生成式模型则对每一类的数据分布分别建模,典型代表是贝叶斯分类器、高斯判别分析、隐马尔可夫模型。

两者的关系不是谁替代谁,而是适用场景不同。生成式模型有它的独特优势:数据量小的时候,先验和类条件概率的估计比较稳定,不容易过拟合;对缺失数据天然友好,因为可以基于概率分布做推断;还能输出“样本属于每个类别的置信度”,而不是只给一个硬标签。缺点也明显:当特征维度很高、分布假设不符合实际时,类条件概率密度很难估准,性能反而不如判别式模型。

我在实际项目里的经验是:特征维度低于50、数据量几千到几万这个区间,贝叶斯分类器是值得优先试的baseline;但如果是图像、文本这类高维稀疏数据,就别指望朴素贝叶斯打天下了,它的独立性假设会被现实狠狠教育。这个分寸感,等你看完后面几章会更清楚。

3. 把公式变成能算的机器:概率密度估计这条主线

3.1 为什么必须假设分布形式:参数化估计的思路

贝叶斯决策规则在理论上很完美,但落地时立刻遇到一个现实问题:P(x|ωᵢ) 是个概率密度函数,你手里只有训练数据,怎么知道它长什么样?最常见也最实用的做法是参数化估计——先假设类条件概率密度服从某种已知分布(最常见的是高斯分布),再用训练数据估计这个分布的参数。这就是“参数化估计”四个字的含义。

选择哪种分布假设,决定了贝叶斯分类器的具体形态:

假设参数形式分类器名字特点
每类特征独立且同方差每类一个均值,共享方差朴素贝叶斯(高斯版)最简单,参数少,小样本友好
每类高斯,协方差矩阵共享每类均值,共享协方差线性判别分析(LDA)决策边界是线性的
每类高斯,协方差各自独立每类均值,各自协方差高斯判别分析(QDA)决策边界是二次曲线,更灵活

这里有个新手容易迷路的点:高斯朴素贝叶斯,和高斯判别分析,都是假设高斯分布,为什么P(x|ωᵢ)的式子长得不一样?区别就在协方差矩阵上。朴素贝叶斯假设特征之间相互独立,协方差矩阵是对角阵,非对角元素全为0;高斯判别分析则允许特征之间存在相关性,协方差矩阵是完整的。这个差异直接决定了决策边界的形状。

参数化估计的好处是效率高——高斯分布只要估计均值和方差两个参数,几百个样本就能估得比较稳。坏处是假设可能不符合实际,比如数据其实是多峰的,硬用单峰高斯去拟合,模型就废了。遇到这种情况,可以考虑混合高斯模型或者核密度估计,那属于非参数估计的范畴,这门课的23页PPT一般是不会展开的,但你要知道有这么条路。

3.2 高斯分布下的极大似然估计:均值与协方差的公式推导

假设我们选定用高斯分布建模某一类的数据,下一步就是用极大似然估计(MLE)来求解参数。给定类别ωᵢ的训练样本集合Dᵢ,假设样本独立同分布,似然函数写成:

L(μᵢ, Σᵢ) = ∏ₖ (1 / ((2π)^(d/2) |Σᵢ|^(1/2))) · exp(-0.5 (xₖ-μᵢ)ᵀ Σᵢ⁻¹ (xₖ-μᵢ))

直接对这个连乘求导很麻烦,通常取对数把连乘变成连加,然后分别对μᵢ和Σᵢ求偏导并令其为0。推导结果非常干净:均值μᵢ的MLE估计就是样本均值,协方差矩阵Σᵢ的MLE估计就是样本协方差矩阵。公式不复杂但推导过程极容易算错,尤其是对Σ求导那一大串矩阵微积分,很多人在期末复习时在这里卡壳。

一个值得注意的细节:MLE估计的协方差矩阵是有偏的,分母除以的是样本数n而不是n-1。对于判别分析这类算法,这个偏差影响不大,因为分母的n和n-1在样本量几百以上时几乎没有区别。但如果你在极端小样本场景下用贝叶斯分类器,建议手动改成无偏估计,也就是分母用n-1,可以让数值更稳。

3.3 朴素贝叶斯的“独立性假设”到底牺牲了什么

朴素贝叶斯之所以带“朴素”二字,是因为它做了一个很强的简化假设:给定类别时,特征之间相互独立。数学上写作:

P(x|ωᵢ) = ∏ⱼ P(xⱼ|ωᵢ)

这个假设在实际数据里几乎从来都不成立——现实生活中很少有什么特征是完全独立的。但神奇的是,朴素贝叶斯在文本分类、垃圾邮件过滤这些场景下表现得非常好,甚至碾压一些复杂模型。原因有两方面:一是很多任务里决策边界对概率估计的误差并不敏感,只要各类后验概率的相对大小没错,绝对值偏一点也没关系;二是参数少、估计方差小,偏差换方差的这笔交易在数据量有限时往往划算。

工程上还有一个隐藏的好处:特征独立假设让计算变得极其廉价。每个特征可以单独建模,支持增量更新,遇到缺失特征时只需要跳过那一项乘积,实现起来非常灵活。所以在做垃圾邮件过滤、情感分析这类高维稀疏特征任务时,先跑一个朴素贝叶斯baseline,几乎不费什么力气。

不过这个假设也有翻车的时候。当特征之间强相关时,朴素贝叶斯会“过度自信”——它把重复的相关特征当作多条独立证据,后验概率被推得过于极端。比如分类电影《唐人街探案》时,如果把“王宝强出演”“喜剧演员王宝强出演”“王宝强+唐仁”这三个高度相关的特征都算进特征向量,朴素贝叶斯会把喜剧类的概率算得奇高无比。这种错误在分类决策上可能不致命,但如果你的下游任务需要校准过的概率输出,就要格外当心了。这个坑我会在第5章展开讲。

4. 用Python复现PPT里的贝叶斯分类器:从公式到可运行代码

4.1 数据准备:为什么用鸢尾花而不是自己造数据

理解了公式之后,最快的验证方式就是写代码把分类器跑通。这里我选择鸢尾花数据集,原因是它只有4个特征、3个类别、150条样本,分布接近高斯,非常适合验证贝叶斯分类器的效果。更关键的是,这个数据集足够小,手写的贝叶斯分类器可以在毫秒级跑完,方便你边写边对照公式。

import numpy as np from sklearn.datasets import load_iris data = load_iris() X = data.data # 形状 (150, 4),四维特征 y = data.target # 三类:setosa, versicolor, virginica # 划分训练集和测试集,保持类别比例一致 from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42, stratify=y ) print(X_train.shape, X_test.shape)

这里有一个容易忽略的参数:stratify=y。它保证训练集和测试集里三个类别的比例和原始数据一致,避免某个类别在训练集中样本过少导致先验概率估计跑偏。random_state=42不是随便写的,它让数据划分可复现,你和我的实验结果才能对得上。跑代码的人如果发现结果不一致,先检查这两个参数是不是一样的。

4.2 手写高斯判别分析:核心逻辑只有三步

接下来不借助sklearn,手写一个高斯判别分析分类器。整个算法可以拆成三步:估计每一类的先验概率、估计每一类的高斯参数、做决策时计算后验概率取最大。理解了这三步,你就把PPT里那几页公式真正吃透了。

class GDA: def __init__(self): self.priors = {} # 每类的先验概率 self.params = {} # 每类的 (均值, 协方差) def fit(self, X, y): classes = np.unique(y) n_total = len(y) for c in classes: X_c = X[y == c] # 先验 = 该类别样本数 / 总样本数 self.priors[c] = len(X_c) / n_total # 均值 = 该类别样本的均值向量 mean = np.mean(X_c, axis=0) # 协方差 = 该类别样本的协方差矩阵(MLE估计,分母n) cov = np.cov(X_c.T, bias=True) # 加一个小的对角项防止奇异 cov += 1e-6 * np.eye(X.shape[1]) self.params[c] = (mean, cov) def predict(self, X): log_posteriors = [] for c, (mean, cov) in self.params.items(): # 计算多元高斯对数似然 + 对数先验 n_dim = X.shape[1] diff = X - mean inv_cov = np.linalg.inv(cov) log_likelihood = -0.5 * np.sum(diff @ inv_cov * diff, axis=1) log_likelihood += -0.5 * np.log(np.linalg.det(cov)) log_likelihood += -0.5 * n_dim * np.log(2 * np.pi) log_posteriors.append(log_likelihood + np.log(self.priors[c])) return np.argmax(np.array(log_posteriors), axis=0) model = GDA() model.fit(X_train, y_train) y_pred = model.predict(X_test) print("准确率:", np.mean(y_pred == y_test))

这里有两个细节值得讲清楚。第一,代码里用的是对数似然而不是直接乘概率密度,原因是高斯密度的连乘(每个样本乘一次)很容易数值下溢——几十个0.001连乘,结果就变成0了,再取对数就变成负无穷。变量名写成log_posteriors就是提醒自己:先取对数再做加法。第二,协方差矩阵加了一个1e-6 * np.eye()的对角扰动,这是防止某类样本量太少导致协方差矩阵不可逆。加了之后协方差矩阵不再精确等于MLE估计值,但换来的是数值稳定性,工程上是值得的。

跑完这个代码,你会发现准确率在95%左右。这个数字可以作为你验证代码是否正确的一个基准值。如果偏差太大,优先检查上面的三个细节:先验有没有归一化、协方差是不是按类别分别计算的、有没有用对数似然。

4.3 用sklearn做对照:GaussianNB与LDA的选择差异

手写代码验证了原理,接下来的标准动作是用sklearn里的现成实现做对照。这个对照是有价值的:库的实现经过大量优化,数值处理更稳健,跑出来的结果可以作为你手写代码的“标准答案”。

from sklearn.naive_bayes import GaussianNB from sklearn.discriminant_analysis import LinearDiscriminantAnalysis # 高斯朴素贝叶斯:假设特征独立,协方差对角化 gnb = GaussianNB() gnb.fit(X_train, y_train) print("GaussianNB 准确率:", gnb.score(X_test, y_test)) # 线性判别分析:各类共享协方差,决策边界为线性 lda = LinearDiscriminantAnalysis() lda.fit(X_train, y_train) print("LDA 准确率:", lda.score(X_test, y_test))

GaussianNB和手写的GDA区别在于协方差矩阵的处理方式:高斯朴素贝叶斯强行把协方差矩阵的非对角元素全部置零,参数数量只有4个均值加4个方差;LDA则假设所有类别共享同一个协方差矩阵;手写的GDA允许每个类别有自己完整的协方差矩阵。三种策略的决策边界复杂度依次递增:朴素贝叶斯是轴对齐的曲线,LDA是直线,GDA是二次曲线。

在鸢尾花这个数据集上,三者准确率差别不大,都在95%左右。但换成更复杂的数据集,差别会显现出来。我的经验是:先用LDA跑一遍,如果效果不行再上QDA(即手写的GDA),最后才考虑朴素贝叶斯。原因很简单,朴素贝叶斯的独立性假设太强,特征相关性一高就掉链子,而LDA只假设共享协方差,比朴素贝叶斯宽松得多,又比QDA更不容易过拟合,是性价比最高的起点。

5. 贝叶斯分类器避坑指南:5个高频问题和排查方法

5.1 协方差矩阵奇异:行列式直接变0,预测报错或乱给结果

现象:代码运行到np.linalg.det(cov)或者np.linalg.inv(cov)时报错LinAlgError: Singular matrix,或者不报错但预测结果随机。

原因:当某个类别的样本数小于特征维度时,样本协方差矩阵的秩不够,矩阵不可逆。另一个常见原因是特征之间存在完全线性相关,比如两个特征总是一样的值。

解决:优先检查类别样本量,至少保证每个类别的样本数大于特征数。然后给协方差矩阵的对角线加一个小的正则项,也就是我在第4章代码里写的cov += 1e-6 * np.eye(d)。如果问题还在,把正则项调大到1e-3。这个操作在统计学里叫岭正则化,本质是给你不充分的样本量“补一点方差”。

5.2 先验概率估计偏差:小样本下数数并不靠谱

现象:某个类别在训练集中只出现了3次,但实际场景里这个类别占比30%。模型预测时把这个类别的后验概率压得极低,几乎永远不会预测这个类别。

原因:先验概率的MLE估计就是样本频率。样本量小时,频率估计的方差极大——抽到3个和抽到8个,算出来的先验差快3倍。这个误差会通过贝叶斯公式直接放大到后验概率上。

解决:对先验做平滑处理。拉普拉斯平滑是最常用的方法:P(ωᵢ) = (nᵢ + α) / (N + α·K),其中K是类别总数,α通常取1。更稳的做法是干脆不依赖先验,用验证集重调决策阈值,这点我在第5.5节展开讲。

5.3 特征强相关时朴素贝叶斯过度自信

现象:分类文本时,某条样本的后验概率算出来高达0.9999,但实际验证时发现是错的。用gnb.predict_proba()查看概率输出,发现极端值远多于其他模型。

原因:特征独立性假设在强相关特征面前失效,相关的特征被当作独立证据重复累计,导致似然连乘被高估。这就是我在3.3节提到的“重复计数”问题。

解决:特征工程上下功夫——做相关性分析,把相关系数超过0.8的特征只保留一个;或者改用LDA/QDA,让协方差矩阵去建模特征相关性。如果必须用朴素贝叶斯,至少要用predict_proba()输出的概率做一次校准(比如Platt scaling),别把原始概率直接当下游置信度用。

5.4 数值下溢:连乘似然变成0,后验全乱了

现象:特征维度很高(比如文本分类的几万维),某个类的似然连乘结果在计算机里变成0,取对数变成-inf,最终各类别后验概率全是NaN或0。

原因:每个特征的条件概率都在0到1之间,几千个小数连乘,结果必然突破浮点数下限。

解决:全程使用对数空间计算,永远不要算原始似然。把贝叶斯公式改成加法:log P(ωᵢ|x) = log P(x|ωᵢ) + log P(ωᵢ) - log P(x)。别忘了log P(x)也用对数求和得到。sklearn的实现已经做了这个处理,但你自己手写代码时最容易在这翻车。判断标准很简单:如果代码里出现了多个概率变量直接相乘的语句,大概率就有数值风险。

5.5 样本不平衡:后验概率有偏,决策阈值需要重调

现象:二分类任务里正样本只占5%,模型准确率95%,但仔细看全部预测成了负类,正样本一个没抓出来。

原因:先验概率本身就不平衡,后验概率自然偏向多数类。这是贝叶斯分类器在类别不平衡场景下最典型的故障模式,算法本身没问题,是决策准则没适配任务目标。

解决:不要直接用0.5作为决策阈值。先算出每个样本的后验概率,然后在验证集上扫描不同阈值,选F1分数或召回率最优的那个点。如果任务对召回率有硬指标(比如必须抓到90%的正样本),直接按这个指标反推阈值。用公式表达就是:argmax over threshold of Metric(P(ωᵢ|x) > threshold)。

6. 把23页PPT吃透之后:一个验证分类器是否真学会的进阶技巧

前面五章解决了“能跑起来”的问题,最后这一步解决“跑对了没有”的问题。很多人训练完贝叶斯分类器只看一个准确率,准确率高就认为万事大吉。这里我建议你多做一个动作:画出决策边界和样本分布,亲眼确认分类器学到的边界符合你对该问题域的直觉。

具体做法是用二维数据。把鸢尾花数据集降到前两个特征,训练一个LDA模型,然后在平面上生成密集的网格点,对每个网格点做预测,用等高线画出分类区域。再把训练样本散点图画在同一个坐标系里,观察样本是否落在对应的决策区域内、边界是否平滑、有没有异常突出的尖角。这个可视化虽然简单,但能一次性暴露出多个问题:协方差估计是否合理、决策边界是否过于复杂、数据点是否被错误地挤到边界的另一边。

一个值得检查的现象是决策边界的形状是否与你的特征分布直觉一致。如果特征是近似高斯分布的,LDA的决策边界应该是一条平滑直线,QDA是一条平滑抛物线。如果你画出来的边界剧烈抖动、呈锯齿状,通常意味着模型过拟合了——协方差矩阵被少量异常样本带偏,这时候应该回退到共享协方差的LDA或者干脆用朴素贝叶斯。

我的个人习惯是每个贝叶斯模型上线前,都会用predict_proba输出后验概率的分布直方图看一眼。正确校准的模型,预测正确的那些样本后验概率应该集中在0.8到1.0,预测错误的样本后验概率应该集中在0.5附近。如果所有样本的后验概率都在0.99以上,或者都在0.6以下,说明概率没有校准好,下游任务不能信任这个数值。这一步做完,模型能不能上线,我心里基本就有数了。

贝叶斯分类器在23页PPT里只是一个章节,但背后的生成式建模思想,会让你之后看混合高斯模型、隐马尔可夫模型甚至是生成对抗网络时都受益。先把这个基础打牢,后面的路会顺畅很多。希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询