ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

灰狼算法GWO优化极限学习机ELM:Matlab实现小样本回归预测与参数优化实战

灰狼算法GWO优化极限学习机ELM:Matlab实现小样本回归预测与参数优化实战 简介一套面向Matlab用户的GWO-ELM回归预测完整代码资源。该方法采用灰狼优化算法自动搜索极限学习机的最优权值与阈值代替人工反复调参结合内置Excel数据集实现多输入单输出回归预测既适合风电功率、负荷、房价等典型预测场景也可应用于气象要素预估与工业过程软测量同时便于入门者理解智能优化算法与神经网络的耦合方式。资源包共7个文件包含6个.m脚本和1个.xlsx示例数据主程序、GWO寻优、ELM训练与预测等模块均独立成文件可对照查看完整调用流程直接运行主文件即可复现压缩包约16KB体量精简但流程完整。目前已有468人学习下载。整体上使用者可直接替换数据完成实验也可作为GWO-ELM算法实现的参考模板对课程设计、毕业设计及科研预研都有实际帮助。1. 为什么要用灰狼算法去调极限学习机GWO-ELM到底解决了什么问题做回归预测时手里往往只有几百条样本BP神经网络梯度下降迭代慢稍微加深网络就过拟合而极限学习机ELM把输入权值和阈值随机生成输出权重用最小二乘一次算完训练速度快但“随机”两个字也是它最大的短板——同一个数据集跑十次预测精度能差出一大截调试起来特别像玄学。灰狼优化算法GWO把ELM的随机参数当作狼群要围捕的猎物在正式训练前先搜出一组更稳定的输入权值和阈值再用这组参数去做极限学习机回归预测。这整套GWO-ELM方案在Matlab里用一份完整源码和数据集就能跑通适合风速、负荷、软测量这类小样本回归预测任务。以下从原理拆解到源码复现从参数调整到实战避坑一次讲透。2. 先把极限学习机和灰狼优化算法拆开看它们各自擅长什么、短板在哪2.1 极限学习机前馈网络里那个“不靠梯度下降”的特例极限学习机本质是单隐藏层前馈神经网络但它不走BP那套反向传播。给定输入矩阵X、隐藏层节点数L和激活函数gELM先随机生成输入层到隐藏层的权值矩阵W和偏置向量b然后计算出隐藏层输出矩阵H g(XW b)。输出权重β直接通过求伪逆一步得到β pinv(H) * T其中T是回归目标。这意味着ELM的训练过程没有迭代计算量集中在一次矩阵求逆上速度比BP快一到两个数量级。在小样本回归预测里这是非常诱人的特性几百条样本、几十个隐藏节点Matlab里一条pinv指令就完成了训练。但它的问题同样明显W和b是随机生成的每次运行都会得到不同的H进而得到不同的β和预测结果。运气好时精度很高运气差时测试集误差能翻倍基本没法用于对稳定性有要求的工程场景。这正是GWO介入的动机——把随机生成变成优化搜索。2.2 灰狼优化算法模拟狼群围猎的元启发式搜索凭什么比网格搜索靠谱灰狼优化算法是模仿灰狼群体捕食行为的元启发式算法。狼群里有严格的等级alpha是头狼beta和delta是次一级的狼其余是omega。搜索过程分成包围、追捕、攻击三个阶段每一头狼的位置都代表ELM参数空间里的一个候选解。更新位置的核心公式围绕alpha、beta、delta三头狼展开D |C * Xp(t) - X(t)| X(t1) Xp(t) - A * D A 2a*r1 - a C 2*r2其中Xp是当前最优狼的位置A和C是系数a从2线性递减到0。当|A| 1时狼群扩大搜索范围做全局探索当|A| 1时狼群收缩包围圈做局部开发。这种自适应机制让GWO在几十维的连续参数空间里往往几十次迭代就能找到不错的解比网格搜索效率高得多也不要求目标函数可导。ELM的参数寻优本质上就是连续值优化GWO恰好对口。2.3 GWO-ELM的协作逻辑把ELM的输入权值和阈值当作狼群猎物GWO和ELM结合的标准做法并不复杂GWO要优化的变量就是ELM的输入权值W和隐藏层偏置b一匹狼的位置向量就代表一组完整的W和b。适应度函数则是“用这组参数训练ELM后在验证集上的回归误差”通常取MSE。流程分五步。第一步初始化狼群每个个体随机生成一组W和b。第二步对每匹狼用对应的W、b计算隐藏层H用训练集求出β。第三步在验证集上做回归预测计算MSE作为该狼的适应度。第四步GWO根据alpha、beta、delta的位置更新所有狼循环迭代。第五步迭代结束后取出alpha狼的位置作为最优W和b在训练集上重新训练ELM再用测试集评估最终效果。这里有个容易翻车的细节适应度到底用训练集还是验证集误差。很多公开源码直接拿训练集MSE当适应度测试集最后测一次这种做法在数据噪声小的学术数据集上问题不大但工程上很容易过拟合。我一般会从训练集里留出最后一段做验证集适应度只算验证集误差。这个习惯后面避坑章节还会展开。3. 在Matlab里跑通GWO-ELM回归预测完整源码结构和数据准备3.1 数据怎么组织训练集、测试集、归一化边界一份完整的GWO-ELM项目通常包含四个文件或区块主脚本、GWO优化函数、ELM适应度函数、数据文件。数据一般用data.mat或data.xlsx存放格式建议是X为N行M列的矩阵每行是一个样本每列是一个特征Y为N行1列的回归目标。划分数据时最稳妥的是按时间或顺序划分不打乱。对非时序回归数据可以用randperm随机打乱但要保证训练集和测试集的分布一致。下面是小样本场景下常用的顺序划分方法%% 加载数据 load(data.mat); % 包含X和YX为样本*特征矩阵 %% 划分训练集和测试集 num_train floor(size(X, 1) * 0.8); X_train X(1:num_train, :); Y_train Y(1:num_train, :); X_test X(num_train1:end, :); Y_test Y(num_train1:end, :);归一化是GWO-ELM里最容易出问题的一步。Matlab的mapminmax默认按行处理所以特征矩阵必须转置后再传入得到归一化后的结果再转置回样本×特征的格式。测试集的归一化必须复用训练集生成的ps结构体不能单独重新计算否则测试集的最大最小值会泄漏进归一化过程导致评估结果虚高。%% 归一化训练集单独计算ps测试集复用 [X_train_n, ps_x] mapminmax(X_train, 0, 1); X_train_n X_train_n; X_test_n mapminmax(apply, X_test, ps_x); X_test_n X_test_n; [Y_train_n, ps_y] mapminmax(Y_train, 0, 1); Y_train_n Y_train_n; Y_test_n mapminmax(apply, Y_test, ps_y); Y_test_n Y_test_n;ps_y必须保存下来因为测试集预测完成后要用它做反归一化把预测值还原到原始量纲。这一步忘掉的话RMSE算出来会小得离谱而画出来的预测曲线完全对不上真实值。3.2 核心代码灰狼优化算法的Matlab实现目标函数与位置更新GWO主函数不依赖任何工具箱纯Matlab代码就能写。下面是一个可以直接放进项目的GWO实现输入参数包括种群数量、最大迭代次数、变量维度、上下界以及目标函数句柄function [Alpha_pos, Alpha_score, Convergence_curve] GWO(SearchAgents_no, Max_iter, dim, lb, ub, fobj) % 初始化狼群位置 Positions repmat(lb, SearchAgents_no, 1) ... rand(SearchAgents_no, dim) .* repmat((ub - lb), SearchAgents_no, 1); Alpha_pos zeros(1, dim); Alpha_score inf; Beta_pos zeros(1, dim); Beta_score inf; Delta_pos zeros(1, dim); Delta_score inf; Convergence_curve zeros(1, Max_iter); for t 1:Max_iter a 2 - t * (2 / Max_iter); % a从2线性衰减到0 % 计算每匹狼的适应度更新alpha、beta、delta for i 1:SearchAgents_no % 越界处理超出上界就拉回上界超出下界就拉回下界 Flag4ub Positions(i, :) ub; Flag4lb Positions(i, :) lb; Positions(i, :) (Positions(i, :) .* (~(Flag4ub Flag4lb))) ... ub .* Flag4ub lb .* Flag4lb; fitness fobj(Positions(i, :)); if fitness Alpha_score Alpha_score fitness; Alpha_pos Positions(i, :); elseif fitness Beta_score Beta_score fitness; Beta_pos Positions(i, :); elseif fitness Delta_score Delta_score fitness; Delta_pos Positions(i, :); end end % 更新每匹狼的位置 for i 1:SearchAgents_no for j 1:dim r1 rand(); r2 rand(); A1 2 * a * r1 - a; C1 2 * r2; D_alpha abs(C1 * Alpha_pos(j) - Positions(i, j)); X1 Alpha_pos(j) - A1 * D_alpha; r1 rand(); r2 rand(); A2 2 * a * r1 - a; C2 2 * r2; D_beta abs(C2 * Beta_pos(j) - Positions(i, j)); X2 Beta_pos(j) - A2 * D_beta; r1 rand(); r2 rand(); A3 2 * a * r1 - a; C3 2 * r2; D_delta abs(C3 * Delta_pos(j) - Positions(i, j)); X3 Delta_pos(j) - A3 * D_delta; Positions(i, j) (X1 X2 X3) / 3; end end Convergence_curve(t) Alpha_score; end end这段代码里fobj接收的是行向量形式的参数返回的是适应度值。越界处理放在适应度计算之前属于常规策略。注意位置更新是逐维进行的A和C每次都要重新随机生成这样能保证探索的随机性。收敛曲线数组记录了每一代alpha狼的适应度画出来就能直观判断优化过程是否平稳。3.3 核心代码用优化后的权值阈值训练ELM并做回归预测GWO优化的是ELM的输入权值和偏置所以适应度函数里要做一次“临时训练”。这里给出一个标准的适应度函数params的结构是前面inputnum*hiddennum个元素是输入权值后面hiddennum个元素是隐藏层偏置function fitness fobj_GWO_ELM(params, X_train_n, Y_train_n, X_val_n, Y_val_n, inputnum, hiddennum) % 从params中拆出输入权值和偏置 W params(1 : inputnum * hiddennum); Bias params(inputnum * hiddennum 1 : end); W reshape(W, inputnum, hiddennum); Bias reshape(Bias, 1, hiddennum); % 计算训练集隐藏层输出 H_train X_train_n * W repmat(Bias, size(X_train_n, 1), 1); H_train 1 ./ (1 exp(-H_train)); % sigmoid激活函数 beta pinv(H_train) * Y_train_n; % 验证集预测 H_val X_val_n * W repmat(Bias, size(X_val_n, 1), 1); H_val 1 ./ (1 exp(-H_val)); Y_pred H_val * beta; % 适应度验证集均方误差 fitness mean((Y_val_n - Y_pred).^2); end这个函数的核心在于ELM的隐藏层输出是先由随机或优化后的W和b决定的β永远是用伪逆一步求出来不需要迭代更新。GWO在搜索过程中反复调用这个函数每次调用都会重新计算β。需要说明的是X_val_n和Y_val_n是从训练集里切出来的一段数据没有参与β的计算因此适应度是严格意义上的验证误差。激活函数选sigmoid是因为ELM传统默认配置换成tansig或relu也很容易只需改一行。最终的主脚本把上述函数串起来%% 从训练集中切出验证集 num_val floor(size(X_train_n, 1) * 0.15); X_val_n X_train_n(end-num_val1:end, :); Y_val_n Y_train_n(end-num_val1:end, :); X_train_fit X_train_n(1:end-num_val, :); Y_train_fit Y_train_n(1:end-num_val, :); %% GWO参数 inputnum size(X_train_n, 2); hiddennum 20; dim inputnum * hiddennum hiddennum; lb -1 * ones(1, dim); ub 1 * ones(1, dim); SearchAgents_no 30; Max_iter 100; %% 调用GWO fobj (x) fobj_GWO_ELM(x, X_train_fit, Y_train_fit, X_val_n, Y_val_n, inputnum, hiddennum); [Alpha_pos, Alpha_score, Curve] GWO(SearchAgents_no, Max_iter, dim, lb, ub, fobj); %% 用最优参数重新训练ELM并预测测试集 W_best reshape(Alpha_pos(1 : inputnum*hiddennum), inputnum, hiddennum); Bias_best reshape(Alpha_pos(inputnum*hiddennum1 : end), 1, hiddennum); H_train X_train_n * W_best repmat(Bias_best, size(X_train_n, 1), 1); H_train 1 ./ (1 exp(-H_train)); beta_best pinv(H_train) * Y_train_n; H_test X_test_n * W_best repmat(Bias_best, size(X_test_n, 1), 1); H_test 1 ./ (1 exp(-H_test)); Y_pred_n H_test * beta_best; Y_pred mapminmax(reverse, Y_pred_n, ps_y); Y_pred Y_pred;注意这里最终训练使用了全部训练集包括之前切出去的验证集部分。这是工程上常见的做法适应度阶段为了调参留出验证集调完参数后用全部数据重新训练让模型充分利用样本信息。测试集从头到尾只出现这一次否则就是数据泄漏。3.4 参数是怎么传的种群数量、迭代次数、上下界设置GWO-ELM的参数分为两类GWO优化参数和ELM结构参数。ELM结构参数里最关键的是hiddennum。hiddennum太小模型容量不足无论怎么优化W和b都达不到较低误差hiddennum太大隐藏层矩阵H可能接近病态pinv计算不稳定。常见做法是用10、20、30、40做一轮粗筛选验证集误差最小的值。GWO参数方面SearchAgents_no和Max_iter直接影响计算量。适应度每次都要算一次pinv训练集样本多时100次迭代乘30匹狼就是3000次伪逆计算Matlab也要跑一阵子。一个可以接受的起步配置是种群30、迭代100。如果收敛曲线在几十代内就完全走平说明不需要那么多迭代如果曲线还在明显下降就到末尾了应增加迭代次数。上下界设置推荐先用[-1, 1]这和ELM输入数据归一化到[0,1]是匹配的。如果你把上下界放大到[-5, 5]搜索空间变大理论上能找到更极端的参数组合但实际中狼群更容易走弯路收敛变慢最终结果反而不如窄区间。权值和偏置的初始化范围内没有必须相同的限制但多数开源实现统一设成同一个区间工程上完全够用。4. GWO-ELM的必调参数和效果验证不只看RMSE还要看稳定性4.1 三个必调参数种群数量、最大迭代次数、搜索边界很多人拿到GWO-ELM源码后只改数据其他参数一律默认然后抱怨结果不稳定。其实GWO算法有几个直接影响效果的旋钮值得逐个摸一遍。首先是种群数量。种群太小比如10匹狼探索能力不足狼群很快被alpha狼带偏迭代十几次就陷入局部最优。种群太大比如100匹狼前期覆盖更充分但每代都要评估100次适应度训练时间成倍上涨。以50个隐藏节点、5个输入特征的场景为例单次适应度计算约需几毫秒100代下来差距就达到分钟级。建议从30起步看收敛曲线决定增减。其次是最大迭代次数。判断依据是收敛曲线如果曲线在迭代后期还在下降说明尚未收敛增加Max_iter到200甚至300如果曲线在前20代就平坦了增加迭代次数只是浪费时间。注意a从2线性衰减到0这一机制决定了GWO的探索能力在后期急剧下降所以不能指望无限迭代来解决问题必要时应该增加种群数量而不是一味拉长迭代。第三是搜索边界。边界决定了解空间的体积。GWO在初始化时是在lb到ub之间均匀撒点边界太宽会让初始种群密度变低搜索效率下降边界太窄则可能把最优参数排除在外。经验做法是先用[-1,1]跑一遍如果alpha_score居高不下再尝试[-3,3]或[-5,5]同时观察收敛曲线是否变得曲折震荡。下表给出了一个可复用的调参起点参数推荐起点调整方向SearchAgents_no30结果不稳定加至50时间紧张减至20Max_iter100收敛曲线仍下降时增至200lb / ub-1 / 1误差过大时放宽至-3 / 3hiddennum20按10的步长在10-50之间粗筛4.2 评价指标怎么选RMSE、MAE、R²的适用场景回归预测的效果不能只看一个指标。RMSE对大误差样本惩罚重适合用来发现问题样本MAE更稳健反映平均绝对偏差R²衡量模型对输出方差的解释程度小样本下容易虚高。三个指标的计算代码很简洁Y_test Y_test(:); Y_pred Y_pred(:); RMSE sqrt(mean((Y_test - Y_pred).^2)); MAE mean(abs(Y_test - Y_pred)); SS_res sum((Y_test - Y_pred).^2); SS_tot sum((Y_test - mean(Y_test)).^2); R2 1 - SS_res / SS_tot;R²在小样本场景下有一个典型陷阱如果测试集只有三五十条样本模型只需要大致抓住趋势R²就能超过0.95。此时一定要看一眼预测值与真实值的散点图确认每个样本点都落在对角线附近而不是靠一两个离群点拉高了整体相关性。另一个实用习惯是同时输出最大绝对误差定位预测最差的那条样本这比平均指标更能暴露模型问题。4.3 对比实验怎么做ELM、BP、GWO-ELM同数据同折数要验证GWO值得做对比实验必须控制变量。同一个数据集同一套归一化同一个训练测试划分ELM和GWO-ELM使用相同的hiddennumBP神经网络则用14个隐藏节点的单隐藏层结构。每组算法独立运行10次记录RMSE的均值和标准差。这里有个关键点ELM本身有随机性GWO-ELM的优化过程也有随机性单次运行的结果没有任何说服力必须用均值±标准差来展示。如果数据本身线性相关性较强你会发现GWO-ELM和ELM的RMSE差异不大甚至GWO-ELM因为优化耗时而显得“不值”。但在噪声较多的小样本回归预测里ELM的十次运行结果可能横跨0.05到0.15的RMSE区间而GWO-ELM能稳定在0.06到0.08之间。如果你是做软测量建模模型稳定性和可重复性往往比零点零几的精度提升更重要这一点只有做过多次重复实验才会有血泪体会。另外也可以和Matlab里的高斯过程回归模型fitrgp做对比高斯过程回归在小样本仿真数据预测上表现不错但它在样本量增大后训练耗时快速上升GWO-ELM在中等规模数据上更均衡。5. GWO-ELM实战避坑从数据泄漏到狼群早熟的5个常见问题5.1 归一化范围不一致预测结果完全对不上现象训练阶段预测值走势正常一到测试集就输出接近常数或者数值大得离谱。原因这是最典型的操作失误。有些代码先对全部X做联合归一化再划分训练测试集。这样测试集的最大最小值已经参与了mapminmax的ps_x计算训练时相当于提前“偷看”了测试集的信息。另一个常见原因是预测完忘了用ps_y做反归一化导致预测值停留在0到1区间。还有一个是隐藏层偏置的维度搞错H矩阵维度对不上Matlab报维度错误还算幸运不报错但结果错得更难排查。解决训练集单独调用mapminmax生成ps_x和ps_y测试集只用apply和reverse。具体写法见3.1节。每次跑完预测先画一个真实值与预测值的对比图如果两条曲线在测试段错开一个常数值基本可以断定是反归一化遗漏或偏置问题。5.2 狼群早熟所有狼都挤在局部最优怎么办现象收敛曲线从第5代开始就完全走平alpha_score停滞在一个不算理想的值多跑几次最终结果差异明显。原因GWO的a系数从2线性降到0如果搜索边界给得太大狼群初期很难覆盖到有希望的区域后期A值变小所有狼都在alpha狼附近转圈。另一种可能性是hiddennum设置太小模型本身表达能力不足GWO再怎么优化也没用。还有一种情况是数据本身存在异常值个别极端样本把MSE撑得很大alpha狼为了迎合这个异常点牺牲了整体精度。解决先放宽到[-5,5]并增加种群到50看收敛曲线是否出现阶梯下降。如果还是早期停滞把hiddennum调大一档再跑。异常值问题要在数据预处理阶段解决用3σ原则剔除或做截尾处理。多个局部最优存在时最实用的方案是连续跑三次GWO取验证集误差最好的那组Alpha_pos。这不是严谨的理论方法但工程上非常可靠属于后悔药式兜底。5.3 数据泄漏把测试集信息卷进了训练过程现象训练误差和测试误差都低得很完美模型看起来非常好但部署到新数据上效果崩塌。原因除了归一化泄漏还有一种隐蔽做法是把测试集误差直接当适应度。有些论文代码为了展示“最优结果”在GWO迭代中把X_test和Y_test传进适应度函数alpha狼的收敛过程相当于反复在测试集上试探最后选出的参数自然在测试集上表现好。这在实际部署中完全不可复现因为真实场景里新数据是未知的。解决适应度只用验证集误差。如果训练集样本太少连验证集都切不出来改用K折交叉验证的均值作为适应度。将训练集分成5份每次用4份训练、1份验证5次验证误差取平均能显著降低随机划分带来的偏差。代价是计算量增加5倍但小样本场景下这点时间换来的稳定性是值得的。5.4 结果每次跑都不一样随机种子与初始化现象同一份代码、同一个数据集两次运行得到的RMSE不同有时差异超过10%。原因GWO初始化狼群位置用的是rand随机数ELM最终训练时虽然W和b固定但数据打乱顺序也可能影响验证集划分。这意味着即使GWO优化完全成功最终结果也会因随机种子不同而波动。解决开发阶段在脚本开头固定随机种子用rng(2024)确保每次运行结果一致便于调试参数。报告阶段再取消固定种子运行10次取均值和标准差如实反映模型稳定性。还有一个好习惯把GWO找出的Alpha_pos保存成best_params.mat下次加载这个文件直接训练ELM跳过优化过程。这样既保留了可复现性又省去了重复调优的时间。5.5 Matlab版本差异2023b及以后对代码的兼容性注意现象在Matlab 2023b或更新版本上运行老代码出现中文注释乱码、mapminmax参数报错、或者pinv运算警告。原因旧版Matlab脚本默认用GBK编码保存中文注释新版默认UTF-8导致注释乱码但代码本身仍可运行。mapminmax的调用方式在不同版本间基本保持兼容但如果你用了老式的归一化写法比如直接写成[normalized_data] mapminmax(data)漏掉了ps输出某些版本会报错或给出错误维度的结果。另外如果电脑装了并行计算工具箱新版Matlab在循环中默认不自动开并行池不会产生额外问题。解决统一把脚本文件编码转成UTF-8这一点在2023b上尤其重要。mapminmax一律写成[Y, ps] mapminmax(...)的标准形式。pinv计算时如果出现Condition number警告说明隐藏层节点设置过多或数据存在强共线性优先减少hiddennum而不是去改算法。新版本环境下任何报错先看变量维度GWO-ELM这类纯m文件代码不依赖特定工具箱绝大多数问题都是维度或编码造成的。6. 把GWO-ELM调到工程可用验证方法、小样本场景与一个偷懒技巧6.1 K折交叉验证下的GWO-ELM评估框架小样本回归预测最怕单次划分的运气成分。如果你只有两百条样本随机划分一次训练集和测试集测试集里刚好多几条异常点RMSE就暴涨。更稳妥的验证方式是把训练数据切成K份每次留一份做验证其余训练最后取K次验证误差的平均值。这里给一个适配GWO-ELM的简化做法外层循环做K折划分内层跑GWO优化最终精度取K折均值。样本特别少时K通常取5或10。这个框架还能顺带解决hiddennum选择问题。把hiddennum从10到50按5步长扫一遍每轮做5折交叉验证选出平均误差最小的hiddennum再固定它做一次完整的GWO-ELM训练。整个过程写成一个脚本挂机跑比手动一次次试快得多。需要留意的是交叉验证会把计算量放大K倍GWO的迭代次数可以适当降到50因为在多折上取平均后细微的适应度差异会被平滑掉。6.2 一个偷懒技巧把调参结果固化成模板GWO-ELM真正让人头疼的不是算法本身而是每次换一套数据都要重新调一遍。我的习惯是把跑通后的参数组合和最优位置存成一个Mat结构体字段包括inputnum、hiddennum、lb、ub、Alpha_pos、ps_x、ps_y。换新数据集时加载模板后只需要更新数据文件和inputnum、hiddennum其余配置沿用上次的参数。这不保证新数据一定最优但至少提供了一个比随机初始化靠谱得多的起点省去大量盲目试探。如果换了领域数据特征差异较大再回到默认参数从头调。这套方案的难点从来不是Matlab代码跑不跑得通而是你有没有认真对待验证流程。数据泄漏会让结果虚高狼群早熟会让结果波动评价指标单一会让你忽略最差样本。把GWO当黑匣子用的同时保留对数据的敏感度才是这类优化算法真正落地到回归预测里的关键。希望这些经验和踩坑记录能帮你在自己的项目里少走几步弯路。本文还有配套的精品资源点击获取
返回列表