☰
MATLAB中BP神经网络多输入单输出预测程序详解与代码实现
2026/10/6 4:30:45 网站建设 项目流程

在MATLAB里做预测,绕不开BP神经网络。我拿到过太多类似“多了个输入单输出、要可直接换数据”的代码需求,坦白讲,市面上的碎片代码不少,但真正注释清楚、结构清晰、拿回来就能跑通并换成自己数据的程序,反而稀缺。这篇就把我优化过的一套BP神经网络预测MATLAB程序掰开揉碎讲清楚,包含了设计思路、关键参数怎么定、完整实操流程和换数据时最容易踩的坑,让刚接触BP的同学也能照着把代码跑起来。

1. 程序整体设计思路:多输入单输出预测的关键拆解

1.1 BP神经网络为什么适合这类预测

BP神经网络是反向传播(Back Propagation)算法的多层前馈网络,核心能力是拟合输入和输出之间的非线性映射关系。对多输入单输出的预测问题来说,输入通常是多个特征变量,比如温度、湿度、压力、流量这些影响因子,输出是一个目标值,比如能耗、产量、浓度或者价格。当变量之间存在复杂的交互作用,而且很难用明确的数学公式写出来时,BP神经网络的优势就出来了:它能在训练过程中自动调整神经元之间的连接权值和阈值,通过不断迭代逼近目标函数。

很多人会问,这种多输入单输出的问题,用线性回归或者多项式拟合不就行了?如果特征和目标之间确实是线性或者低阶多项式关系,那当然可以。但实际问题里,特征之间往往存在非线性耦合,比如某个输入只有在另一个输入超过阈值时才显著影响输出,这种带“开关”效应的关系,线性模型根本表达不了。BP神经网络因为隐含层使用了非线性激活函数,理论上可以逼近任意闭区间上的连续函数,这就是所谓万能逼近定理。所以,当我面对一堆说不清关系的特征变量时,BP是比传统回归更稳妥的默认选择。

有一点必须提醒:BP网络不是“装上就能出好结果”的魔法箱。它对数据质量、参数设置、训练策略都很敏感。一套好的MATLAB预测程序,重要的是把数据预处理、网络构建、训练控制、结果评价这几个环节的边界理清楚,让使用者可以专注于“换数据”,而不是每次都被代码细节绊住。

1.2 多输入单输出建模的整体框架

一个典型的多输入单输出BP预测流程,可以归纳成六个环节:数据准备、数据归一化、样本划分、网络构建与训练、仿真预测、结果评价。整个过程在逻辑上是一条直线:原始数据进来,经过预处理变成网络认识的形式,训练完成后得到网络模型,然后对未知样本做预测,最后用指标判断预测得好不好。

数据准备环节,核心是确定输入矩阵X和输出矩阵Y的形状。我习惯用“行是样本,列是变量”的格式,也就是X的每一行是一个样本,每一列是一个特征;Y是列向量,每一行对应一个样本的输出。这种格式与MATLAB矩阵操作习惯高度一致,也方便后续用mapminmax做归一化。很多初学者第一块绊脚石就是维度搞反了,后面所有代码都会跟着报错。

数据归一化,这步在BP里几乎是必须的。输入特征如果量纲差异大,比如一个特征是0到1之间的比值,另一个特征是一两千的数值,网络训练时梯度更新会被大数值特征主导,导致收敛慢甚至不收敛。归一化把所有特征压到统一的尺度范围内,让每个特征对网络训练的贡献比较均衡。这里有一个很多教程没讲透的细节:归一化参数(最大值、最小值)只能用训练集去算,然后把这个参数直接应用到测试集上,不能把训练集和测试集混在一起做归一化。否则测试集的信息会提前“泄露”给训练过程,得到的误差指标过于乐观,失去参考意义。

