ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

改进鲸鱼算法优化双向LSTM做时间序列预测的Matlab实战

改进鲸鱼算法优化双向LSTM做时间序列预测的Matlab实战

简介:面向时间序列预测与智能优化算法结合需求的Matlab用户,这套代码提供了改进鲸鱼算法优化双向长短期记忆网络(IWOA-BILSTM)与标准BILSTM的完整对比实验方案。优化范围涵盖迭代次数、隐藏层节点个数、学习率及正则化参数,需在MATLAB 2019及以上版本运行,适合希望在预测模型中引入高效超参数寻优机制的学者与工程师。包内共15个文件,以11个.m脚本为主,涵盖数据预处理、适应度计算、模型训练、结果可视化等环节;另有2个.mat保存训练与仿真数据、1个xlsx承载原始数据集、1个txt说明文件辅助快速上手,整体压缩包仅70KB,轻量且结构清晰。代码质量较高,评价指标包含R2、MAE、MSE、RMSE,并配有对比绘图脚本,可直接替换自己的数据进行时间序列预测实验。目前已有199人学习,便于快速验证IWOA-BILSTM相比BILSTM的预测精度提升效果。

1. 用改进鲸鱼算法优化双向LSTM做时间序列预测,到底在优化什么

半夜调 LSTM 的超参是很多做时间序列预测的人共同的噩梦:隐藏层节点设多少、学习率给多大、L2 正则怎么定,每个参数都像开盲盒,试错一轮就是半小时起步。IWOA-BILSTM 这个名字拆开看就两件事:用改进鲸鱼算法(IWOA)自动搜索双向长短期记忆网络(BiLSTM)的关键超参数,然后用搜索到的最优参数完成时间序列预测。它解决的是 BiLSTM 这类模型“结构很强、参数太玄”的痛点,适合做风速、电力负荷、交通流、股价等单变量或多变量预测的工程师和研究者。本文从原理讲到 Matlab 实现,把滑窗构造、适应度函数、参数映射和最容易翻车的数据细节一次说清。

2. 从 BiLSTM 到 IWOA-BILSTM:双向结构为什么适合时序预测,改进鲸鱼算法改在哪

2.1 单向 LSTM 丢掉了什么:BiLSTM 双向结构处理时序的直觉理解

单向 LSTM 沿时间轴从 t=1 读到 t=T,每个时刻的隐藏状态只携带过去信息。做时间序列预测时直觉上“只该看过去”,但这忽略了一个事实:很多序列的局部模式只有放到整段上下文里才能被识别。举个最直白的例子——负荷曲线每天的早高峰和晚高峰形状相近但幅值不同,单向 LSTM 读到某个上升沿时,它分不清这是早高峰的前半段还是晚高峰的反弹;BiLSTM 让信息同时从两个方向流过序列,前向隐藏状态捕捉“从过去到现在”的趋势,后向隐藏状态捕捉“从现在回看过去”的上下文,最后把两个方向的输出拼接起来作为这一时刻的表征。

有人会问:预测未来时根本没有未来数据,后向 LSTM 的信息从哪来?这是初学者最常卡住的地方。训练时 BiLSTM 读的是滑窗构造的一段历史序列,后向传播处理的是这段窗口内从右向左的信息流,并不是真正的未来值。预测时同样输入历史窗口,网络用学到的双向模式做特征提取,不存在标签泄露。理解这一点,后面设计实验时才不会把测试集混进来。

2.2 鲸鱼算法 WOA 的编码方式:超参数映射成鲸鱼位置

鲸鱼优化算法模拟座头鲸的泡泡网捕食行为,核心是三种位置更新策略:收缩包围、螺旋气泡网、随机搜索。在 IWOA-BILSTM 里,每条鲸鱼的位置向量就是一组待优化的超参数组合。我一般把位置向量设计成四维,分别对应 BiLSTM 的隐藏层节点数、初始学习率、L2 正则化系数和 MiniBatchSize。连续变量直接映射,整数变量取 round,学习率和 L2 这类跨数量级的参数做对数映射。

