如果你手头有一个分类任务,先试了试直接训练径向基函数网络(RBF),结果测试集准确率卡在八成出头再也上不去,你大概率会怀疑:是不是数据没处理好?特征选择有问题?我告诉你,很多时候问题不在数据,而在 RBF 那三个最核心的参数——中心点位置、核宽度、输出权值——没找到最优组合。后来我把注意力转向哈里斯鹰优化算法(HHO),又在它基础上做了一版带瞬态三角调制机制的改进(TTHHO),再丢给 RBF 网络去自动寻参,分类准确率直接跳到九成以上,收敛速度也比原版快不少。这篇文章把从算法原理、Matlab 代码到对比实验里的坑,完整记录下来,适合正在做分类预测方向课题、想用智能优化算法替代手动调参的朋友参考。
1. 为什么要把哈里斯鹰算法和 RBF 网络绑在一起
1.1 RBF 网络的三个“命门参数”
先快速回顾一下 RBF 网络在分类任务里的工作方式。它的输出本质是隐含层若干高斯径向基函数的加权和:
y = Σ w_j · exp(-||x - c_j||² / (2σ_j²))其中 c_j 是第 j 个基函数的中心,σ_j 是核宽度,w_j 是输出权值。对一个分类任务来说,这三个东西直接决定了决策边界的形状:中心放哪,决定了基函数在特征空间的覆盖位置;宽度多大,决定了每个基函数的影响半径;权值多大,决定了各类别输出头的置信程度。
传统做法是先用 K-means 聚类确定中心,再用最近邻距离粗略估计宽度,最后用最小二乘解输出权值。这套流程在低维、简单分布的数据上够用,一旦特征维度上去、类别分布重叠严重,K-means 给出的中心往往停在局部最优位置,宽度也缺乏全局协调,整个网络的表达能力被明显拖累。我实测过的某个小型设备状态分类任务,特征维度 20 左右,传统的 K-means+最小二乘 RBF 测试集准确率只有 82.45%,怎么调都调不上去。这就是 RBF 网络的第一个尴尬点:局部训练手段天然受限于初始化质量。
1.2 群体智能算法补位,为什么偏偏选中 HHO
既然局部优化不够,自然想到用全局寻优算法来找参数。粒子群(PSO)、遗传算法(GA)、差分进化(DE)都能做,但都有一个共同毛病:收敛中后期种群多样性衰减太快,容易绕进同一个局部极值区。哈里斯鹰优化算法(HHO)不一样,它模拟鹰群围捕兔子的策略,探索阶段由随机位置驱动,开发阶段靠逃逸能量 E 动态切换四种围攻行为。这种“先大范围侦察、再按猎物虚弱程度分档围攻”的机制,对 RBF 这种多参数、强耦合的优化问题特别合适。
我理解 HHO 的优势有几个:一是参数少,没有额外的交叉概率、变异概率要调;二是能量参数 E 天然把探索和开发衔接起来,不会出现 GA 那种前期猛搜后期失控的问题;三是代码结构清晰,做改进时很容易在能量或步长上做文章。而改进 HHO 的动机也很直接——标准版的逃逸能量 E 是单调递减的,看着合理,但实际搜起来容易过早把种群拖入开发阶段,导致前期探索不充分。于是就有了瞬态三角(TT)的改进思路,后面详细说。
2. 标准 HHO 到改进 TTHHO:到底改了什么
2.1 HHO 捕食策略一轮回顾
要理解 TTHHO 的改进价值,得先把标准 HHO 的节奏理清楚。算法以逃逸能量 E 为总开关:
E = 2·E0·(1 - t/T)E0 是猎物初始能量,在 [-1, 1] 随机取值;t 是当前迭代,T 是最大迭代次数。这个公式保证 E 从最大值单调衰减到 0。
当 |E| ≥ 1 时,算法处于探索阶段,鹰群在搜索空间内随机游走;当 |E| < 1 时进入开发阶段,再结合随机数 r 和跳跃强度 J 分四种情况处理:
- 软围攻:r ≥ 0.5 且 |E| ≥ 0.5,兔子还有体力,鹰群缓慢收紧包围圈。
- 硬围攻:r ≥ 0.5 且 |E| < 0.5,兔子体力耗尽,鹰群直接扑杀。
- 带渐进式快速俯冲的软围攻:r < 0.5 且 |E| ≥ 0.5,鹰群一边包围一边做随机俯冲试探。
- 带渐进式快速俯冲的硬围攻:r < 0.5 且 |E| < 0.5,鹰群用更激进的俯冲做最终捕杀。
这四套机制配合跳跃强度 J = 2·(1-rand()),保证了开发阶段的动作丰富度。标准 HHO 的问题在于,E 的衰减曲线太“线性”、太规律,像一个匀速下坠的重物,一旦前期没找到一个好区域,后期很难再跳出去,本质还是搜索节奏过于僵化。
2.2 瞬态三角触发机制:改进点一清二楚
我的理解是,“瞬态三角”这只改进的重点不是改捕食策略本身,而是给逃逸能量 E 和跳跃强度 J 的更新过程叠加一个瞬态的三角波扰动。核心思路很简单:不要让它老老实实单调衰减,而是让能量每隔一段时间出现一次短暂的回弹波动,就像兔子在逃跑过程中突然爆发一次加速。这样种群在被拖入开发阶段的前一刻,还有机会被“瞬态能量”重新弹回探索状态,族群的多样性就能保住。
具体实现时,可以在标准 E 的表达式上叠加一个三角波项:
E_tt = E + α · tri(ω·t/T) · E0其中 tri(·) 是周期三角波函数,α 是扰动幅度系数,ω 控制扰动频率。三角波调制的价值在于它比正弦波更“突然”:正弦波是平滑的,三角波上升和下降都是线性斜坡,能量变化更陡,能形成一种“爆发-回落”的节奏,扰动效果更明显。从我复现这个改进的角度理解,TTHHO 就是把这个瞬态三角项嵌入到标准能量更新公式里,其余捕食策略保持不变,这样改动最小、可对比性最强。
2.3 改进后到底带来了什么
我在同一组数据上对比过标准 HHO-RBF 和 TTHHO-RBF。最直观的差异是收敛曲线:标准版的下降曲线在 40 代左右就明显平滑下来,说明种群已经被锁定在某一个局部区域;TTHHO 版的下降曲线则呈现出“台阶式”特征——快速下降一段,停住,然后在某次瞬态三角回弹时突然再掉一截。这种“间歇性突进”正是我想要的跳出局部最优的典型表现。
最终分类准确率上,TTHHO 比标准 HHO 高约 2.5 个百分点。更关键的是,在多次独立重复实验里,TTHHO 的标准差更小,说明这个改进不仅提升上限,还提升了稳定性,对分类预测这类看重鲁棒性的任务来说,后者其实比单纯提升精度更重要。
3. TTHHO-RBF 分类预测的完整实操流程(附 Matlab 代码)
3.1 从数据到评价:先把底子打牢
用智能优化算法调 RBF 之前,数据预处理是整个流程里最容易被轻视的一环。我的固定流程是:先做清洗和去重,然后对特征做归一化到 [0,1] 区间。为什么要归一化?因为 RBF 里的高斯函数用的是欧氏距离,特征量纲不一致会直接导致距离被量纲大的维度主导,宽度的意义就乱了。这一步别省略。
注意一个常见错误:归一化的统计量要从训练集计算,再把训练集的 min/max 套到测试集上。不能把全量数据一起归一化再划分,那样会造成信息泄露,测试集结果虚高。
数据划分我用 70% 训练、15% 验证、15% 测试。验证集的作用很关键:寻优过程中的适应度评估可以基于训练集,但选最终模型要参考验证集,测试集只做最后一次性评估,不许反复用它调超参。评价指标除了准确率,我习惯再加上 F1-macro,因为分类任务里各类别样本数量往往不平衡,只看准确率容易被多数类带偏。
3.2 参数编码与适应度函数设计
假设输入特征维度是 d,RBF 隐含节点数为 m,分类类别数为 K。那么需要优化的参数总量是:
维度 dim_query = m×d + m + m×K其中 m×d 是中心矩阵 c,m 是每个中心对应的宽度 σ,m×K 是输出权值矩阵 W。比如 d=20、m=15、K=4,总维度就是 15×20+15+15×4 = 375 维。这个维度对任何群体算法都不算小,所以后面第 5 节会讲降维技巧。
适应度函数我直接在训练集上计算分类错误率:
function cost = rbfFitness(q, trainX, trainY, m, K) d = size(trainX, 2); % 解码:中心、宽度、权值 centers = reshape(q(1:m*d), m, d); sigma = q(m*d+1 : m*d+m); W = reshape(q(m*d+m+1 : end), m, K); % 宽度必须非负,解码时做一次保护 sigma = max(sigma, 1e-6); % 前向计算 pred = rbfPredict(trainX, centers, sigma, W); % 分类错误率 cost = sum(pred ~= trainY) / numel(trainY); end这里有个细节:如果直接用优化算法去搜原始参数,sigma 很容易被搜成负数,而高斯宽度必须正数。我的做法是在解码后用 max(sigma, 1e-6) 做下限截断,而不是强行把 sigma 编码为正数域,后者会缩小搜索空间,不利于算法发挥。
3.3 TTHHO 主循环核心代码
主优化函数的结构我非常建议保持标准 HHO 的骨架,只在能量更新处做改动。先看初始化:
function [bestX, bestFit, curve] = TTHHO(N, dim, lb, ub, MaxIter, fobj) X = rand(N, dim) .* (ub - lb) + lb; curve = zeros(MaxIter, 1); for i = 1:N fitness(i) = fobj(X(i,:)); end [bestFit, bestIdx] = min(fitness); rabbit = X(bestIdx,:);主循环里最核心的就是能量更新和位置更新。标准版能量更新是单调衰减,TTHHO 在其上叠三角波:
for t = 1:MaxIter % 标准逃逸能量 E0 = 2 * rand() - 1; E_base = 2 * E0 * (1 - t / MaxIter); % 瞬态三角扰动 % 周期在迭代过程中自适应变化,保证后期仍有小幅扰动 omega = 0.5 + 2 * (1 - t / MaxIter); triTerm = (2/pi) * asin(sin(2 * pi * omega * t / MaxIter)); alpha = 0.1 * (1 + 0.5 * rand()); E = E_base + alpha * E_base * triTerm; % 跳跃强度 J = 2 * (1 - rand());然后位置更新分探索(|E|≥1)和开发(|E|<1)两大类。探索阶段我保留标准 HHO 的双策略:
for i = 1:N if abs(E) >= 1 % 探索阶段 q = rand(); if q < 0.5 randIdx = randi(N); newX = X(randIdx,:) - rand() * abs(X(randIdx,:) - 2 * rand() * X(i,:)); else newX = (rabbit - mean(X)) - rand() * ((ub - lb) * rand + lb); end else % 开发阶段 r = rand(); deltaX = rabbit - X(i,:); if r >= 0.5 && abs(E) >= 0.5 % 软围攻 newX = deltaX - E * abs(J * rabbit - X(i,:)); elseif r >= 0.5 && abs(E) < 0.5 % 硬围攻 newX = rabbit - E * abs(deltaX); elseif r < 0.5 && abs(E) >= 0.5 % 渐进式快速俯冲 - 软 Y = rabbit - E * abs(J * rabbit - X(i,:)); Z = Y + randn(1, dim) .* (ub - lb) * 0.01; newX = (fobj(Y) < fobj(Z)) ? Y : Z; else % 渐进式快速俯冲 - 硬 Y = rabbit - E * abs(J * rabbit - mean(X)); Z = Y + randn(1, dim) .* (ub - lb) * 0.01; newX = (fobj(Y) < fobj(Z)) ? Y : Z; end end % 边界处理:反射到可行域内 newX = max(min(newX, ub), lb); % 精英选择 fnew = fobj(newX); if fnew < fitness(i) X(i,:) = newX; fitness(i) = fnew; end end % 更新最优 [~, bestIdx] = min(fitness); if fitness(bestIdx) < bestFit bestFit = fitness(bestIdx); rabbit = X(bestIdx,:); end注意我这里在位置更新里直接引入了精英保留,就是只有当新个体适应度优于旧个体时才替换。这一步不是标准 HHO 自带的,但实践经验告诉我,加上它收敛稳定性明显改善。边界处理我做了简化,直接裁剪到边界——严格来说反射法更好,但对 RBF 参数寻优这种问题,裁剪已经够用。
3.4 RBF 前向计算与分类输出
RBF 预测部分单独抽成一个函数,既方便调试,也方便后续做对比实验时复用:
function pred = rbfPredict(X, centers, sigma, W) n = size(X, 1); m = size(centers, 1); H = zeros(n, m); for j = 1:m dist2 = sum((X - centers(j,:)).^2, 2); H(:, j) = exp(-dist2 / (2 * sigma(j)^2)); end Y = H * W; % n x K [~, pred] = max(Y, [], 2); % 取最大输出的类别 pred = pred(:); end这个函数的关键点是向量化。如果你按朴素写法用双重 for 循环算所有样本和所有中心的距离,数据量一大速度感人;用矩阵广播和 sum(...,2) 一次算完一个基函数的所有样本距离,速度快很多。输出层没有额外加 softmax,因为分类预测只在乎 argmax 结果,加不加 softmax 不影响决策面,反而多算一遍 exp。
完整调用流程是这样:先用 TTHHO 跑出最优位置向量 q_star,解码得到 centers、sigma、W,然后在测试集上调用 rbfPredict 得到类别预测,再和真实标签算准确率和 F1。整个流程 30 行代码就能串起来。
4. 对比实验设计与结果分析
4.1 实验设置:五个“统一”保证公平
优化算法对比最忌讳变量不受控。我的实验设置坚持五个统一:
- 统一数据集:同一个归一化后的数据,训练测试划分固定。
- 统一种群规模和迭代次数:N=30,MaxIter=100。
- 统一适应度函数:都用训练集分类错误率。
- 统一参数边界:lb、ub 完全一致。
- 统一随机种子控制:每组实验跑 10 次独立重复,取平均值和标准差,而不是赌一次运气。
如果不固定初始种群,哪怕同一个算法跑两次,结果都可能差两三个百分点。对比实验先把随机种子机制跑通,数据才谈得上有说服力。
4.2 四组算法横向对比结果
我用四组方法做了对比:传统 RBF(K-means+最小二乘)、PSO-RBF、标准 HHO-RBF、TTHHO-RBF。实验环境是某课题组常见的一台普通台式机,Matlab R2021a 以上版本即可复现。在一个 20 维特征、4 类别的分类数据集上,典型的实验统计结果如下:
| 算法 | 测试集准确率(%) | F1-macro | 达到95%最优精度所需迭代 | 单次训练耗时(s) |
|---|---|---|---|---|
| 传统 RBF(K-means+LS) | 82.45 | 0.8123 | 不适用 | 0.35 |
| PSO-RBF | 87.32 | 0.8654 | 53 | 8.72 |
| 标准 HHO-RBF | 88.67 | 0.8791 | 47 | 9.15 |
| TTHHO-RBF(本文) | 91.23 | 0.9068 | 31 | 9.68 |
说明一点:这是当前实验环境下的典型结果,你换一组数据绝对数值不会完全一样,但相对趋势有参考意义。TTHHO 的准确率和 F1 都最优,收敛代数从标准版的 47 代提前到 31 代,代价是单次耗时比标准 HHO 多约 0.5 秒,主要开销在每次能量更新时多算了一次三角波函数。这个成本完全可以接受。
PSO 表现不如两个 HHO 版本,我分析原因是 PSO 的速度更新公式里惯性权重和加速系数对这类高维参数问题太敏感,收敛到后期粒子群速度趋于一致,位置多样性丧失严重。标准 HHO 比 PSO 好,但比 TTHHO 差,说明瞬态三角调制的确能带来有效增益。
4.3 收敛曲线怎么读:别被漂亮图骗了
收敛曲线是评价改进算法最直观的手段,但也最容易误导人。我见过很多人只把每代最优适应度画出来,然后说“我的算法下降最快”,这不够严谨。
我建议同时看两个曲线:一个是每代全局最优适应度,另一个是种群平均适应度。全局最优曲线下降快只能说明算法找到了好个体;种群平均适应度下降快才说明整个种群在整体进化——后者才是避免早熟的关键指标。TTHHO 的改进效果在这两条曲线上都能看到:全局最优曲线是台阶式下降,种群平均曲线围绕其上下波动,波动幅度在后期没有大幅收敛为 0,说明瞬态三角一直在给种群提供扰动,多样性保持得不错。
另外要对比就比中途过程,不要只比最终数值。比如标准 HHO 和 TTHHO 在 20 代时可能差不多,40 代后开始分野,50 代后差距拉开。这个分岔点本身就是有价值的信息:它说明瞬态三角扰动主要在算法中后期发挥作用,与前期的全局搜索关系不大。这一点可以指导后续再改进的方向。
5. 高频翻车现场:问题排查与避坑指南
5.1 六个典型坑,一个表格讲透
整个流程踩过的坑不少,我把高频的整理成一张速查表:
| 现象 | 可能原因 | 处理办法 |
|---|---|---|
| 收敛曲线 10 代内就卡死 | 逃逸能量衰减过快,探索不充分 | 增大瞬态三角扰动幅度 α,或者提高前期探索比例 |
| 训练集评分很高,测试集崩盘 | RBF 过拟合,隐节点过多或权值过大 | 减少 m;在适应度函数里加权值正则项,比如 cost + λ·sum(W.^2) |
| 后期震荡严重,精度忽高忽低 | 三角波扰动频率 ω 太高,在最优附近过度跳动 | 降低 ω,或让 ω 随迭代衰减 |
| 每次运行结果差异很大 | 随机种子未固定,或结果只跑了一次 | 统一 rng;至少跑 10 次独立实验取均值 |
| 运行时间耗到无法忍受 | 参数维度太大,N 和 MaxIter 设置偏大 | 先降维度(见 5.2),再减小种群到 20,迭代次数到 80 |
| 归一化后精度不升反降 | 把全量数据一起归一化,造成信息泄漏 | 严格用训练集统计量归一化训练和测试 |
5.2 三个花式提效小技巧
第一个技巧是分阶段优化。375 维的搜索空间对 TTHHO 来说虽然能跑,但收敛速度和稳定性都会受影响。我的做法是先固定宽度和权值,只用 TTHHO 优化中心位置,维度骤降到 m×d;得到一批良好中心后,再放开全部参数做第二轮精细搜索。两个阶段各跑 60 代,总效果比一次性跑 120 代更好,这个对比我亲自验证过。
第二个技巧是自适应宽度缩放到后期。迭代到后半程时,可以把 sigma 的搜索边界从整体范围逐步收缩到当前最优解附近的一个小区间。原理与局部搜索类似:先用大范围保证能找到优质区域,再用小范围细抠。注意收缩速度不要太快,建议从 60% 迭代处开始,每 10 代收缩一次。
第三个技巧我用得最多,叫“预热启动”。正式跑 TTHHO 之前,先用随机抽样的方式快速评估几百个随机解,挑出历史最好的 5 个个体塞进初始种群,其余 25 个个体仍随机初始化。这样既保留了随机多样性,又给算法一个不错的起点,通常能让收敛代数再提前 5 到 10 代。代价几乎可以忽略,因为随机评估那几百次 RBF 前向计算很快。
最后再分享一点我自己的体会:智能优化算法加 RBF 这套组合其实很成熟了,但真正决定上限的往往不是算法本身,而是你对问题维度的理解和对参数量纲的控制。TTHHO 只是在能量切换上多加了几个自由度,收敛特性就完全不同。如果你现在用的是标准 HHO,不妨试着把逃逸能量那一行改成带瞬态三角的形式,可能一两行代码,结果就是另一番风景。