样本划分环节,一般按照70%训练、15%验证、15%测试这样的比例来分,或者根据样本量灵活调整。训练集用来更新权值,验证集用来监控训练过程中是否过拟合,测试集是完全没参与训练的数据,用来评估模型的泛化能力。网络构建与训练,涉及到隐含层神经元数量、激活函数、训练算法的选择,这些参数直接影响预测精度和训练速度。仿真预测就是用训练好的网络对测试集做正向计算,得到预测值。结果评价则是计算均方误差、决定系数这些指标,同时通过绘图直观观察拟合效果。

1.3 程序模块划分的逻辑

我把整套程序按功能拆成独立的区块,每个区块用注释明确标注起点和终点。结构上分成五段:数据导入与格式检查、归一化参数获取与数据处理、网络创建与参数配置、网络训练与仿真、结果计算与可视化。这种拆分方式最大的好处是方便排查问题,哪段报错就只看哪段,不需要从头捋逻辑。

以我这次的优化程序为例,每一段我都加了指导性的注释,比如头部数据区的注释会写明:“这里只需要修改数据文件路径、输入特征列号和输出列号,其余代码不需要动。”这样即使使用者对MATLAB不熟,也能根据提示完成关键修改。为了让程序真正做到“可直接换数据”,我还在数据导入后加了一个维度自检模块,自动判断输入输出矩阵的行数是否一致、是否有明显缺失值,不一致就直接报错并给出中文提示,避免后续代码在奇怪的报错信息里绕圈子。

这种模块化设计的另一个好处是通用性高。同一个项目里,我可以把整套训练预测流程封装成一个脚本,换一个数据集时,只需要改动数据导入段和输出列索引,剩下的归一化、训练、评价、画图逻辑原封不动就能复用。我实际做过的项目里,从风力发电功率预测到房地产价格评估,再到化工过程软测量,都是同一套代码框架,只改了数据和几个结构参数。这就是模块化的价值所在。

2. 核心代码解析与关键参数设定

2.1 数据怎么导入最省心

我最推荐的方式是把数据放到Excel文件里,用readmatrix读取。Excel对多数人来说是最顺手的数据整理工具,一行一个样本、一列一个变量,整理起来直观。readmatrix在较新的MATLAB版本里很稳定,能直接读出数值矩阵,不需要额外处理单元格对象。如果数据文件是.mat格式,直接用load就可以。

下面是我在程序中使用的数据导入模板:

%% 第一步:数据导入 % 使用表格管理数据,方便修改 data = readmatrix('data.xlsx'); % 读取Excel数据,第一行可以是表头也可以直接是数值 % data = load('data.mat'); data = data.data; % 如果数据是mat格式,用这个 % 检查数据是否为空 if isempty(data) error('数据文件为空,请检查文件路径和文件名'); end % 这里假设:最后一列是目标输出,前面的所有列都是输入特征 X_raw = data(:, 1:end-1); % 输入特征矩阵 Y_raw = data(:, end); % 输出目标向量 % 自检:输入和输出的样本数必须一致 if size(X_raw, 1) ~= size(Y_raw, 1) error('输入样本数与输出样本数不一致,请检查数据文件'); end

这段代码看起来简单,但有几个细节值得说。第一,我在读取之后立刻做空数据检查,万一文件路径写错了或者文件内容格式不对,程序会在第一时间给出中文提示,而不是在后续代码里报一个莫名其妙的索引越界错误。第二,我默认最后一列是输出,前面的列是输入,这种约定在大多数预测任务里都成立,使用者只要按这个规则整理数据,代码里的变量索引完全不用改。第三,我保留了.mat格式的读取注释代码,方便有需要的人切换。

如果用户的数据不是Excel也不是.mat,而是文本文件,可以用readmatrix('data.txt'),同样能读入数值矩阵,只要数据是规整的矩形表就行。其实readmatrix本身就支持txt、csv、xlsx等常见格式,所以一套导入逻辑能覆盖绝大多数场景。这里唯一要注意的是:Excel文件如果包含中文表头,readmatrix默认会跳过非数值行,实际不影响读取;但如果你想读取指定的某些列而不是全部列,可以用data(:, 3:5)这种切片操作,这个我在程序注释里也写明了。