位置维度参数含义取值范围映射方式
x1隐藏层节点数[16, 128]round 取整
x2初始学习率[1e-4, 1e-2]10 的幂次对数映射
x3L2 正则化系数[1e-8, 1e-2]10 的幂次对数映射
x4MiniBatchSize[16, 128]round 取整

每次迭代时,用当前鲸鱼位置解码出超参数,训练一个 BiLSTM,把验证集上的 RMSE 作为适应度值。适应度越小,说明这组超参数越好。整个流程就是把“调参玄学”变成了一个可迭代的数值优化问题。

2.3 IWOA 的三个改进点:收敛因子、种群初始化和精英变异

标准 WOA 在工程实测里有两个典型毛病:一是前期收敛太快,鲸鱼还没充分探索就扎堆到局部最优附近;二是后期种群多样性丢失,最优解周围没有扰动,跳不出局部坑。IWOA 最常见的改进组合是“非线性收敛因子 + 混沌初始化 + 精英变异”三件套。

收敛因子 a 从线性递减改成余弦型递减,例如 a = 2*cos(π/2 * t/MaxIter),这样前中期探索范围更大,后期才逐步转入精细开发。种群初始化用 Tent 混沌映射代替 rand 均匀分布,原理是混沌序列在 [0,1] 区间内分布更均匀且自相关低,初始鲸鱼位置不容易扎堆。精英变异针对每一代的最优个体,在更新完位置后给它加一个小幅高斯扰动,相当于给当前最优解一个跳出去再试一次的机会。这三个改进都不复杂,但对收敛速度和最终精度的提升,在后面的对比实验里能明显看到。

3. 数据与实验设计:先定基准再谈优化,滑窗和归一化这样搭

3.1 数据集选取与划分:单变量先跑通,多变量再扩展

不管是用公开的风速数据集还是自己采集的负荷数据,第一原则是按时间顺序划分,绝不能随机打乱。随机打乱会让模型“偷看”未来,验证集和测试集指标都会虚高,这是时间序列预测里最隐蔽的翻车点。我一般按 7:1.5:1.5 划分训练集、验证集、测试集,验证集专门用来算适应度,测试集只在最终模型确定后碰一次。

如果做多变量预测,常见的做法是把辅助变量和预测目标按同一时间轴对齐后拼成特征矩阵。例如预测电力负荷,可以把温度、湿度、节假日标记作为附加特征,特征维度从 1 变成 4,但滑窗构造逻辑不变。先用单变量把 IWOA-BILSTM 的流程跑通,再扩多变量,排查问题会容易得多。

3.2 滑窗构造样本:时间步长和预测步长的选择逻辑

滑窗是时间序列预测的数据基础。假设用过去 ts 个时刻预测未来 h 步,那么每个样本是一个 ts 长的窗口,标签是窗口末端后第 h 个时刻的值。决定滑窗参数时,ts 要和数据的周期性对齐,比如小时级电力负荷有 24 小时周期,ts 至少取 24 或 48;日级风速数据可以取 7 或 14。预测步长 h 取 1 时是最简单的单步预测,多步预测则需要递归策略或直接多输出。

下面这个 Matlab 函数生成滑窗样本,步长 step 控制窗口之间的间隔:

function [XCell, YVec] = makeSlidingWindows(data, ts, h, step) % data: 归一化后的单变量序列,行向量 % ts: 时间步长(每个样本用过去多少个时刻) % h: 预测步长(预测未来第 h 个时刻) % step: 窗口滑动步长 N = length(data); n = floor((N - ts - h + 1) / step); % 样本总数 X = zeros(ts, n); Y = zeros(1, n); for i = 1:n idx = (i - 1) * step + 1; X(:, i) = data(idx : idx + ts - 1)'; % 每列一个样本 Y(i) = data(idx + ts + h - 1); % 对应标签 end % trainNetwork 需要 cell 数组,每个 cell 是 1×ts 的观测 XCell = cell(1, n); for i = 1:n XCell{i} = X(:, i)'; end YVec = Y; end

