尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
用Python手写BP神经网络实现鸢尾花分类:从原理到调参
简介面向Python初学者的人工智能实践项目使用BP神经网络对经典鸢尾花数据集进行分类配套完整源码、数据集和文档说明可满足期末大作业、课程设计等场景。除BP神经网络两个版本V1/V2外还提供KNN、决策树、聚类等算法脚本便于在同一数据集上比较不同模型效果加深对多种分类方法的理解。压缩包共15个文件大小仅24KB其中8个csv文件为原始数据与训练/测试集6个py文件为各算法实现1个md文件为说明文档整体结构清晰下载后简单部署即可使用。已有244人浏览学习。代码注释详细覆盖数据读取、训练/测试集划分、网络构建、训练与评估的完整流程运行门槛低适合新手通过该实例理解BP神经网络的训练流程与分类建模思路也可作为课程设计和期末大作业的完整参考模板。1. 鸢尾花分类为什么是BP神经网络的第一个项目做人工智能项目实践绕不开鸢尾花数据集。这个数据集只有150个样本、4个特征、3个类别规模小到任何算法都能在几秒内跑完但它把BP神经网络最核心的流程——数据预处理、前向传播、反向传播、参数更新、分类评估——完整地串了起来。很多人在实操时跳过原理直接调库epoch、学习率、激活函数全交给默认值跑完只得到一个Accuracy: 0.97的数字换一个数据集就不知道怎么改。真正能沉淀下来的能力是用Python手写一遍BP神经网络把每条连接的梯度算清楚再在这个基础上引入sklearn做交叉验证和评估。这篇文章从数学原理讲到可运行的源码最后给出调参和验证的具体手法。2. BP神经网络的信号流动与鸢尾花分类的数学映射2.1 从感知机到多层网络的表达能力差异感知机只能处理线性可分问题鸢尾花数据集中setosa和versicolor在特征空间里明明可以被一条直线分开但versicolor和virginica存在重叠区域单一线性模型无法准确划分。BP神经网络通过引入隐藏层和非线性激活函数把原始特征映射到新的特征空间在这个空间里类别边界可以是非线性的。理论上已经证明只要隐藏层节点数足够多三层网络可以逼近任意连续函数这就是万能逼近定理。鸢尾花分类的输入是4个数值特征花萼长度、花萼宽度、花瓣长度、花瓣宽度。输出是3个类别常见做法是使用one-hot编码即[1,0,0]代表setosa、[0,1,0]代表versicolor、[0,0,1]代表virginica。网络结构就确定下来输入层4个节点、输出层3个节点。隐藏层怎么定没有固定公式一般从较小的值起步。2.2 前向传播输入特征如何逐层计算为类别概率前向传播是数据从输入层流向输出层的过程。设输入向量为$x$第一个隐藏层的权重矩阵为$W_1$偏置为$b_1$则隐藏层的线性输出为$z_1W_1xb_1$经过激活函数后得到$a_1\sigma(z_1)$。这个$a_1$再作为下一层的输入重复同样的计算直到输出层。输出层得到的是一个3维向量我们把它转换成概率形式的分布——这正是softmax函数做的事。softmax的公式是$\text{softmax}(z_i)\frac{e^{z_i}}{\sum_{j1}^{3}e^{z_j}}$它把任意实数向量压缩到0到1之间并且所有分量之和为1。选择softmax而不是sigmoid的关键原因在于多分类问题的输出是互斥的3个类别的概率需要竞争性地此消彼长sigmoid对每个输出单独处理总和并不受约束不适合多分类场景。与此对应隐藏层的激活函数则选择ReLU或tanh它们在梯度回传时的表现优于sigmoid的饱和区。2.3 反向传播误差从输出层回流到每条权重连接BP的核心思想是链式法则。以单个训练样本为例交叉熵损失定义为$L-\sum_{k1}^{3}y_k\ln(\hat{y}_k)$其中$\hat{y}_k$是softmax输出的概率$y_k$是one-hot标签。我们的目标是对每个权重计算偏导数$\partial L/\partial W$然后按梯度下降的规则更新$W\leftarrow W-\eta\frac{\partial L}{\partial W}$。反向传播的具体路径分为三步。第一步计算输出层的误差项softmax与交叉熵的组合在求导时有一个极其简洁的形式——$\delta^{(out)}\hat{y}-y$即预测概率减去真实标签的差这省去了复杂的偏导数推导。第二步把误差项传播回隐藏层$\delta^{(h)}(W^{(out)})^T\delta^{(out)}\cdot\sigma(z^{(h)})$其中$\sigma$是激活函数的导数。第三步利用误差项求权重梯度$\partial L/\partial W^{(h)}\delta^{(h)}x^T$。一个容易忽视的细节是偏置的梯度。权重梯度的计算需要乘以对应的输入偏置的梯度则直接等于误差项即$\partial L/\partial b\delta$。在编写代码时如果把权重梯度和偏置梯度混在一个矩阵操作里很容易遗漏偏置的更新最终导致模型收敛到次优解。另外学习率$\eta$控制着参数更新的步长——过大会导致损失振荡过小则收敛速度极慢。3. 用Python实现BP神经网络完整源码3.1 准备工作与依赖安装本项目依赖三个库numpy负责矩阵运算pandas负责数据处理scikit-learn负责数据切分和分类评估。在开始前需要确认环境已经正确安装Python 3.8及以上版本都可以直接运行下面的命令pip install numpy pandas scikit-learn安装完成后用Python解释器快速验证numpy和sklearn的版本python -c import numpy, sklearn; print(numpy.__version__, sklearn.__version__)如果输出类似1.24.3 1.3.0这样的信息说明依赖环境就绪。使用Anaconda环境时建议先创建一个独立的虚拟环境再做安装避免依赖冲突。对刚接触Python生态的读者这一步做完后可以先在代码文件头部执行import numpy as np来检查依赖是否可用。3.2 核心类实现初始化、前向、反向、更新BP神经网络的实现完全基于numpy不依赖深度学习框架。这里用一个类封装网络的初始化、前向传播、反向传播和参数更新四个核心方法import numpy as np class BPNeuralNetwork: def __init__(self, input_size, hidden_size, output_size, learning_rate0.01): # 使用He初始化方法初始化权重缩放系数为sqrt(2/fan_in) self.W1 np.random.randn(input_size, hidden_size) * np.sqrt(2.0 / input_size) self.b1 np.zeros((1, hidden_size)) self.W2 np.random.randn(hidden_size, output_size) * np.sqrt(2.0 / hidden_size) self.b2 np.zeros((1, output_size)) self.lr learning_rate def relu(self, z): # ReLU激活函数max(0, z)注意对负数直接置零 return np.maximum(0, z) def relu_derivative(self, z): # ReLU导数计算大于0的位置为1其余为0 return (z 0).astype(float) def softmax(self, z): # 为防止指数溢出先减去最大值再计算exp结果与原始计算完全等价 exp_z np.exp(z - np.max(z, axis1, keepdimsTrue)) return exp_z / np.sum(exp_z, axis1, keepdimsTrue) def forward(self, X): # 前向传播逐层计算并暂存中间结果供反向传播使用 self.z1 X self.W1 self.b1 self.a1 self.relu(self.z1) self.z2 self.a1 self.W2 self.b2 self.a2 self.softmax(self.z2) return self.a2 def backward(self, X, y, output): m X.shape[0] # m是当前batch的样本数 # 输出层误差softmax交叉熵的组合导数正好是预测值减真实值 delta2 output - y # 计算输出层权重和偏置的梯度 dW2 (self.a1.T delta2) / m db2 np.sum(delta2, axis0, keepdimsTrue) / m # 误差继续向隐藏层传播乘以ReLU的导数 delta1 (delta2 self.W2.T) * self.relu_derivative(self.z1) dW1 (X.T delta1) / m db1 np.sum(delta1, axis0, keepdimsTrue) / m # 梯度下降更新参数W W - lr * dW self.W2 - self.lr * dW2 self.b2 - self.lr * db2 self.W1 - self.lr * dW1 self.b1 - self.lr * db1 def train(self, X, y, epochs, batch_size32): total X.shape[0] for epoch in range(epochs): # 每个epoch打乱数据顺序避免模型记住固定的样本排列模式 perm np.random.permutation(total) X_shuffled X[perm] y_shuffled y[perm] for i in range(0, total, batch_size): X_batch X_shuffled[i:ibatch_size] y_batch y_shuffled[i:ibatch_size] output self.forward(X_batch) self.backward(X_batch, y_batch, output) if epoch % 50 0: # 每隔50轮用当前模型预测一次训练集 train_out self.forward(X_train) loss self.cross_entropy_loss(y_train, train_out) print(fEpoch {epoch}, Loss: {loss:.4f})代码逻辑说明__init__方法根据输入维度、隐藏层节点数和输出维度初始化权重矩阵和偏置权重用He初始化方法缩放避免梯度在深层网络里消失或爆炸。forward方法把numpy矩阵乘法和激活函数串联起来把每一层的中间结果保存为实例属性供反向传播使用。backward方法中值得注意的核心点是delta2 output - y这行代码站在数学推导的终点位置上softmax和交叉熵的复合梯度恰好化简为这个差值。除以m是取mini-batch的平均梯度这样可以减少单个样本噪声对参数更新的干扰。train方法实现了标准的mini-batch梯度下降策略每个epoch先打乱数据顺序再按batch_size切分训练。3.3 数据预处理与训练入口鸢尾花数据集的原始特征是连续浮点数范围差异明显花萼长度在4.3到7.9之间花瓣宽度则在0.1到2.5之间。网络权重初始值通常非常小如果特征直接输入量级大的特征会主导梯度方向导致网络收敛很慢。标准做法是标准归一化使每个特征均值为0、标准差为1。from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler import numpy as np # 加载鸢尾花数据集150个样本4个特征3个类别 iris load_iris() X iris.data y iris.target # 1. 数据切分80%训练、20%测试设置random_state保证结果可复现 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 2. 归一化用训练集计算均值和标准差再用它转换测试集 scaler StandardScaler() X_train scaler.fit_transform(X_train) X_test scaler.transform(X_test) # 3. 标签one-hot编码把类别id转为3维向量 def one_hot_encode(labels): # 输入是形状为(n,)的标签数组输出形状为(n, 3)的矩阵 eye np.eye(3) return eye[labels] y_train_onehot one_hot_encode(y_train) y_test_onehot one_hot_encode(y_test) # 4. 初始化网络并训练 model BPNeuralNetwork(input_size4, hidden_size8, output_size3, learning_rate0.1) model.train(X_train, y_train_onehot, epochs300, batch_size16)这里有几个关键参数需要说明test_size0.2表示预留20%的样本用于最后评估模型泛化能力stratifyy使得训练集和测试集中三个类别的比例与原始数据一致避免某种类别在切分时被抽空。random_state42固定了随机数种子保证每次运行得到相同的数据划分便于调试。隐藏层节点数8是一个折中值太小会欠拟合太大会带来过拟合和高昂的计算开销后续章节会展示如何用实验替代猜测。4. 训练过程监控与分类评估指标体系4.1 损失下降曲线与常见陷阱训练过程中打印的loss值需要仔细观察其形态。正常情况是随着epoch增加损失单调下降并最终趋于平稳。如果loss出现震荡或上升大概率是学习率设置过大如果300轮过后loss还在持续下降说明模型尚未收敛需要增加epoch。一个常见的错误是在每个epoch结束后只输出一次loss值但这个值是在该epoch最后一个batch更新完的结果与epoch开始时的状态存在偏差效果上仍然能作为监控指标实际项目中也经常这样用。为了更精细地观察训练状态可以在每10个epoch同时记录训练集和测试集的准确率。如果训练集准确率不断上升而测试集准确率停滞甚至下降说明发生过拟合——处理手法包括增大数据量、减小网络规模、添加L2正则化或采用Dropout策略。对鸢尾花这样的小数据集最直接有效的对策是使用交叉验证而不是简单地堆数据。4.2 训练集与测试集的准确率对比训练完成后立即在测试集上做预测并计算准确率# 模型预测取概率向量中最大值对应的索引作为类别 train_pred np.argmax(model.forward(X_train), axis1) test_pred np.argmax(model.forward(X_test), axis1) # 准确率计算预测标签与真实标签相等的比例 train_acc np.mean(train_pred y_train) test_acc np.mean(test_pred y_test) print(f训练集准确率: {train_acc:.4f}) print(f测试集准确率: {test_acc:.4f})训练集准确率通常在95%以上这是因为模型已经见过了这些样本。测试集准确率如果与训练集明显接近且都在90%以上说明模型的泛化能力靠谱如果训练集接近100%而测试集只有60%上下过拟合的可能性极高。实际运行中一个训练充分的三层网络在鸢尾花测试集上可以达到90%到100%的准确率区间。准确率能反映整体表现但对类别不平衡的数据集不够敏感需要结合混淆矩阵做深入分析。4.3 混淆矩阵与精确率、召回率、F1的计算准确率在类别不平衡时会产生误导。假如某个类别占比90%模型把所有样本预测为该类别就已经有90%的准确率但另一个类别完全没被正确识别。分类评估的标准做法是输出混淆矩阵并计算每个类别的精确率Precision、召回率Recall和F1值。from sklearn.metrics import confusion_matrix, classification_report import pandas as pd # 计算混淆矩阵并转为DataFrame方便观察 cm confusion_matrix(y_test, test_pred) cm_df pd.DataFrame(cm, indexiris.target_names, columnsiris.target_names) print(混淆矩阵:) print(cm_df) # 打印精确率、召回率、F1值的完整报告 print(\n分类评估报告:) print(classification_report(y_test, test_pred, target_namesiris.target_names))输出示例混淆矩阵主对角线上的数字代表正确分类的样本数非对角线元素是类别间互相混淆的数量。以setosa为例它通常能被100%正确识别因为其特征与其他类别差异显著。versicolor和virginica存在边界重叠天然会有几个样本被互相误判。classification_report按每种类别输出精确率、召回率、F1其中精确率是被预测为该类的样本中真正属于该类的比例召回率是该类真实样本中被找回来的比例F1是两者的调和平均值。这三个指标在二分类中容易理解在多分类中逐类计算后还能用加权平均汇总为一个数字。5. 用学习率衰减和交叉验证找到最优参数组合5.1 一张参数表解决新手调参问题BP神经网络在鸢尾花数据集上主要涉及4个超参数它们的取值范围和调整方向如下参数常见范围调参方向说明学习率0.001~0.5过大则震荡、过小则收敛慢步进式尝试0.5、0.1、0.01隐藏层节点数4~32从8起步按2的幂次递增过小欠拟合、过大过拟合batch_size8~64小数据集用16或32过小梯度噪声大、过大内存占用高epoch100~1000观察loss趋于平稳即可配合早停策略更高效5.2 学习率衰减的动手实现常数学习率的一个痛点训练初期需要较大的学习率快速接近最优解后期则需要较小的学习率在最优解附近精细收敛。常见做法是设置阶梯衰减策略。在第150轮、第300轮将学习率乘以衰减系数0.5。实现上只需修改训练方法def train_with_decay(self, X, y, epochs, batch_size16, decay_epochs150, decay_factor0.5): total X.shape[0] for epoch in range(epochs): # 到达预设的衰减节点时学习率减半 if epoch in decay_epochs: self.lr * decay_factor print(fEpoch {epoch}: Learning rate decayed to {self.lr:.4f}) perm np.random.permutation(total) X_shuffled, y_shuffled X[perm], y[perm] for i in range(0, total, batch_size): X_batch X_shuffled[i:ibatch_size] y_batch y_shuffled[i:ibatch_size] output self.forward(X_batch) self.backward(X_batch, y_batch, output)实现逻辑每个epoch开始时先检查是否到达衰减节点如果到达则将当前学习率更新为前一阶段的值乘以decay_factor。衰减后的学习率会影响这一轮及之后的所有梯度更新。配合此策略初始学习率可以适当设置得更大一些比如0.3开始让训练前期的下降速度更快。5.3 K折交叉验证替代单次数据切分单次数据切分对150个样本的数据集过于依赖运气。如果某一类别的测试样本恰好都是难以识别的边缘点评估结果就会有偏差。K折交叉验证把数据分成K份每轮用K-1份训练、1份验证共训练K次取平均分得到的评估结果稳定得多。from sklearn.model_selection import cross_val_score # 封装一个与sklearn接口兼容的评估函数输入训练数据输出预测结果 # 每次用K-1份数据训练模型并在剩余1份上预测 def bp_predict(X_train, y_train, X_test): # 初始化模型固定随机种子让结果可复现 np.random.seed(42) model BPNeuralNetwork(4, 8, 3, 0.1) # 训练100轮 y_onehot np.eye(3)[y_train] model.train(X_train, y_onehot, 100, batch_size16) return np.argmax(model.forward(X_test), axis1) # 用5折交叉验证计算平均准确率 # cross_val_score接收训练数据和标签自动完成数据切分与评估 scores cross_val_score(estimatorNone, XX_train, yy_train, cv5) print(f5折交叉验证平均准确率: {scores.mean():.4f} (±{scores.std():.4f}))在实际使用中cross_val_score的estimator参数需要传入一个实现了fit和predict方法的对象上面的代码以伪代码形式展示了思路动手实现时可以将BPNeuralNetwork类补充这两个方法后直接接入。与单次划分相比5折交叉验证输出的分数区间能更全面地反映模型在不同数据子集上的表现参数调优的结论也更可信。本文还有配套的精品资源点击获取
RELATED