2.2 归一化:容易被忽略但影响最大的环节

归一化是BP神经网络代码里最容易被初学者跳过或者做错的一步,但它的影响往往比网络结构参数更大。如果不做归一化,训练过程中梯度下降会非常不稳定;如果归一化方式错了,比如把训练集和测试集混在一起算归一化参数,得到的评估结果会失真。

我用的归一化函数是mapminmax,它是MATLAB神经网络工具箱自带的,默认把数据映射到[-1, 1]区间。这里要注意,mapminmax处理矩阵时是按行操作的,每一行是一组独立的数据。所以原始X矩阵如果是“样本×特征”排列,使用mapminmax之前需要先转置成“特征×样本”排列,处理完再转置回来。这个转置细节是新手最容易出错的地方。

具体代码如下:

%% 第二步:数据归一化 % mapminmax默认按行归一化,所以需要先将矩阵转置为“变量×样本”格式 X = X_raw'; % 转置为 特征数 × 样本数 Y = Y_raw'; % 转置为 1 × 样本数 % 获取归一化参数:只统计训练集的数据范围 % 这里为了方便演示,先取前80%样本作为训练集,后20%作为测试集 trainNum = round(size(X, 2) * 0.8); X_train_raw = X(:, 1:trainNum); Y_train_raw = Y(:, 1:trainNum); X_test_raw = X(:, trainNum+1:end); Y_test_raw = Y(:, trainNum+1:end); % 用训练集计算归一化参数,并归一化训练集 [X_train, ps_X] = mapminmax(X_train_raw, -1, 1); [Y_train, ps_Y] = mapminmax(Y_train_raw, -1, 1); % 用训练集的归一化参数直接映射测试集 X_test = mapminmax('apply', X_test_raw, ps_X); Y_test = mapminmax('apply', Y_test_raw, ps_Y); % 转置回 样本×变量 格式,方便后续训练函数使用 X_train = X_train'; Y_train = Y_train'; X_test = X_test'; Y_test = Y_test';

这里我将训练集和测试集的划分提前到了归一化之前,并且用训练集的数据范围来计算归一化参数ps_X和ps_Y。ps_X是一个结构体,里面保存了训练集每个特征的最大值、最小值以及映射公式所需的所有信息。映射测试集时,mapminmax('apply', X_test_raw, ps_X)会使用ps_X中保存的参数,而不是重新计算测试集自己的最大最小值。这就保证了测试集的归一化方式与训练集完全一致,不会出现信息泄露。

有些代码会把所有数据先整体归一化,再划分训练集和测试集,这样做虽然程序简单,但得到的结果会偏乐观,因为归一化时已经把测试集的信息(最大值最小值)用进去了。在论文或者实际报告中,这种做法的结果是站不住脚的。所以,即使多两行代码,也要坚持先划分、后归一化的顺序。

2.3 网络构建与训练参数选择

网络构建这一块,涉及到隐含层层数、隐含层神经元数量、激活函数、训练函数等参数。我使用的程序里,默认构造一个单隐含层的BP网络,隐含层神经元数量按经验公式计算,训练函数用trainlm(Levenberg-Marquardt),这是中小型数据集上收敛最快、精度最高的默认选择之一。

先看核心代码:

