ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

BP神经网络分类实战:鸢尾花与红酒数据集PyTorch实现与调参指南

BP神经网络分类实战:鸢尾花与红酒数据集PyTorch实现与调参指南 简介面向机器学习课程实验的BP神经网络实践资源围绕鸢尾花与红酒数据集的分类任务展开适合正在学习神经网络基础、准备课程作业或进行相关实验的学生。压缩包共含18个文件体量约630KB文件类型覆盖Python脚本、Jupyter Notebook、Excel格式数据集、实验指导文档和课堂PPT并附带PyCharm工程配置便于导入后在集成开发环境中直接运行和调试其中脚本与Notebook分别适合快速执行与逐步推演Excel数据则方便查看样本特征与标签分布。目前已有1034人学习下载。资源针对两类数据集分别给出完整的分类实现包括数据读取、网络结构定义、参数训练和结果评估等环节配合实验文档可理解误差反向传播和权重更新过程同时PPT中对实验原理做了梳理可直接用于汇报或复习。整体目录按实验模块整理上手门槛较低无论是完成课程设计还是作为神经网络入门参考都能提供明确可用的模板。1. BP神经网络做分类实验为什么鸢尾花和红酒是课程作业的“标准答案”如果你在实验课上拿到“用BP神经网络实现对鸢尾花、红酒数据集分类”这个题目大概率不是老师随手选的。鸢尾花数据集只有150条样本、4个特征、3个类别红酒数据集178条样本、13个特征、3个类别两者都是UCI上的经典分类数据集规模小到CPU上几秒钟就能跑完一轮训练但又足够暴露BP神经网络从数据处理到调参的全部常见问题。很多同学做完这个作业最大的感受是跑通代码不难难的是搞明白为什么同一个网络在鸢尾花上轻松到95%换到红酒数据集上却卡在70%上下以及损失曲线震荡、准确率死活上不去时到底该调哪里。这篇笔记我就按自己做实验的顺序把BP网络的结构设计、数据预处理、PyTorch实现、参数调节和踩坑点完整过一遍代码直接可以抄。2. 先把BP神经网络的前向传播和反向传播讲透3层结构就够用2.1 一个神经元在做什么加权求和与非线性激活BP神经网络的核心单元就是神经元它做的事可以拆成两步第一步对输入向量做线性加权求和第二步把求和结果扔进一个非线性激活函数。拿鸢尾花数据集来说每个样本有4个特征花萼长度、花萼宽度、花瓣长度、花瓣宽度输入层就得有4个节点。第一个隐藏层的某个神经元会收到4个输入值每个输入乘上一个权重加上一个偏置得到净输入 z w1x1 w2x2 w3x3 w4x4 b然后送入激活函数。激活函数是网络能拟合非线性边界的关键。如果去掉激活函数多层网络无论堆多少层都等价于单层线性变换分类能力跟线性回归没区别。实验里最常用的隐藏层激活函数是ReLU线性整流单元它的计算量小、梯度消失问题比Sigmoid轻得多。Sigmoid现在一般只用在二分类的输出层因为它的输出范围是(0,1)可以被解释成概率多分类任务输出层用Softmax把多个输出节点压缩成和为1的概率分布。我在实验里习惯隐藏层用ReLU、输出层用Softmax这个组合在鸢尾花和红酒两个数据集上表现都稳定。2.2 反向传播的核心链式法则与梯度下降训练BP神经网络实际是在做两件事前向传播算出预测值反向传播算出每个权重对损失的梯度然后用梯度下降更新权重。损失函数衡量预测值和真实标签的差距多分类任务常用交叉熵损失。交叉熵的一个好处是配合Softmax输出时梯度形式非常简洁数值上也比均方误差稳定——用均方误差训练分类网络经常出现收敛慢、后期loss下降像蠕动的情况。反向传播本身并不神秘它就是链式法则的工程实现。从输出层开始先计算损失对输出层净输入的偏导再乘上当前层的权重逐层向输入方向回传得到每一层权重的梯度。神经网络层数一多梯度在链式相乘过程中会指数级衰减或膨胀这就是梯度消失/梯度爆炸问题。本实验只有3层结构一般不会触发这个问题但如果你的隐藏层加到5层以上且激活函数用Sigmoid就会直观感受到梯度消失——前面几层的参数几乎不动训练效果跟只训练最后一层差不多。2.3 网络结构怎么定输入层、隐藏层、输出层的节点数选择网络结构的三个关键数字分别是输入层节点数、隐藏层节点数和输出层节点数。输入层节点数等于特征维度鸢尾花是4红酒是13。输出层节点数等于类别数两个数据集都是3鸢尾花是Setosa、Versicolor、Virginica三个品种红酒是三个产地类别。唯一需要自己拍板的是隐藏层节点数。隐藏层节点数太小网络容量不足学不到数据里的区分模式准确率上限低节点数太大训练变慢且容易过拟合——训练集准确率一路逼近100%测试集反而往下掉。我的经验公式是先取输入层和输出层节点数的几何平均数附近的值比如鸢尾花可以取sqrt(43)约等于4红酒取sqrt(133)约等于6然后按2倍步长上下各试一两个值。实验中一个隐藏层就够用了先把单隐藏层调好再去加层数不要一开始就堆复杂结构。3. 鸢尾花和红酒数据集的预处理从原始数据到模型能吃的张量3.1 鸢尾花数据集4个特征、3个类别独热编码怎么转换鸢尾花数据集可以在scikit-learn里直接加载不需要手动下载文件这是它适合做课程作业的另一个原因。原始数据里的类别标签是字符串setosa、versicolor、virginica。PyTorch的交叉熵损失函数要求标签是整数索引不是独热编码向量这一点跟TensorFlow有些区别很多同学在第一个实验里就在这里报错。如果你用的是nn.CrossEntropyLoss()标签直接传整数张量即可如果你自己实现损失函数或者用nn.MultiLabelSoftMarginLoss才需要把标签转成独热编码。import torch import numpy as np from sklearn.datasets import load_iris, load_wine from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 加载鸢尾花数据集 iris load_iris() X_iris iris.data # 形状 (150, 4)4个特征 y_iris iris.target # 形状 (150,)取值 0, 1, 2代码逻辑scikit-learn返回的data是二维数组每行一个样本target是一维整数数组正好可以直接喂给nn.CrossEntropyLoss()。如果你确实需要独热编码比如自己想写Softmax交叉熵的底层实现做学习用可以用torch.nn.functional.one_hot或sklearn的OneHotEncoder转换。3.2 红酒数据集13个特征量纲不一致时必须标准化红酒数据集有13个特征包括酒精浓度、苹果酸、灰分、镁含量等它们的数值范围差异很大。比如酒精浓度大约在11到15之间镁含量却在70到160之间非黄酮类酚类可能只有0.2到1.5。如果不做标准化直接喂给BP神经网络数值大的特征会在加权求和里天然占据主导地位权重更新时梯度也被大数值特征带偏网络很难学到正确的特征组合权重。标准化最常见的做法是Z-score标准化对每个特征减去均值、除以标准差让所有特征分布都变成均值0、方差1。需要特别注意的一点是标准化只能用训练集的均值和标准差不能用全量数据计算。如果你用全量数据做标准化再做训练测试划分会引入数据泄露测试集的信息提前混入了训练过程导致测试集准确率虚高。这个操作顺序问题在课程作业里被扣分的情况非常常见。3.3 数据集划分训练集/验证集/测试集随机种子为什么重要数据集划分我一般按70%训练、15%验证、15%测试来切。训练集用于更新权重验证集用于调超参数比如学习率、隐藏层节点数测试集只在最终评估时碰一次。很多同学只切训练集和测试集两部分验证集完全不用调参时直接看测试集表现——这样调几次之后测试集就变成“被调过的数据”最终报告里的准确率参考价值大打折扣。# 先划分训练集测试集再从训练集里切出验证集 X_train, X_test, y_train, y_test train_test_split( X_iris, y_iris, test_size0.15, random_state42, stratifyy_iris ) X_train, X_val, y_train, y_val train_test_split( X_train, y_train, test_size0.176, random_state42, stratifyy_train ) # 用训练集拟合标准化器再应用到验证集和测试集 scaler StandardScaler() X_train scaler.fit_transform(X_train) X_val scaler.transform(X_val) X_test scaler.transform(X_test) # 转换为PyTorch张量 X_train_t torch.tensor(X_train, dtypetorch.float32) y_train_t torch.tensor(y_train, dtypetorch.long) X_val_t torch.tensor(X_val, dtypetorch.float32) y_val_t torch.tensor(y_val, dtypetorch.long) X_test_t torch.tensor(X_test, dtypetorch.float32) y_test_t torch.tensor(y_test, dtypetorch.long)参数说明test_size0.15表示切15%做测试集stratifyy_iris按类别比例分层抽样避免随机切分后某一类在训练集里特别少。第二次划分时验证集占比要按剩余数据的比例算0.176*0.85约等于0.15这样训练、验证、测试各占约70%、15%、15%。random_state42固定随机种子保证每次运行的数据划分完全一致实验结果可复现——这个细节在你写实验报告对比多组参数时尤其重要否则每次跑的准确率波动会掩盖真实的参数差异。4. PyTorch实现BP网络两个数据集共用一个模型框架4.1 模型定义与初始化nn.Module子类与sequential容器PyTorch里定义一个BP神经网络最简洁的方式是继承nn.Module在__init__里声明各层在forward里定义前向传播逻辑。由于鸢尾花和红酒数据集的差别只在输入特征维度模型类可以做成参数化的——输入维度作为构造参数传入这样两个数据集共用同一个网络定义。权重初始化我直接采用PyTorch默认的Kaiming初始化适合ReLU激活函数。如果你发现训练初期损失下降极慢可以考虑手动用nn.init.xavier_uniform_或nn.init.kaiming_normal_重新初始化有时候玄学地有效果。实际上这类小数据集、小网络初始化方式对最终准确率影响不大最关键的还是数据标准化和超参数。import torch.nn as nn import torch.optim as optim class BPClassifier(nn.Module): def __init__(self, input_dim, hidden_dim, num_classes): super(BPClassifier, self).__init__() self.net nn.Sequential( nn.Linear(input_dim, hidden_dim), # 输入层 - 隐藏层 nn.ReLU(), nn.Linear(hidden_dim, num_classes) # 隐藏层 - 输出层 ) def forward(self, x): return self.net(x) # 鸢尾花4个特征 - 4个隐层节点 - 3类 model_iris BPClassifier(input_dim4, hidden_dim4, num_classes3) # 红酒13个特征 - 6个隐层节点 - 3类 model_wine BPClassifier(input_dim13, hidden_dim6, num_classes3)代码逻辑nn.Linear做线性变换nn.ReLU做非线性激活nn.Sequential把层按顺序串联。这里没有在输出层手动加Softmax因为这行代码会把Softmax丢给nn.CrossEntropyLoss()去算它内部做了LogSoftmax和交叉熵的融合计算数值上更稳定也省一步操作。4.2 训练循环前向传播、计算损失、反向传播、更新参数训练循环是所有实验代码里套路最固定的部分置零梯度、前向传播、计算损失、反向传播、更新参数。难理解的是optimizer.zero_grad()这行——PyTorch的梯度是累积的如果不每步清零下一轮反向传播会把新旧梯度加在一起参数更新就乱套了。忘记写这行代码是新手最常见的问题之一表现出来的症状是损失值忽高忽低、完全不收敛。学习率是最敏感的超参数。学习率太大参数更新一步跨太远损失在最优值附近震荡甚至发散学习率太小收敛极慢训练几百轮loss还在缓慢下降像挤牙膏。鸢尾花和红酒这种小数据集我一般从0.01或0.001起步再根据损失曲线微调。优化器用Adam就足够好了它自带自适应学习率对初始学习率的敏感度比SGD低不少。def train_model(model, X_train, y_train, X_val, y_val, epochs200, lr0.01, batch_size16): criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lrlr) train_losses, val_losses [], [] for epoch in range(epochs): model.train() permutation torch.randperm(X_train.size(0)) epoch_loss 0.0 for i in range(0, X_train.size(0), batch_size): indices permutation[i:i batch_size] batch_x X_train[indices] batch_y y_train[indices] optimizer.zero_grad() outputs model(batch_x) loss criterion(outputs, batch_y) loss.backward() optimizer.step() epoch_loss loss.item() * batch_x.size(0) train_losses.append(epoch_loss / X_train.size(0)) # 每个epoch结束后在验证集上评估 model.eval() with torch.no_grad(): val_outputs model(X_val) val_loss criterion(val_outputs, y_val).item() val_losses.append(val_loss) return train_losses, val_losses参数说明batch_size16表示每次用16个样本计算一次梯度这是小数据集上比较稳妥的选择。全批次训练batch_size等于全部样本收敛方向最稳定但每轮更新慢小批次训练引入一定噪声反而可能帮助跳出局部最优。torch.randperm用来打乱样本顺序避免模型学到数据排列顺序里的假规律。model.eval()和with torch.no_grad()在验证时缺一不可——前者切换BatchNorm和Dropout的行为本模型没有这两层但保留这个习惯很必要后者关闭梯度计算省内存且避免误改参数。4.3 训练过程可视化损失曲线和准确率曲线怎么看训练完成后把损失曲线画出来这是判断模型训练状态最直观的手段。正常收敛的曲线应该是平滑下降后趋于平坦如果训练损失持续下降但验证损失先降后升出现了经典的“开口向上”曲线说明模型开始过拟合了如果两条曲线都在高位震荡、没有下降趋势先检查数据标准化是否漏做再检查学习率是否过大。import matplotlib.pyplot as plt plt.figure(figsize(8, 4)) plt.plot(train_losses, labelTrain Loss) plt.plot(val_losses, labelVal Loss) plt.xlabel(Epoch) plt.ylabel(Loss) plt.legend() plt.title(BP Network Training Curve) plt.show()画图时顺便把每轮的验证准确率也记下来跟损失曲线对着看。有时候损失在下降但准确率纹丝不动这说明模型对少数类别的预测在改善、但对多数类别的预测反而变差需要看混淆矩阵才能定位问题光盯损失曲线不够。训练结束以后用测试集跑一次model.eval()加torch.no_grad()得到最终准确率这个数字才是实验报告里该写的。5. BP神经网络的5个典型翻车现场避坑与排查指南5.1 损失不下降学习率过大、数据没标准化、梯度写错现象训练损失从第一个epoch起就在2到5之间徘徊200轮训练后曲线还是锯齿状准确率稳定在33%上下三个类别随机猜的水平。原因第一个要查的就是数据有没有做标准化红酒数据集13个特征量纲不一致时这种情况尤其常见标准化做了还不行就把学习率从0.01降到0.001重跑学习率过大时梯度会在最优解附近来回震荡损失曲线呈现明显的锯齿状。解决先标准化、再降学习率按这个顺序排查能解决90%的“损失不降”问题。5.2 训练集准确率正常但验证集准确率波动剧烈batch_size与小样本冲突现象训练集准确率平滑上升到95%验证集准确率却在80%到95%之间剧烈跳动每个epoch都在大起大落。原因验证集只有几十个样本batch_size一波动准确率对个别样本的预测结果极其敏感。我见过有同学把验证集设成10个样本一个样本预测错就是10%的准确率波动这属于评估指标的方差问题而不是模型问题。解决验证集至少保留30个样本并在多个epoch上做滑动平均后再对比或者直接用测试集做最终评估不要被单次验证的小幅波动干扰判断。5.3 训练集接近100%而测试集只有70%过拟合的识别与缓解现象训练集准确率一路冲到99%测试集准确率却停在70%出头中间有近30个百分点的差距。原因模型把训练数据里的噪声当成规律学进去了常见触发条件是隐藏层节点数设太大、训练轮数跑太多。红酒数据集总共178条样本比鸢尾花还少过拟合风险更高。解决先减少隐藏层节点数比如从8减到4观察测试集变化再配合早停法。如果这两招都用了还过拟合可以加nn.Dropout(p0.3)层但小数据集上Dropout的作用往往不如直接缩小网络来得明显。5.4 红酒准确率远低于鸢尾花特征维度增加后的学习难度差异现象同一个网络框架鸢尾花数据集轻松上95%红酒数据集训练到极限也只有75%。原因鸢尾花数据集特征少、类别边界清晰甚至线性分类器也能达到95%红酒数据集虽然也只有3类但13个特征之间存在复杂相关性需要网络学习更高维的决策边界同样规模的隐藏层表达能力不足。解决把隐藏层节点数从6增加到12或16同时把训练轮数从200加到500观察曲线变化。红酒数据集上深度学习的收益不明显增大宽度比增加深度更有效。5.5 随机种子不固定实验结果复现不了现象同样的代码、同样的参数上午跑准确率95%下午跑变成92%换台机器又变了个数。原因模型权重初始化、数据划分、batch打乱都依赖随机数生成器默认情况下每次运行都会产生不同的随机序列。解决在代码开头固定所有随机种子包括Python内置的random、NumPy和PyTorch三个库的种子数据划分时也传固定的random_state参数。作业报告里写清楚“所有实验在random_state42下运行”评委老师会认可你的实验规范性。6. 把准确率从90%练到98%三个实用技巧与最终验证方法6.1 技巧一隐藏层节点数与学习率的小型网格搜索在固定数据划分的前提下对隐藏层节点数4 8 16和学习率0.001 0.005 0.01做3乘3的网格搜索每次训练跑相同的epoch数记录验证集准确率。两个数据集各跑9组也就几分钟时间但你能直观看到超参数的影响方向和最佳组合。注意网格搜索必须在验证集上选参数选完再用测试集做最终评估——这个顺序错了测试集就失去客观性了。6.2 技巧二早停法与模型保存训练时在每个epoch后比较验证集损失如果连续20个epoch没有改善就停止训练并加载历史最佳模型参数。PyTorch里用torch.save(model.state_dict(), best_model.pt)保存最佳状态训练结束后model.load_state_dict(torch.load(best_model.pt))恢复。这个习惯能同时避免过拟合和训练时间浪费实验报告里写上“早停阈值20轮”也是加分项。6.3 最终验证混淆矩阵与分类报告测试集上只看一个总准确率不够画出混淆矩阵才能发现哪两类容易混淆。鸢尾花数据集通常Setosa山鸢尾全部预测正确主要混淆发生在Versicolor和Virginica之间红酒数据集也可能出现特定产地类别之间的系统性误判。from sklearn.metrics import confusion_matrix, classification_report model_iris.eval() with torch.no_grad(): pred model_iris(X_test_t).argmax(dim1).numpy() print(confusion_matrix(y_test, pred)) print(classification_report(y_test, pred, target_namesiris.target_names))classification_report会输出每个类别的精确率、召回率和F1分数比单纯准确率信息量丰富得多。写实验报告时把混淆矩阵和分类报告一起贴上去再结合数据集的类别特点简单分析几句这份作业的完成度就超过大部分同学了。回看我自己做这个实验的血泪经验最大的教训是遇到准确率上不去先检查数据处理顺序再调网络参数千万不要上来就改网络结构——很多时候问题根本不在模型上。先把标准化、数据划分、固定随机种子这些基本功做扎实BP网络本身在这个量级的数据集上是够用的不需要花哨的改进。希望帮到你祝实验顺利。本文还有配套的精品资源点击获取
返回列表