尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
从零实现朴素贝叶斯分类器:高斯与多项式变体源码详解
简介本资源为面向计算机、人工智能及相关专业学生的课程实验资料包围绕朴素贝叶斯分类器与AODE分类器的Python实现展开适合在校学生完成课程设计、作业或毕设初期立项也可供教师与企业员工参考学习。压缩包共26个文件约256KB以10个py源码文件为核心辅以6张png实验图表、4个npy与3个npz数据文件以及2个md说明文档和1个txt依赖清单覆盖从算法实现到结果可视化的完整流程。内容涉及朴素贝叶斯、AODE、RBF、BP、K-means、LVQ、MoG等多种经典机器学习算法并配有MNIST、lung、yale等数据集的实验脚本与准确率、NMI等指标曲线图便于读者理解模型训练与评估过程。目前已有373人学习下载代码均经测试可运行读者可在此基础上修改扩展快速完成实验报告或二次开发。1. 课程实验里手撸朴素贝叶斯为什么它仍是分类入门的第一选择如果你正在做课程实验要求基于 Python 实现一个朴素贝叶斯分类器并且需要提交源码和项目说明那这篇笔记就是为你写的。我带过几届学生的课程设计也审过不少企业新人的练手项目发现一个反直觉的现象很多人宁愿花三天调一个神经网络的超参数也不愿意花三小时把朴素贝叶斯的概率推导和工程实现吃透。结果就是模型跑通了但问他“为什么拉普拉斯平滑要加在分子和分母上”“连续特征的高斯假设到底怎么落到代码里”答不上来。朴素贝叶斯分类器的源码其实很短核心逻辑不到一百行但它是理解生成式模型、贝叶斯决策和特征条件独立假设的最佳入口。这个实验适合两类人一是正在上机器学习或数据挖掘课程、需要交课程设计的学生二是想从调包侠往回走一步、搞清楚分类器内部到底在算什么的新手。你不需要 GPU不需要复杂环境一台装了 Python 的普通笔记本就能跑完整个流程。接下来我会按“先讲清楚原理和选型理由再给可复现的代码和参数说明最后说踩过的坑”这个顺序把这件事讲透。2. 朴素贝叶斯分类器的三种变体与选型逻辑2.1 高斯、多项式、伯努利你的数据该用哪一个朴素贝叶斯不是单一算法而是一族算法的统称核心区别在于对条件概率 ( P(x_i | y) ) 的建模方式不同。课程实验里最常见的是三种高斯朴素贝叶斯GaussianNB、多项式朴素贝叶斯MultinomialNB和伯努利朴素贝叶斯BernoulliNB。选错变体准确率可能直接掉十几个百分点这不是玄学是假设与数据分布不匹配导致的。高斯朴素贝叶斯假设每个特征在给定类别下服从正态分布适合特征是连续值且大致呈钟形分布的场景比如鸢尾花数据集的花瓣长度、身高、体重这类测量值。它的条件概率计算依赖均值和方差训练时就是按类别统计每个特征的均值和方差。多项式朴素贝叶斯假设特征服从多项分布典型应用是文本分类中的词频特征。它关心的是“某个词在某个类别下出现了多少次”而不是“有没有出现”。如果你把 TF-IDF 或词频矩阵喂给它效果通常不错。伯努利朴素贝叶斯则假设特征是二值的只关心“出现”或“不出现”适合短文本分类或特征本身就是 0/1 的场景。它对未出现的词也会显式建模这在长文本里反而可能引入噪声。选型逻辑可以总结成一句话连续特征用高斯计数特征用多项式二值特征用伯努利。课程实验里如果老师没指定数据集我一般建议用鸢尾花数据集配高斯朴素贝叶斯因为特征连续、类别平衡、样本量小跑得快且结果直观如果想做文本分类就用 20 新闻组数据集配多项式朴素贝叶斯能顺便练一下文本向量化。2.2 条件独立假设它错得离谱但为什么还能用朴素贝叶斯最被诟病的就是“特征条件独立假设”——它假设在给定类别的情况下各个特征之间互不影响。现实中这个假设几乎从来不成立。比如文本分类里“机器”和“学习”这两个词同时出现的概率远高于它们独立出现的概率乘积。但有意思的是即使假设明显错误朴素贝叶斯在很多任务上仍然表现不错尤其是样本量不大、特征维度高的场景。原因有两层。第一层是分类决策只关心后验概率的相对大小不关心绝对概率值。只要正确类别的后验概率比其他类别大分类就正确哪怕概率估计本身有偏差。第二层是特征之间的依赖关系如果在各个类别中表现相似那么在做类别比较时这些依赖带来的偏差会相互抵消。这也是为什么朴素贝叶斯常被称为“朴素但有效”。在课程实验的源码实现里你不需要去纠正这个假设但项目说明里最好写清楚本实现基于条件独立假设适用于特征冗余度不高的场景如果特征间强相关可以考虑半朴素贝叶斯或贝叶斯网络。这样老师一看就知道你理解边界在哪。2.3 拉普拉斯平滑一个参数避免零概率灾难假设你在做文本分类训练集里“比特币”这个词只在“科技”类别中出现过在“体育”类别中一次都没出现。那么当测试集里出现“比特币”时多项式朴素贝叶斯计算 ( P(\text{比特币}|\text{体育}) ) 会得到 0。由于是连乘整个后验概率直接变成 0模型完全无法处理这个样本。这就是零概率问题。拉普拉斯平滑Laplace Smoothing解决这个问题做法是给每个特征的计数加一个小的正数 ( \alpha )通常取 1。公式从 ( P(x_i|y) \frac{N_{yi}}{N_y} ) 变成 ( P(x_i|y) \frac{N_{yi} \alpha}{N_y \alpha \cdot n} )其中 ( n ) 是特征取值个数。分子加 ( \alpha ) 避免零分母加 ( \alpha \cdot n ) 保证概率和为 1。在 sklearn 里alpha参数默认是 1.0对应拉普拉斯平滑如果设成小于 1 的值比如 0.1叫利德斯通平滑Lidstone Smoothing适合特征空间很大、需要更弱平滑的场景。课程实验里我一般先用默认值跑基线再尝试alpha在[0.01, 0.1, 0.5, 1.0, 2.0]里调一下看验证集准确率变化。注意高斯朴素贝叶斯没有alpha参数它的平滑体现在方差估计时加一个极小值var_smoothing防止方差为零导致除零错误。3. 从零实现高斯朴素贝叶斯源码逐段拆解与参数说明3.1 数据准备与按类别统计均值方差先导入必要的库并加载鸢尾花数据集。这个数据集有 150 个样本4 个连续特征3 个类别每个类别 50 个样本非常适合验证实现是否正确。import numpy as np from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split # 加载数据 iris load_iris() X, y iris.data, iris.target # 划分训练集和测试集固定随机种子保证可复现 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) print(f训练集样本数: {X_train.shape[0]}, 测试集样本数: {X_test.shape[0]}) print(f特征维度: {X_train.shape[1]}, 类别数: {len(np.unique(y_train))})这段代码做了三件事加载数据、按 7:3 划分、打印基本信息。stratifyy保证训练集和测试集的类别比例与原始数据一致避免某个类别在测试集中缺失。random_state42是固定随机种子保证每次运行划分结果相同方便调试。接下来按类别统计每个特征的均值和方差。这是高斯朴素贝叶斯训练阶段的核心。class GaussianNBFromScratch: def __init__(self, var_smoothing1e-9): self.var_smoothing var_smoothing self.classes None self.means {} self.vars {} self.priors {} def fit(self, X, y): self.classes np.unique(y) n_features X.shape[1] for c in self.classes: X_c X[y c] self.means[c] np.mean(X_c, axis0) # 方差加平滑项防止为零 self.vars[c] np.var(X_c, axis0) self.var_smoothing self.priors[c] X_c.shape[0] / X.shape[0] return selffit方法遍历每个类别取出该类别下的所有样本计算每个特征的均值和方差。var_smoothing默认设成1e-9加到方差上避免除零。先验概率priors就是类别频率鸢尾花三个类别各占三分之一。这里没有做任何特征缩放因为高斯朴素贝叶斯对尺度不敏感——它按特征独立建模每个特征有自己的均值和方差。3.2 对数后验概率计算与预测函数预测阶段需要计算每个类别的对数后验概率取最大值对应的类别。用对数是为了避免多个小概率连乘导致数值下溢。def _log_likelihood(self, X, c): mean self.means[c] var self.vars[c] # 高斯分布对数概率密度 return -0.5 * np.sum(np.log(2 * np.pi * var)) \ -0.5 * np.sum((X - mean) ** 2 / var, axis1) def predict(self, X): log_probs [] for c in self.classes: log_prior np.log(self.priors[c]) log_lik self._log_likelihood(X, c) log_probs.append(log_prior log_lik) # 按列取最大值的索引映射回类别标签 log_probs np.array(log_probs).T return self.classes[np.argmax(log_probs, axis1)]_log_likelihood里第一项是高斯分布对数归一化常数第二项是指数部分的对数。注意np.sum(..., axis1)是对每个样本的所有特征求和因为条件独立假设下联合概率是各特征概率的乘积取对数后变成求和。predict把先验对数概率和似然对数概率相加得到每个类别的得分然后取最大值的索引。np.argmax(log_probs, axis1)返回每个样本得分最高的类别索引再用self.classes映射回原始标签。跑一下看看准确率model GaussianNBFromScratch() model.fit(X_train, y_train) y_pred model.predict(X_test) accuracy np.mean(y_pred y_test) print(f手写高斯朴素贝叶斯测试集准确率: {accuracy:.4f})在鸢尾花数据集上这个简单实现通常能拿到 0.93 到 0.97 的准确率和 sklearn 的GaussianNB几乎一致。如果差太多检查一下方差平滑项和求和轴是否正确。3.3 与 sklearn 版本对比验证为了确认手写实现没有逻辑错误用 sklearn 的GaussianNB跑同样的数据对比准确率和预测结果。from sklearn.naive_bayes import GaussianNB sk_model GaussianNB() sk_model.fit(X_train, y_train) sk_pred sk_model.predict(X_test) sk_accuracy np.mean(sk_pred y_test) print(fsklearn 高斯朴素贝叶斯准确率: {sk_accuracy:.4f}) print(f两者预测一致的样本数: {np.sum(y_pred sk_pred)} / {len(y_test)})如果手写版本和 sklearn 版本准确率差距在 1% 以内且预测一致的样本数超过 95%说明实现基本正确。差异可能来自方差平滑项的默认值不同——sklearn 的var_smoothing默认是1e-9但它是相对于全局最大方差的比例而手写版本是直接加绝对值。课程实验里不必追求完全一致但项目说明里要写清楚这个差异。4. 多项式朴素贝叶斯做文本分类词频矩阵与平滑参数调优4.1 文本向量化CountVectorizer 的关键参数多项式朴素贝叶斯处理文本时第一步是把文本转成词频矩阵。用 sklearn 的CountVectorizer可以快速完成但有几个参数直接影响模型效果。from sklearn.feature_extraction.text import CountVectorizer from sklearn.naive_bayes import MultinomialNB from sklearn.datasets import fetch_20newsgroups from sklearn.metrics import classification_report # 加载部分新闻组数据只取四个类别加快速度 categories [alt.atheism, comp.graphics, sci.med, soc.religion.christian] newsgroups_train fetch_20newsgroups(subsettrain, categoriescategories, random_state42) newsgroups_test fetch_20newsgroups(subsettest, categoriescategories, random_state42) # 向量化去掉英文停用词限制最大特征数 vectorizer CountVectorizer(stop_wordsenglish, max_features5000) X_train_counts vectorizer.fit_transform(newsgroups_train.data) X_test_counts vectorizer.transform(newsgroups_test.data) print(f训练集词频矩阵形状: {X_train_counts.shape}) print(f测试集词频矩阵形状: {X_test_counts.shape})stop_wordsenglish去掉 the、a、is 这类高频但无区分度的词。max_features5000只保留出现频率最高的 5000 个词控制特征维度避免过拟合和内存爆炸。注意fit_transform只在训练集上调用测试集用transform保证词汇表一致。如果测试集出现训练集没有的词直接忽略这是标准做法。4.2 alpha 参数对分类报告的影响MultinomialNB的alpha参数控制平滑强度。默认alpha1.0是拉普拉斯平滑适合大多数场景。如果特征维度很高、样本量相对不足可以尝试更小的alpha比如 0.1 或 0.01让模型更依赖实际计数。for alpha in [0.01, 0.1, 0.5, 1.0, 2.0]: clf MultinomialNB(alphaalpha) clf.fit(X_train_counts, newsgroups_train.target) y_pred clf.predict(X_test_counts) acc np.mean(y_pred newsgroups_test.target) print(falpha{alpha:.2f}, 测试集准确率: {acc:.4f})跑完你会看到准确率随alpha变化通常alpha在 0.1 到 1.0 之间有一个峰值。如果alpha太小模型对训练集噪声敏感alpha太大概率分布被过度平滑区分度下降。课程实验里可以把不同alpha的准确率画成折线图项目说明里解释为什么选某个值。分类报告能给出每个类别的精确率、召回率和 F1 分数clf_best MultinomialNB(alpha0.1) clf_best.fit(X_train_counts, newsgroups_train.target) y_pred_best clf_best.predict(X_test_counts) print(classification_report(newsgroups_test.target, y_pred_best, target_namesnewsgroups_test.target_names))如果某个类别的召回率明显偏低说明该类别的特征词被其他类别“抢”走了可以考虑增加该类别的训练样本或者调整特征选择策略。注意多项式朴素贝叶斯对类别不平衡比较敏感如果某个类别样本很少先验概率会很低预测时容易被忽略。可以在fit时用class_prior参数手动指定先验或者对训练集做重采样。5. 避坑与排查课程实验里最容易翻车的五个地方5.1 零概率导致预测结果全为同一类现象测试集所有样本都被预测成同一个类别准确率等于该类别在测试集中的占比。原因某个特征在某个类别下从未出现概率为零连乘后整个后验概率为零模型只能选那个没有零概率的类别。解决检查是否使用了平滑。多项式朴素贝叶斯设alpha大于 0高斯朴素贝叶斯设var_smoothing大于 0。如果是手写实现确认分子分母都加了平滑项。5.2 特征尺度差异大导致高斯假设失效现象高斯朴素贝叶斯在某个数据集上准确率远低于预期甚至不如随机猜测。原因某个特征的取值范围是 0 到 1另一个是 0 到 10000高斯分布对大方差特征过度敏感小方差特征被淹没。解决虽然高斯朴素贝叶斯理论上对尺度不敏感但实际计算中方差差异过大会导致数值问题。常见做法是对连续特征做标准化用StandardScaler把每个特征缩放到均值 0、方差 1。注意标准化要在训练集上拟合再应用到测试集。5.3 文本分类里 fit_transform 和 transform 混用现象测试集准确率异常高接近 100%但换一批新数据就崩了。原因对测试集也调用了fit_transform导致词汇表包含了测试集的词相当于提前看到了测试数据。解决严格区分fit_transform和transform。训练集用fit_transform学习词汇表和文档频率测试集只能用transform做映射。这是数据泄露的经典案例课程实验报告里如果出现这种错误会被直接扣分。5.4 先验概率被忽略或手动设错现象模型在类别不平衡数据上表现很差少数类几乎不被预测。原因MultinomialNB默认用训练集的类别频率作为先验如果训练集里多数类占 90%少数类占 10%先验概率会强烈偏向多数类。解决如果业务上需要提升少数类召回率可以在fit时用class_prior参数手动指定先验比如设成[0.5, 0.5]。但要注意这样做会牺牲整体准确率需要根据任务目标权衡。课程实验里如果没要求处理不平衡保持默认即可但项目说明里要提到这个参数的存在。5.5 高斯朴素贝叶斯对非正态特征效果差现象在某个连续特征数据集上高斯朴素贝叶斯准确率明显低于逻辑回归或决策树。原因特征分布严重偏斜比如收入分布、点击次数分布高斯假设完全不成立。解决先画每个特征在每个类别下的直方图确认是否近似正态。如果偏斜严重可以做对数变换或 Box-Cox 变换再喂给高斯朴素贝叶斯。或者换用核密度估计的朴素贝叶斯变体但 sklearn 没有内置需要自己实现。课程实验里如果时间有限直接换多项式朴素贝叶斯做分箱离散化也是一种思路。6. 进阶技巧用交叉验证选平滑参数并画出学习曲线课程实验如果只跑一次训练集测试集划分结论可能不稳定。我一般会加一层交叉验证用GridSearchCV自动搜索最优alpha再画学习曲线看模型是否过拟合或欠拟合。from sklearn.model_selection import GridSearchCV from sklearn.pipeline import Pipeline # 构建管道向量化 多项式朴素贝叶斯 pipeline Pipeline([ (vectorizer, CountVectorizer(stop_wordsenglish, max_features5000)), (clf, MultinomialNB()) ]) # 搜索 alpha 参数 param_grid {clf__alpha: [0.01, 0.05, 0.1, 0.5, 1.0, 2.0]} grid_search GridSearchCV(pipeline, param_grid, cv5, scoringaccuracy, n_jobs-1) grid_search.fit(newsgroups_train.data, newsgroups_train.target) print(f最优 alpha: {grid_search.best_params_[clf__alpha]}) print(f交叉验证最优准确率: {grid_search.best_score_:.4f})Pipeline把向量化和分类器串在一起避免在交叉验证中数据泄露——每一折的向量化只在训练部分拟合。cv5是五折交叉验证n_jobs-1用满所有 CPU 核心加速。grid_search.best_params_给出最优alpha通常和之前手动试出来的峰值一致但交叉验证的结果更可靠。学习曲线能看出模型随训练样本增加的表现from sklearn.model_selection import learning_curve import matplotlib.pyplot as plt train_sizes, train_scores, test_scores learning_curve( grid_search.best_estimator_, newsgroups_train.data, newsgroups_train.target, cv5, n_jobs-1, train_sizesnp.linspace(0.1, 1.0, 5), scoringaccuracy ) train_mean np.mean(train_scores, axis1) test_mean np.mean(test_scores, axis1) plt.plot(train_sizes, train_mean, o-, label训练集准确率) plt.plot(train_sizes, test_mean, o-, label交叉验证准确率) plt.xlabel(训练样本数) plt.ylabel(准确率) plt.legend() plt.title(多项式朴素贝叶斯学习曲线) plt.show()如果训练集准确率远高于交叉验证准确率说明过拟合可以增大alpha或减少max_features。如果两条曲线都很低且靠得很近说明欠拟合可以减小alpha或增加特征数。我一般会把这张图放进课程实验报告比单纯列一个准确率数字有说服力得多。最后说一个我自己的习惯每次写完朴素贝叶斯源码都会先用一个极小的手工数据集比如 6 个样本、2 个特征、2 个类别跑一遍把中间概率打印出来手算验证。这个习惯帮我抓过好几次求和轴搞反、平滑项加错位置的 bug。朴素贝叶斯分类器的源码虽然短但概率计算的每一步都值得较真。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