%% 第三步:BP神经网络构建 % 输入层节点数 = 输入特征数 inputDim = size(X_train, 2); % 输出层节点数 = 输出变量数,这里固定为1 outputDim = 1; % 隐含层神经元数量经验公式:sqrt(输入节点数+输出节点数)+1~10之间的常数 hiddenNum = round(sqrt(inputDim + outputDim) + 5); % 创建前馈神经网络 net = feedforwardnet(hiddenNum); % 设置训练参数 net.trainParam.epochs = 1000; % 最大训练轮数 net.trainParam.lr = 0.01; % 学习率 net.trainParam.goal = 1e-5; % 目标误差 net.trainParam.showWindow = true; % 显示训练窗口 net.trainParam.min_grad = 1e-8; % 最小梯度 % 设置网络层激活函数(默认即可,也可以按需修改) % net.layers{1}.transferFcn = 'tansig'; % 隐含层:双曲正切S型函数 % net.layers{2}.transferFcn = 'purelin'; % 输出层:线性函数 % 划分训练集、验证集、测试集比例 net.divideFcn = 'divideblock'; net.divideParam.trainRatio = 0.85; net.divideParam.valRatio = 0.15; net.divideParam.testRatio = 0.0;

关于隐含层神经元数量,我使用的是经典的实验经验公式:hiddenNum = round(sqrt(inputDim + outputDim) + a),其中a通常在1到10之间取整数。这个公式给的是一个初始参考值,实际操作中可以在附近做几次网格搜索。隐含层节点太少,网络拟合能力不足,容易出现欠拟合;节点太多,网络容量过大,容易把训练集的噪声也学进去,产生过拟合。我程序里默认a取5,是考虑到中等规模数据集的通用情况,实测下来大多数项目在这个值附近表现都不错。

训练函数方面,feedforwardnet默认使用trainlm。Levenberg-Marquardt算法结合了梯度下降法和高斯-牛顿法的优点,在中小数据集上收敛速度极快,精度也比较高,是MATLAB神经网络工具箱里最常用的训练函数。它的缺点是在大样本和高维权值空间下内存消耗很大,如果你的样本量超过几万条,trainlm可能会把内存吃得很难看,这时可以考虑换成trainbr(贝叶斯正则化)或者trainscg(规模化共轭梯度)。trainbr的优势在于自动控制模型复杂度,对防止过拟合很有效,但训练速度会慢不少。trainscg是内存友好的选择,适合大数据集。

学习率这一步,默认的0.01是偏保守但稳妥的取值。学习率设置太大,训练过程会震荡,loss曲线上下跳动,甚至直接发散;学习率太小,收敛极慢,可能训练几千轮都达不到目标误差。我通常会在0.001到0.1之间做几次尝试,先用0.01摸个底,如果loss下降太慢再调大一点,如果震荡明显就调小。如果只是做快速验证,保持0.01也能出可接受的结果。

3. 完整实操流程:从数据到结果

3.1 示例数据准备与说明

为了把整套流程走通,我用了一个带明确规律的数据集做演示。数据由三个输入变量X1、X2、X3构成,目标输出Y由下面这个函数生成:

%% 生成示例数据 rng(42); % 固定随机种子,保证结果可复现 n = 500; % 样本数量 X1 = linspace(0, 5, n)'; X2 = randn(n, 1); X3 = sin(X1) + 0.2 * randn(n, 1); Y = 3 * X1 + 2 * sin(X1 .* X2) + 0.5 * X3.^2 + 0.8 * randn(n, 1); data_demo = [X1, X2, X3, Y]; writematrix(data_demo, 'data_demo.xlsx');

这里Y与X1存在明显的线性趋势,同时叠加了与X2相关的非线性项,以及X3的平方项和随机噪声。这样的数据设计能有效检验神经网络是否真正学习了非线性关系,而不是简单套了一条直线。rng(42)的作用是固定随机数生成器的种子,保证每次运行生成的示例数据完全一致,这样不同人或者不同次运行之间才有可比性。做实验时,固定的数据种子是保证结果可复现的基础,这点对论文和项目复查都特别重要。

生成的data_demo.xlsx有500行4列,前三列是输入特征,最后一列是输出。把这个文件放到程序同目录下,修改readmatrix读取的文件名,程序就可以直接运行。如果你是把自己的数据放进去,注意保持同样的列组织逻辑就行。

3.2 训练过程与中间结果怎么看

