简介:面向时间序列预测需求,提供Matlab实现的鲸鱼算法优化双向长短期记忆网络(WOA-BiLSTM)完整程序,适合正在研究时序预测或需要构建深度学习预测模型的学生、科研人员与工程师,可应用于电力负荷、交通流量、气象要素等典型随时间变化数据的回归预测场景。压缩包共10个文件,以5个功能明确的m源程序为核心,分别承担数据读取、预处理、WOA优化、BiLSTM训练及性能评估等任务,另附4个png预测结果图和1个xlsx示例数据文件,整体仅135KB,结构精炼适合快速理解与二次开发。已有471人学习下载。程序自动优化学习率、隐藏层节点数和正则化参数,用户可直接运行主程序复现完整预测流程,得到预测曲线与误差对比图,并可通过xlsx文件替换为自身业务数据,便于在真实场景中验证WOA-BiLSTM的优化效果,代码结构清晰,注释到位,适合教学演示与论文实验参考。
1. WOA-BiLSTM核心机制与建模路径
时间序列预测里,LSTM 的隐藏层维度、学习率、批大小这些超参数直接决定拟合上限。手动试凑耗时,网格搜索在高维参数空间里几乎不可行。鲸鱼算法(WOA)是一种模拟座头鲸气泡网捕食的元启发式算法,用种群迭代逼近目标函数的极值,天然适合给 BiLSTM 当超参数搜索器。BiLSTM 在普通 LSTM 基础上增加反向时序传播,能同时读取历史与未来上下文,对负荷、流量、股价这类前后关联强的序列更友好。本文面向的是已经跑通单步 LSTM、想换 BiLSTM 并引入自动调参的工程人员,重点讲清楚 WOA 与 BiLSTM 的接口设计,以及从数据到预测曲线的完整闭环。
2. 数据预处理与数据集划分
2.1 读入与归一化
时间序列预测的第一步不是搭网络,而是把数据整理成监督学习格式。假设你有 1000 个时间步的单变量序列data,要预测未来 1 步,需要构造X: (样本数, 特征数, 序列长度)与Y: (样本数, 输出维度)。Matlab 的lstmLayer默认接收不同格式,看你是用trainNetwork还是dlnetwork。传统做法常用 cell 数组,每个样本是一个numFeatures × numTimeSteps的矩阵。
自分倾向于dlnetwork路线,因为后面要接 WOA 的循环迭代,每次都要重建网络或更新参数,dlnetwork更适合程序化操作。读入 CSV 后先做归一化,常见做法是 mapminmax 或 zscore。对时间序列,推荐用 mapminmax 把数据压到 [-1,1],因为 BiLSTM 默认激活函数是 tanh,输入落在这个区间能缓解梯度问题。
data = readmatrix('series.csv'); data = data(:); dataNorm = mapminmax(data', -1, 1); dataNorm = dataNorm';mapminmax的输出是行向量,所以读入后转置两次,保持列向量结构。注意readmatrix在 Matlab R2023a 之后对混合类型支持更好,如果你用的版本较老,建议改用csvread或load看数据格式。归一化参数ps保存下来,预测结束后要用mapminmax('reverse', ...)还原,不要只保存归一化后的数据。
2.2 构造训练集与测试集
监督学习格式的滑窗构造,核心是lag值。预测步长futureSteps=1,则样本X{i}是1 × lag的向量,Y(i)是滞后一步的真值。注意要避免数据泄露,测试集的归一化参数必须来自训练集,不能对全序列统一归一化。滑动窗口构造用循环即可,数据量到十万级时再考虑buffer函数加速。
function [X, Y, ps] = createSlidingWindow(data, lag, trainRatio) [dataNorm, ps] = mapminmax(data', -1, 1); dataNorm = dataNorm'; numSamples = length(dataNorm) - lag; X = cell(numSamples, 1); Y = zeros(numSamples, 1); for i = 1:numSamples X{i} = dataNorm(i : i + lag - 1)'; Y(i) = dataNorm(i + lag); end trainNum = round(numSamples * trainRatio); XTrain = X(1:trainNum); YTrain = Y(1:trainNum); XTest = X(trainNum+1:end); YTest = Y(trainNum+1:end); end代码里X{i}是lag×1的列向量,对应sequenceInputLayer的InputSize=lag。trainRatio=0.8表示前 80% 的样本训练,后 20% 测试。注意这里的时间顺序没有打乱,乱序会破坏序列依赖。
| 参数 | 推荐值 | 说明 |
|---|---|---|
| lag | 24 | 周期序列取一个周期长度,日数据取 7/30 |
| trainRatio | 0.8 | 验证集可从训练集尾部切 10% |
| futureSteps | 1 | 单步预测,多步见第 5 章 |
3. 构建BiLSTM网络与WOA参数绑定
3.1 BiLSTM 层配置
BiLSTM 在 Matlab 中以bilstmLayer形式提供,底层是两个方向的 LSTM 拼接。关键参数有三个:NumHiddenUnits、OutputMode和InputSize。OutputMode='last'只输出最后时间步的隐藏状态,适合回归到单值预测;OutputMode='sequence'保留完整序列输出,适合序列到序列任务。
网络结构从输入到输出依次是:sequenceInputLayer→bilstmLayer→fullyConnectedLayer(1)→regressionLayer。这里不额外加 dropout,因为 WOA 搜索时需要快速迭代,dropout 会拖慢训练速度,且小数据集上收益不确定。
layers = [ sequenceInputLayer(1) bilstmLayer(50, 'OutputMode', 'last') fullyConnectedLayer(1) regressionLayer ];sequenceInputLayer(1)表示每个时间步只有 1 个特征。如果你的数据是多变量,改成特征数即可。bilstmLayer(50, ...)中的 50 是隐藏单元数,实际运行时参数量是单向 LSTM 的两倍,训练时间也近似翻倍,这在调参预算里要提前算进去。
3.2 WOA 的种群编码与边界处理
WOA 中每个个体代表一组 BiLSTM 超参数。常见的编码有三维:学习率lr、隐藏单元数numHiddenUnits、批大小miniBatchSize。隐藏单元数和批大小需取整,学习率通常用10^a指数缩放。
function net = buildBiLSTM(x) lr = x(1); numHiddenUnits = round(x(2)); miniBatchSize = round(x(3)); layers = [ sequenceInputLayer(1) bilstmLayer(numHiddenUnits, 'OutputMode', 'last') fullyConnectedLayer(1) regressionLayer ]; options = trainingOptions('adam', ... 'InitialLearnRate', lr, ... 'MiniBatchSize', miniBatchSize, ... 'MaxEpochs', 50, ... 'Verbose', false); net = trainNetwork(XTrainCell, YTrain, layers, options); endtrainNetwork要求输入是 cell 数组,YTrain是列向量。这里把网络构建和训练包在一个函数里,WOA 每次迭代只需调用buildBiLSTM并传入位置向量。MaxEpochs=50是折中值——WOA 种群如果是 10 个个体、30 次迭代,就要训练 300 次网络,每轮 epoch 太多会耗尽时间预算。
3.3 WOA 的包围、气泡网与搜索猎物
WOA 的三类位置更新公式分别是包围猎物、气泡网攻击和随机搜索。Matlab 实现的核心是rand和系数向量A、C的计算。a从 2 线性递减到 0,A的绝对值决定当前是搜索还是开发。
function [positions, bestPos, bestScore] = woa(fitnessFunc, dim, lb, ub, SearchAgents, MaxIter) positions = rand(SearchAgents, dim) .* (ub - lb) + lb; [bestScore, bestIdx] = min(arrayfun(@(i) fitnessFunc(positions(i,:)), 1:SearchAgents)); bestPos = positions(bestIdx, :); for t = 1:MaxIter a = 2 - t * (2 / MaxIter); for i = 1:SearchAgents r1 = rand; r2 = rand; A = 2 * a * r1 - a; C = 2 * r2; p = rand; if p < 0.5 if abs(A) < 1 D = abs(C * bestPos - positions(i,:)); positions(i,:) = bestPos - A * D; else randIdx = randi(SearchAgents); D = abs(C * positions(randIdx,:) - positions(i,:)); positions(i,:) = positions(randIdx,:) - A * D; end else D = abs(bestPos - positions(i,:)); positions(i,:) = D .* exp(1) .* cos(2 * pi * rand()) + bestPos; end positions(i,:) = max(min(positions(i,:), ub), lb); end scores = arrayfun(@(i) fitnessFunc(positions(i,:)), 1:SearchAgents); [minScore, minIdx] = min(scores); if minScore < bestScore bestScore = minScore; bestPos = positions(minIdx, :); end end end参数说明:dim=3对应当前编码维度;lb和ub是三维向量,学习率的范围一般设为[1e-4, 1e-2],隐藏单元数[10, 100],批大小[16, 128]。WOA 默认是用连续值更新,边界裁剪在每次更新后执行,防止越界导致trainNetwork报错。这里的exp(1)是气泡网更新中的常数,与 WOA 原文的b=1对应。
4. 迭代寻优与训练预测
4.1 损失函数与搜索策略的选择
适应度函数是 WOA 的指挥棒。时间序列回归常见选择是测试集 RMSE 或 MAPE。用验证集 RMSE 作为适应度,避免了以训练集误差为目标的过拟合反馈。注意,每次 WOA 评估都要完整训练一次 BiLSTM,代价不低。如果数据集规模大,建议把MaxEpochs降到 30,或者用早停机制。
fitnessFunc = @(x) evaluateRMSE(x, XTrain, YTrain, XVal, YVal);其中evaluateRMSE内部调用buildBiLSTM训练模型,然后对验证集做预测。这里要防止一个问题:trainNetwork每次初始化的随机权重不一样,即使同一组超参数,两次评估得分也有波动。WOA 的搜索机制会放大这种噪声,常见的缓解做法是固定随机种子rng(42),或者在每次评估时训练多次取平均。
4.2 训练过程与预测还原
得到bestPos之后,用该组超参数重新训练最终模型,并对测试集做预测。预测时mapminmax('reverse', ...)调用前要确保用训练集的ps结构。
finalNet = buildBiLSTM(bestPos); YPredNorm = predict(finalNet, XTest, 'MiniBatchSize', 32); YPred = mapminmax('reverse', YPredNorm', ps); YTestReal = mapminmax('reverse', YTest', ps);'MiniBatchSize'是predict的选项,不设置时取训练时的默认值。预测结果YPredNorm的维度可能是(numObservations, numResponses)或(numTimeSteps, numObservations)的转置形式,取决于OutputMode和输入是 cell 还是数值数组,建议用size(YPredNorm)打印确认再反归一化。reverse函数要求输入行向量,第二行代码里有两个转置,逻辑是:YPredNorm若是列向量,转成行向量传给mapminmax,输出再转回列向量。
4.3 多迭代轮的稳定性检查
跑完一轮 WOA 后,只拿bestScore评估并不可靠。很多实践者用「多轮 WOA 取结果对比」来判断是否收敛。常见做法是:用不同随机种子跑三轮 WOA,比较三组bestPos的差异。如果学习率位置都在 1e-3 附近,隐藏单元都在 60~80,说明搜索结果是可复现的;如果三轮结果差异极大,多半是适应度函数噪声太大,优先检查数据归一化与训练集划分是否一致。
5. 从单步到多步:决策级扩展与模型诊断
5.1 多步预测的两种范式
单步预测跑通后,业务上往往需要未来 12 小时或 7 天的值。常见做法有两种:递归多步预测和直接多步预测。递归方式把上一步的预测值作为下一步的输入,误差会累积,适合短期延伸;直接方式则为每个预测步训练一个模型,参数多但误差被隔离。中间路线是 seq2seq,用编码器 BiLSTM 读入历史窗口,解码器输出多步预测向量。Matlab 的dlnetwork支持自定义训练循环,做 seq2seq 时自由度更高。
5.2 评估指标与误差回检
多步预测不能只看单步 RMSE。分步位查看 MAPE 的衰减趋势是判断模型边界的好方法,如果第 5 步的误差比第 1 步翻了一倍,说明模型对长程依赖特征的提取不足,这时应增大numHiddenUnits而非加深层数。另一种方式是计算预测误差的自相关系数,若误差在滞后 1 处存在明显相关,说明时序信息仍有残留,可以尝试把输入窗口加长。
| 指标 | 公式 | 适用场景 |
|---|---|---|
| RMSE | sqrt(mean((y - yhat).^2)) | 量纲敏感,配合业务单位解读 |
| MAPE | mean(abs((y - yhat)./y)) * 100 | 适合无零值、等比重业务 |
| R2 | 1 - SSres / SStot | 比较不同特征的模型时更直观 |
5.3 WOA 参数与训练成本的权衡
WOA 有三组参数直接影响开销:SearchAgents控制并行评估的粒度,MaxIter控制搜索预算。建议从SearchAgents=6, MaxIter=20起步,跑通后再逐步加到10×30。批大小miniBatchSize对收敛速度的影响比学习率直接:批大小翻倍,训练时间近似减半但梯度噪声增大;学习率过大容易在训练后期震荡,过小则迭代不足。另外,Matlab 的并行计算工具箱可以并行评估种群适应度,用parfor代替for就能让多个个体同时训练网络,代码改动只有一行,但提速效果显著。
5.4 工程化输出建议
最终交付时,把归一化参数ps、WOA 最优位置bestPos、模型结构信息一起保存到.mat文件中,便于后续加载。同时打印每个测试点的预测值与真实值到 CSV,方便与基线模型对比。若用codex之类的代码辅助工具处理 Matlab 脚本,注意需要显式指定trainNetwork是否在循环内调用,IDE 自动补全有时会隐藏模型变量被重复赋值的问题。
本文还有配套的精品资源,点击获取