这段代码的关键在于样本组织和标签对齐。X 的每一列是一个样本,列数 n 是样本数,trainNetwork 要求序列输入是 cell 数组,所以第二个循环把每一列转成行向量塞进 cell。标签 YVec 是 1×n 的行向量,对应每个样本的预测目标。step 参数常被忽略,但它的作用很大:step=1 时窗口高度重叠,样本量最大但信息冗余;step=ts 时窗口完全不重叠,样本量小但独立性强。对 BiLSTM 来说,我通常先选 step=1,再用验证集误差判断是否需要增大。

3.3 对比实验的三条基线:BILSTM、WOA-BILSTM、IWOA-BILSTM

对比实验的目的不是为了好看,是为了说清楚“改进到底改进在哪”。我一般固定同一份滑窗数据和同一个随机种子,跑三条基线:

模型超参数来源说明
BiLSTM经验手动设置基线模型
WOA-BiLSTM标准鲸鱼算法寻优验证标准 WOA 的效果
IWOA-BiLSTM改进鲸鱼算法寻优最终方案

评估指标用 RMSE、MAE、MAPE、R² 四个,注意全部按反归一化后的原始量纲计算。对比时看两个层面:一是 IWOA 的适应度收敛曲线是否比 WOA 更快、最终值更低;二是三个模型在测试集上的预测误差,确认改进没有过拟合验证集。

4. 在 Matlab 里把 IWOA-BILSTM 跑通:适应度函数、参数映射与网络训练的最小代码

4.1 适应度函数设计:用验证集 RMSE 还是 MAPE

适应度函数是整个 IWOA-BILSTM 的“评价标准”,它决定鲸鱼往哪个方向进化。用 RMSE 时大误差被平方放大,模型会优先压平那些偏离大的点;用 MAPE 时误差被相对化,更关注小数值时刻的预测精度。如果数据里有接近零的值,MAPE 会爆炸,这时 RMSE 更稳。我习惯用验证集 RMSE 作为适应度,因为它对异常点敏感、量纲明确,且和最终测试指标的衡量口径一致。

注意一个细节:适应度函数内部要训练完整的 BiLSTM,这是整个算法最耗时的部分。因此迭代次数和种群规模必须克制,常见做法是种群 N=5、迭代 MaxIter=10,每轮要训练 5×10=50 个网络,单变量小数据集在 CPU 上大约需要十几分钟,GPU 会快很多。工程验证阶段可以先 N=3、MaxIter=5,确认流程没问题再加大。

4.2 鲸鱼位置与 BiLSTM 超参数的映射:核心解码代码

鲸鱼位置是 [0,1] 区间内的归一化连续值,解码成真实超参数需要按维度做映射。下面这段代码实现了 4.2 节表格里的映射逻辑:

function params = decodePosition(pos, lb, ub) % pos: 1×4 鲸鱼位置向量,取值 [0,1] % lb: 1×4 各维度下界 % ub: 1×4 各维度上界 posReal = lb + pos .* (ub - lb); % 线性映射到实际范围 hiddenUnits = round(posReal(1)); % 隐藏层节点数,取整 learnRate = 10^(posReal(2)); % 学习率,对数映射 l2Reg = 10^(posReal(3)); % L2 正则化系数,对数映射 miniBatch = round(posReal(4)); % MiniBatchSize,取整 params = [hiddenUnits, learnRate, l2Reg, miniBatch]; end

这里的关键是第 4 和第 5 行的对数映射。学习率 1e-4 到 1e-2 看起来跨度只有两个数量级,但如果做线性映射,随机初始化很容易落在 0.005 附近,导致大量候选点集中在高学习率区间。对数映射把 1e-4、1e-3、1e-2 变成 -4、-3、-2 的均匀分布,候选点在各数量级上分布更合理。整数参数在 round 前可能取到 16 到 128 之间的任意浮点数,取整后直接传给 bilstmLayer。

