简介:这份资源面向需要掌握时序数据回归预测的机器学习初学者与工程人员,提供基于MATLAB实现的长短期记忆神经网络(LSTM)完整代码与配套说明。LSTM作为循环神经网络的变体,通过门控单元与记忆单元有效捕捉序列中的长期依赖关系,适用于负荷预测、股价走势、传感器信号等回归场景。压缩包共3个文件,约197KB,包含一份可直接运行的m脚本作为核心实现,一份xlsx格式的示例数据集用于训练与验证,以及一份docx使用教程,讲解网络搭建、参数设置与注意事项。已有194人学习下载,读者可据此快速理解LSTM回归的建模流程,掌握数据组织、隐藏状态利用、损失函数选择与反向传播训练等关键环节,并借助示例数据复现预测效果,为后续迁移到自身时序任务提供可参考的代码框架与排错思路。
1. LSTM 回归预测在 MATLAB 里到底怎么落地
手上有一批时序数据,可能是设备振动、电力负荷、股票收盘价,也可能是化工反应釜的温度曲线,你想预测下一个时刻的数值。这种“输入一段历史,输出一个连续值”的任务就是回归预测,而 LSTM 是目前在 MATLAB 里最容易跑通、也最容易踩坑的一条路。MATLAB 从 R2017b 开始内置了 LSTM 层,配合 Deep Learning Toolbox 就能搭网络,不需要额外装 Python 环境。但真正动手时你会发现:数据怎么切窗口、归一化放在哪一步、训练完怎么反归一化、预测出来为什么是一条直线——这些问题教程里往往一笔带过。这篇笔记按“数据准备 → 网络搭建 → 训练调参 → 预测还原 → 避坑”的顺序,把 MATLAB 实现 LSTM 回归预测的完整链路拆开讲,每一步都给可复现的代码和参数说明。适合已经会用 MATLAB 做数据处理、想快速把 LSTM 跑起来做回归的工程师,也适合被 Python 环境折腾烦了想换条路的人。
2. 数据准备:从原始序列到 LSTM 能吃的输入格式
2.1 为什么 LSTM 的输入必须是三维数组
MATLAB 的sequenceInputLayer接受的输入格式是C×B×T,即特征数×批大小×时间步。很多人第一次用trainNetwork报错“Invalid training data”,八成是数据维度不对。回归预测里最常见的单变量场景,特征数 C=1,但你不能直接把一列数据丢进去,必须 reshape 成1×N×1或者按窗口切成1×batch×T。
这里有个关键选择:你是用“整个序列作为一个样本”还是“滑窗切多个样本”。如果数据量只有几百个点,整段作为一个序列训练,网络学不到足够的模式;如果数据有上万点,滑窗切样本是必须的。我一般用滑窗,窗口长度取 20~100,取决于你的数据周期。比如电力负荷有日周期,窗口至少覆盖 24 个点;振动信号频率高,窗口可以短一些。
% 假设原始数据是列向量 data,长度 N data = (data - mean(data)) / std(data); % 先做标准化,后面会讲为什么 windowSize = 48; % 窗口长度,覆盖两个日周期 forecastStep = 1; % 预测未来几步 numSamples = length(data) - windowSize - forecastStep + 1; X = zeros(1, numSamples, windowSize); % C×B×T Y = zeros(1, numSamples); for i = 1:numSamples X(1, i, :) = data(i : i+windowSize-1); Y(i) = data(i + windowSize + forecastStep - 1); end % 划分训练集和测试集,按时间顺序切,不能随机打乱 splitRatio = 0.8; splitIdx = floor(numSamples * splitRatio); XTrain = X(:, 1:splitIdx, :); YTrain = Y(1:splitIdx); XTest = X(:, splitIdx+1:end, :); YTest = Y(splitIdx+1:end);这段代码做了三件事:标准化、滑窗切样本、按时间顺序划分。标准化用 z-score,因为 LSTM 的激活函数对输入范围敏感,原始数据如果量纲差异大(比如温度 20~80,压力 0.1~0.5),不归一化会导致梯度爆炸或收敛极慢。注意标准化的均值和标准差只能用训练集算,测试集要用训练集的参数变换,否则就是数据泄露。滑窗切样本时,X的第三维是时间步,第二维是样本数,这个顺序不能反。划分时按时间顺序切,不能随机打乱,因为时序数据的自相关性会被随机划分破坏,导致测试集精度虚高。
2.2 多变量输入时特征维度怎么对齐
实际工程里往往不止一个输入变量。比如预测风电功率,输入可能有风速、风向、温度、气压四个特征。这时候sequenceInputLayer的参数要改成 4,X的第一维变成 4。但多变量有个坑:不同特征的量纲和变化范围差异很大,必须分别标准化。我一般把每个特征单独做 z-score,而不是整体减一个均值。
% 假设 rawData 是 N×4 矩阵,每列一个特征 numFeatures = size(rawData, 2); dataNorm = zeros(size(rawData)); mu = mean(rawData(1:splitIdx, :)); sigma = std(rawData(1:splitIdx, :)); dataNorm = (rawData - mu) ./ sigma; % 滑窗时每个时间步取所有特征 X = zeros(numFeatures, numSamples, windowSize); for i = 1:numSamples X(:, i, :) = dataNorm(i : i+windowSize-1, :)'; Y(i) = dataNorm(i + windowSize + forecastStep - 1, targetCol); end注意X(:, i, :)的赋值方式:dataNorm的一个窗口是windowSize×numFeatures,转置后变成numFeatures×windowSize,再塞进三维数组的对应位置。目标变量Y如果也是多变量之一,记得用同一个标准化参数。如果目标变量不在输入特征里,那它单独标准化,反归一化时用它的均值和标准差。
提示:标准化参数一定要保存下来,预测新数据时要用同一套
mu和sigma,否则预测值会偏得离谱。
3. 网络搭建:层数、隐藏单元和训练选项怎么定
3.1 一个能跑通的最小 LSTM 网络结构
MATLAB 里搭 LSTM 回归网络,核心层就四个:sequenceInputLayer、lstmLayer、fullyConnectedLayer、regressionLayer。中间可以加dropoutLayer防过拟合。下面是一个最小可运行的结构:
numFeatures = 1; % 输入特征数 numHiddenUnits = 128; % LSTM 隐藏单元数 outputSize = 1; % 回归输出一个值 layers = [ sequenceInputLayer(numFeatures, 'Normalization', 'none') lstmLayer(numHiddenUnits, 'OutputMode', 'last') dropoutLayer(0.2) fullyConnectedLayer(outputSize) regressionLayer ];OutputMode设为'last'表示只取最后一个时间步的输出,这是回归预测的标准做法。如果你要做序列到序列的预测(每个时间步都输出一个值),改成'sequence',但那样fullyConnectedLayer的输出维度要匹配时间步数。dropoutLayer的丢弃率一般 0.1~0.3,数据量小的时候用 0.1,数据量大可以到 0.3。numHiddenUnits是最关键的参数,太小欠拟合,太大过拟合且训练慢。我的经验是:单变量回归从 64 或 128 起步,多变量可以到 256。如果训练集 loss 下降但验证集 loss 上升,说明隐藏单元太多,往下调。
3.2 训练选项:学习率、批大小和早停
trainingOptions里的参数直接决定训练能不能收敛。下面是一组我常用的配置:
options = trainingOptions('adam', ... 'MaxEpochs', 200, ... 'MiniBatchSize', 32, ... 'InitialLearnRate', 0.005, ... 'LearnRateSchedule', 'piecewise', ... 'LearnRateDropPeriod', 50, ... 'LearnRateDropFactor', 0.5, ... 'GradientThreshold', 1, ... 'ValidationData', {XTest, YTest}, ... 'ValidationFrequency', 10, ... 'ValidationPatience', 20, ... 'Shuffle', 'never', ... 'Verbose', true, ... 'Plots', 'training-progress');Shuffle必须设为'never',因为时序数据的顺序不能打乱。GradientThreshold设为 1 是防止梯度爆炸,LSTM 在长序列上很容易出现这个问题。ValidationPatience设为 20 表示验证集 loss 连续 20 次不下降就停止训练,这是省时间的后悔药。InitialLearnRate用 0.005 比默认的 0.01 稳,如果 loss 震荡厉害就降到 0.001。MiniBatchSize根据显存来,CPU 训练用 16 或 32,GPU 可以到 64 或 128。
注意:
ValidationData的格式必须和训练数据一致,也是C×B×T的三维数组加一维标签,不能只给标签。
3.3 训练完怎么存和怎么加载
训练完的net是一个SeriesNetwork对象,直接save('lstmModel.mat', 'net')就行。但要注意,如果你在训练后改了数据预处理参数,加载模型时这些参数不会自动恢复,必须单独存。
save('lstmModel.mat', 'net', 'mu', 'sigma', 'windowSize', 'forecastStep'); % 加载时 loaded = load('lstmModel.mat'); net = loaded.net; mu = loaded.mu; sigma = loaded.sigma;预测新数据时,先用mu和sigma标准化,再滑窗构造X,然后predict(net, X),最后反归一化。反归一化公式是y_real = y_pred * sigma + mu。如果目标变量和输入特征用了不同的标准化参数,这里要用目标变量的mu和sigma。
4. 预测与还原:从网络输出到真实物理量
4.1 单步预测和多步预测的代码差异
单步预测就是给定一个窗口,预测下一个点。多步预测有两种做法:递归预测和直接多输出。递归预测是预测一个点后,把预测值塞回窗口,再预测下一个,误差会累积。直接多输出是把fullyConnectedLayer的输出改成forecastStep个节点,一次输出多个值。
% 单步预测 YPred = predict(net, XTest); YPred_real = YPred * sigma_target + mu_target; YTest_real = YTest * sigma_target + mu_target; % 递归多步预测 futureSteps = 10; lastWindow = XTest(:, end, :); predSeq = zeros(1, futureSteps); for t = 1:futureSteps yNext = predict(net, lastWindow); predSeq(t) = yNext; lastWindow = [lastWindow(:, :, 2:end), yNext]; end predSeq_real = predSeq * sigma_target + mu_target;递归预测的lastWindow更新逻辑是:去掉最老的时间步,把新预测值加到末尾。注意yNext的维度要和lastWindow的第三维对齐,MATLAB 里predict返回的是1×1的标量,需要 reshape 成1×1×1再拼接。
4.2 评价指标:RMSE、MAE 和 R² 在 MATLAB 里怎么算
回归预测不能只看 loss 曲线,必须算真实量纲下的误差。三个最常用的指标:
rmse = sqrt(mean((YPred_real - YTest_real).^2)); mae = mean(abs(YPred_real - YTest_real)); ssRes = sum((YTest_real - YPred_real).^2); ssTot = sum((YTest_real - mean(YTest_real)).^2); r2 = 1 - ssRes / ssTot; fprintf('RMSE: %.4f\nMAE: %.4f\nR2: %.4f\n', rmse, mae, r2);R² 在时序预测里容易虚高,因为测试集如果趋势明显,随便预测一个滑动平均都能到 0.9。我一般同时看 RMSE 和预测曲线图,如果曲线明显滞后于真实值,说明网络没学到动态特性,需要加隐藏单元或加长窗口。
4.3 预测结果可视化的三个必画图
第一个图:训练集和测试集的 loss 曲线,看有没有过拟合。第二个图:测试集真实值 vs 预测值的时间序列对比,看相位和幅值。第三个图:散点图,横轴真实值纵轴预测值,理想情况是落在 y=x 线上。
figure; subplot(1,3,1); plot(rmseTrain, 'b'); hold on; plot(rmseVal, 'r'); legend('Train RMSE', 'Val RMSE'); title('Loss Curve'); subplot(1,3,2); plot(YTest_real, 'b'); hold on; plot(YPred_real, 'r'); legend('True', 'Predicted'); title('Test Set Prediction'); subplot(1,3,3); scatter(YTest_real, YPred_real, 10, 'filled'); hold on; plot([min(YTest_real), max(YTest_real)], ... [min(YTest_real), max(YTest_real)], 'r--'); xlabel('True'); ylabel('Predicted'); title('Scatter');散点图如果呈现明显的“八字形”或“纺锤形”,说明模型对极端值预测能力差,这时候要考虑加更多训练数据或者换更复杂的网络结构。
5. 避坑与排查:LSTM 回归预测最常见的五个翻车现场
5.1 预测出来是一条直线
现象:测试集预测值几乎不变,或者只比均值好一点点。原因:最常见的是标准化没做对,或者做了标准化但反归一化时用错了参数。另一个原因是OutputMode设成了'sequence'但后面接的fullyConnectedLayer输出维度不对,网络实际上没学到东西。解决:先检查X的数值范围,如果都在 0 附近且方差很小,说明标准化有问题;再检查YPred的方差,如果接近 0,说明网络输出饱和了,把学习率降到 0.001 重新训练。
5.2 训练 loss 震荡不收敛
现象:loss 曲线上下跳动,验证集 loss 始终不下降。原因:学习率太大,或者MiniBatchSize太小导致梯度估计噪声大。解决:把InitialLearnRate从 0.01 降到 0.001 或 0.0005,MiniBatchSize从 16 提到 32 或 64。如果还不行,加GradientThreshold为 1,并在lstmLayer后面加batchNormalizationLayer。
5.3 验证集 loss 比训练集低
现象:验证集 loss 曲线在训练集下方。原因:这通常不是过拟合,而是验证集比训练集简单,或者验证集的数据分布和训练集不一致。解决:检查划分时是不是按时间顺序切的,如果随机切了,验证集可能包含训练集见过的模式。另外检查标准化参数是不是只用了训练集算,如果用了全体数据算,验证集的信息泄露了。
5.4 多步预测误差累积太快
现象:递归预测 5 步后误差就超过 20%。原因:递归预测把预测值当真实值用,误差指数级放大。解决:改用直接多输出,把fullyConnectedLayer的输出改成forecastStep个节点,训练时标签Y也改成矩阵,每行是一个样本的多个未来值。这样网络一次输出所有步,不依赖递归。
5.5 GPU 显存不够或训练速度慢
现象:trainNetwork报 out of memory,或者一个 epoch 跑好几分钟。原因:MiniBatchSize太大,或者数据是double类型占显存。解决:把X和Y转成single类型,MiniBatchSize降到 16 或 8。如果还是不够,用executionEnvironment设为'cpu',虽然慢但能跑。MATLAB 的 LSTM 在 CPU 上训练小网络其实不慢,几百个样本几分钟就能跑完。
提示:训练前用
whos看一下X和Y的内存占用,single比double省一半显存。
6. 进阶技巧:用贝叶斯优化自动调 LSTM 超参数
手动调numHiddenUnits、InitialLearnRate、MiniBatchSize很费时间,MATLAB 的bayesopt可以自动搜。核心思路是定义一个目标函数,输入超参数,输出验证集 RMSE,然后让bayesopt去找最小值。
function rmse = lstmObjective(params, XTrain, YTrain, XVal, YVal) numHiddenUnits = round(params.numHiddenUnits); learnRate = params.learnRate; dropoutRate = params.dropoutRate; layers = [ sequenceInputLayer(1, 'Normalization', 'none') lstmLayer(numHiddenUnits, 'OutputMode', 'last') dropoutLayer(dropoutRate) fullyConnectedLayer(1) regressionLayer ]; options = trainingOptions('adam', ... 'MaxEpochs', 100, ... 'MiniBatchSize', 32, ... 'InitialLearnRate', learnRate, ... 'Shuffle', 'never', ... 'Verbose', false, ... 'Plots', 'none'); net = trainNetwork(XTrain, YTrain, layers, options); YPred = predict(net, XVal); rmse = sqrt(mean((YPred - YVal).^2)); end % 定义搜索空间 params = [ optimizableVariable('numHiddenUnits', [32, 256], 'Type', 'integer') optimizableVariable('learnRate', [1e-4, 1e-2], 'Transform', 'log') optimizableVariable('dropoutRate', [0, 0.5]) ]; results = bayesopt(@(p) lstmObjective(p, XTrain, YTrain, XTest, YTest), ... params, 'MaxObjectiveEvaluations', 30, 'IsObjectiveDeterministic', false);bayesopt默认跑 30 次,每次训练一个网络,总时间取决于数据量和网络大小。如果数据大,建议先用小样本搜,找到大致范围再全量训练。IsObjectiveDeterministic设为false因为神经网络训练有随机性,同样的超参数两次结果可能不同。搜完之后results.XAtMinObjective就是最优超参数组合。
我自己的习惯是:先用默认参数跑一遍,看 loss 曲线和预测图,确定是欠拟合还是过拟合,再决定调哪个参数。如果欠拟合,优先加numHiddenUnits和MaxEpochs;如果过拟合,优先加dropoutRate和早停。贝叶斯优化适合在参数范围不确定的时候用,但别指望它能找到“完美”参数,LSTM 的玄学成分比 CNN 大得多,有时候换个随机种子结果就差很多。所以训练完一定要在测试集上验证,别只看验证集 loss。希望帮到你。
本文还有配套的精品资源,点击获取