拓冰建站拓冰建站
首页 / 资讯中心 / 正文

用ELMAN神经网络预测天然气用气量:MATLAB完整实现

简介ELMAN神经网络属于递归神经网络的一类通过上下文层保留历史信息对天然气消费量这类时序数据有较好的拟合能力。本资源以用气量预测为主题提供完整可运行的MATLAB工程面向时间序列建模初学者以及需要快速搭建预测模型的开发者。压缩包内共有六个文件核心包括两个Excel表格存放历史日用气量与可能影响用气的因素若干MATLAB源码负责数据读取、归一化、网络构建与训练另有自动生成的备份文件辅助比对修改。整个资源包仅约290KB体量轻便下载后可离线运行与反复调试。目前已有185人浏览学习。通过该案例读者能够理解从原始数据到最终预测输出的完整链条学会设置网络层数、神经元数量与训练参数并借助误差曲线评估模型效果。该工程还可延伸至电力负荷、交通流量等其它时间序列预测场景适合作为课程设计或入门科研的起点。代码注释清晰结构分块明确便于读者逐步调试并替换为自己的数据。1. 为什么天然气用气量预测偏偏选了ELMAN做能源调度的人都有一个共识用气量预测比电价预测更难受。电价有明确的峰谷时段天然气的消费曲线却同时叠加了日周期、周周期、季节性波动和突发降温带来的脉冲式跳变。传统BP神经网络在这种序列上经常出现同一个毛病——预测值总比真实值慢半拍因为前馈网络把每个时间点当独立样本处理完全没有“记住上一刻发生了什么”的能力。这个项目采用ELMAN神经网络来啃这块骨头本质上是把时间维度显式地建进网络结构里让隐藏层多出一组承接层节点专门负责保存上一时刻的隐层状态再反馈回当前输入。对天然气的日消费序列来说这种结构就像给普通前馈网络加了一截“短期记忆”在处理连续几天的用气惯性、寒潮累积效应这类问题上比BP和普通前馈网络有明显的结构优势。适合谁来用做城市燃气调度、能源规划、工业园区用气量预估的工程师以及需要把时间序列预测从理论推到MATLAB可运行结果的在校研究者。整个项目代码完整数据齐全从数据读取到训练、预测、画图一条链都是通的拿来做基线模型再合适不过。2. 把时间序列变成ELMAN能吃的样本数据处理与网络结构设计2.1 原始数据文件结构怎么理解压缩包里有两个Excel数据文件附件1.xlsx和附件2.xlsx从常见的数据组织方式来看这两个文件对应的分别是训练集和测试集或者一个是原始序列、另一个是带外部变量的扩展表。最稳妥的做法是先读进来看一眼列名。% 读取训练数据 train_data readtable(附件1.xlsx); % 显示前几行确认列名和数据格式 disp(train_data(1:5, :));readtable会自动识别表头输出的是table类型方便按列名索引。如果数据里第一列是时间戳第二列是用气量那后续处理就比较直接如果还有温度、节假日等额外特征列则需要单独做特征筛选。多数情况下燃气公司提供的历史数据是类似两列的格式——日期和日用量少数会附带平均温度、是否为工作日等标记这些在ELMAN里可以作为额外输入节点。2.2 数据归一的必要性ELMAN对输入尺度比BP更敏感ELMAN的隐藏层激活函数通常是tansig输出层是purelin。tansig的输出范围是[-1,1]如果输入数据是日均几十万立方米的用气量不经过归一化直接丢进去激活函数会进入饱和区梯度接近零训练基本推不动。归一化的标准做法是mapminmax把数据映射到[-1,1]区间。% 提取用气量序列假设第二列是用气量 load_data train_data{:, 2}; % 归一化到[-1,1] [data_norm, ps] mapminmax(load_data, -1, 1);mapminmax返回两个值第一个是归一化后的数据第二个是结构体ps里面保存了原始数据的最小值、最大值和映射公式。后面预测完必须用mapminmax(reverse, output, ps)还原成真实量纲否则预测结果会停留在[-1,1]区间没法直接用。整个归一化过程必须在训练集上计算ps测试集用同一个ps变换不能各自独立做否则两个数据集不在同一个尺度空间里模型等于白训。2.3 滞后步长选择构造ELMAN的输入输出样本对ELMAN虽然内部有反馈但外部输入仍然需要构造“用前几步预测下一步”的监督样本。滞后步长的选择直接影响预测精度步长太小网络看不到足够的序列上下文步长太大输入维度膨胀训练时间变长且容易过拟合。日度天然气数据一般取7到14天覆盖一周的周期性。% 构造输入输出样本对 lag 7; % 用前7天预测第8天 n length(data_norm); inputs zeros(lag, n-lag); targets zeros(1, n-lag); for i lag1:n inputs(:, i-lag) data_norm(i-lag:i-1); % 前7天 targets(i-lag) data_norm(i); % 第8天 end这里的inputs是7行乘N列的矩阵每一列是一个样本包含连续7天的归一化用气量targets是对应的第8天真实值。MATLAB的神经网络工具箱默认接受“样本按列排列”的格式行数是输入维度列数是样本数量排列顺序反了会直接报维度错误。滞后步长选7的好处是一周的周期性被完整包含进去周一和周一之间、周末和周末之间的模式可以对齐。2.4 承接层尺寸对网络记忆容量的影响ELMAN的承接层context layer节点数量和隐藏层节点数量相等它保存的是隐藏层上一时刻的输出并在当前时刻作为额外的输入重新进入隐藏层。承接层的存在让网络在t时刻的输出不仅依赖当前输入还间接依赖t-1、t-2时刻的信息。隐藏层节点数决定了这个记忆通道的宽度——节点太少记忆容量不够复杂序列模式学不进去节点太多参数量骤增训练数据量不够就容易记住噪声。MATLAB中创建ELMAN网络的标准代码是newelm但注意R2010a之后的版本已经用feedforwardnet和自定义网络代替。当前主流的写法是% 创建ELMAN网络 net newelm(minmax(inputs), [10, 1], {tansig, purelin}, traingdx);第一个参数是输入数据的范围矩阵用于初始化权值第二个参数[10, 1]表示隐藏层10个节点、输出层1个节点第三个参数是两个层的激活函数traingdx是带动量项和自适应学习率的梯度下降法是ELMAN训练的常用选择。隐藏层10个节点是一个合理的起点对于日度天然气数据来说序列复杂度不高10个节点已经能捕获基本的用气模式如果换成小时级数据或者包含多个外部变量的场景则需要适当上调到15到20个。3. MATLAB代码完整跑通从main.m到main1.m的工程实现3.1 主脚本main.m内部的典型流程拆解main.m是这个项目的核心脚本打开后能看到一个完整的ELMAN预测流程。整个流程分成五个步骤读数据、构造样本、建网络、训练、预测画图。实际工程中我习惯在这五个步骤之间加disp输出方便追踪每一步的执行状态避免脚本跑了几分钟还不知道卡在哪一步。%% 加载数据 raw_data readmatrix(附件1.xlsx); % 读取数值矩阵 flow raw_data(:, 2); % 取用气量列 %% 归一化 [flow_norm, ps] mapminmax(flow, -1, 1); %% 构造训练样本 lag 7; [train_x, train_y] create_sequence(flow_norm, lag); % 自定义函数构造输入输出对 %% 创建ELMAN网络 net newelm(minmax(train_x), [12, 1], {tansig, purelin}, traingdx); net.trainParam.epochs 1000; % 最大迭代次数 net.trainParam.goal 1e-5; % 目标误差 net.trainParam.lr 0.01; % 学习率 %% 训练 [net, tr] train(net, train_x, train_y); %% 测试与反归一化 test_raw readmatrix(附件2.xlsx); test_flow test_raw(:, 2); [test_norm] mapminmax(apply, test_flow, ps); % 用训练集的ps做归一化 test_x create_sequence(test_norm, lag); pred_norm sim(net, test_x); % 预测 pred mapminmax(reverse, pred_norm, ps); % 反归一化readmatrix是R2019a之后引入的函数比xlsread更快处理纯数值表格时优先使用。mapminmax(apply, data, ps)的作用是使用训练阶段保存的归一化参数ps来转换新数据这样测试集和训练集就在同一个映射空间内。最关键的一句是sim(net, test_x)——在MATLAB神经网络工具箱里训练和预测是分开的train函数负责更新权重sim函数负责前向计算输出这两个不能混用。如果预测时序列构造成本比训练集少了一步输出长度会比真实值多或少画图时会出现数组维度不匹配的报错。3.2main1.m和.asv文件是干什么用的.asv是MATLAB的自动保存文件。当你编辑脚本但没有手动保存时MATLAB会每隔几分钟自动生成一个.asv备份。main.asv和main1.asv分别对应main.m和main1.m的上一个版本。如果主脚本改坏了直接重命名.asv为.m就能找回改之前的版本。main1.m和main.m的差异常见做法有两种可能一种是main.m做的是单步预测用前7天预测下一天main1.m做的是多步滚动预测把预测值当输入继续往后推另一种是main.m用的是历史数据对比main1.m用的是未来预测。至于具体是哪一种跑一下看预测步长就能判断。一般来说main.m对应的是论文里的主实验结果main1.m是附加实验或者对比实验。3.3create_sequence函数的实现细节上面代码里用到了自定义函数create_sequence这个函数在MATLAB里需要单独存成一个.m文件或者直接内嵌在主脚本末尾。写成单独文件的好处是可以同时被main.m和main1.m调用避免两段重复代码不一致。function [inputs, targets] create_sequence(data, lag) % CREATE_SEQUENCE 构造ELMAN训练样本 % 输入: data - 归一化后的序列(1xN) % lag - 滞后步长 % 输出: inputs - lag x (N-lag) 矩阵 % targets - 1 x (N-lag) 向量 n length(data); inputs zeros(lag, n-lag); targets zeros(1, n-lag); for i lag1:n inputs(:, i-lag) data(i-lag:i-1); targets(i-lag) data(i); end end这段代码的核心逻辑在循环体里data(i-lag:i-1)取的是当前时刻之前的lag个值构成输入向量data(i)是当前时刻的真实值构成目标值。每次循环窗口向后滑动一个时间点这样构造出的相邻样本之间有大量重叠信息这是时间序列预测的标准做法。需要注意这种构造方式下各样本不是独立同分布的前向网络会因此存在一定的信息冗余但对ELMAN这类循环网络来说这恰好模拟了时间轴上的连续观测场景。3.4 训练参数设置的建议值和调整方向newelm创建网络后训练参数一般需要手调。epochs设1000次只是上限实际训练里如果误差在几百次后就稳定了再多的迭代只会浪费算力。判断收敛与否的关键指标有两个tr.best_epoch表示验证误差最小的迭代轮次tr.best_perf是对应的最小误差值。如果best_epoch远小于epochs说明收敛得很快可以适当减少迭代次数如果best_epoch接近1000说明还没收敛完需要增大迭代次数或者调整学习率。lr学习率的关键性最大。traingdx自带自适应学习率调整机制初始学习率设在0.01到0.05之间通常问题不大。但如果训练过程中误差出现了剧烈震荡那就是学习率偏大权重在最优解附近来回摆动如果误差下降像蜗牛爬500个epoch才降了一点点说明学习率太小或者隐藏层节点数不够。还有一种常见情况是误差函数前期下降很快、后期完全不动这通常是因为网络陷入了局部极小值改一下随机种子或换traingdm带动量的梯度下降可以缓解。4. 模型评估与预测效果核验误差指标和可视化对比4.1 评估指标计算MSE、RMSE、MAE、R² 一个都不能少模型训练完评估是躲不掉的步骤。写论文、做汇报光贴一张预测对比图远远不够。均方误差MSE对大误差值更敏感平均绝对误差MAE更贴近真实感知误差决定系数R²反映模型对序列方差的解释能力。这组指标在MATLAB里几行代码就能算完。% 计算误差指标 err pred - real; % pred是预测值real是真实值注意先反归一化再计算 mse_val mean(err.^2); rmse_val sqrt(mse_val); mae_val mean(abs(err)); ss_res sum(err.^2); ss_tot sum((real - mean(real)).^2); r2 1 - ss_res / ss_tot; fprintf(MSE: %.4f\nRMSE: %.4f\nMAE: %.4f\nR²: %.4f\n, mse_val, rmse_val, mae_val, r2);指标的计算顺序很关键——必须在反归一化之后算因为在[-1,1]空间里算误差和在实际量纲下算误差是完全不同的尺度。fprintf在MATLAB里的%.4f是格式化输出\n是换行。R²越接近1说明模型解释能力越强但一个隐藏层10个节点的ELMAN如果R²能在0.9以上就已经是不错的水平如果R²低于0.7优先检查数据预处理和数据切分是不是出了问题而不是急着堆网络深度。这一点对天然气这类有强周期性的序列来说特别重要。4.2 预测曲线和误差曲线的MATLAB绘图实现可视化对比是判断模型质量最直观的方式。绘图代码需要输出两张图一张是预测值与真实值随时间变化的对比曲线另一张是残差分布图。残差图中如果出现明显的时间段性波动说明模型没有捕获到某些周期性特征。%% 预测值与真实值对比 t 1:length(real); figure; plot(t, real, b-, LineWidth, 1.5); hold on; plot(t, pred, r--, LineWidth, 1.5); legend(真实值, 预测值); xlabel(时间天); ylabel(用气量); title(ELMAN神经网络用气量预测结果); grid on; %% 残差图 figure; plot(t, err, k-, LineWidth, 1); xlabel(时间天); ylabel(预测误差); title(预测残差分布); grid on;hold on的作用是让两条曲线画在同一个坐标系里不加这个命令第二个plot会覆盖第一个图。残差图的观察重点是残差是否围绕零轴均匀分布如果某一段连续时间的残差全是正值或全是负值说明模型在该时间段存在系统性偏差。比如冬天用气高峰期的预测残差如果是系统性负值说明模型对寒潮期间的用气量变化反应不够快这时需要把滞后步长调大或者引入温度特征作为额外输入。4.3 模型过拟合的快速判断方法ELMAN是递归网络参数量相对不大但数据量太小时照样过拟合。快速判断方法是比较训练集误差和测试集误差的差距训练集MSE远小于测试集MSE基本可以断定过拟合。另一个更直观的判断看训练过程的收敛曲线——train函数会自动弹出一个训练窗口里面有三条曲线训练误差、验证误差、测试误差如果验证误差在某个迭代点之后开始上升而训练误差继续下降这就是典型的过拟合信号此时应该停止训练将epochs截断在tr.best_epoch的位置。天然气日度数据的样本量通常在几百到几千条之间对于10个隐藏层节点的ELMAN来说属于中等规模数据。如果过拟合现象严重第一选择不是加正则化而是减少隐藏层节点数或者增大滞后步长——这两个操作分别降低了模型的记忆容量和输入复杂度比L2正则化更直接有效。4.4 两个预测文件的对比场景单步预测与滚动预测main.m和main1.m如果分别对应单步预测和滚动预测那么在评估方式上要注意一个关键区别单步预测的每个输入都是真实的历史值误差不会向后传播滚动预测是把前一步的预测值当作输入继续预测下一步误差会随着步数增加而累积。因此滚动预测的误差曲线通常呈现喇叭口形状——时间越往后误差越大。%% 滚动预测示例main1.m的核心逻辑 steps length(test_flow) - lag; rolling_input test_norm(1:lag); % 初始输入为前lag个真实值 rolling_pred zeros(1, steps); for i 1:steps p sim(net, rolling_input); % 单步预测 rolling_pred(i) p; % 保存预测结果 rolling_input [rolling_input(2:end), p]; % 滚动窗口丢掉最老的值加入预测值 end滚动预测的核心在窗口更新那一行rolling_input(2:end)去掉了窗口里最早的一个值然后拼接上最新的预测值p。这模拟的是真实业务中“没有未来数据可用”的场景。如果滚动预测的衰减速度很快预测5步之后误差就已经不可接受说明ELMAN的递归深度不足以捕获该序列的长程依赖此时可以考虑换成NARX网络或者LSTM。5. 把预测结果接进业务可视化存档与模型更新的工程化技巧模型能在MATLAB里跑通只是第一步真正要把它用起来得让预测结果能以标准格式输出、存档、并在新数据到达时自动更新模型。这一章把预测从“实验室脚本”推到“可持续运行的预测模块”层面。预测结果最标准的输出格式是Excel表格保留两个关键列预测日期和预测用气量。MATLAB的writetable函数一行代码就能完成数据落盘。% 生成预测结果表 pred_table table(dates, pred, VariableNames, {Date, PredictedFlow}); writetable(pred_table, prediction_output.xlsx);VariableNames指定的列名会直接作为Excel的表头后续做报表或者对接其他系统时可以直接读取。注意pred在MATLAB里可能是行向量写表前要转置成列向量否则输出维度对不上。日期列建议用datetime类型存储后面接任何分析工具都不会出时区或格式问题。关于模型更新的策略工程上常见的做法是每周用最新数据重新训练一次而不是每次预测前都从头训练。重训的触发条件可以是一个固定时间表比如每周日凌晨跑一次也可以设一个误差阈值——当本周预测的平均绝对误差超过某个警戒值时自动触发重训。重训时保留原有训练参数作为初始值用新数据继续迭代50-100轮这样模型能平滑过渡到新的数据分布避免从一个随机初始状态重新开始而导致的预测波动。还有一个容易踩的坑是文件路径的管理。代码里附件1.xlsx是中文文件名MATLAB在Windows下读取没问题但在Linux服务器上跑就很容易因为编码问题找不到文件。建议代码里统一用英文文件名或者加一段路径修正逻辑来判断当前操作系统。这部分虽然不起眼但往往决定了预测脚本能不能稳定地在生产环境里每周定时执行而不是每次都要人盯着手动改路径。最后提一下ELMAN的自动调参方向这段思路在跑不同数据集时很实用。隐藏层节点数先取输入维度的1.5到2倍试一轮记录训练集和测试集的RMSE然后往上和往下各调一档对比三次结果取最优。学习率在0.005到0.05之间按对数间隔扫三四个点。一次完整实验的总耗时控制在几分钟量级批量跑也不心疼。这些参数在天然气日预测场景下就是足够好的基线后续如果换了数据集这套流程也能快速迁移。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门