ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

鲸鱼算法WOA优化GRU超参数:Matlab回归预测实战

鲸鱼算法WOA优化GRU超参数:Matlab回归预测实战 简介这套资源是针对多输入单输出数据回归预测场景的WOA-GRU完整Matlab实现以鲸鱼算法优化门控循环单元的超参数适合电气、经济、工程等领域的预测建模学习者与科研人员。程序已在Matlab 2020及以上版本环境设计包含主优化程序、网络构建、适应度计算与结果指标计算等模块并配有输入、输出两组Excel样例数据便于直接替换成自己的数据集进行实验。压缩包共6个文件主要由4个.m脚本和2个.xlsx数据文件组成整体仅108KB轻量易部署。目前已有203人学习下载可用于快速复现鲸鱼算法与GRU结合的回归预测流程辅助论文实验或课程设计。1. 鲸鱼算法优化 GRU 回归预测到底在优化什么鲸鱼算法WOA优化 GRU 做多输入单输出回归预测针对的是 GRU 超参数难拍板的问题隐藏单元数、学习率、批大小三组值直接决定拟合效果手动试要半天。WOA 把这 3 个数当作搜索空间里的坐标用迭代找使验证集 RMSE 最小的组合在 Matlab 的 Deep Learning Toolbox 下和 trainNetwork 天然衔接。对刚接触深度学习的工程师这套流程比网格搜索直观对熟手也能把调参成本压到一两个小时以内。下文按数据处理、适应度函数、WOA 主循环和评价指标的顺序给出可直接替换数据的完整代码。2. WOA 与 GRU 的原理和边界更新公式、超参数与数据形态先讲清原理再动手写代码。WOA 模拟座头鲸的捕食行为把待优化超参看成搜索空间里的坐标点GRU 决定这个坐标点对应的模型效果多输入单输出则决定样本怎么摆成 GRU 能吃的张量。三者理解到位后面调参出错时才能快速定位。2.1 WOA 的三种位置更新机制包围、螺旋、随机搜索常见 WOA 实现里每一轮迭代先算系数a 2 - 2*t/maxIter然后按每条鲸鱼的随机数p分流。更新规则用一张表就能看清随机数条件位置更新公式行为p 0.5且abs(A) 1Xnew Xbest - A.*abs(C.*Xbest - X)包围猎物向当前最优解收缩p 0.5且abs(A) 1Xnew Xrand - A.*abs(C.*Xrand - X)随机搜索跳出局部最优p 0.5Xnew D.*exp(b*l).*cos(2*pi*l) Xbest其中D abs(Xbest - X)螺旋气泡网更新其中A 2*a*r1 - aC 2*r2r1、r2是[0,1]均匀随机数。a从 2 线性降到 0所以迭代前期abs(A)大于 1 的概率高种群倾向于大范围探索后期abs(A)小于 1 的概率高逐步收敛到最优解附近。C的作用是随机放大或缩小目标距离破坏搜索的对称性避免所有鲸鱼走同一条路径。WOA 真正值得用的地方在于它没有梯度信息要求也不要求目标函数连续可导。GRU 的训练误差和超参数之间是黑盒关系用 WOA 去搜恰好比梯度类优化更合适。但它也有代价一次适应度评估就是一次完整的trainNetwork调用种群规模和迭代次数不能像测试函数那样随便设到几百否则计算时间吃不消。后面章节会把参数控制在 10 条鲸鱼、20 轮迭代的实用范围。2.2 GRU 回归中值得用 WOA 去调的超参数GRU 相比 LSTM 少了输出门只有更新门和重置门参数数量大约少四分之一训练更快在中等规模回归数据上通常不会明显差于 LSTM。Matlab 里用gruLayer(hiddenUnits, OutputMode, last)即可构建最后一个时间步的输出接一个fullyConnectedLayer(1)就能做单目标回归。影响回归效果的主要超参数有三个隐藏单元数、初始学习率、批大小。它们各自影响面不同范围也应按变量类型区分。超参数建议搜索范围变量类型对模型的影响hiddenUnits1 ~ 100整数决定网络容量过高易过拟合learningRate0.001 ~ 0.1连续决定收敛速度与稳定性miniBatchSize16 ~ 128整数影响梯度稳定性和训练时长maxEpochs固定 200整数保证训练充分不建议作为搜索变量hiddenUnits和miniBatchSize是整数而 WOA 的位置更新产生的是连续值适应度函数里必须先round再传给trainNetwork否则报错。learningRate用对数尺度更能反映实际效果许多实现会把搜索范围取成指数形式但为保持代码直观下面仍用线性边界实际使用时可以用logspace(-3, -1, 10)把离散候选值喂给 WOA。还有一个边界条件要注意如果你的 Matlab 版本较旧、没有gruLayer把这一行替换成lstmLayer即可WOA 部分完全不用动。2.3 多输入单输出数据如何整理成 GRU 序列样本多输入单输出回归的数据原始形态是N x (F1)矩阵每行一个样本前 F 列是特征最后一列是目标。GRU 吃的是序列数据所以要把平面样本变成滑动窗口序列用前lookback行预测下一行目标。Matlab 的trainNetwork要求序列预测器的输入是元胞数组每个元胞内是一个F x lookback矩阵F 个特征按行排时间步按列排。写一个通用函数来做这件事function [XCell, y] makeSlidingWindow(data, nFeature, lookback) % data: 已归一化样本矩阵前 nFeature 列为输入最后一列为目标 n size(data, 1); nSamples n - lookback; XCell cell(nSamples, 1); y zeros(nSamples, 1); for i 1:nSamples % 每一列是一个时间步每一行是一个特征 XCell{i} data(i:ilookback-1, 1:nFeature); y(i) data(ilookback, end); end end调用参数的含义data是归一化后的完整样本矩阵nFeature是输入特征个数lookback是滑窗长度一般根据数据周期来定比如日数据取 5 或 7月数据取 3 或 12。函数返回的XCell中每个元胞都是一个nFeature x lookback矩阵y是对应的下一时刻目标值。窗口长度太短模型看不到足够的历史依赖太长则样本数量骤减训练效率下降。这里的取舍可以先用验证集误差做一轮粗略扫描再交给 WOA 细调。若特征之间量纲差异大还需在滑窗前先做归一化这个在下一章统一处理。3. Matlab 完整实现数据处理、适应度函数与 WOA 主循环下面这套代码按顺序拼起来就是一个完整的最小工程主脚本加一个数据文件即可运行不需要额外工具箱。流程固定为读数据、归一化、切分、滑窗、定义适应度函数、WOA 迭代、终训、反归一化。3.1 数据读入、归一化与训练/验证/测试划分% 数据格式每行一个样本前 F 列为输入最后一列为目标 data readmatrix(your_data.csv); nFeature size(data, 2) - 1; N size(data, 1); % 归一化特征和目标共享同一映射结构 ps便于反归一化 [Xnorm, psX] mapminmax(data(:, 1:nFeature), 0, 1); [ynorm, psy] mapminmax(data(:, end), 0, 1); Xnorm Xnorm; ynorm ynorm; % 强时间序列数据按顺序切分样本彼此独立时可改成 randperm 随机划分 nTrain floor(N * 0.70); nVal floor(N * 0.15); trainData [Xnorm(1:nTrain, :), ynorm(1:nTrain)]; valData [Xnorm(nTrain1:nTrainnVal, :), ynorm(nTrain1:nTrainnVal)]; testData [Xnorm(nTrainnVal1:end, :), ynorm(nTrainnVal1:end)]; lookback 10; [XTrain, yTrain] makeSlidingWindow(trainData, nFeature, lookback); [XVal, yVal] makeSlidingWindow(valData, nFeature, lookback); [XTest, yTest] makeSlidingWindow(testData, nFeature, lookback);这段代码的三个细节值得解释。第一mapminmax是按行归一化的所以传入前要转置得到psX、psy后再转置回来。第二归一化必须在划分之前完成且所有划分共用一个ps否则测试集的数据分布被独立归一化后与训练集不再对齐。第三时间序列数据不要随机打乱否则用前 10 个时刻预测第 11 个时刻时训练集和测试集之间会发生序列信息串透测试指标会虚高。提示如果数据本身没有时间顺序只是多变量静态回归样本仍可用滑窗把每行构造成一个短序列但此时lookback的意义会弱化更合理的做法是直接用全连接网络或把特征展平后输入 GRU。3.2 适应度函数把超参数翻译成验证集 RMSEWOA 的目标函数直接决定搜索质量。惯用做法是固定 GRU 的层结构和轮数只让隐藏单元数、学习率、批大小三个变量参与搜索。适应度函数写成独立文件gruFitness.m接收一个三维向量param返回验证集 RMSE。function [rmse, net] gruFitness(param, XTrain, yTrain, XVal, yVal) % param [hiddenUnits, learningRate, miniBatchSize] hiddenUnits round(param(1)); lr param(2); miniBatchSize round(param(3)); layers [ sequenceInputLayer(size(XTrain{1}, 1)) gruLayer(hiddenUnits, OutputMode, last) fullyConnectedLayer(1) regressionLayer ]; options trainingOptions(adam, ... MaxEpochs, 200, ... InitialLearnRate, lr, ... MiniBatchSize, miniBatchSize, ... GradientThreshold, 1, ... Verbose, 0, ... Plots, none); net trainNetwork(XTrain, yTrain, layers, options); yPred predict(net, XVal); rmse sqrt(mean((yPred - yVal).^2)); end参数含义hiddenUnits必须round否则gruLayer会因小数输入报错GradientThreshold设为 1 可以抑制梯度爆炸GRU 在样本量小、学习率偏高时经常梯度剧烈震荡这一行能极大减少 NaN 损失的出现MaxEpochs固定在 200 是为了让每组超参都在相同训练预算下比较公平性优先于单次速度。验证集 RMSE 用归一化后的数据计算这样不同超参组合之间的数值可以直接比较最终报告指标时再反归一化回原始尺度。3.3 WOA 主循环、边界约束与收敛曲线记录主循环按 2.1 节的三种更新规则实现。种群规模取 10迭代 20 次即最多 200 次 GRU 训练对千行量级的数据十几分钟内能跑完。rng(2026); % 固定种子保证可复现 nPop 10; maxIter 20; dim 3; lb [1, 0.001, 16]; ub [100, 0.1, 128]; Pop repmat(lb, nPop, 1) rand(nPop, dim) .* repmat(ub - lb, nPop, 1); Fitness zeros(nPop, 1); for i 1:nPop Fitness(i) gruFitness(Pop(i, :), XTrain, yTrain, XVal, yVal); end [bestF, bestIdx] min(Fitness); Xbest Pop(bestIdx, :); curve zeros(maxIter, 1); for t 1:maxIter a 2 - 2 * t / maxIter; % a 从 2 线性递减到 0 for i 1:nPop p rand; r1 rand; r2 rand; A 2 * a * r1 - a; C 2 * r2; if p 0.5 if abs(A) 1 D abs(C .* Xbest - Pop(i, :)); Xnew Xbest - A .* D; else r randi(nPop); D abs(C .* Pop(r, :) - Pop(i, :)); Xnew Pop(r, :) - A .* D; end else b 1; l 2 * rand - 1; D abs(Xbest - Pop(i, :)); Xnew D .* exp(b * l) .* cos(2 * pi * l) Xbest; end Xnew min(max(Xnew, lb), ub); % 越界复位 fnew gruFitness(Xnew, XTrain, yTrain, XVal, yVal); if fnew Fitness(i) Pop(i, :) Xnew; Fitness(i) fnew; end end [bestF, bestIdx] min(Fitness); Xbest Pop(bestIdx, :); curve(t) bestF; fprintf(iter %d, best RMSE %.4f\n, t, bestF); endA的符号决定了鲸鱼靠近还是远离最优解A为正时向正方向移动为负时反向掠过猎物这种来回摆动正是 WOA 能跳出局部极小的原因。C只做放大或缩小不随迭代变化保证了搜索初期的随机性。螺旋更新里的l取[-1, 1]的随机数exp(b*l)让步长呈对数螺旋状变化。需要注意越界复位这里用的是截断到边界如果你预期最优解靠近边界可以改成边界内反射但截断实现简单且对三维超参空间足够稳定。运行结束后Xbest就是你需要的超参组合curve保存每一代最优 RMSE用来画收敛曲线。3.4 最优参数终训与反归一化WOA 搜索得到Xbest后惯例是把训练集和验证集合并用该超参组合再训练一次再在测试集上评估。这样模型能多利用验证集的数据又不至于把测试集提前泄露进训练。layers [ sequenceInputLayer(size(XTrain{1}, 1)) gruLayer(round(Xbest(1)), OutputMode, last) fullyConnectedLayer(1) regressionLayer ]; options trainingOptions(adam, ... MaxEpochs, 200, ... InitialLearnRate, Xbest(2), ... MiniBatchSize, round(Xbest(3)), ... GradientThreshold, 1, ... Verbose, 0, ... Plots, none); XTV vertcat(XTrain, XVal); yTV vertcat(yTrain, yVal); net trainNetwork(XTV, yTV, layers, options); YPred predict(net, XTest); YPred mapminmax(reverse, YPred, psy); yTestRaw mapminmax(reverse, yTest, psy);最后两行最容易出错。predict返回的是列向量mapminmax(reverse, ...)要求行向量输入所以必须先把YPred传入再把结果转置回列向量。如果不做反归一化最终 RMSE 是在[0,1]尺度上的数值和原始数据的量纲对不上写报告时会出现 RMSE 只有 0.03 但 R2 很差的诡异组合。4. 回归预测效果验证指标计算、散点图与对比口径模型训出来只算完成一半另一半是证明它比固定参数 GRU 好。验证分三层数值指标、可视化、对照组设计。三层口径一致结论才立得住。4.1 RMSE、MAE、MAPE、R2 的计算代码function [rmse, mae, mape, r2] regMetrics(yt, yp) rmse sqrt(mean((yt - yp).^2)); mae mean(abs(yt - yp)); mape mean(abs((yt - yp) ./ yt)) * 100; ssres sum((yt - yp).^2); sstot sum((yt - mean(yt)).^2); r2 1 - ssres / sstot; end四个指标按使用频率排RMSE 对特大误差敏感适合衡量预测精度的上下界MAE 更稳健不受个别离群点干扰MAPE 以百分比形式展示误差占比但目标值接近 0 时计算会爆炸数据里有近零值时建议改用 SMAER2 反映模型对目标方差的解释程度取值可能为负说明模型比直接用均值预测还差。调用时传入反归一化后的yTestRaw和YPred得到的就是原始尺度的评估结果。4.2 WOA 收敛曲线与预测散点图怎么看收敛曲线直接暴露搜索过程的问题。理想形状是前 5 代快速下降中段缓慢波动末期几乎水平。如果曲线整体平滑但下降缓慢说明最大迭代数不够或者种群多样性不足可把nPop从 10 提到 15如果曲线上下剧烈震荡始终不收敛多半是learningRate搜索范围上界太高把ub(2)从 0.1 降到 0.05 再跑。画图代码很简单figure; plot(curve, LineWidth, 1.5); xlabel(迭代次数); ylabel(验证集 RMSE); title(WOA 收敛曲线); figure; plot(yTestRaw, YPred, .); hold on; plot([min(yTestRaw), max(yTestRaw)], [min(yTestRaw), max(yTestRaw)], r--); xlabel(真实值); ylabel(预测值);散点图里点越贴近对角线说明预测与真实值一致如果点在对角线下方成片出现说明系统偏差可能是目标归一化时没有覆盖全部取值范围如果点呈放射状离散则模型欠拟合优先加大隐藏单元数而不是增加迭代轮数。红色对角线的两端范围直接用数据的最小最大值避免坐标轴自动缩放过窄导致视觉上高估拟合效果。4.3 WOA-GRU 与固定参数 GRU 的对比口径对比必须有相同的训练数据和测试集否则没有意义。常见做法是拿一组人工经验参数作基线比如 hiddenUnits 取 50、LearningRate 取 0.01、MiniBatchSize 取 32直接训练并在同一测试集上算指标结果列表如下数字只作示例用来演示表格结构。方法hiddenUnitslearningRateminiBatchSize测试 RMSER2固定参数 GRU500.01320.04830.891WOA-GRU360.004640.03170.942WOA-GRU 的优势集中在隐藏单元数这个维度GRU 的容量对数据规模很敏感手动设 50 在样本量只有几百时明显偏大而在验证集 RMSE 引导下WOA 有机会收敛到 30 到 40 的更优区间。必须承认这种改进不一定总出现。如果你的基线参数本来就调得很好WOA 的收益可能只有零点几个百分点这时更值得关注的是它能否稳定复现而不是单次精度。所以对比时长建议至少跑 3 次取中位数再下结论。5. 让 WOA-GRU 更稳定的三个不起眼的坑WOA-GRU 的坑大多不在算法本身而在工程细节。三个最常见的问题每一个都足以让结果无法复现。5.1 整数超参数要在适应度函数里 roundXbest是连续向量gruLayer不接受隐藏单元数带小数。更隐蔽的问题是如果只在外层roundWOA 内部比较适应度时用的仍是连续值位置两个不同的连续位置可能都 round 到同一个整数导致适应度相同搜索失去区分度。解决方式是只在适应度函数入口处round边界本身就设成整数。miniBatchSize同理但要注意它不能超过训练样本数否则trainNetwork会直接报错建议上限取样本数的三分之一。5.2 归一化与反归一化要成对出现测试集必须使用训练集同款的mapminmax映射不能单独对测试集再构造一套ps。更常见的失误是对YPred做了反归一化却忘了yTest反归一化导致 RMSE 数据在同尺度上但实际是错位的。验证方法很简单把最终 RMSE 和测试集目标的标准差对比如果 RMSE 大于标准差说明模型基本没学到东西如果 RMSE 比标准差小一个数量级以上先怀疑是不是归一化尺度搞混了。5.3 随机初始化带来的重复性问题GRU 权重初始化和trainNetwork内部的批次顺序都有随机性同样一组超参跑两次RMSE 会有微小差异。WOA 在选择最优解时依赖这种带噪声的适应度可能出现 A 组合真实效果略差但因运气好被选中。惯用做法是同一组最优超参下用不同随机种子重复训练三次取测试集指标的中位数作为最终结果testRMSE zeros(3, 1); for k 1:3 rng(k); net trainNetwork(XTV, yTV, layers, options); yPred predict(net, XTest); yPred mapminmax(reverse, yPred, psy); testRMSE(k) sqrt(mean((yPred - yTestRaw).^2)); end median(testRMSE)最后一条实战经验是缩短搜索成本WOA 搜索阶段把MaxEpochs从 200 降到 60适应度评估一次只要原来不到三分之一的时间搜索出的参数位置通常变化不大得到最优组合后再用 200 轮终训配合GradientThreshold和固定种子整体调参时间可以压缩到原来的四成左右。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

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