ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MATLAB LSTM风速预测实战:从数据管线到滚动预测的完整拆解

MATLAB LSTM风速预测实战:从数据管线到滚动预测的完整拆解

简介:这份资源面向具备一定MATLAB基础、希望入门深度学习时间序列预测的学生与研究人员,聚焦短期风速预测这一典型回归任务。压缩包共13个文件,以7个m脚本、3个mat数据文件、2个xlsx数据表和1个md说明为主,整体约149KB,涵盖数据加载、LSTM建模、训练与预测的完整流程。内容围绕LSTM的记忆单元与门控机制展开,涉及数据预处理、网络结构定义、MSE损失与优化器选择、早停防过拟合,以及RMSE、MAE、R²等评估指标,并包含实际值与预测值的对比可视化。目前已有1858人学习下载,适合作为风电场运营决策支持或相关课题的实践参考,帮助读者快速理解LSTM在风速序列建模中的落地方式,并在此基础上调整网络结构、扩充数据以进一步优化预测精度。

1. 风速预测翻车三次后,我把这份 MATLAB LSTM 回归包拆透了

做风电功率预测的同行多半有过这种经历:用 persistance 模型(拿上一时刻风速当下一时刻预测)在 1 小时尺度上能混个不错的 RMSE,一旦把预测步长拉到 4 小时以上,误差直接起飞。风速序列的自相关性衰减得比想象中快,线性模型根本抓不住那种「上午还平稳、午后突然起风」的突变模式。这份LSTM-regression-master.rar就是冲着这个问题来的——它把 LSTM 回归网络在 MATLAB 里跑短期风速预测的完整链路打包好了,从数据加载、网络搭建、训练到预测评估,代码文件齐全,还附带了两份日度气象与污染数据。适合谁?手头有 MATLAB 深度学习工具箱、想快速验证 LSTM 在自己风速数据上效果的工程师,以及需要一份可复现基线来对比 ELM、BP 等浅层模型的研究者。下面按「资源拆解 → 数据管线 → 网络搭建 → 训练调参 → 避坑 → 进阶验证」的顺序,把这份包里的东西一件件说清楚。

2. 拆开压缩包:文件清单与两条技术路线

拿到一个源码包,我习惯先看目录结构再动手跑。LSTM-regression-master里的文件不是随便堆的,它其实藏了两条技术路线:一条是 LSTM 回归主线,另一条是 ELM(极限学习机)对照线。搞清楚谁调用谁,后面改代码才不会改错地方。

2.1 核心文件逐个点名

先把文件按功能分个类,这样你打开文件夹不会懵:

文件类型作用
LSTM.m主脚本LSTM 网络定义、训练、预测全流程入口
LSTM2.m脚本LSTM 的另一个版本,可能是不同层数或不同数据划分
LSTM_updata_weight.m函数手动更新网络权重的逻辑,用于自定义训练循环
ELM.m脚本极限学习机主脚本,作为对照模型
elmtrain.m/elmpredict.m函数ELM 的训练与预测子函数
data_lstm.mat/data.mat数据预处理后的训练数据,直接 load 就能用
outputps.mat数据输出归一化参数,反归一化时必用
沧州气象日度数据.xlsx原始数据风速等气象要素的日度记录
沧州污染日度数据.xlsx原始数据污染物浓度日度记录,可做多变量输入
Untitled2.m脚本临时脚本,通常是数据探索或画图用的
README.md文档项目说明,先读它

提示:data_lstm.mat和data.mat大概率是已经归一化、划分好训练/测试集的数据。如果你要用自己的数据,别急着改LSTM.m,先搞清楚这两个 mat 文件里的变量名和维度,照着格式准备自己的数据最省事。

2.2 LSTM 主线与 ELM 对照线的关系

