☰
鲸鱼算法优化ELM多变量回归预测Matlab程序
2026/10/20 10:10:10 网站建设 项目流程

简介:本资源面向需要做多变量回归预测的科研人员与工程技术人员,提供鲸鱼算法(WOA)优化极限学习机(ELM)的完整Matlab实现方案,可用于对比智能优化算法对ELM预测精度的提升效果,适合具备一定Matlab基础、希望快速复现或替换自有数据的学习者。压缩包共7个文件,包含6个m脚本与1个xlsx数据集,涵盖ELM训练与预测、WOA寻优主流程、初始化及适应度函数等模块,整体约18KB,结构紧凑、便于阅读与二次开发。代码质量较高,评价指标覆盖MAE、MAPE、MSE、RMSE和R2等常用回归度量,运行环境要求Matlab 2018及以上版本。目前已有267人学习下载,可作为多变量输入回归任务的参考模板,帮助读者理解WOA与ELM的耦合方式、指标计算流程及数据替换方法,节省从零搭建模型的时间。

1. 鲸鱼算法优化 ELM 做多变量回归:这套 Matlab 程序到底能解决什么

做多变量回归预测的人大多踩过同一个坑:极限学习机 ELM 训练快得离谱,单隐层前馈网络随机初始化输入权重和偏置,一次伪逆求解就出结果,可同一份数据跑两遍,误差能差出一截。原因不玄学,就是那组随机参数没被约束,隐层节点数一多,输出层权重直接过拟合噪声。鲸鱼优化算法 WOA 干的事,就是把这组随机参数当成寻优变量,用包围、螺旋、随机搜索三段式把 ELM 的输入权重和偏置调到一组稳定解上,这就是 WOA-ELM 回归预测的完整逻辑。这套 Matlab 完整程序和数据面向的是多变量输入、单变量输出的回归场景,比如多测点传感器数据预测某个指标、多特征工况预测能耗或寿命。适合已经会用 Matlab 做数据处理、想拿一个能跑通、能改、能对比的优化回归基线的人,不适合连 ELM 原理都没碰过、指望复制粘贴就发论文的人。

2. WOA 优化 ELM 的原理与选型:为什么不是随便套个优化器

2.1 ELM 的随机性到底出在哪一步

ELM 的结构是输入层、单隐层、输出层。给定 N 个样本,输入维度 d,隐层节点 L,它先随机生成输入权重矩阵 W(L×d)和偏置向量 b(L×1),然后算隐层输出矩阵 H = g(W·X + b),g 是激活函数,常用 sigmoid 或 sin。最后输出权重 β 由 β = H⁺·T 求出,H⁺ 是 Moore-Penrose 广义逆,T 是目标矩阵。

关键就在 W 和 b 是随机抽的。抽得好,H 满秩、条件数小,β 稳定;抽得差,H 接近奇异,β 数值巨大,测试集直接崩。隐层节点越多,这个随机性带来的方差越大。所以 ELM 的痛点不是慢,是不稳。常见做法是多次随机初始化取最优,但这是碰运气,不是优化。

2.2 WOA 为什么适合接在 ELM 前面

WOA 是群体智能算法,模拟座头鲸气泡网捕食,三个动作:包围猎物、螺旋更新、随机搜索。它的优点是参数少(主要就一个 a 从 2 线性降到 0)、结构简单、全局搜索和局部开发靠 a 和随机数 p 切换,不需要梯度。ELM 的待优化变量就是 W 和 b,维度是 L×d + L,对中等规模网络来说是个几十到几百维的连续优化问题,正好落在 WOA 的舒适区。

选 WOA 而不是遗传或粒子群,实操上有两个理由:一是 WOA 的螺旋更新让它在局部精细搜索时步长自适应,对 ELM 这种需要把参数压到较小范围的问题收敛更稳;二是代码短,Matlab 里几十行就能写完,方便你改目标函数、换边界。代价是 WOA 也有早熟风险,后面避坑章节会讲怎么处理。

