尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

MATLAB实现LSTM多步时间序列预测:从数据预处理到100步递推

MATLAB实现LSTM多步时间序列预测:从数据预处理到100步递推 简介面向时间序列预测与深度学习入门者这份资源提供基于MATLAB的LSTM未来多步预测完整实现可应用于电力需求预测、股票市场分析、气象预报等场景也适合作为课程设计或毕业设计的参考项目。压缩包内共7个文件包含LSTMTIMEN.m源码、datatimen.xls示例数据、docx步骤说明文档以及4张结果对比图整体仅544KB轻量易用。源码覆盖数据加载与归一化、LSTM网络搭建、训练以及将预测值迭代输入生成未来多步输出的核心流程并配有预测结果与实际值对比图像便于直观评估模型效果。代码结构清晰、注释明确读者可快速修改数据集与网络参数迁移到自身业务数据上目前已有5549人学习下载适合希望快速上手MATLAB深度学习工具箱、完成时间序列多步预测实验或毕业设计的读者参考与二次开发。1. 100 步预测才是 LSTM 时间序列的真正考验同样是 LSTM单步预测漂亮不代表 100 步预测能用。这个工程的价值在于给了LSTMTIMEN.m和datatimen.xls从原始数据直接跑到 100 步未来的结果图中间没有省略数据预处理和结果反归一化。很多人在单步上把 MSE 做到很小一拉长预测步数就发散问题大多出在递推策略和训练样本构造上而不是网络结构。本文按数据读取、样本构造、网络训练、递归多步预测、结果评价这条线拆开讲代码可以直接抄下来对着自己的数据改。适合已经在 MATLAB 里跑过简单 RNN 或深度学习例程、想把预测步数从 1 步扩展到 100 步的工程师和学生。2. datatimen.xls 到监督学习序列数据预处理与样本构造2.1 用 readtable 读入时间列和观测值先要搞清楚datatimen.xls的格式。最常见是两列第一列时间戳第二列观测值。不要直接用xlsread一把梭我一般用detectImportOptions先检视列类型再决定是否把时间列转成datetime。示例opts detectImportOptions(datatimen.xls, Sheet, 1); dataTable readtable(datatimen.xls, opts); % 取数值观测列时间列单独保存 t dataTable{:, 1}; % 可能是 datetime 或数值编号 x dataTable{:, 2}; % 观测值 if isa(t, datetime) tNum datenum(t); else tNum t; enddetectImportOptions会自动识别表头、列类型比xlsread对混合类型更稳。如果时间列是文本日期需要datetime(t, InputFormat, yyyy-MM-dd HH:mm)这类带格式的转换如果本身是 Excel 序列号直接datenum后当时间坐标用。这里重要的是把时间列和观测列都转成数值或 datetime因为后续绘图和计算都要靠时间坐标对齐。如果数据没有时间列只有一列观测值也可以只取x dataTable{:, 1}时间索引就用1:length(x)。代码里t的作用是最后的横轴标签不会参与 LSTM 输入所以不需要对时间做特征编码。2.2 归一化与滑动窗口生成训练样本LSTM 对输入尺度敏感尤其后续用 sigmoid 和 tanh输入值落在 [-1,1] 附近训练更稳定。常用两种归一化Z-Score 与 Min-Max。我的习惯是在这段时序数据上先用 Z-Score因为时序数据偶尔有较大幅值波动Min-Max 容易被离群点压低整体变化。mu mean(x); sigma std(x); xNorm (x - mu) / sigma;预测完成后必须用x yPred * sigma mu反归一化否则所有误差指标都会失真。窗口构造用循环写便于理解输入窗口 10 步预测目标 1 步然后递归到 100 步。numSteps 10; % 输入窗口长度可根据自相关函数调整 numFuture 1; % 训练时输出 1 步 XTrain {}; YTrain []; for i 1:length(xNorm)-numSteps-numFuture1 XTrain{end1, 1} xNorm(i:inumSteps-1); % 1 x numSteps YTrain(end1, :) xNorm(inumSteps:inumStepsnumFuture-1); end这里XTrain是 cell 数组每个元素是[1, numSteps]的行向量这是 MATLABtrainNetwork对 sequence-to-one 回归的标准格式YTrain是普通矩阵行数等于样本数列数等于输出步数numFuture。如果numFuture 1YTrain是列向量。窗口numSteps太小会丢掉周期信息太大样本量骤降可以先做自相关autocorr看显著滞后阶数再定。2.3 训练集切分与数据泄漏边界切分时间序列不能用随机打散。代码里通常把前 80% 作训练集、后 20% 作测试集并且测试集要严格在时间上靠后。这样评价才有「用历史预测未来」的意义。numTrain round(0.8 * size(XTrain, 1)); XTrainData XTrain(1:numTrain); YTrainData YTrain(1:numTrain, :); XTestData XTrain(numTrain1:end); YTestData YTrain(numTrain1:end, :);这里XTrain的 cell 数组索引方式要用对XTrain(1:numTrain)返回子 cell 数组XTrain{1}才返回内容。归一化参数mu、sigma只能从训练集计算再作用到测试集不能在测试集上重新算否则测试指标会被高估。很多初学者在这步把测试数据也纳入归一化统计量导致验证阶段 MSE 非常好看部署后直接劣化。另一个容易忽略的泄漏点是滑动窗口的边界构造样本时用到了整个时间序列如果测试集样本的时间窗口跨越了训练集和测试集的分界点相当于测试时看到了未来的归一化统计量。安全做法是先切分原始时间序列再在训练段和测试段分别构造窗口而不是先构造所有窗口再切分。3. LSTM 网络搭建与 trainNetwork 训练参数3.1 用深度学习工具箱搭建序列网络在 MATLAB 里sequenceInputLayer定义输入维度lstmLayer定义隐藏单元数dropoutLayer防止过拟合fullyConnectedLayer把 LSTM 输出映射到预测维度最后regressionOutputLayer计算回归损失。这个资源里的LSTMTIMEN.m大概率就是按这套结构组织的。numFeatures 1; % 单变量序列 numHiddenUnits 50; % 隐藏单元先给一个中等值 numResponses 1; % 每次预测 1 步 layers [ sequenceInputLayer(numFeatures) lstmLayer(numHiddenUnits, OutputMode, sequence) dropoutLayer(0.2) fullyConnectedLayer(numResponses) regressionOutputLayer ];重点看lstmLayer的OutputMode。这里把输出设为sequence让 LSTM 每个时间步都输出隐含状态再接fullyConnectedLayer后回归输出会是一个与输入时间步等长的序列。如果只想用最后一个时间步的输出预测未来应该设OutputMode last。两种模式都能训练但递推多步预测时我的经验是last更直接最后一步输出已经包含整个窗口的信息拿它接全连接层做 1 步预测训练更容易收敛。很多 Python 教程里的return_sequencesTrue对应这里的sequencereturn_sequencesFalse对应last迁移时可以换算。如果numResponses大于 1比如一次预测 5 步fullyConnectedLayer(5)配合OutputModelast也能处理输出是 5 维向量。但这种训练方式会把未来第 1 步到第 5 步的错误同等看待长期预测时效果不一定比递归 1 步好。3.2 训练选项求解器、学习率、mini-batch 与序列长度trainNetwork的训练选项用trainingOptions控制。与图像任务不同时间序列样本长度差异不大时不需要太在意MiniBatchSize大小但学习率要保守一些。options trainingOptions(adam, ... MaxEpochs, 200, ... MiniBatchSize, 64, ... InitialLearnRate, 0.005, ... GradientThreshold, 1, ... Shuffle, every-epoch, ... Verbose, false, ... Plots, training-progress);GradientThreshold是 LSTM 训练最容易崩溃的点。递推多步预测场景里梯度容易爆炸设置成 1 是把每个梯度的范数截断到 1比单纯降学习率更能稳定训练。Shuffle设成every-epoch每个 epoch 内打乱样本顺序但不会打乱样本内部时间步。学习率从 0.005 开始如果损失振荡就降到 0.001如果收敛太慢就提到 0.01。MaxEpochs不要只看自己设定的 200早停更有效可以在训练进度图上判断。下面的表格是我在类似时序任务上的常用初始值具体还要看数据波动幅度和样本量参数初始值调整方向numHiddenUnits50数据复杂度高可加到 100~200不要轻易超过 300InitialLearnRate0.005损失不降时降到 0.001梯度消失时尝试 0.01MiniBatchSize64样本少用 32样本多且序列长用 128GradientThreshold1训练中断或 NaN 时保持 1 或降到 0.5Dropout0.2训练损失低测试损失高时加到 0.3~0.53.3 训练过程观察与过拟合判断训练进度图里最值得看的是训练集 RMSE 和验证集 RMSE 的差距。trainingOptions支持ValidationData可以传一个 cell{XTrainVal, YTrainVal}。如果训练损失一直降但验证损失从某个 epoch 开始上升就是过拟合直接取那个 epoch 的模型参数而不是等MaxEpochs跑完。时间序列的验证集也要按时间顺序切在训练集之后不能随机抽。另外trainNetwork默认用单精度训练输出net也是单精度。如果数据量小训练很快不必刻意转 double。如果 GPU 显存不够把MiniBatchSize调小或者把序列长度固定到numSteps并用SequenceLength,shortest避免 padding 大量无意义时间步。这里容易踩的坑是把lstmLayer的OutputMode误解为预测步数。OutputMode控制的是每个时间步是否输出隐含状态不是未来走多少步。未来多步预测靠的是第 4 章的递推逻辑。4. 递归预测循环从 predict 到未来 100 步4.1 用 predict 做单步推理训练完成后模型输入一个[1, numSteps]的序列输出一个 1 维预测值。假设已经用测试集最后一个numSteps窗口作为起点inputSeq xNorm(end-numSteps1:end); % 最后 10 个观测1 x 10 yhat predict(net, {inputSeq}); % yhat 是 1 x numResponsespredict对单个样本时第二个参数需要 cell 数组里面的每个元素是[features, timeSteps]。这里inputSeq是[1, 10]所以包成{inputSeq}。yhat的尺寸和fullyConnectedLayer的输出维度一致在numResponses1时是1×1。如果numResponses 1yhat是1×numResponses取第 1 列作为下一步输入。注意predict和predictAndUpdateState的区别前者每次调用都独立推理状态不保留后者维护网络状态适合在线滚动场景。这里用独立推理已经足够。4.2 滚动递推循环生成 100 步预测未来多步预测最常用的做法是把刚预测出的值当作新观测拼到输入序列末尾同时移除最老的一个值保持窗口长度不变。这个操作在信号处理里叫滑动窗口递推在 LSTM 预测里是最直观的多步外推方式。numPredSteps 100; yPred zeros(numPredSteps, 1); for i 1:numPredSteps yhat predict(net, {inputSeq}); yPred(i) yhat(1); inputSeq [inputSeq(:, 2:end), yhat(1)]; end yPredDenorm yPred * sigma mu;循环里做了三件事预测当前步的结果并保存把预测值接到窗口尾部把窗口头部的旧值丢掉。如果训练时设了numFuture 5则yhat是 5 维向量此时仍然只取yhat(1)作为输入因为未来第 2 步到第 5 步的预测量是建立在「第 1 步预测正确」前提上的在实际长程递推中误差更大回灌反而会让后续结果偏离真实轨迹。这种滚动递推在金融时序预测里也很常见预测结果的可解释性比一刀切输出 100 步要强。多步预测的另一种策略是直接多步即一次输出 100 步不滚动。这在 MATLAB 里也简单把fullyConnectedLayer输出改成 100训练时YTrain每一行存未来 100 个观测。但样本量和数据周期性要求很高100 步同时优化的参数很多很容易在远期预测上输出均值回归。对比两种策略资源里描述的是滚动递推这也是实际中更容易定位误差来源的方案。4.3 反归一化与评价指标预测结果yPred还在归一化空间直接和原始数据比会得到离谱的 RMSE。先反归一化再算误差yReal yTestOriginal; % 真实未来 100 步注意这里要对应最后 100 步 rmse sqrt(mean((yPredDenorm - yReal).^2)); mae mean(abs(yPredDenorm - yReal)); ssRes sum((yPredDenorm - yReal).^2); ssTot sum((yReal - mean(yReal)).^2); r2 1 - ssRes / ssTot;RMSE 对大误差敏感MAE 更鲁棒R² 能反映模型相对均值预测的提升。如果 R² 为负说明预测还不如直接拿历史均值当结果这时要检查是不是归一化泄漏、窗口太短或训练轮数不够。多步预测的误差要按步长分别统计比如第 1 步 RMSE、第 50 步 RMSE、第 100 步 RMSE而不是只给一个整体均值。实际经验是误差随步长非线性增长第 100 步的误差往往是第 1 步的 3~5 倍这个现象在单变量序列上基本无法避免只能通过更好的特征和模型结构缓解。5. 可视化与调参把 100 步预测结果讲清楚5.1 四张结果图怎么对照看资源中LSTMTSN1.png到LSTMTSN4.png对应训练过程、预测曲线或损失曲线。看预测图时先看纵轴是否还原到真实尺度再看训练段和预测端的交界处是否有跳变。如果交界处连续但后续发散是递推误差累积的正常表现如果交界处就断掉多半是反归一化参数没对齐。可以自己再画一张更清晰的预测图figure; plot(tTest, yTestOriginal, LineWidth, 1.5); hold on; plot(tFuture, yPredDenorm, --, LineWidth, 1.5); legend(真实值, 100步预测, Location, best); xlabel(时间); ylabel(观测值); title([RMSE, num2str(rmse, %.3f), R^2, num2str(r2, %.3f)]);tTest是测试段的时间坐标tFuture是从预测起点往后 100 个时间点。如果两者不是等间距先用linspace生成一致的时间轴否则预测曲线会被拉伸或压缩。5.2 快速落地的调参路径与验证方法第一固定随机种子rng(0)放在脚本开头否则两次训练因随机初始化不同无法判断是哪项改动起作用。第二把测试集最后 100 步从训练窗口构造中隔离出来绝对不能让构造训练样本时用到这 100 步内的数据。第三观察远期预测形状如果 100 步后输出收敛到常数可把窗口numSteps从 10 加到覆盖一个完整周期或换成双向bilstmLayer对比。第四每个参数组合都记录 RMSE、MAE、R²我一般会做三组实验隐藏单元 50/100/200、学习率 0.005/0.001、窗口 10/20/30用最少组合跑出趋势。别忘了analyzeNetwork(layers)可以可视化每层输出尺寸排查维度不匹配比反复爆错更高效。本文还有配套的精品资源点击获取
返回列表