尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python手写BP神经网络:从原理到回归预测实战
简介BP神经网络是机器学习中应用广泛的多层前馈网络这份资源以Python实现了基于BP神经网络的手写体数字识别模型面向希望掌握神经网络原理与图像分类实践的初学者及开发者。项目针对手写数字识别数据集通过反向传播算法迭代更新权重和偏置完成0-9共10个数字的分类任务。压缩包共4个文件含2个Python脚本和2个pickle权重文件大小约1.17MB结构精简。核心Python脚本涵盖网络搭建、前向传播与反向传播训练逻辑两个pickle文件保存训练完毕的权值与偏置可直接加载进行预测另有一个测试脚本用于在测试集上验证模型统计识别率。这样的文件安排既便于从零学习BP神经网络的训练过程也方便快速复用已训练好的模型。资源已有815人学习下载对想快速上手神经网络数字识别、了解Python模型持久化的读者来说是一份小巧实用的参考资料。1. 从“调包能跑”到“手写能懂”Python做BP神经网络预测先搞清楚这笔账很多人第一次接触BP神经网络预测模型是在课程作业或者比赛里手头有一批数据要预测一个连续值老师提了一句“可以用BP神经网络试试”然后你就打开Pythonimport了几个库跑通了一个demo。但真正到了换一批数据、调一个参数、解释一次结果的时候你会发现这个模型像只黑匣子跑通了不知道为什么跑崩了更不知道从哪下手。这篇笔记要做的是把这个黑匣子拆开。我们会先用Python从零手写一个BP神经网络把前向传播、反向传播、梯度下降这三件事彻底讲透再讨论隐藏层节点数、学习率、激活函数这些参数到底在起什么作用。最后通过完整代码实例搭好训练、预测、评价的闭环。你不用先精通数学只要会Python基础语法跟着章节把代码敲一遍就能拥有一个属于自己的、能解释、能改动、能排错的BP神经网络预测模型。文章会更偏向实战所以不会堆公式推导但会告诉你每个公式在代码里对应哪一行、出了问题看哪个变量。适合正在做回归预测、时间序列预测和课程设计的读者如果你想用BP做图像或文本分类原理部分依然通用只是最后一层和损失函数要换。2. BP神经网络的三块地基层、激活函数、损失函数各自该怎么选2.1 网络结构怎么定层数和神经元数没有银弹但有止损线BP神经网络的结构听着简单输入层、隐藏层、输出层。但放到一个具体预测任务里到底要几层、每层多少个神经元就成了第一个劝退点。很多人在这一步就开始“玄学调参”实际上是有基本规律可循的。先说一个结论绝大多数回归预测任务单隐藏层就够用。单隐藏层的BP神经网络可以逼近任意一个在闭区间内连续的映射函数这是有理论保证的。如果你的数据关系没那么复杂先别急着堆层数把单层训练好、评估好再考虑加深。因为隐藏层一旦加到两层以上梯度消失、训练不稳、调参成本都会随之上升新手很容易在这里翻车。隐藏层神经元数量则有个经验公式常见的做法是取输入维度与输出维度的平均值附近并向上取整。比如输入层5个特征输出1个值那隐藏层先取3到6个。这个范围不需要精确它只是一个起点后面要通过观察训练误差的下降曲线来判断要不要增加或减少。这里的关键思维是神经网络不是越复杂越好而是越贴合数据复杂度越好。用10个神经元能拟合的曲线用100个神经元去拟合不仅慢还容易把噪声也记进权重里表现为训练误差很低但预测误差很高。另外要注意的是BP神经网络对输入数据的尺度极其敏感。常见做法是在训练前把所有特征缩放到0到1之间或者标准化到均值为0、方差为1。这一步看似不起眼却直接决定模型能不能收敛后续的代码演示里我会把它放在数据处理的第一步并解释具体原因。2.2 激活函数决定网络能不能“学会”非线性关系如果没有激活函数BP神经网络不管堆多少层本质上都只是一个线性回归模型。激活函数的作用是给网络引入非线性让它有能力拟合那些不是一条直线就能描述的数据关系。所以选择什么样的激活函数直接决定了网络的表达能力和训练难度。隐藏层最常用的激活函数是ReLU它在正半轴是线性的负半轴直接输出0计算简单能有效缓解梯度消失问题。但ReLU有个著名的缺点如果某个神经元的输入长期是负数它的梯度就是0这个神经元就再也不会被更新了俗称“神经元死亡”。训练时如果发现loss突然不动了、很多权重变成0就要怀疑是不是这个原因。输出层的激活函数则需要根据预测任务来定。如果我们做的是回归预测输出值是任意实数那么输出层就不要用sigmoid或tanh因为它们会把输出限制在一个范围内比如sigmoid只能输出0到1。正确的做法是输出层不用激活函数直接输出线性值。如果做二分类输出层用sigmoid把输出压到0到1之间当作概率做多分类输出层用softmax。sigmoid和tanh并没有完全被淘汰在层数不深、做小规模数据拟合时tanh的表现往往比ReLU更稳定因为它输出有正有负能让梯度更新更均衡。我一般会这样选隐藏层优先试ReLU如果训练曲线出现严重震荡换tanh试试输出层按任务选回归任务直接线性输出。选好激活函数后代码里只改一行对比一下训练曲线就能看出差别这个动作值得多试几次。2.3 损失函数和梯度下降反向传播到底在“传”什么BP神经网络的学习过程由两部分构成前向传播计算输出反向传播计算梯度然后用梯度更新每一层的权重。这个过程反复迭代直到预测误差降到可接受的范围。损失函数就是衡量“预测值和真实值差多少”的函数它决定了反向传播时梯度往哪个方向走。回归任务最常用的是均方误差损失函数它对大误差的惩罚更重能让模型优先修正偏差大的样本。在Python里只需要一行计算np.mean((y_pred - y_true) ** 2)。理解均方误差就够了因为它对预测值求导后形式非常简洁便于手写实现时推导梯度。反向传播本身并不神秘。链式法则告诉我们输出层每个神经元的误差可以沿着连接权重一层层往回传递拆解出每个权重对整体误差的“贡献程度”这个贡献程度就是梯度。拿到梯度后用梯度下降法更新权重方向是梯度的负方向步长由学习率控制。学习率设大了权重来回震荡设小了收敛速度极慢甚至困在局部极小点。在代码层面手写BP神经网络时我们只需要做三件事初始化权重、前向传播算输出、反向传播更新权重。一旦你亲手写完这三步就会发现所谓“神经网络训练”本质上就是不断重复前向和反向的过程而Python的numpy数组运算可以一次性完成整个矩阵的梯度计算这让手写实现并不需要多少代码量。接下来一章我把完整代码拆开讲每一行都对应这里的某个概念。3. 用Python从头写BP神经网络数据、前向、反向、预测一条龙3.1 数据准备与归一化先让数据别“捣乱”很多教程喜欢一上来就搭建网络结构但实战里第一步应该是数据处理。BP神经网络对特征的量纲很敏感如果一个特征是0到1的小数另一个特征是几百到几千的大整数网络会默认后者更重要因为它的数值波动天然就大。这会导致权重更新方向被个别特征绑架模型拟合效果很差。我们要做的是把所有特征压缩到同一个尺度范围。最常见的做法是归一化公式很简单x_new (x - x_min) / (x_max - x_min)把数据映射到0到1区间。这里有一点要注意归一化必须用训练集的x_min和x_max而不是整个数据集的。因为测试集在训练阶段是“未来数据”不能提前让它影响归一化参数否则会造成数据泄露验证结果虚高。下面是一段数据准备和归一化的示例代码用了numpy和pandas这是Python数据处理最基础的组合几乎不需要额外配置环境。import pandas as pd import numpy as np # 读取数据。举个例子假设Excel里有3个特征列X1, X2, X3和一个目标列Y df pd.read_excel(data.xlsx) X df[[X1, X2, X3]].values y df[Y].values.reshape(-1, 1) # 转成列向量方便矩阵运算 # 手动实现归一化记录训练集的最小值和最大值 X_min X.min(axis0) X_max X.max(axis0) X_norm (X - X_min) / (X_max - X_min 1e-8) # 加一个极小值防止除零 # 同样处理目标值 y_min y.min(axis0) y_max y.max(axis0) y_norm (y - y_min) / (y_max - y_min 1e-8)数据准备好后别忘了划分训练集和测试集。常见做法是把数据随机打乱取前80%做训练后20%做测试也可以用sklearn的train_test_split。打乱顺序这一步很重要因为原始数据按时间或编号排列时往往存在某种单调趋势不打乱会让训练集和测试集的分布不一致导致模型评估失真。归一化的代码逻辑很直接对每一列特征独立计算最小值和最大值然后逐元素做线性变换。代码里的1e-8是一个很小的常数当某列最大值等于最小值时分母不会变成0这个细节在特征列只有一种值时能够避免除零错误。目标值的归一化同样重要因为输出层的激活函数如果是sigmoid或者tanh输出范围有限目标值也必须落在对应区间内才合理。3.2 前向传播与误差反向传播核心代码拆开讲搭建BP神经网络最核心的部分就是参数的初始化和前向、反向传播逻辑。网络结构参考上面说的经验公式输入层3个节点隐藏层4个节点输出层1个节点。权重初始化用随机数但注意不能太大否则激活函数容易饱和梯度趋近于0训练几乎不动。常见做法是用均匀分布的随机数乘以一个缩放系数让初始权重落在-0.5到0.5之间。下面这段代码定义了网络结构、初始化权重以及前向传播和反向传播是BP神经网络最核心的部分建议手敲一遍而不是复制粘贴。自己敲代码的过程会迫使你注意到每个变量的形状这对理解矩阵运算是决定性的。import numpy as np np.random.seed(42) # 固定随机种子保证实验可复现 input_size 3 # 输入特征数 hidden_size 4 # 隐藏层神经元数 output_size 1 # 输出值个数 # 初始化权重和偏置std参数控制初始权重的大小 W1 np.random.randn(input_size, hidden_size) * 0.5 b1 np.zeros((1, hidden_size)) W2 np.random.randn(hidden_size, output_size) * 0.5 b2 np.zeros((1, output_size)) def tanh(x): return np.tanh(x) def tanh_derivative(x): return 1 - np.tanh(x) ** 2 # 前向传播 def forward(X): z1 np.dot(X, W1) b1 # 隐藏层线性变换 a1 tanh(z1) # 隐藏层激活 z2 np.dot(a1, W2) b2 # 输出层线性变换 return z2, a1 # 输出层不加激活函数回归任务直接输出 # 反向传播 def backward(X, y, a1, output): m X.shape[0] # 样本数量 # 输出层误差预测值减真实值对应均方误差的导数 delta2 (output - y) / m # 隐藏层误差输出层误差经过权重矩阵和激活函数导数的链式传播 delta1 np.dot(delta2, W2.T) * tanh_derivative(a1) # 计算梯度并更新参数 dW2 np.dot(a1.T, delta2) db2 np.sum(delta2, axis0, keepdimsTrue) dW1 np.dot(X.T, delta1) db1 np.sum(delta1, axis0, keepdimsTrue) return dW1, db1, dW2, db2这段代码里输出层的delta2只有一个减法那是均方误差对输出z2求导后的结果。为什么没有乘以某个激活函数的导数因为输出层没有加激活函数导数就是1。而隐藏层的delta1里出现了tanh_derivative(a1)这就是链式法则的体现——输出层的误差沿着W2传播到隐藏层再乘以隐藏层激活函数的导数得到隐藏层每个神经元对误差的贡献。理解这两行反向传播就不再是黑匣子了。初始化权重时乘以0.5是为了避免让初始权重落入非线性函数的饱和区。如果用np.random.randn直接初始化权重的标准差是1输入经过加权求和后可能达到很大的绝对值tanh在输入绝对值大时导数趋近于0网络一开始就学不动。权重初始化是一个值得多花时间理解的操作它往往是模型能不能收敛的第一道门槛。3.3 训练循环、预测与反归一化把模型凑成完整闭环有了前向和反向的函数定义接下来就是把它们放进训练循环里反复迭代。训练循环包含三个步骤前向传播计算预测值反向传播计算梯度再按学习率更新参数。我们需要记录每一轮迭代的误差绘制出训练曲线方便观察收敛情况。下面是一段标准的训练循环代码用梯度下降法更新权重并在每100轮打印一次误差。learning_rate 0.1 epochs 2000 # 拆分训练集和测试集 split int(len(X_norm) * 0.8) train_X, test_X X_norm[:split], X_norm[split:] train_y, test_y y_norm[:split], y_norm[split:] loss_history [] for epoch in range(epochs): # 前向传播 output, a1 forward(train_X) # 计算损失值 loss np.mean((output - train_y) ** 2) loss_history.append(loss) # 反向传播计算梯度 dW1, db1, dW2, db2 backward(train_X, train_y, a1, output) # 更新参数 W1 - learning_rate * dW1 b1 - learning_rate * db1 W2 - learning_rate * dW2 b2 - learning_rate * db2 if epoch % 200 0: print(fEpoch {epoch}, Loss: {loss:.6f}) # 测试集预测 test_output, _ forward(test_X) # 反归一化把预测值还原到原始量纲 test_pred test_output * (y_max - y_min) y_min test_true test_y * (y_max - y_min) y_min # 计算预测误差 rmse np.sqrt(np.mean((test_pred - test_true) ** 2)) print(fRMSE: {rmse:.4f})这里有几个细节需要说明。学习率0.1是起步值不一定最优具体的调整策略我放在下一章专门讲。2000轮迭代对一个小数据集来说足够但判断标准不是看是否跑完而是看loss是否收敛。如果loss打印到最后还在明显下降说明迭代不够如果loss曲线像锯齿一样来回跳动说明学习率偏大。反归一化的公式就是归一化的逆运算乘上原始范围再加回最小值。这一步非常容易被忽略因为它不影响训练只影响最终结果的展示。但如果不做反归一化你测试集上算出来的“预测误差”是0到1量纲的根本没法跟业务指标直接对应。很多初学者训练时loss看起来很小一拿到真实数据上验证就发现对不上多半是这个原因。4. numpy版BP跑通后的调参顺序从学习率到隐藏节点数4.1 先调学习率一切训练不稳定的源头排查当代码能跑通但效果不理想时我建议按照一定的顺序去调参而不是随机乱试。第一个要检查的永远是学习率。学习率决定了每一步权重更新的跨度它带来的问题最有规律可循学习率太大loss曲线震荡明显甚至越训越大学习率太小loss下降非常缓慢训练跑了很多轮还像没动一样。判断学习率是否合适的办法很简单训练完成后把loss_history画出来看。如果曲线是一条平滑下降然后趋于平稳的弧线说明学习率合适。如果曲线在下降过程中出现明显的周期性波动先试着把学习率除以10再跑一次。如果曲线几乎是一条水平直线且数值较大除了学习率太小之外还要检查是否梯度消失、数据没有归一化。下面是绘制loss曲线的代码用matplotlib就能完成这个可视化的习惯强烈建议建立起来。训练不画图等于闭着眼睛开车。import matplotlib.pyplot as plt plt.plot(loss_history) plt.xlabel(Epoch) plt.ylabel(Loss) plt.title(Training Loss Curve) plt.grid(True) plt.show()看曲线时有一个误区训练loss还没有完全平稳就停下来调结构。常见做法是先让学习率先保持0.1或0.01不变加大迭代次数跑到底看loss最终的收敛水平然后再回头调学习率。学习率不是一个孤立参数它与迭代次数、网络结构都有关联所以不要单独只看一个指标就下结论。4.2 隐藏节点数的试探方法从小往大加观察边际收益隐藏层节点数是BP神经网络最像“玄学”的地方但有一个比较可靠的实践方案从较小的节点数开始逐步递增观察测试集误差的变化。这里的核心逻辑是测试集误差先下降后上升的那个拐点通常是一个不错的选择。拐点之前模型在欠拟合结构容量不够拐点之后模型在过拟合把训练集里的噪声也记住了。实际操作中我会用3、5、8、12这样一档一档去试每一档跑相同的epoch数记录测试集误差最后画一条“节点数-误差”的曲线。如果5个节点时测试误差是0.018个节点变成了0.012那就没必要再往上加了。这个过程的成本很低因为每个档位只需要几十秒训练时间。需要提醒的是隐藏节点数和数据量是有配比关系的。如果你的训练集只有几百个样本隐藏节点数加到几十上百即使训练误差能降到很低预测效果也一定很差。数据量越少网络结构越要保守。这一点在时间序列预测、故障诊断这类小样本场景里特别常见。合理设置隐藏层节点数还需要结合正则化来理解。正则化的常见做法是在损失函数里加一个L2惩罚项让权重的绝对值不要过大从而抑制过拟合。在numpy手写代码里加L2正则化只需在损失计算时多一项lambda * np.sum(W1**2)反向传播时在梯度里对应加上2 * lambda * W1。lambda_reg 0.001 # 带L2正则的损失计算 loss np.mean((output - train_y) ** 2) lambda_reg * (np.sum(W1**2) np.sum(W2**2)) # 反向传播梯度对应修改 dW2 2 * lambda_reg * W2 dW1 2 * lambda_reg * W1L2正则化的作用是让权重在每次更新时都额外往0的方向偏移一点相当于限制了模型的复杂度。正则化系数lambda_reg不宜设得过大否则模型退化成一条接近线性的直线欠拟合。从0.001开始尝试是一个务实的起点。4.3 训练与验证的节奏不要只用训练loss判断模型好坏很多人在训练结束以后只打印训练集上的loss就认为模型成功了。这是实战里最常见的误判。训练loss低只能说明模型记住了训练集不能说明它对未知数据有预测能力。正确的评估方式是在训练过程中周期性地计算测试集误差观察两者之间的差距。如果训练loss和测试误差都在下降说明模型还在正常学习阶段如果训练loss继续下降但测试误差开始回升说明过拟合已经开始应该立即停止训练。这种提前停止的策略在深度学习框架里的术语叫early stopping在numpy手写代码里实现起来也很简单只需在循环里记录历史最优模型参数一旦测试误差连续多轮不降反升就结束训练并恢复最优参数。下面这段代码展示的是完整的训练节奏控制逻辑包含训练情况记录和最优参数保持实用性非常高。best_loss float(inf) best_W1, best_b1, best_W2, best_b2 None, None, None, None patience 50 # 连续50轮测试误差不降低就提前停止 wait 0 for epoch in range(epochs): # 前向、反向、更新省略同上文 output, a1 forward(train_X) loss np.mean((output - train_y) ** 2) # 计算测试集误差 test_out, _ forward(test_X) test_loss np.mean((test_out - test_y) ** 2) if test_loss best_loss: best_loss test_loss best_W1, best_b1 W1.copy(), b1.copy() best_W2, best_b2 W2.copy(), b2.copy() wait 0 else: wait 1 if wait patience: print(fEarly stopping at epoch {epoch}) break # 训练结束后恢复最优参数 W1, b1, W2, b2 best_W1, best_b1, best_W2, best_b2注意保存最优参数时用了copy()这是numpy里很常见的坑。如果直接写成best_W1 W1那只是把引用保存下来后面W1被更新best_W1指向的同一个对象也被改了保存就完全失效。这种引用与拷贝的细节在Python的多个使用场景里都会出现值得养成习惯。5. BP神经网络Python实现避坑高频翻车现场复盘5.1 数据没归一化loss像卡住了一样不动现象训练时loss一直是同一个数量级比如0.9左右怎么增加epoch都降不下去。你以为是激活函数选错了或者学习率调得不对但改来改去毫无起色。原因特征量纲差异过大比如某个特征取值范围是0到1另一个是1000到2000。加权求和后大数值特征完全支配了输出梯度更新时小数值特征对应的权重几乎收不到有效梯度整个网络在用一个特征做预测其余全是噪声。解决方式数据处理阶段就做归一化或标准化这是BP网络的硬性前提。如果已经训到一半才发现忘了别浪费时间回到数据预处理重新跑一遍。很多“玄学不收敛”的问题排查到最后根源就是这一步。5.2 输出全是同一个值激活函数用错了位置现象预测结果几乎是一个常数比如所有测试样本的输出都接近0.5或者都接近同一个数看测试集预测值和真实值的散点图像一条水平的直线。原因一种情况是输出层用了sigmoid激活函数导致输出范围被限制在0到1之间如果目标值本来在50到100之间网络再怎么学都不可能预测准。另一种情况是sigmoid或tanh进入饱和区权重初始值过大输入经过加权求和后落在激活函数两端平缓的位置梯度接近于0。解决方式回归任务的输出层直接用线性输出不加任何激活函数。如果隐藏层用tanh或sigmoid把初始化权重的缩放系数调小比如从0.5降到0.1让初始输入落在激活函数的敏感区训练才能顺利启动。5.3 loss曲线震荡像锯齿学习率不匹配现象loss不是平稳下降而是上下剧烈跳动或者前几百轮正常下降后突然跳高之后再也不恢复。原因学习率太大。每一步权重更新跨度太大直接越过了最优点导致loss在最优值附近来回震荡。有时初始位置恰好有利于下降前几轮看着正常一旦靠近极小值区域就会开始来回横跳。解决方式先按数量级调学习率0.1不行就试0.01再不行0.001。一次调一个数量级观察loss曲线形状。震荡消失且下降平稳的学习率就是当前结构下的合理起点。5.4 反向传播手写代码出现NaN梯度爆炸现象训练若干轮后loss突然变成nan或者权重矩阵里出现了inf。原因梯度在反向传播过程中乘以了多个权重数值和激活函数导数当网络层数稍深或权重偏大时梯度会指数级放大最后溢出。这是一个数值稳定性问题不一定是你的业务数据有问题。解决方式第一步检查学习率先调小看是否缓解。第二步检查初始权重范围np.random.randn的结果乘0.5乘0.1都可以试。第三步查看本机Python环境的数据类型确认numpy数组默认是float64而不是float32后者更容易在训练中溢出。5.5 训练完成后权重变成全0ReLU神经元死亡现象使用ReLU作为隐藏层激活函数时训练后期部分权重和偏置变成0这些神经元对应的输出恒为0相当于整个网络容量在缩小训练效果明显变差。原因ReLU的负数区间梯度为0一旦某次更新把这批神经元的输入推到负数区域它们就再也无法恢复。学习率偏大时这种情况更容易发生。解决方式使用ReLU时注意初始化方式和学习率配合。更稳妥的做法是在隐藏层改回tanh它对负数输入依然有非零梯度不会有“死亡神经元”问题。数据量不大、网络层数不深时tanh的表现往往更可靠不少老手回归任务默认就是tanh加线性输出。6. 掌握模型验证的三把尺梯度检查、对比基线和回归指标6.1 梯度检查用数值方法验证反向传播没写错手写反向传播最容易出现的问题是公式推导对了但代码写错比如矩阵转置方向弄反、delta1和delta2顺序颠倒。梯度检查是为了验证反向传播实现是否正确做法很朴素把某个权重参数扰动一个极小量用前向传播计算两次loss的差值除以扰动量得到数值梯度再和反向传播得到的梯度对比两者应非常接近。以W1的某一个元素为例数值梯度公式为dW1_num (loss(W1eps) - loss(W1-eps)) / (2*eps)。相对误差在1e-6量级以上基本可以判定反向传播的梯度计算正确。这个检查在完整的训练循环之前做一次能省去后面几天排查bug的时间。6.2 对比一个足够简单的基线多元线性回归任何神经网络在上手之前都应该先跑一个简单的基线模型。对于回归预测任务最合适的基线就是多元线性回归Python里实现不超过三行sklearn的LinearRegression可以一步完成。如果BP神经网络的测试误差还不如线性回归好那要先检查数据或网络是否出了问题而不是继续加复杂度。基线方法能提供两个信息一是当前数据关系的可预测性上限大致在什么水平二是模型的复杂度是否匹配数据量。如果线性回归和BP网络误差接近说明数据可能本身偏向线性分布或当前特征无法提供足够信息增加BP网络的隐藏节点数就不会带来性能提升。这个对比能避免很多盲目的网络结构调整。6.3 三个回归指标不只测一个RMSE测试集上最常用的三个指标是均方根误差RMSE、平均绝对误差MAE和决定系数R2。RMSE对大误差更敏感适合业务中难以接受大偏差的场景MAE更直观地反映平均预测偏差R2衡量模型对数据方差的解释程度R2越接近1说明模型越好。from sklearn.metrics import mean_absolute_error, r2_score # test_true和test_pred是反归一化后的真实值和预测值 mae mean_absolute_error(test_true, test_pred) rmse np.sqrt(np.mean((test_true - test_pred) ** 2)) r2 r2_score(test_true, test_pred) print(fMAE: {mae:.4f}) print(fRMSE: {rmse:.4f}) print(fR2: {r2:.4f})单独一个RMSE低不能说明模型有效比如某些业务场景数值普遍很大RMSE的绝对值自然大。只有同时看R2和MAE并结合业务理解判断误差水平可不可接受。我还习惯把测试集真实值和预测值画在同一个坐标轴里肉眼可见的走势趋势才是最有说服力的结果。画图不需要多少代码却是我每次实验的最后一环也是发现预测系统性偏差最直接的方法——看曲线是预测超前了、滞后了还是整体偏低这些信息是数字指标给不了的。这种从头手写再逐步验证的习惯比直接调用深度学习框架更能帮你理解每一层的作用。哪怕回归任务本身不算复杂这一套流程跑下来你对批量数据处理、权重更新、循环控制的熟练度都会上一个台阶后续上手更复杂的模型也就不慌了。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