开源代码评审工具 open-code-review:设计思路与落地实践

开源代码评审工具 open-code-review:设计思路与落地实践

下午三点,我刚把分支推上去,群里就炸了。A 说这个函数名看不懂,B 说边界情况没处理,运维那边又催着问接口日志打全了没有。我一边回消息一边改代码,突然意识到:这一轮所谓"代码评审",…

📅 2026/10/12 16:04:27
基于MATLAB的BP神经网络代码:工具箱快速上手与手写实现

基于MATLAB的BP神经网络代码:工具箱快速上手与手写实现

简介:一份基于MATLAB的BP神经网络完整入门示例,面向对人工神经网络与非线性回归、分类问题感兴趣的初学者及科研人员,旨在解决实践中如何从零搭建并训练BP模型的问题。压缩包共有5个文件,内含可直接运行的MATLAB脚本、配套训练数据…

📅 2026/10/12 16:04:27
Python爬虫入门实战:批量下载游戏宠物图片

Python爬虫入门实战:批量下载游戏宠物图片

1. 从"想收集宠物图鉴"这个念头说起很多刚学Python的朋友都会经历这样一个阶段:语法学完了,练习题也刷了不少,但真让自己独立做点什么东西,脑子里一片空白。我当初也是这样,后来发现最好的突破口就是找一个自…