为什么一个风速预测包里会塞 ELM?这不是凑数。LSTM 训练慢、调参玄学,而 ELM 训练极快、结构简单,拿它当 baseline 是合理做法。常见做法是:先用 ELM 跑一个结果,心里有个底,再看 LSTM 能提升多少。如果 LSTM 费了半天劲还不如 ELM,那要么是数据量不够,要么是网络结构或归一化出了问题。

两条线的数据入口是共用的——都从data.mat或data_lstm.mat读。区别在于:

  • ELM 路线:ELM.m调elmtrain.m训练,调elmpredict.m预测,输入是二维矩阵,不需要序列维度。
  • LSTM 路线:LSTM.m里要把数据整理成cell数组或N×1的序列格式,因为trainNetwork对序列输入有特定要求。

我一般会先跑ELM.m,确认数据加载和评估指标计算没问题,再切到LSTM.m。这样如果 LSTM 报错,能快速判断是数据格式问题还是网络配置问题。

2.3 环境依赖与版本注意

这份包依赖 MATLAB 的 Deep Learning Toolbox。trainNetwork、sequenceInputLayer、lstmLayer、regressionLayer这些函数在 R2018a 之后才逐步完善,R2020a 以后比较稳。如果你用的是更早版本,trainNetwork对回归任务的支持可能不完整,会报「无法识别 regressionLayer」之类的错。

检查环境最快的方式是在命令行敲:

% 检查深度学习工具箱是否安装 ver('nnet') % 检查 trainNetwork 是否可用 which trainNetwork

如果ver('nnet')返回空或者which trainNetwork找不到,说明工具箱没装或版本不对。另一个容易忽略的点是 GPU 支持——LSTM 训练在 CPU 上跑小数据量还行,数据一多就慢得让人想砸键盘。有 NVIDIA 显卡的话,确认 MATLAB 能识别:

% 检查 GPU 是否可用 gpuDeviceCount

返回大于 0 说明能用 GPU 加速。没有也不强求,把trainingOptions里的ExecutionEnvironment设成'cpu'就行,只是训练时间会长一些。

3. 数据管线:从 Excel 到 LSTM 能吃的序列格式

LSTM 对输入格式比传统回归模型挑剔得多。BP 神经网络你给它一个N×M矩阵就能跑,LSTM 要求输入是序列——要么是cell数组,每个 cell 是一个时间步的特征向量;要么是N×T矩阵,N 是特征数,T 是时间步数。这一步搞错,后面全是白搭。

3.1 读取与检查原始数据

包里给了两份 Excel,先别急着往网络里灌。用readtable读进来,看看列名、行数、有没有缺失值:

% 读取气象日度数据 raw = readtable('沧州气象日度数据.xlsx'); % 查看前几行和列名 head(raw) % 检查缺失值 missing_count = sum(ismissing(raw)); disp(missing_count)

逻辑说明:readtable自动识别表头,返回 table 类型。head看数据结构,ismissing统计每列缺失值数量。风速预测里,缺失值处理方式直接影响结果——日度数据如果缺一两天,线性插值通常够用;如果连续缺一周以上,插值就是自欺欺人,不如把那段时间整段剔除。

参数说明:readtable的'VariableNamingRule'参数在 R2019b 之后默认是'preserve',列名里有中文或特殊字符时不会被改掉。如果你的 MATLAB 版本较老,可能需要手动指定。

3.2 归一化与反归一化:outputps.mat 的用法

LSTM 对输入尺度敏感,风速值一般在 0~20 m/s 之间,但如果你把污染数据也加进去,PM2.5 浓度可能是几百,量纲差了两个数量级,不归一化网络根本学不动。包里outputps.mat存的就是归一化参数,典型做法是用mapminmax:

% 假设风速序列存在变量 wind_speed 中 [wind_norm, ps] = mapminmax(wind_speed', 0, 1); % ps 是归一化参数,保存下来用于反归一化 save('outputps.mat', 'ps');

逻辑说明:mapminmax默认把数据映射到[-1, 1],这里指定0, 1也行,看网络输出层激活函数。如果输出层用regressionLayer(线性输出),归一化到[0, 1]或[-1, 1]都可以;如果用sigmoid输出,必须归一化到[0, 1]。

反归一化的时候:

% 预测结果反归一化 pred_original = mapminmax('reverse', pred_norm', ps);

这里有个血泪经验:mapminmax对行向量和列向量的处理方向不一样。它默认按行归一化,如果你的数据是列向量(时间步在行方向),必须先转置。我见过不止一个人在这里翻车,预测结果出来是一条直线,查了半天才发现是归一化方向搞反了。

3.3 构造监督学习样本:滑动窗口的两种写法

时间序列预测要把连续序列切成「输入窗口 → 输出值」的样本对。常见做法是滑动窗口,比如用过去 7 天预测第 8 天:

% 滑动窗口构造样本 lookback = 7; % 回看天数 data = wind_norm; % 归一化后的风速序列 n = length(data); X = cell(n - lookback, 1); Y = zeros(n - lookback, 1); for i = 1:(n - lookback) X{i} = data(i:i+lookback-1)'; % 每个 cell 是一个 1×lookback 的序列 Y(i) = data(i + lookback); end

逻辑说明:X是 cell 数组,每个元素是一个时间步序列;Y是对应的预测目标。LSTM 的sequenceInputLayer要求输入维度是特征数,这里单变量风速就是 1。如果你要用多变量(风速+温度+气压),X{i}的维度要改成lookback×feature_dim,并且sequenceInputLayer(feature_dim)。

参数说明:lookback是关键超参数。太小,网络看不到足够的历史信息;太大,训练样本数减少,且 LSTM 对过长序列的记忆能力也有限。日度数据一般取 5~14,小时数据取 24~72。我一般会试 7、14、30 三档,看验证集 RMSE 再定。

注意:data_lstm.mat里可能已经做好了滑动窗口,直接 load 看变量名。如果变量是XTrain、YTrain这种,说明作者已经切好了,你改lookback就得重新生成。

3.4 划分训练集、验证集与测试集

时间序列不能随机打乱划分,否则未来信息泄露到训练集,测试结果好看但实际用不了。正确做法是按时间顺序切:

% 按时间顺序划分 train_ratio = 0.7; val_ratio = 0.15; n_samples = length(Y); n_train = floor(n_samples * train_ratio); n_val = floor(n_samples * val_ratio); XTrain = X(1:n_train); YTrain = Y(1:n_train); XVal = X(n_train+1:n_train+n_val); YVal = Y(n_train+1:n_train+n_val); XTest = X(n_train+n_val+1:end); YTest = Y(n_train+n_val+1:end);

逻辑说明:训练集在前,验证集居中,测试集在最后。验证集用于早停和调参,测试集只在最后评估一次。常见错误是把验证集和测试集混用——调参时看了测试集结果,测试集就不再「干净」了。

参数说明:train_ratio和val_ratio根据数据量定。数据量少于 500 条时,验证集比例可以降到 0.1,把更多数据留给训练。数据量充足时,7:1.5:1.5 是比较稳的划分。

4. 网络搭建与训练:层数、门控与优化器怎么定

数据管线通了,接下来是 LSTM 网络本身。MATLAB 的trainNetwork把训练循环封装好了,你只需要定义层结构和训练选项。但封装不等于简单——层数、隐藏单元数、优化器、学习率,每个参数都影响最终 RMSE。

4.1 定义 LSTM 回归网络结构

一个典型的单变量风速预测 LSTM 网络长这样:

% 定义 LSTM 网络 numFeatures = 1; % 输入特征数 numHiddenUnits = 64; % LSTM 隐藏单元数 numResponses = 1; % 输出维度 layers = [ sequenceInputLayer(numFeatures) lstmLayer(numHiddenUnits, 'OutputMode', 'last') fullyConnectedLayer(numResponses) regressionLayer ];

逻辑说明:sequenceInputLayer接收序列输入;lstmLayer的'OutputMode'设为'last'表示只取最后一个时间步的输出,用于回归预测;fullyConnectedLayer把隐藏状态映射到输出维度;regressionLayer计算 MSE 损失。

参数说明:numHiddenUnits是最关键的超参数。太小(如 16),网络欠拟合,训练集 RMSE 都降不下去;太大(如 256),训练慢且容易过拟合。日度数据、样本量几百条时,32~128 是比较合理的范围。我一般从 64 开始试,看训练曲线再调。

如果要堆多层 LSTM:

layers = [ sequenceInputLayer(numFeatures) lstmLayer(64, 'OutputMode', 'sequence') lstmLayer(32, 'OutputMode', 'last') fullyConnectedLayer(numResponses) regressionLayer ];

注意第一层 LSTM 的'OutputMode'要设成'sequence',把完整序列传给下一层;最后一层设成'last',只输出最后时间步。多层 LSTM 对复杂模式拟合能力更强,但训练时间翻倍,数据量不够时反而更容易过拟合。

4.2 trainingOptions 参数逐项拆解

训练选项决定了优化器怎么走、走多快、什么时候停:

% 训练选项 options = trainingOptions('adam', ... 'MaxEpochs', 200, ... 'MiniBatchSize', 32, ... 'InitialLearnRate', 0.005, ... 'LearnRateSchedule', 'piecewise', ... 'LearnRateDropPeriod', 50, ... 'LearnRateDropFactor', 0.5, ... 'ValidationData', {XVal, YVal}, ... 'ValidationFrequency', 10, ... 'ValidationPatience', 20, ... 'Shuffle', 'never', ... 'Verbose', 1, ... 'Plots', 'training-progress', ... 'ExecutionEnvironment', 'auto');

逐项说明:

  • 'adam':自适应学习率优化器,对 LSTM 这种参数多的网络比 SGD 稳。如果数据量很小,也可以试'sgdm',但学习率要调小。
  • 'MaxEpochs':最大训练轮数。200 是保守值,配合早停用。如果验证损失还在降,可以加到 500。
  • 'MiniBatchSize':批大小。32 是常见起点。太小(如 8)训练不稳定,太大(如 128)收敛慢且可能陷局部最优。
  • 'InitialLearnRate':初始学习率。0.005 对 Adam 来说偏保守,0.01 也可以试。如果训练损失震荡不降,降到 0.001。
  • 'LearnRateSchedule', 'piecewise':分段衰减。每 50 轮学习率乘 0.5,帮助后期精细收敛。
  • 'ValidationData':验证集。用于监控过拟合和早停。
  • 'ValidationPatience', 20:验证损失连续 20 次不降就停。防止过拟合的后悔药。
  • 'Shuffle', 'never':时间序列不能打乱!这是最容易踩的坑之一。打乱后 LSTM 学到的时序关系就乱了。
  • 'Plots', 'training-progress':弹出训练进度窗口,实时看损失曲线。调试阶段必开。

4.3 训练与预测:trainNetwork 和 predict 的配合

层和选项都定义好,训练就一行:

% 训练网络 net = trainNetwork(XTrain, YTrain, layers, options);

训练完成后预测:

% 预测 YPred_norm = predict(net, XTest); % 反归一化 YPred = mapminmax('reverse', YPred_norm', ps); YTest_original = mapminmax('reverse', YTest', ps);

逻辑说明:predict返回的是归一化后的预测值,必须用训练时保存的ps反归一化才能和原始风速对比。YPred_norm的维度可能是1×N或N×1,取决于网络输出和输入格式,转置一下再送mapminmax比较稳。

参数说明:predict和classify不同,回归任务用predict。如果报错「输入维度不匹配」,检查XTest的 cell 内序列长度是否和训练时一致——lookback必须相同。

4.4 评估指标:RMSE、MAE 与 R² 的计算

预测出来不算完,得量化评估:

% 计算评估指标 rmse = sqrt(mean((YPred - YTest_original).^2)); mae = mean(abs(YPred - YTest_original)); ss_res = sum((YTest_original - YPred).^2); ss_tot = sum((YTest_original - mean(YTest_original)).^2); r2 = 1 - ss_res / ss_tot; fprintf('RMSE: %.4f\n', rmse); fprintf('MAE: %.4f\n', mae); fprintf('R2: %.4f\n', r2);

逻辑说明:RMSE 对大误差敏感,是风速预测最常用的指标;MAE 反映平均偏差;R² 衡量拟合优度,越接近 1 越好。三个指标一起看,避免单一指标误导。

参数说明:风速预测里,RMSE 在 1.0~2.0 m/s 之间算可用,低于 1.0 算不错。如果 R² 低于 0.5,说明模型基本没学到东西,得回头查数据或网络结构。

5. 避坑与排查:五个让 RMSE 翻倍的细节

这份包跑通不难,但跑出合理结果不容易。下面五条是我在类似项目里踩过的坑,按「现象 → 原因 → 解决」写清楚。

5.1 预测曲线是一条直线

现象:训练损失降不下去,预测结果几乎不随时间变化,RMSE 和直接用均值预测差不多。

原因:最常见的是归一化方向搞反了。mapminmax默认按行处理,如果你的数据是列向量,归一化后每个时间步被独立处理,序列信息全丢了。另一个可能是Shuffle设成了'every-epoch',时间顺序被打乱。

解决:检查mapminmax输入前是否转置。确认trainingOptions里'Shuffle'是'never'。如果还不行,把lookback调大,让网络看到更多历史。

5.2 验证损失先降后升,测试集 RMSE 远大于验证集

现象:训练集 RMSE 很低,验证集和测试集 RMSE 高出一大截,典型过拟合。

原因:网络参数太多(隐藏单元数过大、层数过多),训练样本太少,或者训练轮数太多没有早停。

解决:先降numHiddenUnits,从 64 降到 32 试试。加'ValidationPatience'早停。如果数据量实在少,考虑用 dropout:

lstmLayer(64, 'OutputMode', 'last', 'Dropout', 0.2)

Dropout在 LSTM 里是随机丢弃输入连接,0.1~0.3 之间试。注意 dropout 只在训练时生效,预测时自动关闭。

5.3 训练速度慢到无法接受

现象:一个 epoch 跑好几分钟,200 个 epoch 要等一晚上。

原因:没用 GPU,或者MiniBatchSize太小导致频繁更新权重,或者序列太长。

解决:确认gpuDeviceCount大于 0,ExecutionEnvironment设成'gpu'。适当增大MiniBatchSize到 64 或 128。如果lookback设了 100 以上,考虑降到 30 以内——LSTM 对超长序列的训练效率很低。

5.4 反归一化后预测值范围离谱

现象:归一化域内预测看着还行,反归一化后出现负风速或超过历史最大值的离谱结果。

原因:outputps.mat里的ps和训练时用的不是同一个,或者mapminmax('reverse', ...)的输入维度不对。

解决:确保训练和预测用的是同一个ps。反归一化前打印YPred_norm的范围,确认在[0, 1]或[-1, 1]内。如果超出,说明网络输出层有问题,检查是否误用了sigmoid或softmax。

5.5 ELM 结果比 LSTM 还好

现象:ELM.m跑出来 RMSE 比LSTM.m低,LSTM 白折腾了。

原因:数据量太小,LSTM 参数多但样本不够,欠拟合;或者 LSTM 的lookback设得太小,没发挥时序建模优势;或者 ELM 的输入构造方式不同,用了更多历史信息。

解决:先确认两者用的是同一份训练/测试数据划分。如果数据量确实少(少于 300 条),LSTM 可能不是最优选择,ELM 或简单的 ARIMA 反而更稳。如果坚持用 LSTM,试试多变量输入——把温度、气压、污染数据都加进去,增加信息量。

6. 进阶验证:多变量输入与滚动预测的实操

单变量 LSTM 跑通只是起点。实际风速预测里,多变量输入和滚动预测才是更接近业务场景的做法。这一章说两个具体技巧,附上可复现的代码和验证方法。

6.1 多变量输入:把气象和污染数据都用上

单靠历史风速,LSTM 能抓到的模式有限。把温度、气压、PM2.5 等作为额外特征加进去,往往能降 RMSE。做法是修改sequenceInputLayer的输入维度,并重新构造X:

% 多变量输入构造 features = [wind_norm, temp_norm, pressure_norm, pm25_norm]; % N×4 lookback = 7; n = size(features, 1); X = cell(n - lookback, 1); Y = zeros(n - lookback, 1); for i = 1:(n - lookback) X{i} = features(i:i+lookback-1, :)'; % 4×lookback Y(i) = wind_norm(i + lookback); end % 网络输入层改为 4 numFeatures = 4; layers = [ sequenceInputLayer(numFeatures) lstmLayer(64, 'OutputMode', 'last') fullyConnectedLayer(1) regressionLayer ];

逻辑说明:X{i}的维度从1×lookback变成4×lookback,sequenceInputLayer的参数同步改成 4。其他训练流程不变。

参数说明:多变量输入时,每个特征都要独立归一化。别把温度和风速放在一起做mapminmax,量纲不同会互相干扰。我一般对每个特征单独归一化,保存各自的ps,反归一化时只对风速用对应的ps。

验证多变量是否有效的方法:固定其他参数,只改输入特征数,对比测试集 RMSE。如果多变量 RMSE 反而更高,说明额外特征引入了噪声,或者特征之间相关性太强导致冗余。

6.2 滚动预测:一步预测 vs 多步预测

前面做的都是「用过去 7 天预测第 8 天」的一步预测。实际业务里往往需要预测未来 24 小时甚至 72 小时,这就涉及滚动预测。最简单的做法是递归滚动:

% 递归滚动预测未来 n 步 n_steps = 24; last_sequence = XTest{end}; % 最后一个输入序列 pred_rolling = zeros(n_steps, 1); for k = 1:n_steps pred_norm = predict(net, last_sequence); pred_rolling(k) = pred_norm; % 把预测值追加到序列末尾,去掉最早的值 last_sequence = [last_sequence(:, 2:end), pred_norm]; end % 反归一化 pred_rolling_original = mapminmax('reverse', pred_rolling', ps);

逻辑说明:每次预测一个值,把它当作已知历史追加到输入序列,再预测下一个。这样误差会累积,步数越多 RMSE 越大。

参数说明:n_steps根据业务需求定。日度数据预测未来 7 天比较合理,小时数据预测 24 小时。滚动预测的 RMSE 通常比一步预测高 30%~50%,如果高得离谱,说明模型对误差累积太敏感,可以考虑直接训练一个多输出网络(OutputMode设成'sequence',输出完整预测序列)。

验证滚动预测效果的方法:用测试集最后一段做滚动,和真实值对比。重点看第 1 步、第 6 步、第 12 步、第 24 步的 RMSE 变化趋势。如果第 1 步就很大,说明模型本身有问题;如果第 1 步小但后面迅速变大,说明误差累积快,需要改进模型或缩短预测步长。

6.3 一个我常用的验证习惯

每次改完网络结构或数据管线,我不会直接看测试集结果,而是先跑三个检查:训练集损失是否降到 0.01 以下、验证集损失是否在 50 个 epoch 内开始上升、预测值和真实值的相关系数是否大于 0.7。三个都过了,再看测试集 RMSE。这个习惯帮我省了很多「调了半天发现是数据格式错了」的时间。从那以后我每次拿到新的时序数据,都强制先跑一遍滑动窗口构造和归一化检查,确认输入输出对得上再动网络。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进