4.3 网络训练与验证:trainNetwork 的两个关键设置

解码出超参数后,就要构造网络结构并训练。核心代码块如下:

function fitness = iwoaFitness(params, XTrain, YTrain, XVal, YVal) hiddenUnits = params(1); learnRate = params(2); l2Reg = params(3); miniBatch = params(4); % 网络结构:单变量输入 -> BiLSTM -> 全连接 -> 回归输出 layers = [ sequenceInputLayer(1) bilstmLayer(hiddenUnits, 'OutputMode', 'last') fullyConnectedLayer(1) regressionLayer]; % 训练选项:静默训练,关闭训练进度图 options = trainingOptions('adam', ... 'InitialLearnRate', learnRate, ... 'L2Regularization', l2Reg, ... 'MiniBatchSize', miniBatch, ... 'MaxEpochs', 50, ... 'Shuffle', 'every-epoch', ... 'Verbose', 0, ... 'Plots', 'none'); % 训练并预测验证集 net = trainNetwork(XTrain, YTrain, layers, options); YPred = predict(net, XVal); % 反归一化后计算 RMSE(需要传入 PS 结构体,这里省略) rmse = sqrt(mean((YPred - YVal).^2)); fitness = rmse; end

这段代码有两个必须注意的设置。第一个是'OutputMode', 'last':BiLSTM 层的输出模式决定网络输出的是每个时刻的隐藏状态还是最后一个时刻的状态,回归预测必须用'last',否则全连接层收到的是整个时间维度的输出序列,维度对不上。第二个是'Verbose', 0和'Plots', 'none':适应度函数要被调用几十次,开着训练进度条和损失曲线会拖慢速度,而且控制台刷屏严重影响调试心情。

trainNetwork 要求 XTrain 是 cell 数组,且每个 cell 内的矩阵维度是 特征数×时间步长。单变量场景每个 cell 是 1×ts,与我们 3.2 节生成的格式完全对应。这里的'MaxEpochs', 50是适应度评估时的训练轮数,不是最终模型的训练轮数;最终模型确定后要用更多轮次重新训练一次。

5. IWOA-BILSTM 避坑指南:容易翻车的五个参数与数据细节

5.1 适应度函数只在训练集上算,测试集指标断崖式下跌

现象:迭代过程中适应度一路下降,最终 IWOA-BILSTM 在验证集上 RMSE 很低,但换到测试集后误差突然放大 30% 以上。原因:适应度函数只用训练集拟合、验证集评估,但超参数搜索过程本身就在验证集上做了隐式“训练”,搜索轮次越多,验证集的信息被吸收得越多,最终模型在验证集上必然偏乐观。解决:一是适应度统一用验证集,测试集严格留在最后;二是观察训练集和验证集误差的差距,如果差距持续拉大,说明隐藏层节点数或学习率组合偏向过拟合,优先调大 L2 正则化系数。

5.2 归一化泄漏:mapminmax 把整个数据集的最大最小值都算进去了

现象:训练时模型表现极好,测试集却出现幅度错位,预测曲线整体偏低或偏高。原因:归一化时用mapminmax(data)对全量数据求了最大最小值,相当于模型在训练阶段“见过”了测试集的数值范围,这是标准的时间序列数据泄漏。解决:先用训练集单独拟合归一化参数 PS,再对验证集和测试集应用同一组参数。Matlab 里[~, PS] = mapminmax(trainData, 0, 1)得到映射参数,之后用mapminmax('apply', valData, PS)处理验证集,测试集同理。

5.3 滑窗步长设成 1 导致样本高度重叠,模型“背答案”

