鲸鱼算法WOA优化GRU超参数:Matlab回归预测实战
2026/9/12 22:21:04 网站建设 项目流程

简介:这套资源是针对多输入单输出数据回归预测场景的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.5abs(A) < 1Xnew = Xbest - A.*abs(C.*Xbest - X)包围猎物,向当前最优解收缩
p < 0.5abs(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*r2r1r2[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整数保证训练充分,不建议作为搜索变量

hiddenUnitsminiBatchSize是整数,而 WOA 的位置更新产生的是连续值,适应度函数里必须先round再传给trainNetwork,否则报错。learningRate用对数尺度更能反映实际效果,许多实现会把搜索范围取成指数形式,但为保持代码直观,下面仍用线性边界,实际使用时可以用logspace(-3, -1, 10)把离散候选值喂给 WOA。还有一个边界条件要注意:如果你的 Matlab 版本较旧、没有gruLayer,把这一行替换成lstmLayer即可,WOA 部分完全不用动。

2.3 多输入单输出数据如何整理成 GRU 序列样本

多输入单输出回归的数据原始形态是N x (F+1)矩阵,每行一个样本,前 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:i+lookback-1, 1:nFeature)'; y(i) = data(i+lookback, 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(nTrain+1:nTrain+nVal, :), ynorm(nTrain+1:nTrain+nVal)]; testData = [Xnorm(nTrain+nVal+1:end, :), ynorm(nTrain+nVal+1:end)]; lookback = 10; [XTrain, yTrain] = makeSlidingWindow(trainData, nFeature, lookback); [XVal, yVal] = makeSlidingWindow(valData, nFeature, lookback); [XTest, yTest] = makeSlidingWindow(testData, nFeature, lookback);

这段代码的三个细节值得解释。第一,mapminmax是按行归一化的,所以传入前要转置,得到psXpsy后再转置回来。第二,归一化必须在划分之前完成,且所有划分共用一个ps,否则测试集的数据分布被独立归一化后,与训练集不再对齐。第三,时间序列数据不要随机打乱,否则用前 10 个时刻预测第 11 个时刻时,训练集和测试集之间会发生序列信息串透,测试指标会虚高。

提示:如果数据本身没有时间顺序,只是多变量静态回归样本,仍可用滑窗把每行构造成一个短序列,但此时lookback的意义会弱化,更合理的做法是直接用全连接网络或把特征展平后输入 GRU。

3.2 适应度函数:把超参数翻译成验证集 RMSE

WOA 的目标函数直接决定搜索质量。惯用做法是固定 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); end

A的符号决定了鲸鱼靠近还是远离最优解: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 时计算会爆炸,数据里有近零值时建议改用 SMAE;R2 反映模型对目标方差的解释程度,取值可能为负,说明模型比直接用均值预测还差。调用时传入反归一化后的yTestRawYPred,得到的就是原始尺度的评估结果。

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.891
WOA-GRU360.004640.03170.942

WOA-GRU 的优势集中在隐藏单元数这个维度:GRU 的容量对数据规模很敏感,手动设 50 在样本量只有几百时明显偏大,而在验证集 RMSE 引导下,WOA 有机会收敛到 30 到 40 的更优区间。必须承认,这种改进不一定总出现。如果你的基线参数本来就调得很好,WOA 的收益可能只有零点几个百分点,这时更值得关注的是它能否稳定复现,而不是单次精度。所以对比时长建议至少跑 3 次取中位数,再下结论。

5. 让 WOA-GRU 更稳定的三个不起眼的坑

WOA-GRU 的坑大多不在算法本身,而在工程细节。三个最常见的问题,每一个都足以让结果无法复现。

5.1 整数超参数要在适应度函数里 round

Xbest是连续向量,gruLayer不接受隐藏单元数带小数。更隐蔽的问题是,如果只在外层round,WOA 内部比较适应度时用的仍是连续值位置,两个不同的连续位置可能都 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和固定种子,整体调参时间可以压缩到原来的四成左右。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询