--- title: "朴素贝叶斯算法 讲解及示例(课程作业)" description: date: 2026-04-12T22:10:40+08:00 image: math: license: rooms: - '书房' hidden: false comments: false draft: false --- ### 朴素贝叶斯算法 #### 1.简介 朴素贝叶斯算法的原理,是通过贝叶斯公式$$\underbrace{P(Y|X)}_{{后验}} = \frac{\overbrace{P(X|Y)}^{{似然}}\cdot\overbrace{P(Y)}^{先验}}{\underbrace{P(X)}_{证据}}$$由 - “先验”$P(Y_j)$(样本中目标事件$Y_j$的发生频率) - “似然”$P(X_i|Y_j)$(样本中目标事件$Y_j$发生时,各“证据”$X_i$的出现频率) 计算得到“后验”$P(Y_j|X_i)$(给定待判断的“证据”$X_i$,该“证据”出现的情况下目标事件$Y_j$的发生概率),通过比较后验的大小,来判断该“证据”出现的情况下最可能发生什么事件。 由于$P(X_i)$固定,实际上只需要计算固定$i$下的$P(Y_jX_i)$(被称为“联合概率”)并比较大小,最大的$P(Y_jX_i)$对应的$Y_j$即为判断结果。 #### 2.“朴素”的理解 “朴素”的含义,是各个“证据”$X_i$的发生是相互“条件独立”事件。也就是说,在“条件”事件$Y_j$发生下,各个“证据”$X_i$的发生是相互独立事件。公式为: $$P(X_mX_n|Y_j)=P(X_m|Y_j)\cdot P(X_n|Y_j)$$ 这直接决定了给定多个待判断的“证据”$x_1,x_2,\cdots,x_n$时,“后验”$P(Y_j|x_1x_2\cdots x_n)$的计算公式:$$ P(Y_j|x_1x_2\cdots x_n)=P(Y_j)\cdot\frac{\Pi_{k=1}^nP(x_k|Y_j)}{P(x_1\cdots x_n)} $$ (本文以“多项式”朴素贝叶斯算法为例,其它类别还有“高斯”(“证据”在区间内连续分布)以及“伯努利”(“证据”只有0和1)) 实际上只需要计算“联合概率”$P(Y_j)\cdot\Pi_{k=1}^nP(x_k|Y_j)$。 由于计算机的浮点数精度误差,为了防止过多连乘把上述结果约成0,一般取对数,比较不同$Y_j$下$\ln P(Y_j)+\Sigma_{k=1}^n\ln P(x_k|Y_j)$的大小,来判断给定证据$x_1,x_2,\cdots,x_n$下最可能出现哪个$Y_j$。 简单来说,“朴素”决定多“证据”下“后验”的计算公式。 #### 3.“似然”的计算讲解 由于“先验”$P(Y_j)$可以直接通过统计得到,“后验”$P(Y_j|X_i)$由上述公式计算而得,因此算法的技术主要在于“似然”$P(X_i|Y_j)$的计算。 一个比较常用的技术是:**拉普拉斯平滑**。这个技术被用来解决某个$P(X_i|Y_j)=0$的情况,也就是在样本中,某个事件$Y_j$下,某个“证据”$X_i$从未出现过的情况。 若放任$P(X_i|Y_j)=0$,可能会在给出待判断“证据”$X_i$时,$\ln P(X_i|Y_j)$不存在,以及所有的$P(Y_jX_i)=0$,无法通过比较得出目标$Y_j$。 为了解决这个问题,在计算“似然”$P(X_i|Y_j)$时,将每个$X_i$的出现频数增加1(被称为$\alpha=1$的拉普拉斯平滑,最为常用),得到新的“似然”计算公式:$$ P(X_i|Y_j)=\frac{N(X_i|Y_j)+1}{N(Y_j)+m} $$ 其中$m$表示所有会出现的“证据”$X_i$的种类数。 #### 4.示例与相关代码 问题:判断一封邮件是**垃圾邮件(Spam)**,还是**正常邮件(Ham)**。 已知的样本数据: | 邮件 | 类别 | 包含的单词 | |------|------|-----------| | 邮件1 | Spam | 免费,优惠 | | 邮件2 | Spam | 免费 | | 邮件3 | Ham | 你好 | | 邮件4 | Ham | 优惠,你好 | 所有“证据”:**免费、优惠、你好**$\to m=3$ 1. 计算先验$P(Y_j)$ 根据样本数据:$P(Spam)=\frac{1}{2},P(Ham)=\frac{1}{2}$。 2. 计算似然$P(X_i|Y_j)$(使用$\alpha=1$的拉普拉斯平滑): **Spam**: $$\begin{align*} P(免费|Spam)=\frac{2+1}{3+3}&=\frac{1}{2}\\ P(优惠|Spam)=\frac{1+1}{3+3}&=\frac{1}{3}\\ P(你好|Spam)=\frac{0+1}{3+3}&=\frac{1}{6} \end{align*} $$ **Ham**: $$\begin{align*} P(免费|Ham)=\frac{0+1}{3+3}&=\frac{1}{6}\\ P(优惠|Ham)=\frac{1+1}{3+3}&=\frac{1}{3}\\ P(你好|Ham)=\frac{2+1}{3+3}&=\frac{1}{2} \end{align*} $$ 1. 用“朴素”条件独立假设计算后验 1. 示例邮件:包含“证据”:**免费、你好** 首先计算每个事件对应的“联合概率”$P(Y_j)\cdot\Pi_{k=1}^n(x_k|Y_j)$ **Spam**事件:$$\begin{align*} &P(Spam)P(免费|Spam)P(你好|Spam)\\ &=\frac{1}{2}\cdot\frac{1}{2}\cdot\frac{1}{6}\\ &=\frac{1}{24} \end{align*} $$ (使用对数:$\ln P(Spam)+\ln P(免费|Spam)+\ln P(你好|Spam)=-\ln24$) **Ham**事件:$$\begin{align*} &P(Ham)P(免费|Ham)P(你好|Ham)\\ &=\frac{1}{2}\cdot\frac{1}{6}\cdot\frac{1}{2}\\ &=\frac{1}{24} \end{align*} $$ (使用对数:$\ln P(Ham)+\ln P(免费|Ham)+\ln P(你好|Ham)=-\ln24$) 两者相等,说明有相等把握认为该邮件为Spam或Ham。 2. 示例邮件:包含“证据”:**免费、优惠** 首先计算每个事件对应的“联合概率”$P(Y_j)\cdot\Pi_{k=1}^n(x_k|Y_j)$ **Spam**事件:$$\begin{align*} &P(Spam)P(免费|Spam)P(优惠|Spam)\\ &=\frac{1}{2}\cdot\frac{1}{2}\cdot\frac{1}{3}\\ &=\frac{1}{12} \end{align*} $$ (使用对数:$\ln P(Spam)+\ln P(免费|Spam)+\ln P(优惠|Spam)=-\ln12$) **Ham**事件:$$\begin{align*} &P(Ham)P(免费|Ham)P(优惠|Ham)\\ &=\frac{1}{2}\cdot\frac{1}{6}\cdot\frac{1}{3}\\ &=\frac{1}{36} \end{align*} $$ (使用对数:$\ln P(Ham)+\ln P(免费|Ham)+\ln P(优惠|Ham)=-\ln36$) $\frac{1}{12}>\frac{1}{36}$或$-\ln12>-\ln36$,表明最有可能是Spam。 示例代码: ```matlab X_train=[ % 训练样本(每行代表一封邮件,每列代表一个关键词) 1, 1, 0; % Spam 1, 0, 0; % Spam 0, 0, 1; % Ham 0, 1, 1; % Ham ];%“免费” “优惠” “你好” y_train=[1,1,0,0]'; % 标签:1表示垃圾邮件,0表示正常邮件 n_samples=length(y_train); n_spam=sum(y_train==1); n_ham=sum(y_train==0); % 计算先验概率 P_spam=n_spam/n_samples; % P(Spam) P_ham=n_ham/n_samples; % P(Ham) alpha=1,m=3; % 拉普拉斯平滑法计算似然概率 % 分别统计 Spam 和 Ham 类中每个特征的出现次数 spam_samples = X_train(y_train == 1, :); % Spam类样本 ham_samples = X_train(y_train == 0, :); % Ham类样本 % 计算各类别的总词数(分母 N(Y_j)) N_spam = sum(sum(spam_samples)); % Spam类总词数 = 3 N_ham = sum(sum(ham_samples)); % Ham类总词数 = 3 % 计算每个特征在各类别中出现的次数(分子 N(X_i|Y_j)) N_feat_spam = sum(spam_samples, 1); % [2, 1, 0] N_feat_ham = sum(ham_samples, 1); % [0, 1, 2] % 应用拉普拉斯平滑公式:P(X_i|Y_j) = (N(X_i|Y_j) + alpha) / (N(Y_j) + alpha*m) P_feat_spam = (N_feat_spam + alpha) / (N_spam + alpha * m); % [0.5, 0.333, 0.167] P_feat_ham = (N_feat_ham + alpha) / (N_ham + alpha * m); % [0.167, 0.333, 0.5] % 测试邮件1:包含 [免费, 你好] test_mail_1 = [1, 0, 1]; % 免费=是,优惠=否,你好=是 fprintf('测试邮件1:[免费, 你好]\n'); % 计算联合概率(非对数形式) joint_spam_1 = P_spam * prod(P_feat_spam(test_mail_1 == 1)); joint_ham_1 = P_ham * prod(P_feat_ham(test_mail_1 == 1)); % 计算对数形式(防止数值下溢) log_joint_spam_1 = log(P_spam) + sum(log(P_feat_spam(test_mail_1 == 1))); log_joint_ham_1 = log(P_ham) + sum(log(P_feat_ham(test_mail_1 == 1))); fprintf(' 联合概率: Spam=%.4f, Ham=%.4f\n', joint_spam_1, joint_ham_1); fprintf(' 对数形式: Spam=%.4f, Ham=%.4f\n', log_joint_spam_1, log_joint_ham_1); if joint_spam_1 > joint_ham_1 fprintf(' → 预测结果: 垃圾邮件(Spam)\n'); elseif joint_spam_1 < joint_ham_1 fprintf(' → 预测结果: 正常邮件(Ham)\n'); else fprintf(' → 预测结果: 无法判断(概率相等)\n'); end fprintf('\n'); % 测试邮件2:包含 [免费, 优惠] test_mail_2 = [1, 1, 0]; % 免费=是,优惠=是,你好=否 fprintf('测试邮件2:[免费, 优惠]\n'); joint_spam_2 = P_spam * prod(P_feat_spam(test_mail_2 == 1)); joint_ham_2 = P_ham * prod(P_feat_ham(test_mail_2 == 1)); log_joint_spam_2 = log(P_spam) + sum(log(P_feat_spam(test_mail_2 == 1))); log_joint_ham_2 = log(P_ham) + sum(log(P_feat_ham(test_mail_2 == 1))); fprintf(' 联合概率: Spam=%.4f, Ham=%.4f\n', joint_spam_2, joint_ham_2); fprintf(' 对数形式: Spam=%.4f, Ham=%.4f\n', log_joint_spam_2, log_joint_ham_2); if joint_spam_2 > joint_ham_2 fprintf(' → 预测结果: 垃圾邮件(Spam)\n'); elseif joint_spam_2 < joint_ham_2 fprintf(' → 预测结果: 正常邮件(Ham)\n'); else fprintf(' → 预测结果: 无法判断(概率相等)\n'); end ``` 运行结果: ``` 测试邮件1:[免费, 你好] 联合概率: Spam=0.0417, Ham=0.0417 对数形式: Spam=-3.1781, Ham=-3.1781 → 预测结果: 无法判断(概率相等) 测试邮件2:[免费, 优惠] 联合概率: Spam=0.0833, Ham=0.0278 对数形式: Spam=-2.4849, Ham=-3.5835 → 预测结果: 垃圾邮件(Spam) ``` 与手动计算相吻合。 #### 5.应用场景 - 文本分类:垃圾邮件识别、情感分析、新闻分类 - 推荐系统:基于用户行为的兴趣分类 - 医疗诊断:根据症状初步判断疾病 - 信用评估:判断用户是否会违约