相关推荐

基于GPT-6 Astra的跨平台GitHub查询机器人:QQ与飞书双端实现

基于GPT-6 Astra的跨平台GitHub查询机器人:QQ与飞书双端实现

上个月我把公司内部使用的 GitHub 辅助查询机器人从单一聊天工具迁移到了 QQ 和飞书双端,同时接入了 GPT-6 Astra 的智能体能力。现在同事在 QQ 群里发一句“帮我看下 fastapi 这个仓库最近的 issue 情况”,机器人会自动调用 GitHub API、拉取数据、再交…

📅 2026/9/14 7:45:45
Python毕业设计:恶意代码检测分类平台搭建与实现

Python毕业设计:恶意代码检测分类平台搭建与实现

简介:面向计算机相关专业毕业设计的高分项目源码包,以Python实现恶意代码检测与分类平台,适用于正在准备毕设、课程设计或期末大作业的学生。项目经导师指导认可,评审分97分,覆盖数据预处理、模型训练、分类识别等完整…

📅 2026/9/14 7:45:45
二分查找算法原理、实现与优化指南

二分查找算法原理、实现与优化指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/9/14 7:45:45
MORE NEWS

更多资讯

📰

深入剖析 ScyllaDB Commitlog 段文件格式:从文件头到碎片化条目的逐字节解析

