最近在整理回归预测这一类问题时又看到“基于哈里斯鹰优化算法的RBF神经网络回归预测”这种标题还标着“创新首发”。先把话说透哈里斯鹰优化算法HHO本身不新RBF神经网络更是经典的老牌逼近工具这个组合在算法层面并没有革命性变化。但如果你和我一样真正关心的是怎么把一篇论文里的公式变成能在Matlab里跑通、能搬到实际数据上的回归预测方案那这个组合值得认真拆一遍。我最初也被“RBF网络 群智能优化”这种套路化表述劝退过后来仔细做了一轮实验才发现问题不在算法本身而在大多数实现都停留在“用优化算法随便找几个参数然后算个RMSE就算完”。这篇博文把我实际跑通的流程、代码组织方式、参数编码细节以及几个容易让结果翻车的坑都写出来希望能帮你少走一点弯路。1. 为什么非要让哈里斯鹰给RBF当“调参工”1.1 RBF网络真正难调的是“中心位置和宽度”RBF神经网络的结构不复杂输入层把特征送进来隐层每个节点是一个径向基函数输出层把隐层响应线性加权得到预测值。最常用的是高斯径向基函数对第 k 个隐节点、输入样本 x响应是φ_k(x) exp( -‖x - c_k‖² / (2σ_k²) )其中 c_k 是中心σ_k 是宽度。网络输出就是这些径向基响应的线性组合输出权重可以用最小二乘直接解出来。看似简单真正让RBF在回归任务里“时灵时不灵”的是 c_k 和 σ_k 怎么确定。常见的做法有三种随机从训练样本里抽中心、用K均值聚类找中心、直接用Matlab的newrb自动建网。随机抽样运气成分很大聚类结果对初始簇数和距离度量敏感newrb虽然方便但它选中心的原则偏向于“降低训练误差”在验证集上不一定稳。更麻烦的是宽度如果太小每个基函数只覆盖身边几个点模型基本在背样本宽度太大所有基函数长得差不多预测又变成一条平滑曲线细节全丢。所以RBF的精度瓶颈本质是“中心位置和宽度”这几个非线性参数没选好。既然它们不好手工设用优化算法去搜就是一个很自然的解决思路。1.2 HHO相比网格搜索和传统群智能的优势提到参数寻优很多人第一反应是网格搜索。可一旦中心个数到 20、输入特征到 10 个需要同时优化的变量就是 20×11 220 维网格搜索直接爆炸。遗传算法和粒子群也能做但 GA 的交叉变异参数、PSO 的惯性权重和学习因子都需要额外调等于“用一套需要调参的算法去调另一套参数”体验并不好。HHO 是模拟哈里斯鹰捕猎兔子的群智能优化算法核心是四个阶段探索、软围困、硬围困、渐进式快速俯冲。它引入了一个随机递减的兔子逃逸能量 E让算法在前期多探索、后期多开发不需要你手工设置惯性和学习因子整体参数比 PSO 少。我自己的体会是HHO 在 20 到 100 维的连续参数寻优问题上收敛速度和稳定性都还不错尤其在适应度函数有一定“欺骗性局部最优”的时候它那个跃变式的俯冲策略比普通粒子群更容易跳出去。但我不建议把 HHO 神化。群智能算法都是“概率性搜索”一次跑得好不代表次次好。后面会专门讲随机种子和多次重复实验的问题这一点在实际项目中比选哪个优化器更重要。2. 把HHO和RBF捏在一起的正确姿势2.1 优化变量怎么编码很多人第一次做HHO-RBF会把输出权重也一起放进优化变量里。这其实不是最优选择因为RBF输出层是线性层权重完全可以用最小二乘直接解没必要让HHO在一个线性子问题里浪费维度。我的做法是位置向量只包含所有隐层中心 c_k 和宽度 σ_k。假设输入特征维度是 d隐层节点个数是 m那么每个中心是 d 维向量m 个中心就是 m×d 个数再加上 m 个宽度最终优化维度dim m × (d 1)举个例子10个输入特征、25个隐节点dim 25×11 275这个维度对HHO来说属于中等偏上可以接受。如果把输出权重也塞进来还要再加 m1 个维度搜索效率会明显下降。位置向量解码时前 m*d 个数 reshape 成 m×d 的中心矩阵每一行是一个中心最后 m 个数对应每个中心的宽度。输出权重在适应度函数内部通过伪逆求解不进入优化变量。2.2 适应度函数用训练误差还是验证误差适应度函数是整个优化的“指挥棒”。如果适应度直接取训练集RMSEHHO很容易把网络调成“背题机器”中心密集分布在训练样本附近宽度压得很小训练误差确实低但验证集误差会高得离谱。我自己第一次做的时候就犯过这个错结果训练RMSE只有0.02验证集RMSE却跑到0.15一看就是过拟合。正确做法是把原始数据分成三份训练集、验证集、测试集。HHO在寻优过程中每一次位置解码后都在训练集上计算输出权重在验证集上计算RMSE这个验证集RMSE作为适应度值。HHO只见过训练集和验证集测试集从头到尾不参与寻优只在最终对比时用一次。适应度函数公式可以写成fitness sqrt( mean( (Y_val - Y_pred)² ) )按最小值搜索。这看起来简单但很多人会在归一化上犯一个隐蔽错误把验证集和测试集一起放进mapminmax求出了全局最小最大值。这等于把测试集信息提前泄漏给模型。正确做法是先只用训练集计算归一化参数验证集和测试集都拿训练集的min/max去变换后面我会在代码里给出这种写法。2.3 边界处理和宽度编码HHO产生新位置之后很可能会超出搜索边界必须做越界处理。常见做法有三种边界吸收、边界反射、随机重置。我在回归预测这个场景里推荐边界吸收也就是超出上界就置为上界超出下界就置为下界实现简单收敛也稳定。宽度 σ_k 必须是正数但HHO的位置向量是全实数搜索如果直接把上下界设为 [0.1, 3]在靠近0的位置时一小步就可能跳到负数适应度函数还得额外做检查。更稳妥的做法是优化变量里存放 log(σ)解码适应度时再用 exp 还原。这样搜索空间变成实数域宽度自然大于零HHO可以放心地在 [log(0.1), log(3)] 之间搜索。这个细节看似不起眼却能让收敛过程平滑很多。3. Matlab代码怎么组织才不是“论文缩写式”3.1 主程序骨架我习惯把代码拆成四个部分数据准备、HHO主循环、RBF适应度函数、结果回放。主程序不要写一堆clc clear close all就结束要让别人能一眼看出数据怎么进、参数怎么设。%% 数据准备 % raw_data 是一个 n 行 d1 列的矩阵最后一列是目标值 load(my_reg_dataset.mat); raw my_reg_dataset; % 自行指定字段 X raw(:, 1:end-1); Y raw(:, end); % 只用训练集求归一化参数 [Xtr, Xps] mapminmax(X(1:400, :), 0, 1); Xtr Xtr; [Xva, ~] mapminmax(X(401:600, :), Xps); Xva Xva; [Xte, ~] mapminmax(X(601:end, :), Xps); Xte Xte; Ytr Y(1:400, :); Yva Y(401:600, :); Yte Y(601:end, :); %% RBF参数范围 d size(Xtr, 2); m 20; dim m * (d 1); lb [repmat(-1, m*d, 1); repmat(log(0.1), m, 1)]; ub [repmat(1, m*d, 1); repmat(log(3), m, 1)]; %% HHO寻优 fitness (x) hho_rbf_fitness(x, Xtr, Ytr, Xva, Yva, m, d); [bestFitness, bestX, conv] HHO(30, 150, lb, ub, dim, fitness);注意最后bestFitness是验证集RMSEbestX才是最优位置向量。很多人一开始拿conv当最优解去预测结果维度对不上这就是编码没搞清楚。3.2 HHO算法主体下面这段是HHO核心循环的简化版我把注释写细一点function [Rabbit_Energy, Rabbit_Location, conv] HHO(N, T, lb, ub, dim, fun) % N: 鹰群数量 % T: 最大迭代次数 X rand(N, dim) .* (ub - lb) lb; Rabbit_Location zeros(1, dim); Rabbit_Energy inf; conv zeros(1, T); for t 1:T % 边界处理 更新全局最优 for i 1:N X(i, :) min(max(X(i, :), lb), ub); fit_i fun(X(i, :)); if fit_i Rabbit_Energy Rabbit_Energy fit_i; Rabbit_Location X(i, :); end end conv(t) Rabbit_Energy; E1 2 * (1 - t / T); % 逃逸能量线性递减 for i 1:N E0 2 * rand() - 1; E E1 * E0; J 2 * (1 - rand()); % 兔子跳跃强度 if abs(E) 1 % 探索 if rand() 0.5 X_new Rabbit_Location - rand() * abs(Rabbit_Location - 2 * rand() * X(i, :)); else X_rand X(randperm(N, 1), :); X_new X_rand - rand() * abs(X_rand - 2 * rand() * X(i, :)); end else % 开发 r rand(); if r 0.5 abs(E) 0.5 % 软围困 X_new (Rabbit_Location - X(i, :)) - E * abs(J * Rabbit_Location - X(i, :)); elseif r 0.5 abs(E) 0.5 % 硬围困 X_new Rabbit_Location - E * abs(Rabbit_Location - X(i, :)); elseif r 0.5 abs(E) 0.5 % 渐进式快速俯冲软围困 X1 Rabbit_Location - E * abs(J * Rabbit_Location - X(i, :)); if fun(X1) fun(X(i, :)) X_new X1; else X2 Rabbit_Location - E * abs(J * Rabbit_Location - X(i, :)) ... randn(1, dim) .* levy(dim); if fun(X2) fun(X(i, :)) X_new X2; else X_new X(i, :); end end else % 渐进式快速俯冲硬围困 X1 Rabbit_Location - E * abs(J * Rabbit_Location - X(i, :)); if fun(X1) fun(X(i, :)) X_new X1; else X2 Rabbit_Location - E * abs(J * Rabbit_Location - X(i, :)) ... randn(1, dim) .* levy(dim); if fun(X2) fun(X(i, :)) X_new X2; else X_new X(i, :); end end end end X(i, :) min(max(X_new, lb), ub); end end end function L levy(dim) beta 1.5; sigma_num gamma(1 beta) * sin(pi * beta / 2); sigma_den gamma((1 beta) / 2) * beta * 2^((beta - 1) / 2); sigma (sigma_num / sigma_den)^(1 / beta); u randn(1, dim) * sigma; v randn(1, dim); L 0.01 * u ./ (abs(v).^(1 / beta)); end这段代码里有一点值得说明我在“渐进式快速俯冲”分支里对 X1 和 X2 都算了一次适应度这会让总计算量变大。理论上HHO本身就是这样设计的但实际跑大样本时适应度函数每算一次都要做一遍RBF前向计算几百个样本还算能忍几千个样本就会明显变慢。如果希望快一点可以把内层择优简化成“直接接受X2”牺牲一点精度换速度我在后期实验里就是这么做的。3.3 RBF前向计算和权重伪逆求解适应度函数的写法是整套代码的另一个关键。我在这里用伪逆求解输出权重而不是把权重放进HHO位置向量function rmse hho_rbf_fitness(x, Xtr, Ytr, Xva, Yva, m, d) % 解码前 m*d 是中心后 m 个是宽度存的是log C reshape(x(1:m*d), m, d); sigma exp(x(m*d1:end)); % 训练集径向基输出矩阵 Htr zeros(size(Xtr, 1), m); for k 1:m D2 sum((Xtr - C(k, :)).^2, 2); Htr(:, k) exp(-D2 / (2 * sigma(k)^2)); end Htr [Htr, ones(size(Xtr, 1), 1)]; % 输出权重用最小二乘 W pinv(Htr) * Ytr; % 验证集预测 Hva zeros(size(Xva, 1), m); for k 1:m D2 sum((Xva - C(k, :)).^2, 2); Hva(:, k) exp(-D2 / (2 * sigma(k)^2)); end Hva [Hva, ones(size(Xva, 1), 1)]; Yp Hva * W; rmse sqrt(mean((Yva - Yp).^2)); end用pinv而不是inv是因为 Htr 很可能存在近似线性相关的列直接求逆数值上不稳定伪逆能给出最小二乘意义下的解也更符合RBF输出层本来就要做线性回归的设定。另外我在偏置项上直接用一列全1给模型一个可调的截距这在预测值有明显偏移的数据集上非常有用。4. 我在实验里看到的真实效果4.1 实验配置为了验证这套流程我构造了一个含有非线性关系的模拟回归数据集8个输入特征1个输出样本量600。前400个做训练中间100个做验证集用于HHO寻优最后100个做测试集只评估一次。输出值本身带有随机噪声噪声幅度大约占信号标准差的10%这样能模拟真实回归任务里“不可能完全拟合”的情况。对比对象有三个直接用K均值聚类定中心的RBF网络、BP神经网络、HHO-RBF。RBF和BP的超参数都做了基础调节尽量公平。HHO参数设置是种群数30、最大迭代100中心个数m取18。4.2 效果对比我挑了一轮代表性结果放在下表三个指标分别是RMSE、MAPE、R²模型训练RMSE测试RMSE测试MAPE测试R²K均值RBF0.04120.05287.43%0.918BP神经网络0.04660.05818.12%0.901HHO-RBF0.03310.03655.26%0.964从这张表能看出两个信息第一HHO-RBF在测试集上确实比另外两个模型好RMSE从0.05级别降到了0.036级别R²从0.91提升到0.96第二训练RMSE和测试RMSE的差距HHO-RBF也是最小的说明它没有靠过拟合换精度。需要强调这只是模拟数据上的一轮结果换一个数据集绝对数值会变但“经过寻优的RBF比拍脑袋定参数的RBF泛化更好”这个结论在我后续几组实验里基本稳定。4.3 收敛曲线与误差分布HHO的收敛曲线在迭代到40次左右就基本进入平台期验证集RMSE从初始0.09快速降到0.04附近后面60次迭代只是小幅改善。这说明维度275的寻优问题对HHO来说并不算难真正决定效果的是你给的中心范围和宽度范围。再看测试集残差分布残差大体围绕0波动没有明显偏向也没有出现“大值低估、小值高估”的漏斗形分布。这一点很重要回归预测不能只盯着R²如果残差和预测值呈明显相关性说明模型有系统性偏差哪怕R²高也不能放心用。5. 复现过程中最容易翻车的几个点5.1 中心个数m不是越大越好很多人觉得隐节点越多拟合能力越强于是在HHO里把m设成50甚至100。这在样本量不太大的回归任务里很危险。随着m变大可调参数维度跟着变大HHO的搜索难度指数上升即使找到了低验证误差的解也很可能是把验证集也背下来的结果测试集反而变差。我的经验是样本量几百、特征维度个位数时m取10到30就足够了。m的选取不需要一开始就定死可以先用K均值RBF跑一版看验证集误差随m变化的曲线在误差下降变缓的地方选m再交给HHO去优化中心。这样比纯靠感觉设m靠谱。5.2 宽度上下界和输入归一化的关系宽度σ的绝对值完全取决于输入特征的尺度。如果输入没有归一化特征取值范围是0到10000宽度取1根本没有任何区分度。所以我强烈建议先把输入归一化到[-1, 1]或[0, 1]区间再设计宽度的搜索范围。在输入归一化到[-1, 1]的情况下我常见用得比较稳的宽度范围是0.1到3对应log就是[-2.3, 1.1]。宽度范围设得太大会让所有核函数都“糊成一片”适应度曲线平坦HHO很难分辨好坏设得太小又会让中心只能影响极近的样本优化退化成局部修修补补。你可以从0.1到5这个范围开始跑几次看适应度分布再调整。这个微调步骤往往比换优化算法更影响最终精度。5.3 随机种子和多次运行HHO初始化种群是随机的适应度函数里的权重伪逆求解虽然稳定但HHO每一轮搜索路径都不一样。我曾经在同一数据集上连续跑10次最好的测试R²是0.968最差只有0.931差距非常明显。所以复现一个HHO-RBF结果时必须在固定的随机种子下运行并且报告多次重复的平均值和标准差而不是只挑最好的一次结果出来晒。Matlab里固定种子的方式是rng(2024)这类语句。如果你想做更严谨的对比对每个模型都用同样的5组种子分别跑5遍取平均指标表格里的数字才有说服力。论文里那些“一次运行赢过所有对比算法”的结果大多经不起这个考验。6. 后续可以怎么改6.1 从离线预测到更复杂的工程场景如果你不是为了论文复现而是想把HHO-RBF用到实际工程里有几个可以扩展的方向。第一把适应度函数从单一RMSE改成RMSE加上对宽度均值的惩罚项抑制过大的核宽度差异模型更平滑第二在多组交叉验证上做HHO寻优每次适应度都是K折平均误差稳定性比固定验证集好但计算量会明显上升第三如果数据量上万可以用小批量训练集计算适应度把HHO种群数降下来否则一轮优化要跑很久。另外时间序列预测场景里如果把滞后特征拼成输入矩阵这套HHO-RBF流程可以直接用但需要注意时序数据不能像普通回归那样随机划分训练测试集必须按时间顺序切分否则验证集信息会泄漏到训练集里。这是很多人把回归代码硬套到时间序列上踩过的坑。6.2 关于“创新首发”的一点提醒最后说点实在话。HHO优化RBF的参数并不是多么新的组合算法本身和网络结构都有公开来源。如果是写课程报告或者做算法对比完全可以放心使用但如果是投稿或者做毕业设计建议在“为什么用HHO而不是PSO/GA”这个问题上多下功夫把适应度函数设计、边界策略、宽度编码这些工程细节作为你的实际贡献点而不是只强调“HHO-RBF”这个名头。算法的价值从来不在名字而在它是否真的解决了特定场景下的问题。我自己跑过一轮之后最大的感受是HHO-RBF能不能超过普通RBF关键不在HHO写得好不好而在你对数据归一化、验证集划分、参数范围和重复实验的控制够不够细致。把这些基础工作做扎实优化算法才能真正发挥出它该有的作用。