现象:训练误差非常低,但测试误差不稳定,同一组参数换一次随机种子波动很大。原因:step=1 时相邻样本只差一个时刻,重叠率高达 (ts-1)/ts,训练集里大量高度相似的样本让模型记住了窗口的具体模式而非一般规律。解决:先把 step 设为 ts/2,让样本重叠率降到 50% 左右,再观察验证集误差变化。如果数据量充足,step=ts 完全不重叠的效果更稳。这个参数值得专门做一组小实验,它不参与鲸鱼寻优,但对最终性能影响极大。

5.4 隐藏层节点数与鲸鱼种群规模乱设,一次实验跑半小时

现象:鲸鱼位置解码出的隐藏层节点数偏大,加上种群规模 N=10、迭代次数 MaxIter=30,一次完整实验跑了快两个小时才出结果。原因:BiLSTM 的参数量随隐藏层节点数平方级增长,训练时间也随之暴涨;而 IWOA 的每一轮都要重新训练网络,总耗时等于 种群×迭代×单次训练耗时。解决:把隐藏层节点数上限从 256 压到 128,种群 N=5、MaxIter=10,先跑通再放规模。如果单次训练超过 2 分钟,就缩减样本量或训练轮数,优先保证能快速迭代。

5.5 earlyStopping 开着,结果每次实验预测曲线风格都不一样

现象:同样一组超参数,连续运行两次得到的测试集 RMSE 相差很大,预测曲线一条平滑一条抖动,像抽奖。原因:trainingOptions 里没有关闭验证集早停,而 BiLSTM 权重初始化是随机的,不同运行在早停时停在了不同的局部最优。解决:适应度评估阶段固定随机种子,Matlab 里每次训练前调用rng(0),再用'ValidationPatience', Inf或直接不设验证集早停参数,确保同一组超参数的评估结果可复现。这个细节决定了你最后写对比表格时,数字是真实差距还是随机噪声。

6. 误差分布与消融实验:用这两个手段验证 IWOA-BILSTM 不是 Paper 模型

跑完 IWOA-BILSTM,光看测试集 RMSE 数字还不够,我习惯用两个手段确认模型是真的变好了,而不是换了个随机种子碰运气。

第一个手段是画预测误差分布直方图。把测试集每个样本的预测误差(预测值减真实值)收集起来,用 Matlab 的 histogram 画出来,看它的均值和形状。正常的模型误差应近似零均值的正态分布,峰值在 0 附近,拖尾不明显。如果直方图明显偏移,说明模型存在系统性偏差,比如归一化泄漏或滑窗标签错位;如果拖尾很长,说明少数极端时刻预测很差,需要检查这些时刻是不是数据里的异常尖峰。这一步比单一指标更能暴露问题。

第二个手段是严格做消融实验。对比表至少包含三行:BiLSTM 基线、WOA-BILSTM、IWOA-BILSTM,三者在同一份数据、同一种滑窗参数、同一个随机种子下运行。我习惯在跑对比前先钉死随机种子,确保三组实验的权重初始化基准一致,不然你比出来的差距可能是运气差。消融结果一般会看到两个现象:WOA-BILSTM 相比 BiLSTM 提升有限,说明标准鲸鱼算法找到了还不错的参数但未必跳出了局部坑;IWOA-BILSTM 进一步提升,且多次重复实验的方差更小,说明改进算法不仅找到了更优解,而且更稳定。

最后再提一个工程小技巧:预测结果画图时,把真实值、BiLSTM 预测、IWOA-BILSTM 预测画在同一张图里,再把两者误差的上下包络带画成阴影区域。这样能一眼看出改进主要发生在哪些时段——是尖峰时刻拟合得更好,还是整体趋势更贴近。做时序预测的都知道,一张清晰的对比图比一堆指标数字更能让方案被接受。这些验证手段看起来不起眼,但正是它们把 IWOA-BILSTM 从“代码能跑”推进到“结果可信”。我自己的习惯是每次都先把消融表跑出来再调参,跑完再回头优化数据预处理,希望帮到你。

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

RELATED READING

延伸阅读

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