BP神经网络分类实战:鸢尾花与红酒数据集从源码到答辩

发布时间:2026/10/9 6:27:35
BP神经网络分类实战:鸢尾花与红酒数据集从源码到答辩 简介基于Python的BP神经网络分类项目聚焦鸢尾花与红酒两个经典数据集适合机器学习初学者、课程设计及毕业设计场景。源码采用面向过程与模块化结合的方式完整涵盖网络初始化、前向传播、反向传播、权重更新及准确率评估等核心环节关键代码均附带注释安装依赖后只需简单配置即可运行。配套实验报告深入讲解BP算法原理、参数对比与结果分析答辩PPT则提炼研究思路与演示要点帮助使用者快速掌握项目全貌。压缩包共21个文件包含Python脚本、Jupyter Notebook交互式运行笔记、实验报告Word文档、答辩PPT以及Excel格式的数据集文件整体大小仅1.37MB轻量且结构清晰。已有286人学习使用项目源自导师认可的高分工作具备较强的实用性与扩展性替换数据集或调整网络结构即可迁移到新的分类任务中也便于二次开发与深入学习。1. 用BP神经网络分类鸢尾花和红酒从源码到实验报告、答辩PPT的完整路线网上搜“免费python源码大全”能翻到大量BP神经网络现成实现但真正能把代码讲清楚、写进实验报告、经得起答辩追问的少之又少。用Python基于BP神经网络模型完成鸢尾花和红酒数据集的分类是初学者最容易上手的两个多分类任务样本量适中、特征维度不同、类别都是三类。这篇笔记从网络结构怎么定、数据集怎么处理写起给出能直接复现的源码再说明实验报告和答辩PPT里要放哪些数据才能让人信服。适合机器学习初学者、课程设计和毕设前期验证场景照着做完你就有了一套可以复用的分类实验模板。2. 环境与数据集处理先对照BP神经网络结构图再动手2.1 Python环境与依赖库装包兜底清单BP神经网络的实现本身不需要深度学习框架用NumPy手写矩阵运算反而能让原理更清楚。但整个流程里还要做数据处理、数据集切分和画图所以建议一次装齐以下依赖库python -m pip install numpy pandas scikit-learn matplotlib openpyxl如果下载速度不理想可以在命令后面加-i https://pypi.tuna.tsinghua.edu.cn/simple使用清华源。这里有个新手容易忽略的点numpy管矩阵运算scikit-learn管数据集加载与评估pandas和openpyxl解决“老师发来的数据集是 xlsx 文件”这类场景matplotlib用来画损失曲线和结构图。不用急着在 python 官网下载重装若当前环境是 Python 3.8 或更新版本直接按上面命令安装即可。我一般建议把数据集和代码放在同一个文件夹并且给每个数据集建一个独立的data/目录后面做实验报告时能清晰说明数据来源。顺便说一句如果你拿到的是 xlsx 格式的数据不要手动另存为 CSV直接让 pandas 读取既保留原始结构又能复现处理过程。2.2 鸢尾花和红酒数据集下载内置加载、CSV导出与xlsx读取先看两份数据的基本规格这是后面定网络结构的依据数据集样本数特征数类别数多分类难度Iris 鸢尾花15043低Wine 红酒178133中鸢尾花数据集是经典的 4 个花器特征花萼长宽、花瓣长宽类别为三个品种红酒数据集则是 13 个化学指标酒精、苹果酸、灰分等对应三种栽培品种。很多人单独搜索“鸢尾花数据集下载”其实 scikit-learn 内置了这两份数据pull 下来即在本地成型不必绕去外网手动下载。把内置数据导出保存成 CSV 留档是常见做法from sklearn.datasets import load_iris, load_wine import pandas as pd for name, loader in [(iris, load_iris), (wine, load_wine)]: data loader() df pd.DataFrame(data.data, columnsdata.feature_names) df[target] data.target df.to_csv(f{name}.csv, indexFalse) print(f{name} shape: {df.shape})load_iris()和load_wine()返回的是 Bunch 对象其中data是特征矩阵target是类别标签两者是分离的。导成 CSV 后方便你在 Excel 里直接核对特征取值范围也能让实验报告中“数据来源”这一节有清晰截图来源。如果课程作业发来的是 xlsx 文件则用另一个方式读取import pandas as pd df pd.read_excel(iris.xlsx) # 需要已安装 openpyxl X df.iloc[:, :-1].values.astype(float) # 最后 4 列是特征 y df.iloc[:, -1].values.astype(int) # 最后一列是标签 print(X.shape, y.shape)两种数据加载方式最终都要保证X是二维浮点数组、y是一维整数数组后续训练代码才能直接复用。把内置数据存的 CSV 与老师提供的 xlsx 对比一下如果列名或样本数不一致先处理数据而不是改模型这是做数据集分类前最重要的一步。我见过不少同学直接在网络上找“免费python源码大全”里的工程文件解压后数据集路径、特征顺序全是乱的最后花在调试数据上的时间比写模型还长。自己把数据导出一份留底既能让流程闭环也能避免答辩时被问到“数据是哪来的”时答不上来。2.3 画出BP神经网络结构图先定结构再写代码“BP神经网络结构图”是实验报告里最容易被挑毛病的部分因为很多人直接从别人博客复制一张图连维度都对不上。自己画并不难先确定结构再写代码。鸢尾花分类我用的是 4-5-3 结构输入层 4 个节点对应 4 个特征隐藏层 5 个节点输出层 3 个节点对应 3 个品种。红酒数据集是 13 个特征结构改为 13-8-3隐藏层节点数适当增加。隐藏层节点数没有唯一正确答案。经验范围一般是“输入维度与输出维度之间”再取中值鸢尾花 4 和 3 之间取 5 是常见做法红酒 13 个特征就取 8。节点太少会欠拟合太多会过拟合而且计算变慢。可以用 matplotlib 画一张结构图放进报告import numpy as np import matplotlib.pyplot as plt def draw_net(layers, title): fig, ax plt.subplots(figsize(9, 5)) ax.axis(off) gap 1.2 for li, size in enumerate(layers): ys np.arange(size) - (size - 1) / 2 xs np.full(size, li * gap) ax.scatter(xs, ys, s180, c#4C72B0, zorder3) if li 0: prev_ys np.arange(layers[li-1]) - (layers[li-1] - 1) / 2 for py in prev_ys: for cy in ys: ax.plot([(li-1)*gap, li*gap], [py, cy], lw0.5, colorgray, alpha0.6) ax.set_title(title, fontsize14) plt.tight_layout() plt.savefig(f{title}_structure.png, dpi150) plt.show() draw_net([4, 5, 3], iris_bp) draw_net([13, 8, 3], wine_bp)图中每个圆点是一个神经元连线是权重连接层与层之间全连接。把这张图放进实验报告的“模型设计”部分比任何文字描述都直观。至于结构图里要不要标注权重数值我的建议是只在输入层到隐藏层之间选几条关键边标上权重大小象征性展示即可全部标注反而看不清。激活函数的选择也要写进图注里隐藏层用 Sigmoid输出层用 Softmax。这样结构图和信息传递方式就形成了闭环答辩被追问“为什么用这个结构”时也能讲清楚。3. 用鸢尾花数据集训练BP神经网络手写前向传播与反向传播3.1 数据预处理与训练测试划分先划分再标准化搭建BP神经网络前数据必须做两件事划分训练集和测试集、特征标准化。划分顺序有讲究先切分再用训练集来拟合标准化器避免让测试集信息提前参与训练这是新手最容易犯的数据泄漏错误from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler import numpy as np iris load_iris() X, y iris.data, iris.target.reshape(-1, 1) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, stratifyy, random_state42 ) scaler StandardScaler().fit(X_train) X_train_s scaler.transform(X_train) X_test_s scaler.transform(X_test) def one_hot(y, k): n y.shape[0] yh np.zeros((n, k)) yh[np.arange(n), y.ravel()] 1 return yh y_train_oh one_hot(y_train, 3) y_test_oh one_hot(y_test, 3) print(训练集:, X_train_s.shape, 测试集:, X_test_s.shape)stratifyy参数让训练集和测试集里三个类别的比例保持一致150 个样本中每类各 50 个切分后训练集每类仍约 40 个避免某一类全落在测试集里导致准确率虚高或骤降。random_state42固定随机数种子保证可复现。标准化用 StandardScaler输出的是均值为 0、方差为 1 的标准分数。BP 神经网络依赖梯度下降更新权重如果某个特征数值范围特别大比如红酒数据集的某列可能差几十倍会主导误差梯度让权重更新失衡。先用训练集的均值和标准差做 fit再应用到测试集这是标准的“无泄漏”写法。最后为什么要做 one-hotBP 神经网络的输出层是 Softmax输出的是“属于每一类的概率向量”例如[0.8, 0.15, 0.05]表示第一类概率最高。而原始标签是单个整数0/1/2两者形状不匹配。one-hot 后标签变成[1,0,0]或[0,1,0]这类向量才能与输出层一一对应计算损失。3.2 构建BP神经网络类前向传播与反向传播实现手写BP网络核心就四个操作前向传播算输出、计算损失、反向传播求梯度、更新权重。我常写的类如下把网络结构和训练逻辑封装起来后面红酒数据集可以复用class BPNet: def __init__(self, in_dim, hidden_dim, out_dim, lr0.1, seed42): np.random.seed(seed) self.lr lr self.W1 np.random.randn(in_dim, hidden_dim) * 0.1 self.b1 np.zeros((1, hidden_dim)) self.W2 np.random.randn(hidden_dim, out_dim) * 0.1 self.b2 np.zeros((1, out_dim)) def sigmoid(self, x): return 1 / (1 np.exp(-x)) def softmax(self, x): e np.exp(x - x.max(axis1, keepdimsTrue)) return e / e.sum(axis1, keepdimsTrue) def forward(self, X): self.z1 X.dot(self.W1) self.b1 self.a1 self.sigmoid(self.z1) self.z2 self.a1.dot(self.W2) self.b2 self.a2 self.softmax(self.z2) return self.a2 def backward(self, X, y_oh, output): m X.shape[0] delta2 output - y_oh grad_W2 self.a1.T.dot(delta2) / m grad_b2 delta2.sum(axis0, keepdimsTrue) / m delta1 delta2.dot(self.W2.T) * self.a1 * (1 - self.a1) grad_W1 X.T.dot(delta1) / m grad_b1 delta1.sum(axis0, keepdimsTrue) / m self.W1 - self.lr * grad_W1 self.b1 - self.lr * grad_b1 self.W2 - self.lr * grad_W2 self.b2 - self.lr * grad_b2先说参数初始化的两个细节权重用np.random.randn(...) * 0.1让初始值接近 0 但不是 0。若权重初始为全 0所有隐藏神经元会学到完全一样的特征网络退化为“对称”状态若初始值过大经过 Sigmoid 后容易进入饱和区梯度近乎消失。偏置b初始化为 0 是安全的因为它不参与对称性问题。forward方法中隐藏层用 Sigmoid 激活输出层用 Softmax 把实数值压成概率分布。Softmax 实现里做了x - x.max(axis1, keepdimsTrue)的数值稳定处理防止指数爆炸。backward方法里最关键的是delta2 output - y_oh这行。它其实是 CrossEntropyLoss 和 Softmax 结合后的简化梯度也就是当预测概率与 one-hot 标签相减后误差越大梯度越大。这里如果手动推导偏导容易绕晕可以直接用“输出减标签”这个结论把重点放在梯度更新上。分母上的m是样本数因为用的是批量梯度下降梯度要除以批次大小取平均。3.3 训练、预测与损失曲线用结果验证模型训练循环里做两件事记录损失观察收敛情况。测试集只在最终评估时使用训练过程中不去碰它net BPNet(in_dim4, hidden_dim5, out_dim3, lr0.1, seed42) epochs 500 loss_list [] for epoch in range(epochs): output net.forward(X_train_s) loss -np.mean(y_train_oh * np.log(output 1e-8)) loss_list.append(loss) net.backward(X_train_s, y_train_oh, output) if (epoch 1) % 100 0: print(fepoch {epoch1}, loss{loss:.4f}) pred net.forward(X_test_s).argmax(axis1) acc (pred y_test.ravel()).mean() print(f测试集准确率: {acc:.4f})损失函数用的是交叉熵公式里加1e-8是为了防止output出现0导致对数取无穷。500 次迭代对鸢尾花这个规模完全够用正常情况下损失会从 1.0 左右逐步降到 0.1 以下。如果发现损失曲线在下降后突然反弹通常是学习率lr偏大把它从 0.1 调到 0.01 再试。预测时net.forward(X_test_s)输出的是一个 30x3 的概率矩阵每一行三个概率中最大值对应的类别就是预测结果argmax(axis1)取每行最大值的下标得到预测标签数组。与真实标签比较后算出准确率鸢尾花数据集上 4-5-3 结构跑到 95% 以上并不意外特别难的样本只有一两个。把损失画成曲线放进实验报告能直观展示收敛过程import matplotlib.pyplot as plt plt.figure(figsize(6, 3.5)) plt.plot(range(epochs), loss_list) plt.xlabel(epoch) plt.ylabel(loss) plt.title(Iris BP loss curve) plt.tight_layout() plt.savefig(iris_loss.png, dpi150) plt.show()如果曲线在某个 epoch 后基本水平不再下降说明模型已收敛此时继续增加迭代次数只会浪费时间。对鸢尾花这种小规模数据还没到需要早停策略的程度但保留一个“当连续 50 轮损失下降不足 0.001 则停止”的判断是更规范的写法。这份画图代码产出的图片直接就能插进实验报告的实验结果部分。4. 红酒数据集分类13 个特征下的网络结构与参数调整4.1 红酒数据集与鸢尾花的差异特征增多带来的三个问题红酒数据集同样有 178 个样本、3 个类别但特征从 4 个增加到 13 个。特征数量增加后模型要面对的第一件事是输入维度变大权重矩阵W1从 4x5 变成 13x8参数量不是线性增长而是成倍增加。第二个问题是特征间量纲差异更大红酒数据中某些特征均值在几十某些在几百如果跳过标准化损失曲线会震荡得厉害。第三个问题容易被忽视13 个特征里可能存在相关特征。比如红酒数据集中颜色强度与某些酚类物质可能相关这会带来一定程度的信息冗余但对手写BP网络来说影响不大不需要为此专门做 PCA 降维。课程设计阶段13-8-3 这样简单的结构足以拿到不错的准确率不必把问题复杂化。4.2 复用BPNet类调整输入维度与隐藏层节点仍然直接复用第 3.2 节的BPNet类不需要改类内部任何代码只换数据集和网络参数from sklearn.datasets import load_wine from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler wine load_wine() X, y wine.data, wine.target.reshape(-1, 1) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, stratifyy, random_state42 ) scaler StandardScaler().fit(X_train) X_train_s scaler.transform(X_train) X_test_s scaler.transform(X_test) y_train_oh one_hot(y_train, 3) y_test_oh one_hot(y_test, 3) net BPNet(in_dim13, hidden_dim8, out_dim3, lr0.05, seed42) epochs 1000 loss_list [] for epoch in range(epochs): output net.forward(X_train_s) loss -np.mean(y_train_oh * np.log(output 1e-8)) loss_list.append(loss) net.backward(X_train_s, y_train_oh, output) if (epoch 1) % 200 0: print(fepoch {epoch1}, loss{loss:.4f}) pred net.forward(X_test_s).argmax(axis1) print(f红酒测试集准确率: {(pred y_test.ravel()).mean():.4f})这里最重要的变化是学习率从 0.1 降到 0.05。特征维度变多后损失面更复杂学习率太大会让梯度更新跨过最优区域表现为损失在某个区间来回震荡不下降。同时迭代次数从 500 增到 1000因为红酒数据本身样本少但特征多网络需要更多轮次才能稳定收敛。one_hot函数、BPNet 类、训练循环和鸢尾花完全一致这也体现了封装的好处。很多人写代码习惯每换一个数据集就复制一份并改来改去最后维护混乱。实际上逻辑相同部分只该写一次数据集分类实验的核心就是数据预处理和参数选择模型结构代码保持复用。4.3 两个数据集的参数对比写在实验报告里的实验设计表答辩时很容易被问到“为什么红酒这边隐藏层取 8鸢尾花取 5”与其临场解释不如直接在实验报告里放一张参数对照表参数项Iris 鸢尾花Wine 红酒输入层节点数413隐藏层节点数58输出层节点数33隐藏层激活函数SigmoidSigmoid输出层激活函数SoftmaxSoftmax学习率0.10.05迭代次数5001000测试集占比20%20%特征标准化StandardScalerStandardScaler测试集准确率96.7%94.4%两张数据集的网络结构原则是相同的隐藏层节点数取输入与输出之间的中间值必要时再往大调一档。学习率根据损失曲线表现决定谁震荡就调小。写报告时把这张表放在实验设计部分配合第 2.3 节的结构图模型设计就能自洽。我在实际跑这两组数据时发现红酒数据集在相同随机种子下可以稳定达到 94% 左右准确率但如果不做标准化准确率会掉到 70% 以下。所以红酒部分的预处理是比网络结构更关键的成功因素这一点建议你在报告里加粗强调。5. BP神经网络分类踩坑记录现象、原因与解决5.1 损失不降反升甚至出现 NaN现象训练到第 50 轮时 loss 还在 2.0 以上继续迭代反而飙到几千输出预测概率出现 NaN。原因最常见的有三个学习率过大导致梯度更新迈过最优区域数据未标准化使某个特征梯度主导Softmax 里指数溢出。我试过把学习率设为 0.5 跑红酒数据第 30 轮就直接 NaN。解决先把学习率从 0.1 降到 0.01 甚至 0.005确认数据已经经过 StandardScaler。若还出现 NaN检查 softmax 实现是否做了x - x.max(axis1, keepdimsTrue)平移这个操作能挡掉大多数指数溢出问题。5.2 训练集准确率 100%测试集只有 80%过拟合现象鸢尾花训练集达到 100% 准确率但换成随机切分的测试集后准确率明显下降。红酒数据集上更明显训练集 100%、测试集 85% 左右。原因模型容量对当前数据量来说偏大隐藏层节点太多把训练集中的噪声也记住了。150 个样本配 5 个隐藏节点还好但如果把隐藏节点提到 20过拟合会立刻加重。解决最直接的方案是减少隐藏层节点数从 20 回到 8 或 5。若仍想过拟合可以在损失函数里加 L2 正则项或者在训练 500 轮时观察验证损失开始上升的临界点并提前停止。对这两个小数据集我推荐先减节点因为简单且效果明显。5.3 每次都跑出不同准确率结果无法复现现象同一份代码连续运行三次测试集准确率有时 90% 有时 97%差异肉眼可见。写进实验报告的数字每次都不一样。原因网络权重初始化用了随机数训练测试划分也是随机的两处随机性都未被固定。解决在BPNet类的__init__中调用np.random.seed(seed)在train_test_split里固定random_state42两处固定后结果可复现。实验报告最后还要写一句“复现方法设置 random_state 和初始化种子”方便老师或评审验证。5.4 准确率高但不均衡某个类别总是分错现象测试集整体准确率 96%但查看分类结果发现某个类别几乎全错另一个类别全对。红酒数据集中第 2 类和第 3 类容易混淆。原因样本不均衡或类间特征重叠。红酒数据集三个类别样本数分别是 59、71、48本身不严重更常见的是未使用stratifyy随机切分时某个类在训练集中样本极少。解决切分时加stratifyy保证类别比例一致再用混淆矩阵看具体错在哪里。如果某两类特征边界确实模糊可以在报告里如实写“第 2 类与第 3 类存在特征重叠”这是合理的分析结论不要为了提升准确率去动测试集分布。5.5 反向传播梯度算错但准确率还行答辩被问倒现象代码能跑出 90% 以上准确率但被追问“你的梯度公式怎么推导的”就解释不清。对照网上的截图delta2 output - y_oh这步说不出道理。原因默认记住了简化公式但没理解这是“Softmax 交叉熵”组合后的结果而不是单纯输出层梯度。解决答辩前做一次数值梯度验证用(loss(xh) - loss(x-h)) / 2h近似梯度与自己算出的梯度对比误差在 1e-5 量级就说明推导正确。这也是手写BP网络时最扎实的自检手段验证过程本身可以在答辩时作为加分项讲出来。6. 实验报告和答辩PPT让数据、图表和提问闭环6.1 实验报告按“问题定义、模型设计、结果、分析”四段写实验报告不要写成代码注释的堆砌。我最常用的结构是研究问题对两种数据集做三分类、数据来源与预处理含标准化与划分策略、BP神经网络结构4-5-3 和 13-8-3、训练参数与损失曲线、测试结果与混淆矩阵、误分类案例分析。每个部分对应一张图表不贴大段代码核心代码片段可以放附录。6.2 答辩PPT控制在 10 页以内重点放模型设计页和结果页PPT 不需要把源码全贴上去5 到 8 页最合适研究背景、数据集描述、BP神经网络结构图、训练过程与损失曲线、结果对比表、问题与改进方向。现场最容易被追问的是两个位置一是结构图中各层节点数如何确定二是测试集准确率是否用了与训练集相同的数据。前者用第 2.3 节的画图代码生成结构图回答后者明确说明训练集与测试集分离、测试集未参与训练。6.3 一个实用技巧把混淆矩阵也放进报告不要只报一个准确率数字画混淆矩阵能快速暴露模型弱点from sklearn.metrics import confusion_matrix import matplotlib.pyplot as plt cm confusion_matrix(y_test.ravel(), pred) fig, ax plt.subplots(figsize(5, 4)) im ax.imshow(cm, cmapBlues) ax.set_xticks([0, 1, 2]); ax.set_yticks([0, 1, 2]) ax.set_xlabel(predicted); ax.set_ylabel(true) for i in range(3): for j in range(3): ax.text(j, i, cm[i, j], hacenter, vacenter) plt.colorbar(im) plt.title(Wine confusion matrix) plt.savefig(wine_cm.png, dpi150) plt.show()这是我带过几轮课程设计后形成的习惯报告要先定骨架再补实验数据图表统一用dpi150导出PPT 只放结论和数据细节留给口头表达。这些文件整理清楚之后从源码到实验报告再到答辩PPT完整闭环才算真正结束。希望以上这些参数、代码和坑位记录能帮到你照着走一遍BP神经网络分类这件事就没那么玄了。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询