深入剖析 ScyllaDB Commitlog 段文件格式:从文件头到碎片化条目的逐字节解析 【免费下载链接】scylladb NoSQL data store using the Seastar framework, compatible with Apache Cassandra and Amazon DynamoDB 项目地址: https://gitcode.com/GitHub_Trending/s…

📰

数据驱动MPC与机组组合优化:预测、滚动求解与Matlab实现

简介:针对电力系统机组组合与模型预测控制交叉方向,这份Matlab项目案例提供了完整可运行的代码框架,适合自动化、电气工程、人工智能等相关专业学生与研究人员用于学习或二次开发。资源共29个文件,核心为16个.mat数据文件与11个.m…

📰

MATLAB计算太阳天顶角:从赤纬、时角公式到完整实现

简介:SolarAngle.MATLAB 是一份面向太阳能工程、气象与环境科学研究者的 MATLAB 计算工具,用于根据地理位置、日期和时间精确求取太阳天顶角、太阳高度角与方位角,为光伏电站朝向优化、建筑采光设计及辐射分析提供基础数据。太阳天顶角与高度…

📰

Matlab实现区域能源系统双层优化与需求响应

1. 项目背景与核心价值区域综合能源系统(RIES)作为能源互联网的重要载体,正在推动传统能源系统向低碳化、智能化转型。这个Matlab复现项目源自核心期刊论文,聚焦"需求响应双层优化"这一前沿方向,其核心价值在…

📰

政府科技管理部门技术转移体系构建与实践

1. 政府科技管理部门推动技术转移的现状与挑战技术转移作为科技创新成果转化为现实生产力的关键环节,一直是政府科技管理部门工作的重点。但在实际操作中,我们常常面临以下典型问题:信息不对称:高校科研院所的研究成果与企业需求之…

📰

用Python手写BP神经网络实现鸢尾花分类:从原理到调参

简介:面向Python初学者的人工智能实践项目,使用BP神经网络对经典鸢尾花数据集进行分类,配套完整源码、数据集和文档说明,可满足期末大作业、课程设计等场景。除BP神经网络两个版本(V1/V2)外,还提…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

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

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

📞 💬