运行训练代码后,MATLAB会弹出一个神经网络训练窗口(nntraintool),这是观察训练过程的主战场。窗口里有三块核心信息值得关注:性能曲线、训练状态、回归图。

性能曲线(Performance)展示的是均方误差随迭代次数的变化曲线,通常有三条线,分别对应训练集、验证集和测试集的MSE。理想情况下,训练集和验证集的误差都持续下降并趋于平稳,而且两条线挨得比较近,说明没有严重的过拟合。如果训练集误差一直下降,但验证集误差先降后升,那基本可以确定过拟合出现了,应该在验证集误差最低点对应的迭代次数附近停止训练。MATLAB的train函数自带early stopping机制,当验证集误差连续多次不再下降时,训练会自动终止,这是默认启用的一项非常实用的保护功能。

训练状态(Training State)窗口里可以看到梯度(Gradient)和验证检查(Validation Checks)的变化。梯度随着迭代逐渐变小,说明参数在收敛到某个局部最优解附近。验证检查次数指的是验证集误差连续不改善的次数,默认达到6次后训练就会提前终止。如果你的训练过程在Validation Checks达到6次后停止了,但在性能曲线上看验证误差还在下降趋势中,那可能是验证集选的数据太少或者恰好包含了一些噪声大的样本,可以试试换一个划分随机种子,或者调整divideParam里的比例。

下面这段代码是整个训练仿真过程:

%% 第四步:BP神经网络训练与仿真 % 训练网络 [net, tr] = train(net, X_train', Y_train'); % 测试集仿真预测 Y_pred_train = net(X_train'); Y_pred_test = net(X_test'); % 反归一化 Y_pred_train = mapminmax('reverse', Y_pred_train, ps_Y); Y_pred_test = mapminmax('reverse', Y_pred_test, ps_Y); Y_true_train = mapminmax('reverse', Y_train', ps_Y); Y_true_test = mapminmax('reverse', Y_test', ps_Y);

