ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

MATLAB LSTM股票预测:数据预处理、状态管理与滚动推理实战

MATLAB LSTM股票预测:数据预处理、状态管理与滚动推理实战 简介本资源是一份面向深度学习初学者与金融量化实践者的MATLAB实战项目聚焦利用长短期记忆网络LSTM解决股票价格这一典型非线性时间序列预测问题。资源提供开箱即用的完整实现含3个核心M文件主程序main2.m、评估脚本MSE_RMSE_MBE_MAE.m与R_2.m、1份原始股票价格数据Excel文件及1份预测结果CSV输出共5个文件总大小仅114KB轻量易部署。所有代码基于MATLAB编写无需额外工具箱依赖数据预处理、LSTM建模、训练验证与多指标评估MAE、RMSE、R²等流程完备可直接运行复现。目前已有1062人学习下载适合希望快速掌握LSTM在金融时序中落地方法、理解模型输入构造与评价体系的读者尤其适合作为课程设计、毕业设计或量化入门的参考范例。1. 为什么用 MATLAB 做 LSTM 股票价格预测不是“跑个模型就完事”很多人第一次在 MATLAB 里调用lstmLayer喂进几列开盘价、收盘价、成交量跑出一条和真实股价贴合度还不错的曲线就以为“深度学习预测股票成功了”。但实际落地时这条曲线往往在回测中剧烈漂移在实盘信号生成阶段频繁误触发买卖点——问题不在于 LSTM 结构本身而在于 MATLAB 环境下时间序列建模的数据预处理链路、状态管理机制、滚动预测范式这三处隐性门槛。本篇聚焦真实工程场景如何用 MATLAB R2021b 及以上版本含 Deep Learning Toolbox构建可复现、可回测、可部署为.mex或 Simulink 模块的 LSTM 股票价格预测流程。内容覆盖从原始 CSV 数据清洗、多步滞后特征构造、序列截断与填充策略到训练时隐藏状态显式传递、验证集动态重置、测试阶段滚动前向推理的完整闭环。适合已掌握基础神经网络概念、正用 MATLAB 做量化研究或教学实验的工程师与研究生。2. 构建可复现的 LSTM 时间序列输入管道从 raw CSV 到 normalized sequenceMATLAB 的 LSTM 实现对输入格式极为敏感它要求输入是三维数组XTrain{1×N}其中每个XTrain{i}是featureDim × timeStep矩阵且所有样本的timeStep必须一致。但原始股票数据是变长、带缺失值、量纲差异大的一维时间序列。直接readtable(stock.csv)后堆叠会导致训练失败或梯度爆炸。必须建立标准化的数据流。2.1 原始数据清洗与结构化对齐股票数据常含停牌日、复权不一致、分钟级高频噪声等问题。以 A 股日线为例典型清洗步骤如下% 读取并按日期排序确保时间连续 T readtable(sh600519_daily.csv); T.Date datetime(T.Date, InputFormat, yyyy-MM-dd); T sortrows(T, Date); % 删除含 NaN 的整行非插值避免未来信息泄露 T rmmissing(T, Rows, min); % 强制补齐交易日用前向填充标记停牌不伪造价格 allDates (min(T.Date):caldays(1):max(T.Date)); T_full table(Size, [height(allDates), width(T)], VariableTypes, repmat({double}, 1, width(T))); T_full.Date allDates; [~, loc] ismember(T_full.Date, T.Date); validIdx loc 0; T_full(validIdx, :) T(loc(validIdx), :); % 对关键字段做前向填充仅限停牌日并添加停牌标识列 T_full.IsSuspended ~validIdx; T_full.Open fillmissing(T_full.Open, previous); T_full.High fillmissing(T_full.High, previous); T_full.Low fillmissing(T_full.Low, previous); T_full.Close fillmissing(T_full.Close, previous); T_full.Volume fillmissing(T_full.Volume, previous);提示fillmissing(..., previous)是唯一安全的填充方式。任何线性插值、均值填充都会引入未来信息导致回测结果虚高。停牌标识IsSuspended后续将作为特征参与建模而非简单丢弃。2.2 多变量滞后特征工程构造符合 LSTM 输入维度的张量LSTM 预测效果高度依赖特征表达能力。仅用Close单变量是基线但实际需融合价格动量、波动率、量能关系。MATLAB 中推荐用lagmatrixhorzcat构造固定窗口特征% 提取数值型特征剔除日期、字符串列 numVars T_full{:, {Open,High,Low,Close,Volume,IsSuspended}}; % 构造 10 日窗口每步包含当前及前9日的 Open/High/Low/Close/Volume/IsSuspended windowSize 10; X_raw lagmatrix(numVars, 0:windowSize-1); % 输出 size: (N, 6*10) % 重塑为 featureDim × timeStep × sampleNum 格式MATLAB LSTM 要求 % 先转置为 [6, 10, N-9]再 permute 为 [6, 10, N-9] X_3D reshape(X_raw(windowSize:end, :), [], windowSize, height(X_raw)-windowSize1); X_3D permute(X_3D, [1, 2, 3]); % - [6, 10, N-9] % 标准化按特征维度独立归一化非全局 mu mean(X_3D, [1,3]); % [1,10,N-9] → 按第1维特征和第3维样本求均值 → 得 [6,1,1] sigma std(X_3D, 0, [1,3]); X_norm (X_3D - mu) ./ sigma; % 目标变量预测未来第1日的 Close即 label 为 t1 的 Close Y_raw T_full.Close(windowSize1:end); Y_norm (Y_raw(:) - mu(4)) ./ sigma(4); % 第4维是 Close 特征表特征维度与物理含义映射表供调试核对X_norm(i,j,k)中i对应原始变量归一化参数来源1Openmu(1),sigma(1)2Highmu(2),sigma(2)3Lowmu(3),sigma(3)4Closemu(4),sigma(4)5Volumemu(5),sigma(5)6IsSuspendedmu(6),sigma(6)注意lagmatrix输出列顺序严格按输入列顺序展开。numVars中第4列是Close因此Y_norm必须使用mu(4)和sigma(4)还原否则反归一化后价格数量级错误。2.3 序列截断与填充解决变长样本与 batch 训练冲突X_norm是[6,10,N-9]但实际训练需划分为训练集/验证集/测试集且每个 mini-batch 内所有序列长度必须一致。MATLAB 不支持动态 padding必须手动对齐% 划分索引按时间连续性非随机打乱 totalLen size(X_norm, 3); trainEnd floor(0.7 * totalLen); valEnd floor(0.85 * totalLen); XTrain X_norm(:, :, 1:trainEnd); XVal X_norm(:, :, trainEnd1:valEnd); XTest X_norm(:, :, valEnd1:end); YTrain Y_norm(1:trainEnd); YVal Y_norm(trainEnd1:valEnd); YTest Y_norm(valEnd1:end); % 检查各集长度是否为 batch_size 整数倍否则 trainNetwork 报错 batchSize 32; if mod(size(XTrain,3), batchSize) ~ 0 dropNum mod(size(XTrain,3), batchSize); XTrain XTrain(:, :, 1:end-dropNum); YTrain YTrain(1:end-dropNum); end此步骤确保trainNetwork不因 batch 内长度不一致而中断。若原始数据长度无法整除宁可舍弃末尾少量样本也不用 zero-padding —— 股票序列末尾信息价值更高。3. 定义与训练 LSTM 网络状态管理、损失函数与早停策略MATLAB 的trainNetwork默认将 LSTM 层视为无状态stateless即每次 forward 时隐藏状态重置为零。这对分类任务可行但对时间序列预测是灾难性的模型无法维持跨 batch 的长期记忆。必须显式启用状态保持并设计匹配的训练循环。3.1 网络架构定义为何必须包含sequenceFoldingLayer与sequenceUnfoldingLayer标准 LSTM 回归输出是单值但股票预测常需多步如预测未来3日。MATLAB 要求输出层与标签维度严格匹配。若预测单步则regressionLayer即可若预测多步必须用sequenceFoldingLayer将序列展平再接全连接层最后用sequenceUnfoldingLayer还原% 单步预测最常用信号生成稳定 layers [ sequenceInputLayer(6, Normalization, zscore, Name, input) lstmLayer(128, OutputMode, last, Name, lstm) dropoutLayer(0.3, Name, drop) fullyConnectedLayer(1, Name, fc) regressionLayer(Name, output) ]; % 多步预测如预测未来3日 Close→ 必须用 folding/unfolding layers_multi [ sequenceInputLayer(6, Normalization, zscore, Name, input) lstmLayer(128, OutputMode, sequence, Name, lstm) dropoutLayer(0.3, Name, drop) sequenceFoldingLayer(Name, fold) fullyConnectedLayer(3, Name, fc) % 输出3维 sequenceUnfoldingLayer(Name, unfold) regressionLayer(Name, output) ];关键区别OutputMode,last输出最后一个时间步的隐藏状态标量适合单步预测OutputMode,sequence输出整个序列的隐藏状态128×timeStep配合sequenceFoldingLayer才能实现多步输出。忽略此区别会导致维度不匹配错误。3.2 训练选项配置状态重置时机与早停判据默认trainingOptions在每个 epoch 开始重置 LSTM 状态这破坏了时间连续性。必须设置ResetStateEvery并配合自定义训练循环options trainingOptions(adam, ... InitialLearnRate, 0.005, ... MaxEpochs, 100, ... MiniBatchSize, 32, ... Shuffle, never, ... % 时间序列严禁打乱 Verbose, true, ... Plots, training-progress, ... ValidationData, {XVal, YVal}, ... ValidationFrequency, 10, ... ValidationPatience, 15, ... % 连续15次验证 loss 不降则停 OutputNetwork, best-validation-loss, ... ResetStateEvery, never); % 核心禁用自动重置 % 但 never 仅保证 batch 内状态延续跨 epoch 仍需手动管理 % 因此实际采用自定义训练循环见下方自定义训练循环显式控制隐藏状态生命周期% 初始化网络与状态 net trainNetwork(XTrain, YTrain, layers, options); [net, state] predictAndUpdateState(net, XTrain(:, :, 1:1)); % 预热状态 % 主训练循环替代 trainNetwork for epoch 1:options.MaxEpochs for mb 1:batchSize:size(XTrain,3) idx mb:min(mbbatchSize-1, size(XTrain,3)); X_mb XTrain(:, :, idx); Y_mb YTrain(idx); % 前向传播使用上一轮保留的状态 [YPred, state] predictAndUpdateState(net, X_mb, InitialState, state); % 计算损失MSE loss mean((YPred - Y_mb).^2); % 反向传播MATLAB 自动处理 gradients dlgradient(loss, net.Learnables); net adamupdate(net, gradients, ..., epoch); end % 每 epoch 后用验证集评估并决定是否保存最佳模型 YValPred predict(net, XVal); valLoss mean((YValPred - YVal).^2); if valLoss bestValLoss bestNet net; bestValLoss valLoss; patience 0; else patience patience 1; end if patience options.ValidationPatience; break; end end逻辑说明predictAndUpdateState返回更新后的state该state包含 LSTM 的hhidden和ccell状态。将其传入下一次predictAndUpdateState即实现跨 batch 的状态延续。这是 MATLAB 中实现真正“有状态 LSTM”的唯一可靠方式。3.3 损失函数选择为何不用 MAPE 而坚持 MSE股票价格绝对值范围大如贵州茅台 vs 低价股MAPEMean Absolute Percentage Error在低价股上放大误差在高价股上压缩误差导致训练目标偏移。MSE 对异常值敏感但可通过以下方式缓解在YTrain中剔除单日涨跌幅 10% 的样本abs(diff(log(Close))) 0.1使用 Huber loss 替代 MSEMATLAB R2022a 支持layers [..., huberRegressionLayer(Delta, 0.5, Name, huber)];Huber loss 在误差小于Delta时为二次损失大于时为线性损失兼顾鲁棒性与可导性。4. 滚动预测与实盘信号生成避免未来信息泄露的关键操作训练完成的模型在测试集上predict(net, XTest)得到的是“一次性全量预测”但这在实盘中毫无意义——你只能知道今天该买还是卖而不是知道过去30天每天的预测值。必须实现滚动窗口rolling window预测且每次只用已知历史数据推演下一步。4.1 滚动预测函数状态初始化与单步前向function [yPred, updatedState] rollingPredict(net, X_hist, initialState) % X_hist: [6, 10, 1] —— 最近10日完整特征含今日 % initialState: 从训练结束时保存的 final state % 输出: yPred (1x1), updatedState (用于下次预测) % Step 1: 用历史序列初始化状态非零初始化 [~, state] predictAndUpdateState(net, X_hist, InitialState, initialState); % Step 2: 取最后一步输出即预测明日 Close yPred predict(net, X_hist(:, end, :)); % 注意X_hist(:,end,:) 是 [6,1] % Step 3: 更新状态供下次调用实际只需 cell hidden此处简化 updatedState state; end滚动执行示例模拟实盘每日运行% 加载训练好的 net 和 finalState load(bestLSTMNet.mat); % 包含 net 和 finalState % 初始化滚动窗口取测试集前10日作为初始历史 X_window XTest(:, :, 1:10); yTrue YTest(11:end); % 真实值从第11日开始 yPredRolling zeros(length(yTrue), 1); % 滚动预测循环 for i 1:length(yTrue) % 当前窗口X_window 是 [6,10,1]对应第i到i9日 [yPredRolling(i), finalState] rollingPredict(net, X_window, finalState); % 更新窗口移除最老一日加入新一日但新一日特征未知 % → 实际中X_window(:,1:9,:) 保留X_window(:,10,:) 用今日真实值填充 % 假设我们已有今日 Open/High/Low/Volume/IsSuspendedClose 待预测 % 则 X_window(:,10,:) [Open_t; High_t; Low_t; Close_t; Volume_t; IsSuspended_t]; % 此处仅示意用真实值填充实盘中 Close_t 是未知的故需另建特征 if i length(yTrue) % 用真实值更新窗口模拟已知今日数据 X_window cat(3, X_window(:, 2:end, :), XTest(:, :, 10i)); end end核心约束滚动预测中X_window的第10列最新日的Close字段在预测当日是未知的因此不能直接用XTest填充。正确做法是构造特征时不依赖当日Close如改用Open、High-Low、Volume/MA5等滞后可得指标或将Close替换为昨日预测值形成自回归闭环需评估误差累积效应4.2 信号生成规则从预测值到买卖指令单纯比较yPred yTrue无意义。需定义可交易规则% 定义阈值预测涨幅超过1.5%且置信区间足够宽用预测标准差估计 threshold 0.015; yPred_actual yPredRolling * sigma(4) mu(4); % 反归一化 yTrue_actual YTest(11:end) * sigma(4) mu(4); % 生成信号1买入-1卖出0持有 signal zeros(size(yPred_actual)); for i 2:length(yPred_actual) % 仅当预测值比前一日真实值高 threshold且连续2日满足 if (yPred_actual(i) yTrue_actual(i-1) * (1threshold)) ... (yPred_actual(i-1) yTrue_actual(i-2) * (1threshold)) signal(i) 1; elseif (yPred_actual(i) yTrue_actual(i-1) * (1-threshold)) ... (yPred_actual(i-1) yTrue_actual(i-2) * (1-threshold)) signal(i) -1; end end % 计算累计收益假设每次满仓无手续费 position cumsum(signal); % 持仓状态0,1,-1 ret diff([yTrue_actual(1); yTrue_actual]) ./ yTrue_actual(1:end-1); strategyRet position(1:end-1) .* ret;此规则避免单日噪声触发引入趋势确认机制。position向量可直接接入交易系统 API。5. 模型诊断与过拟合识别用 MATLAB 内置工具定位失效环节训练完成后plotTrainingProgress只显示 loss 曲线无法判断是数据问题、结构问题还是优化问题。必须结合 MATLAB 的analyzeNetwork、plotconfusion虽为分类设计但可改造及残差分析。5.1 残差时序图识别系统性偏差模式YTestPred predict(bestNet, XTest); residuals YTestPred - YTest; % 绘制残差 vs 时间检测趋势、周期性 figure; plot(residuals, LineWidth, 0.8); hold on; yline(0, --k, Reference); xlabel(Trading Day); ylabel(Residual (Normalized)); title(LSTM Prediction Residuals Over Time); % 添加自相关检验Ljung-Box [h,p] lbqtest(residuals, Lags, 20); fprintf(Ljung-Box test p-value: %.4f\n, p); % p 0.05 表明残差存在显著自相关 → 模型未捕获时间依赖若p 0.05说明 LSTM 未充分学习序列模式需增加 LSTM 层数、增大 hiddenSize 或延长windowSize。5.2 特征重要性分析用排列重要性Permutation Importance量化输入贡献MATLAB 无内置排列重要性但可用predict 扰动实现function imp permutationImportance(net, X, Y, featureIdx) % featureIdx: 要扰动的特征索引1~6 X_perturbed X; for i 1:size(X,3) idx randperm(size(X,2), round(0.1*size(X,2))); % 随机扰动10%时间步 X_perturbed(featureIdx, idx, i) X_perturbed(featureIdx, idx, i) 0.1*randn(size(idx)); end Y_pred_pert predict(net, X_perturbed); imp mean((Y - Y_pred_pert).^2) - mean((Y - predict(net,X)).^2); end % 计算各特征重要性 imp zeros(6,1); for f 1:6 imp(f) permutationImportance(bestNet, XTest, YTest, f); end bar(imp); xlabel(Feature Index); ylabel(Importance Score);若IsSuspended第6维重要性最高说明模型主要靠停牌信息区分波动而非价格动力学——需检查数据清洗逻辑或增加技术指标。5.3 预测区间估计用 Dropout Monte Carlo 获取不确定性训练时开启 dropout预测时多次前向NumPredictions50统计输出分布% 训练时已含 dropoutLayer预测时启用 dropout YPred_MC zeros(50, length(YTest)); for i 1:50 YPred_MC(i,:) predict(bestNet, XTest, EnableDropout, true); end predMean mean(YPred_MC, 1); predStd std(YPred_MC, 0, 1); % 绘制 95% 置信区间 figure; plot(predMean, b, LineWidth, 1.2); hold on; fill([1:length(YTest), length(YTest):-1:1], ... [predMean-1.96*predStd, flip(predMean1.96*predStd)], ... b, FaceAlpha, 0.2); plot(YTest, r--, LineWidth, 1); legend(Predicted Mean, 95% CI, True Value);若predStd在牛市中显著缩小、熊市中急剧扩大说明模型隐含学习了波动率聚类特性是良性信号若predStd与残差绝对值强相关则表明模型不确定性建模有效。最后一句技术要点在predict调用中显式指定EnableDropout,true是启用 Monte Carlo Dropout 的唯一方式默认false会关闭所有 dropout 层导致确定性输出。本文还有配套的精品资源点击获取
返回列表