📅 2026/10/12 16:04:27
MORE NEWS

更多资讯

📰

Python机器学习人脸识别系统:从期末大作业到门禁机实战

简介:这份资源是面向高校学生与Python初学者的机器学习人脸识别系统完整项目包,可直接用于期末大作业、课程设计或自学练手。压缩包共4个文件,包含2个py源码文件、1份pdf报告文档和1份md说明文件,整体约631KB,体积轻便…

📰

基于Python的单通道脑电睡眠分期:从数据到模型全流程实战

简介:这是一套面向计算机相关专业学生与项目实战学习者的单通道脑电信号自动睡眠分期研究资料,源自经导师指导并通过评审的高分毕业设计,适合作为毕设参考、课程设计或期末大作业。资源围绕单通道脑电信号展开,涵盖数据预处理、模…

📰

Python机器学习实战:从环境配置到模型训练复现的完整流程

简介:《Python机器学习编程与实战》配套源代码与实验数据压缩包,面向从零起步的机器学习初学者和高校教学场景,旨在通过实际代码与数据集,帮助读者把NumPy、Pandas、Matplotlib、sklearn等库的操作落到真实案例中。包内共77个文件…

📰

供应链断裂与重构测度:基于Python的级联失效与替代路径实现

简介:针对《经济研究》2024年第8期丁浩员等提出的跨国供应链断裂与重构测度方法,这份Python代码包给出了从数据清洗到指标计算的完整复现路径。资源面向经济学研究者、供应链分析人员及数据科学从业者,基于FactSet Revere全球供应链数据库&am…

📰

Python车牌识别计费系统实战:从OCR识别到状态机与跨天计费

简介:面向需要构建智能停车管理系统的 Python 开发者、计算机专业学生以及毕业设计人员,这份源码包提供了一套从车牌识别、车辆入场出场判断、收入统计到满预警的完整可运行方案,并可在 PyCharm 中直接运行,借助百度 AI 开放平台的…

📰

南开编译原理作业实战:简单C语言编译器源码解析与避坑指南

简介:这份资源是南开大学软件学院编译原理课程的作业成果,包含一个可运行的简单C语言编译器完整源码与配套文档,面向计算机、人工智能、通信工程等专业的在校学生及教师,也适合作为课程设计、毕业设计或自学编译原理的参考范例。压…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