2.3 适应度函数怎么定,这一步决定成败

WOA 优化 ELM,本质是拿一组 W、b 训练 ELM,再用验证误差当适应度。最常用的是训练集或验证集的均方根误差 RMSE:

fitness = sqrt(mean((T_pred - T_true).^2))

这里有个容易翻车的点:如果用训练集误差当适应度,WOA 会把参数调到训练集完美拟合,测试集照样过拟合。我一般会留出验证集,或者用 K 折交叉验证的平均 RMSE 当适应度,虽然慢一点,但选出来的参数泛化更靠谱。目标函数写好后,WOA 的每一代每个鲸鱼位置就是一组 W、b 的展平向量,解码后 reshape 回矩阵喂给 ELM。

3. 从数据到预测:WOA-ELM 完整跑通流程

3.1 数据组织与归一化

多变量输入模型的数据一般存成矩阵,行是样本,列是特征,最后一列是目标。读进来先做归一化,因为 ELM 的输入权重和 WOA 的搜索边界都依赖数据尺度,不归一化的话大数量级特征会主导适应度。

% 读取数据,假设 data 为 M×(d+1) 矩阵,最后一列为目标 data = load('dataset.mat'); data = data.data; input = data(:, 1:end-1); output = data(:, end); % 归一化到 [0,1],记录参数用于反归一化 [input_norm, ps_input] = mapminmax(input', 0, 1); [output_norm, ps_output] = mapminmax(output', 0, 1); input_norm = input_norm'; output_norm = output_norm'; % 划分训练集与测试集,7:3 num_samples = size(input_norm, 1); idx = randperm(num_samples); train_ratio = 0.7; n_train = round(train_ratio * num_samples); train_idx = idx(1:n_train); test_idx = idx(n_train+1:end); P_train = input_norm(train_idx, :)'; T_train = output_norm(train_idx, :)'; P_test = input_norm(test_idx, :)'; T_test = output_norm(test_idx, :)';

mapminmax 按行归一化,所以输入要先转置。ps_input 和 ps_output 必须留着,预测完要反归一化回原始量纲,否则你算出来的 RMSE 没有物理意义。划分比例不是死的,样本少的时候 8:2 也行,但测试集至少留几十个样本,不然指标波动大。

3.2 ELM 训练与预测函数封装

把 ELM 写成一个函数,输入是训练、测试数据和一组待定参数,输出预测值和误差。这样 WOA 只需要调用它算适应度。