论文数据分析不会做?这个工具帮你搞定统计分析输出

论文数据分析不会做?这个工具帮你搞定统计分析输出

不少理工科、经管类同学写到论文数据分析章节就陷入瓶颈。拿到调研、实验原始数据之后无从下手,不知道该选用哪一种统计方法;不会操作SPSS等统计软件,做不出规范图表;输出结果之后,不知道如何解读回归、方差、T检验的运…

📅 2026/9/23 9:57:05
电力窃电识别实战:从时序数据清洗到可解释决策树建模

电力窃电识别实战:从时序数据清洗到可解释决策树建模

简介:本资源是一份面向Python数据挖掘与机器学习初学者及电力行业数据分析从业者的实战项目包,聚焦电力系统中窃漏电用户自动识别这一典型工业场景,覆盖从数据清洗、特征构建到模型训练与评估的完整流程。压缩包共13个文件,含4个n…

📅 2026/9/23 9:57:05
基于改进YOLOv5的人群密度检测:FasterNet主干与Soft-NMS优化实践

基于改进YOLOv5的人群密度检测:FasterNet主干与Soft-NMS优化实践

简介:这份资源面向深度学习与计算机视觉方向的学习者,提供一套基于改进YOLOv5的人群密度检测系统完整实现方案,可用于商场、车站、体育场等人流密集场景的实时计数与安全监控研究。方案以FasterNet替换原YOLOv5主干网络提升推理速度&#xff…