训练完成后,net就是保存了最终权值和阈值的网络对象。tr是训练记录结构体,里面保存了每次迭代的误差、验证表现等信息。仿真时使用net(X_train')对归一化后的输入做正向传播,得到归一化后的预测输出,然后通过mapminmax('reverse', ...)把预测值恢复到原始量纲。这一步很多人会漏掉,导致最后画的图里预测值和真实值差了好几个数量级。

在调用train之前,需要明确一点:train函数会重新随机初始化权值,然后开始训练。即使数据完全一样,每次训练的初始权值不同,最终结果也可能有细微差异。因此如果在做实验对比或者发表结果,我建议在训练前设置随机种子,命令是rng('default')或者rng(固定数字),这样结果可以复现。

3.3 结果评价指标与可视化输出

预测做完了,光肉眼看预测值和真实值接近不接近不够客观,需要计算量化指标。回归预测问题中,我常用的指标有四个:均方误差MSE、均方根误差RMSE、平均绝对误差MAE、决定系数R²。MSE和RMSE对较大误差比较敏感,能反映预测的“极端偏差”情况;MAE更直观反映平均偏差水平;R²衡量模型解释了多少方差,越接近1说明拟合效果越好。

%% 第五步:结果评价与可视化 % 计算训练集和测试集误差指标 mse_train = mean((Y_true_train - Y_pred_train).^2); rmse_train = sqrt(mse_train); mae_train = mean(abs(Y_true_train - Y_pred_train)); r2_train = 1 - sum((Y_true_train - Y_pred_train).^2) / sum((Y_true_train - mean(Y_true_train)).^2); mse_test = mean((Y_true_test - Y_pred_test).^2); rmse_test = sqrt(mse_test); mae_test = mean(abs(Y_true_test - Y_pred_test)); r2_test = 1 - sum((Y_true_test - Y_pred_test).^2) / sum((Y_true_test - mean(Y_true_test)).^2); % 在命令行打印评价结果 fprintf('训练集: MSE=%.4f, RMSE=%.4f, MAE=%.4f, R2=%.4f\n', mse_train, rmse_train, mae_train, r2_train); fprintf('测试集: MSE=%.4f, RMSE=%.4f, MAE=%.4f, R2=%.4f\n', mse_test, rmse_test, mae_test, r2_test); % 绘制测试集预测值与真实值对比曲线 figure; plot(Y_true_test, 'b-o', 'LineWidth', 1.5, 'DisplayName', '真实值'); hold on; plot(Y_pred_test, 'r--s', 'LineWidth', 1.5, 'DisplayName', '预测值'); legend('Location', 'best'); xlabel('测试样本序号'); ylabel('输出值'); title('BP神经网络测试集预测效果对比'); grid on; % 绘制误差分布直方图 figure; errors = Y_true_test - Y_pred_test; histogram(errors, 20); xlabel('预测误差'); ylabel('频数'); title('预测误差分布直方图');

测试集预测对比曲线是我每次做项目必看的图。如果蓝色真实值曲线和红色预测值曲线走势高度重合,说明模型捕捉到了主要变化规律。误差直方图则能直观展示误差是否存在明显的偏置,比如误差大多集中在正值区间,说明预测存在系统性偏低的情况,可以考虑在输出层增加一个偏置修正项,或者检查数据是否存在未处理干净的趋势成分。

除了这两张图,MATLAB还提供了几个内置可视化函数:plotperform(tr)可以绘制训练过程的性能曲线,plottrainstate(tr)展示训练状态,plotregression(net, X_test', Y_test')输出回归图,展示预测值与目标值的线性拟合程度。在整理项目报告时,这些图都是很好的材料。

4. 换数据使用时的避坑指南

4.1 换数据前必须做的检查

拿到新数据集后,最忌直接把Excel丢进程序里就跑。我在实际项目中吃过不少亏,总结了一套换数据前必须检查的事项,按重要性排序如下:

第一,确认数据没有缺失值。Excel或者原始采集数据里常有空单元格,readmatrix读取时空值会变成NaN,NaN进入训练过程会让梯度计算直接出错。排查方法很简单:用any(isnan(data(:)))检查,有的话需要先插值或者剔除对应样本。我程序里没默认加这个检查,因为插值策略因数据而异,但至少要在命令行自查一遍。

第二,检查特征量纲和分布。如果某些特征明显存在尖峰或者重尾分布,比如个别样本的值比其他样本大几个数量级,这种离群点对方差影响极大,拖累归一化和训练效果。处理方式要么剔除,要么做对数变换压缩量纲。注意:对数变换的底数和偏移量也要在训练集上确定,再应用到测试集,和归一化的逻辑一致。

第三,样本量是否足够。多输入单输出问题,隐含层神经元数量一多,网络的可训练参数数量会快速膨胀。比如输入特征10个,隐含层神经元15个,那么输入层到隐含层的权值就有10×15=150个,再加上隐含层到输出层的15个权值和各层阈值,总参数接近180个。如果训练样本只有几十条,很容易过拟合。一般我要求训练样本数至少是网络参数数量的5到10倍,如果样本不够,优先减少隐含层神经元的数量,而不是盲目增加网络容量。

第四,检查输出是否有明显的趋势或者周期性。BP神经网络本质上是在做静态映射,它对时间序列中的先后依赖关系不敏感。如果预测对象是带强自相关的时序数据,比如逐小时的电力负荷,直接用当前时刻的特征去预测下一时刻的值,而不把历史值也纳入输入特征,效果往往会差强人意。这种情况的解决方案是把滞后几期的历史输出值也作为输入特征加入数据表,让网络有更多上下文信息可用。

4.2 常见问题排查实录

我把实际使用中出现频率最高的问题整理成了速查表,方便大家定位自己的情况:

现象可能原因排查思路
训练不收敛,误差曲线平直学习率太小或网络结构不合理尝试增大学习率到0.05~0.1,增加隐含层神经元数量
训练发散,误差爆炸学习率太大,或数据包含极端离群点调小学习率到0.001,检查特征中是否存在异常大值
训练集效果很好,测试集一塌糊涂过拟合增加样本量,减少隐含层神经元,或改用trainbr
预测值全部接近某个常数数据未归一化或输出激活函数饱和检查是否执行了mapminmax,输出层改用purelin
换了数据后维度不匹配报错输入特征数和原程序不一致检查隐含层公式,确认inputDim正确更新
两次运行结果波动很大未设置随机种子固定rng,或者多次训练取最优结果
训练极慢样本量大且用trainlm改用trainscg,或减少训练轮数

这里我特别说一个容易迷惑的“预测值接近常数”问题。有的读者反馈:明明训练时误差很低,但预测出来的Y值基本都在一个固定值附近抖动。这种情况多半是因为输出层激活函数用了sigmoid或者tansig,而真实输出范围超出了激活函数的饱和区间。BP网络的输出层应该设置为purelin(线性激活函数),这样输出范围不受限制。feedforwardnet默认输出层就是purelin,所以如果不动默认设置不会出这个问题,但如果有人参考了某些旧代码用newff手动定义结构,就容易踩中这个坑。

另一个值得注意的是数据划分方式。我程序里用的是divideblock,也就是按顺序取前85%作为训练集、后15%作为验证集。这种划分适合数据本身已经随机分布的情况。但如果数据按时间排列且存在明显的时间趋势,顺序划分会让训练集和测试集的分布差异过大,这时建议改用randperm随机打乱后划分,让训练集和测试集在数值分布上更接近。随机划分的代码很简单:

% 随机打乱样本顺序 idx = randperm(size(X_raw, 1)); X_raw = X_raw(idx, :); Y_raw = Y_raw(idx, :);

但要注意:如果做的是时间序列预测,随机打乱会破坏时序依赖,必须用顺序划分或者按时间窗口划分,不能无脑随机。

4.3 一个快速有效的调优路线

换数据后,如果预测效果不理想,我一般按下面的顺序调优,而不是一上来就动网络结构:

第一步,看数据的分布问题。先画各特征的直方图,看有没有明显的偏态分布或者离群点。大多数实际数据都需要一定的清洗才能进入模型,这步花的时间往往比调参还多,但收益显著。

第二步,调整隐含层神经元数量。在经验公式的基准值附近,依次尝试基准值减3、减1、加1、加3,找到测试集误差最低的那个值。这个过程可以用一个for循环自动跑,记录每次的R²,选最大的。

第三步,调整训练函数。如果数据量在几千条以下,trainlm是首选;如果过拟合迹象明显,换成trainbr;如果样本量上万或者训练内存吃紧,用trainscg。同一个数据在不同训练函数下的表现差异可能很明显,值得都试一遍。

第四步,适当调整学习率和目标误差。学习率按0.1、0.05、0.01、0.005的顺序尝试,目标误差goal一般设为1e-5就够用,设太大约束太松,设太小容易过拟合。

最后提醒一句:神经网络预测的精度上限,很大程度上由数据的信息量决定。如果特征本身与目标输出之间相关性很弱,无论怎么调参,模型也不可能凭空变出好效果。这时候应该回过头去审视特征工程,考虑是否遗漏了重要的输入变量,而不是继续在训练参数上死磕。我见到太多人把时间浪费在无意义的参数网格搜索上,而从不质疑输入特征本身是否合理,这是最可惜的。

从我个人的实操体会来说,这套BP神经网络预测程序最大的价值在于“稳定”:该归一化的提前归一化,该固定的种子固定住,该拆除的信息泄露路径拆除掉。只要你按数据格式要求把Excel表填好,跑出来的结果至少是可信的、可复现的。至于精度能不能再上一层楼,那就回到特征和数据质量上做文章了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询