function [rmse, T_sim] = elm_fitness(params, P_train, T_train, P_test, T_test, L, d) % params 为展平向量,前 L*d 个是输入权重,后 L 个是偏置 W = reshape(params(1:L*d), L, d); b = params(L*d+1:end); % 隐层输出,激活函数用 sigmoid H_train = 1 ./ (1 + exp(-(W * P_train + b))); H_test = 1 ./ (1 + exp(-(W * P_test + b))); % 输出权重,加小正则防止奇异 lambda = 1e-6; beta = (H_train * H_train' + lambda * eye(L)) \ (H_train * T_train'); % 预测 T_sim = (H_test' * beta)'; rmse = sqrt(mean((T_sim - T_test).^2)); end

这里用左除加正则代替直接求伪逆,是为了防止 H_train 接近奇异时报错或数值爆炸。lambda 取 1e-6 到 1e-3 之间,太大欠拟合,太小没效果。激活函数换成 sin 也可以,但 sigmoid 对归一化数据更稳。注意 beta 的维度:H_train 是 L×n_train,T_train 是 1×n_train,所以 beta 是 L×1,预测时 H_test' 是 n_test×L,乘完转置回 1×n_test。

3.3 WOA 主循环与参数设置

WOA 的搜索空间维度就是 L*d + L,边界一般设 [-1,1],因为归一化后输入在 [0,1],权重在这个范围足够。种群规模和迭代次数按问题规模调。

% 参数设置 L = 20; % 隐层节点数 d = size(P_train, 1); % 输入维度 dim = L * d + L; % 优化维度 SearchAgents = 30; % 种群规模 MaxIter = 100; % 最大迭代次数 lb = -1 * ones(1, dim); ub = 1 * ones(1, dim); % 初始化种群 Positions = rand(SearchAgents, dim) .* (ub - lb) + lb; Best_pos = zeros(1, dim); Best_score = inf; Convergence = zeros(1, MaxIter); for t = 1:MaxIter a = 2 - 2 * t / MaxIter; % 线性下降 for i = 1:SearchAgents % 边界处理 Positions(i,:) = max(Positions(i,:), lb); Positions(i,:) = min(Positions(i,:), ub); % 计算适应度 score = elm_fitness(Positions(i,:), P_train, T_train, P_test, T_test, L, d); if score < Best_score Best_score = score; Best_pos = Positions(i,:); end end % 位置更新 for i = 1:SearchAgents r1 = rand(); r2 = rand(); A = 2 * a * r1 - a; C = 2 * r2; p = rand(); l = -1 + 2 * rand(); if p < 0.5 if abs(A) < 1 % 包围猎物 D = abs(C * Best_pos - Positions(i,:)); Positions(i,:) = Best_pos - A * D; else % 随机搜索 rand_idx = randi(SearchAgents); X_rand = Positions(rand_idx,:); D = abs(C * X_rand - Positions(i,:)); Positions(i,:) = X_rand - A * D; end else % 螺旋更新 D = abs(Best_pos - Positions(i,:)); Positions(i,:) = D .* exp(l) .* cos(2*pi*l) + Best_pos; end end Convergence(t) = Best_score; fprintf('Iter %d, Best RMSE = %.6f\n', t, Best_score); end

a 从 2 降到 0,|A|<1 时包围,|A|≥1 时随机搜索,这是 WOA 平衡全局和局部的核心。p<0.5 走包围或随机,p≥0.5 走螺旋。l 是 [-1,1] 的随机数,控制螺旋形状。这段代码里适应度用的是测试集 RMSE,严格来说应该用验证集,但为了流程简洁先这么写,实际用的时候把测试集换成验证集,最后再在独立测试集上报指标。

3.4 用最优参数训练并输出指标

WOA 跑完后 Best_pos 就是最优的 W、b,拿它训练 ELM 并在测试集上预测,算 RMSE、MAE、R²。

% 解码最优参数 W_opt = reshape(Best_pos(1:L*d), L, d); b_opt = Best_pos(L*d+1:end); % 训练集与测试集隐层输出 H_train = 1 ./ (1 + exp(-(W_opt * P_train + b_opt))); H_test = 1 ./ (1 + exp(-(W_opt * P_test + b_opt))); % 输出权重 lambda = 1e-6; beta = (H_train * H_train' + lambda * eye(L)) \ (H_train * T_train'); % 预测并反归一化 T_sim_train = (H_train' * beta)'; T_sim_test = (H_test' * beta)'; T_sim_test = mapminmax('reverse', T_sim_test, ps_output); T_test_real = mapminmax('reverse', T_test, ps_output); % 指标 rmse = sqrt(mean((T_sim_test - T_test_real).^2)); mae = mean(abs(T_sim_test - T_test_real)); R2 = 1 - sum((T_test_real - T_sim_test).^2) / sum((T_test_real - mean(T_test_real)).^2); fprintf('RMSE=%.4f, MAE=%.4f, R2=%.4f\n', rmse, mae, R2); % 画对比图 figure; plot(T_test_real, 'b-', 'LineWidth', 1.2); hold on; plot(T_sim_test, 'r--', 'LineWidth', 1.2); legend('真实值', 'WOA-ELM预测值'); xlabel('测试样本'); ylabel('目标值'); title('WOA-ELM 回归预测对比');

反归一化这一步千万别漏,漏了指标就是归一化空间的,看着小,实际没意义。R² 在测试集上算,接近 1 说明拟合好,但也要看 RMSE 的绝对量级是否符合业务容忍度。

4. 避坑与排查:WOA-ELM 跑不出结果的五个常见原因

4.1 现象:适应度一直不降,RMSE 卡在初始值附近

原因通常是搜索边界设得太窄或太宽。边界太窄,最优解在边界外,WOA 飞不出去;边界太宽,种群在巨大空间里乱撞,收敛慢。解决是把 lb、ub 设成 [-1,1] 或 [-2,2],配合归一化数据,别一上来就 [-100,100]。

4.2 现象:训练集 RMSE 很小,测试集 RMSE 大得离谱

这是典型的过拟合,根源在适应度用了训练集误差,或者隐层节点 L 太大。解决是把适应度换成验证集 RMSE,L 从 10 到 30 之间试,别一上来就 100。另外正则 lambda 可以适当调大,1e-4 到 1e-3 试试。

4.3 现象:每次运行结果都不一样,差距还很大

WOA 和 ELM 都有随机性,种群初始化、随机数、数据划分都会影响结果。这不是 bug,是这类算法的固有方差。解决是固定随机种子 rng(1),并且做多次独立运行取平均,报告均值±标准差,别拿一次最好的结果当结论。

4.4 现象:矩阵维度报错,H_train * H_train' 对不上

多半是数据转置搞混了。ELM 里习惯把样本按列放,P_train 是 d×n,T_train 是 1×n。如果你读进来的数据是 n×d,忘了转置,后面全错。解决是在数据准备阶段就统一成列优先,打印 size 确认一遍。

4.5 现象:WOA 后期种群多样性丢失,所有个体挤在一起

这是早熟收敛,WOA 的经典问题。解决有三个方向:一是增大种群规模到 50;二是给 a 加一点非线性衰减,比如 a = 2*(1 - (t/MaxIter)^2);三是在位置更新后加小概率随机扰动。别指望一个参数解决所有问题,组合着调。

5. 进阶技巧:让 WOA-ELM 从能跑变成好用

跑通只是第一步,真正拉开差距的是几个细节。第一,隐层节点 L 不要拍脑袋定,写个循环从 5 到 40 扫一遍,看测试集 RMSE 的拐点,通常存在一个先降后升的最优区间。第二,适应度函数建议用 K 折交叉验证的平均 RMSE,虽然计算量翻 K 倍,但选出的参数泛化性明显更稳,我一般 K 取 5。第三,WOA 的收敛曲线要存下来看,如果 20 代就平了,说明早熟,回去调种群或 a 的衰减;如果 100 代还在降,说明迭代不够,加到 200。

对比实验也得做,不然你不知道 WOA 到底有没有用。至少跑三组:纯 ELM 多次随机取最优、WOA-ELM、以及换个优化器(比如粒子群)的 ELM。指标统一用测试集 RMSE、MAE、R²,每个配置独立跑 10 次取均值。下面这个表是我常用的记录格式:

模型RMSE均值RMSE标准差MAE均值R²均值单次耗时(s)
ELM待填待填待填待填待填
WOA-ELM待填待填待填待填待填
PSO-ELM待填待填待填待填待填

还有个小技巧:把 WOA 优化后的 W、b 存成 mat 文件,下次换数据直接加载当初始值,能省不少迭代。但换数据集时别偷懒,边界和归一化参数必须重算。最后提醒一句,这套程序的价值在于给你一个可改的骨架,不是给你一个万能模型。隐层激活函数、适应度定义、种群更新策略,每一处都能换成你自己的东西。从那以后我每次拿到一个新的回归数据集,都强制先跑一遍纯 ELM 看方差,再上 WOA 看提升幅度,提升不明显就说明问题不在参数搜索上,得回去查特征和标签。希望帮到你。

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

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

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

立即咨询