📅 2026/9/23 9:57:05
MORE NEWS

更多资讯

📰

Octop:MIT开源的Python轻量级安全模式扫描器

1. “Octop”不是拼写错误,而是MIT实验室里跑出来的Python代码审计轻骑兵你搜“Octop”时,大概率会一头雾水——没有官网、没有GitHub star破千的仓库、PyPI上查不到包、Ruff文档里不提它、连MIT官网的公开项目列表里都难觅其踪。我第一次在同事的终端里…

📰

沙箱管理套件配 TaoToken:为 AI Agent 搭建可观测代码执行环境的 config.toml 骨架

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

📰

主动网snscn源码深度拆解:搞定性能优化与调试难题

主动网snscn源码深度拆解:搞定性能优化与调试难题 手里拿着从网上扒来的“主动网snscn”相关代码,直接丢进项目里跑,结果报错信息一堆,连哪里卡住都不知道怎么调?别急,这种“复制即崩溃”的尴尬,在市政公用工程相关的软件开发中太常见了。很…

📰

主数据管理(MDM)在投资集团的核心价值与实践

1. 主数据管理的战略价值解析在大型投资集团的实际运营中,数据就像一座漂浮的冰山——我们日常看到的报表和分析只是露出水面的10%,而真正决定企业决策质量的,是水面下那90%的主数据质量。三年前我们集团就曾因为客户主数据不统一&#xff0c…

📰

A320飞行模拟器在航空教学中的应用与架构解析

1. 项目背景与价值解析去年协助某航空类高校搭建飞行模拟实验室时,我第一次真切感受到A320模拟器在教学中的巨大潜力。这种1:1还原真实驾驶舱的高仿真设备,正在改变传统航空人才培养模式——学生不再需要等到大四实习才能接触真实飞行操作,从…

📰

江苏正规的耐火砖定制生产厂家,华耀镁碳砖合作实力参考

工业高温窑炉耐火砖选购,你可能踩了这4个典型坑挑选耐火砖时,很多人都会遇到这些糟心事: 怕买到掺假减配的产品,MgO含量虚标、批次不稳定,用不了多久就开裂剥落,频繁停炉检修;想定制适配工况的耐火砖&#…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

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

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

📞 💬