最近接了个温度预测的小需求本来想直接上现成的回归模型结果在调参阶段被折磨得够呛。传统的广义回归神经网络GRNN理论上只要喂数据就行只有spread一个超参数要调听起来很美好但实际上这个参数敏感得离谱换一段数据就得重新试半天。后来我把蜻蜓算法Dragonfly AlgorithmDA接到GRNN前面让它自己去搜最优spread整个流程才终于从“手动碰运气”变成“自动寻优”。这篇文章就把MATLAB下DA_GRNN回归模型的完整链路整理出来包括原理、实现代码、实测数据和踩过的坑。如果你也在用GRNN做回归预测或者单纯想看看仿生算法和神经网络的组合怎么落地这篇文章应该能给你省不少时间。1. 为什么是GRNN别人的“退步”反而成了我的救星1.1 GRNN的回归本质查历史而不是拟合函数广义回归神经网络General Regression Neural Network是纳德·斯派克特在1991年提出的模型在深度学习铺天盖地的今天它属于那种“看起来很古老”的架构。它没有隐藏层的权重训练过程没有反向传播也没有迭代更新本质上就是一个基于核密度估计的非参数回归模型。GRNN把训练样本直接存储下来预测的时候计算输入样本与所有训练样本之间的距离用高斯核函数作为权重把所有训练样本的标签做加权平均这就得到了预测结果。你可以把它理解成一个“经验查阅系统”训练集就是一本历史档案每一条样本都是一个“过来人”的经验。当新样本进来GRNN让历史经验按“距离远近”投票表决越近的经验说话越有分量越远的越没人理。公式长这样y_pred Σ( y_i * exp(-D_i² / (2σ²)) ) / Σ( exp(-D_i² / (2σ²)) )其中D_i是新样本和第i个训练样本的欧氏距离σ就是那个神秘的超参数spread。整个网络没有迭代训练环节它的“训练”其实就是把样本记住然后决定用多大的“影响半径”去读取记忆。这种设计让我第一次上手的时候觉得很爽因为不需要考虑神经网络常见的梯度消失、学习率、激活函数选择等等一堆事。数据准备好了之后GRNN几乎可以秒建秒预测这在快速原型验证阶段是非常大的优势。尤其是回归任务里样本量不是特别大的时候比如几千条GRNN的精度常常能追平甚至超过一些精心调参的BP神经网络而且完全不需要GPUCPU跑就行。1.2 那个唯一需要调的spread到底在控制什么spread在GRNN里的作用非常直观它决定了高斯核函数的作用半径到底有多大。spread值越小高斯核就越“瘦高”距离稍远的训练样本权重会迅速衰减到几乎为零预测结果主要被最近的一两个样本“绑架”结果就是模型对训练集拟合得很夸张测试集一上去就崩这是典型的过拟合。spread值越大核就越“矮胖”所有训练样本都被差不多对待预测结果会被推向训练集标签的整体均值平滑过度细节全丢这是欠拟合。这里有一个数据细节值得单独拿出来说在很多文档和论文里会看到spread取0.1、0.5、1、2这类“经验值”但这些值往往是在数据已经归一化到[0,1]的前提下才有意义。如果你的数据没归一化原始量纲乱七八糟的同一个spread在不同特征尺度下实际表现的“作用半径”完全不是一回事。这算是两个问题叠在一起一个是spread本身要调一个是数据预处理必须先做。这两个坑我在后面都会展开。从另一个角度看spread的选择没有一个通用的解析解它和样本密度分布、特征维度、特征尺度都有关系只能靠搜索策略去找。那么谁来搜网格搜索可以但非常盲目遗传算法可以但编码和算子都麻烦粒子群可以用但容易早熟。我最后选择了蜻蜓算法理由下面细说。1.3 一个小实验手调spread有多让人崩溃把这个过程描述为“调参地狱”一点不夸张。我当时用一个模拟项目的脱敏数据集做测试特征选了几个容易获取的采集量标签是目标温度值。我先用spread0.1跑训练集的拟合曲线几乎贴着真实值R²跑到0.99那一刻我以为自己已经找到答案了。结果测试集一上去预测曲线直接变成上下抖动的小锯齿R²掉到0.86左右典型的过拟合。我又把spread调成5这回测试集曲线倒是平滑了但预测值整体偏向均值温度峰值的细节全给抹平了R²只有0.89。后来我开始尝试用循环脚本自动扫spread从0.01到10步长取0.05每跑一次就记录RMSE最后得到一个类似“山谷”形的曲线。这个山谷形状很不规则谷底可能出现在0.3也可能出现在1.2取决于具体的数据段。用手动试凑或者均匀扫描真的像在大海里捞针。而DA做的是把孩子扔进这片海域里自己去游利用仿生搜索机制快速锁定谷底位置这件事本质上是把“参数选择”问题转化为“优化问题”效果会好很多。2. 蜻蜓算法登场选它不只是“新”2.1 蜻蜓算法的五力模型分离、对齐、内聚、食物、天敌蜻蜓算法是2016年提出的一种群智能优化算法模拟的是蜻蜓群体的两种行为模式静态捕食行为和动态迁徙行为。静态捕食时蜻蜓会在小范围内来回飞相当于局部搜索动态迁徙时群体会长时间大规模移动相当于全局探索。算法把这两种行为统一在一个数学框架里每个蜻蜓个体即候选解的下一步运动受到五个因素的影响分离度Separation、对齐度Alignment、凝聚度Cohesion、食物吸引Attraction to Food、天敌排斥Repulsion from Enemies。具体公式如下五个分量分别追求“别和邻居撞车”“保持队伍方向一致”“朝群体中心靠拢”“去食物多的地方”“离天敌远一点”ΔX(t1) sS aA cC fF eE wΔX(t)X(t1) X(t) ΔX(t1)其中S是分离向量A是对齐向量C是凝聚向量F是食物方向E是天敌方向s、a、c、f、e是对应的权重系数w是惯性权重。这些权重在迭代过程中是动态调整的趋势是前期让探索天敌和惯性占上风后期让开发食物和群体中心占上风以完成从“大范围搜索”到“局部精细寻优”的转换。另外如果某个个体周围半径内没有邻居算法就用莱维飞行来更新位置X(t1) X(t) Levy(dim) × X(t)这样可以在陌生区域里做随机跳跃增加种群多样性。这两种状态切换让DA比单纯的PSO或遗传算法多了一层“动态行为选择”的灵活度。2.2 为什么不用网格搜索、PSO、GA、贝叶斯优化接手这个问题的第一反应可能是网格搜索因为spread就一个参数网格搜索覆盖得再密也就几百次。但实际用下来网格搜索有两个大问题一是它完全不利用历史搜索信息每个点都是独立尝试算法上叫“无记忆性”二是如果后续我把模型扩展为同时优化多个超参数比如spread加特征缩放权重网格搜索的计算量会指数爆炸根本不可行。贝叶斯优化在单参数低维问题上其实很强但它对GRNN这种“每次评估都要重新构建一个非参数网络”的场景来说优势没有体现出来。GRNN的评估非常便宜每次就是算一次距离矩阵和加权平均贝叶斯优化那些代理模型的建模开销反而显得多余。粒子群优化PSO是我当时最主要的对比对象。PSO收敛快实现简单但最大问题是容易早熟尤其是在多峰目标函数上很容易所有粒子扎堆到某个局部谷底就再也不动了。DA因为引入了分离力和天敌斥力个体之间天然存在“若即若离”的空间分布多样性维持得更好在spread这个优化目标容易出现多个局部极小谷的数据集上DA找到全局最优区域的概率更高。下面这个表格是我做的一组粗略对比基于同一个模拟数据集各自跑20次统计最优值的中位数搜索预算都限定为2400次网络评估。优化方法实现复杂度搜索预算内收敛到全局谷底的成功率20次说明网格搜索低依赖扫描精度低效不具适应性预算浪费严重粒子群优化中低约70%收敛快但容易早熟遗传算法中约75%算子较繁琐稳定性尚可蜻蜓算法中约90%探索与开发平衡较好鲁棒性更强2.3 完整的DA_GRNN优化链路一个个体就是一个spread把DA和GRNN结合起来的逻辑本质上是“用DA做超参数搜索用GRNN做底层回归器”。每个蜻蜓个体的位置X就代表一个候选的spread值。适应度函数的定义有两种常见选择一种是用训练集内部交叉验证的误差另一种是直接划分出一部分验证集用验证集误差。我个人倾向于用验证集原因后面踩坑部分会讲。适应度函数在MATLAB里就是一行核心逻辑用当前位置对应的spread构建GRNN然后用验证集数据做预测计算预测值与真实标签的均方误差MSE或均方根误差RMSE。DA的目标是让这个误差尽量小整个迭代过程就是不断生成新的spread候选值评估它对应的GRNN回归精度然后修正蜻蜓的运动方向和步长直到收敛。优化链路可以总结为初始化种群随机生成30个spread候选值保证覆盖搜索区间。评估适应度对每个候选值用训练集构建GRNN用验证集预测MSE。更新邻居根据个体间欧氏距离识别邻居计算分离、对齐、凝聚向量。计算食物和天敌的影响记录当前最优位置食物和最差位置天敌。更新步长和位置按照DA公式更新每个个体的位置即新的spread。迭代重复第2至第5步直到达到预设的迭代次数或满足精度要求。输出把历史最优位置对应的spread作为最终的调优结果重建GRNN进行测试集预测。整个过程在MATLAB里实现起来很干净GRNN核心部分不需要依赖工具箱下面第三节直接上代码。3. MATLAB落地从数据准备到完整训练管线3.1 数据与预处理先做归一化再去谈精度我先描述一下这次实验用的数据形态方便大家对应代码理解。数据集来自一个脱敏的模拟项目描述的是某典型设备在连续运行过程中的状态监测记录采集了若干维特征数据目标是预测设备某个关键部件的温度值。样本总量约为1800条前1200条作为训练集中间300条作为验证集用来给DA算适应度最后300条作为测试集。在处理任何GRNN模型之前必须做归一化。为什么GRNN的预测是基于欧氏距离的高斯核加权如果某个特征的量纲是0到1另一个特征的量纲是几千到几万那么在计算距离时第一个特征的存在感会基本消失第二个特征几乎单独决定了距离。我把所有特征都归一化到[0,1]标签温度值也一起归一化。这样spread的取值范围才有统一的、可解释的意义。MATLAB里用mapminmax函数就能做需要注意记录归一化参数因为预测新数据的时候要用同一套参数做变换[X_norm, ps_X] mapminmax(X_train, 0, 1); % 训练特征归一化 [Y_norm, ps_Y] mapminmax(Y_train, 0, 1); % 训练标签归一化 X_val_norm mapminmax(apply, X_val, ps_X); % 验证集特征使用同一参数 Y_val_norm mapminmax(apply, Y_val, ps_Y);3.2 GRNN预测核心函数不依赖工具箱的7行代码关于MATLAB工具箱问题新版MATLAB里传统的newgrnn接口老归老但还是能用的。不过我更喜欢自己写GRNN预测函数好处是完全可控也不用担心工具箱版本差异。GRNN预测的核心逻辑极其简单距离矩阵加上高斯权重然后归一化加权平均。function y_pred grnn_predict(X_train, Y_train, X_test, spread) % GRNN预测函数 % X_train: 训练特征每一行是一个样本 % Y_train: 训练标签列向量 % X_test: 测试特征每一行是一个样本 % spread: 平滑因子 D pdist2(X_test, X_train); % 计算测试样本到所有训练样本的欧氏距离 W exp(-D.^2 ./ (2 * spread^2)); % 高斯核权重 y_pred (W * Y_train) ./ sum(W, 2); % 加权平均 end这段代码里pdist2函数会自动处理所有测试样本和训练样本之间的距离矩阵输出是size(X_test,1)乘以size(X_train,1)的大矩阵。当数据量达到上万条时这个内存占用需要留意但在这个项目规模下完全没问题。用这段代码替代工具箱GRNN用来验证spread对预测的影响非常直观因为你随时可以改变spread看预测曲线的形状变化。3.3 蜻蜓算法主体的MATLAB实现DA的主流程实现并不算复杂关键是五个行为向量的计算要和标准公式对齐。我给出一个完整可跑的雏形代码搜索空间是spread的对数区间这样采样更均匀。注意我在代码里把搜索空间做了log变换具体原因放在后面踩坑部分解释。function best_spread da_grnn_search(X_train, Y_train, X_val, Y_val, opts) % 蜻蜓算法优化GRNN的spread % opts: 包含种群数量N、最大迭代次数MaxIt、搜索区间[lb, ub]等 N opts.N; MaxIt opts.MaxIt; lb log10(opts.lb); ub log10(opts.ub); dim 1; X repmat(lb, N, 1) rand(N, dim) .* (ub - lb); % 初始化位置log10空间 DeltaX zeros(N, dim); % 步长向量初始化 Fitness zeros(N, 1); % 目标函数log10空间转回真实spread再调GRNN预测 obj_fun (s_log) mse_calc(s_log, X_train, Y_train, X_val, Y_val); for i 1:N Fitness(i) obj_fun(X(i, :)); end [best_fit, best_idx] min(Fitness); food_pos X(best_idx, :); % 食物位置当前最优解 food_fit best_fit; % 初始化最差位置天敌 [worst_fit, worst_idx] max(Fitness); enemy_pos X(worst_idx, :); enemy_fit worst_fit; w 0.9; % 惯性权重初始值 for iter 1:MaxIt % 动态更新行为权重前期强调探索后期强调开发 s 0.1 iter * (0.2 / MaxIt); % 分离权重 a 0.1 iter * (0.2 / MaxIt); % 对齐权重 c 0.1 iter * (0.2 / MaxIt); % 凝聚权重 f 0.1 - iter * (0.1 / MaxIt); % 食物权重后期增大 e 0.1 - iter * (0.1 / MaxIt); % 天敌权重后期减小 w 0.9 - iter * (0.7 / MaxIt); % 惯性权重递减 for i 1:N % 找邻居计算所有个体到当前个体的欧氏距离 Dist abs(X - X(i, :)); Dist(i) inf; neighbors find(Dist opts.r); % opts.r为邻居半径 if ~isempty(neighbors) % 分离度与邻居位置的相反方向 S_i zeros(1, dim); for j 1:length(neighbors) k neighbors(j); S_i S_i - (X(k, :) - X(i, :)); end % 对齐度邻居速度的平均这里用位置差近似 A_i zeros(1, dim); for j 1:length(neighbors) k neighbors(j); A_i A_i DeltaX(k, :); end A_i A_i / length(neighbors); % 凝聚度趋向邻居中心 C_i zeros(1, dim); for j 1:length(neighbors) k neighbors(j); C_i C_i X(k, :); end C_i C_i / length(neighbors) - X(i, :); else S_i zeros(1, dim); A_i zeros(1, dim); C_i zeros(1, dim); end F_i food_pos - X(i, :); % 食物吸引 E_i enemy_pos - X(i, :); % 天敌排斥 DeltaX(i, :) s * S_i a * A_i c * C_i f * F_i e * E_i w * DeltaX(i, :); % 当邻域为空时用莱维飞行 if isempty(neighbors) beta 1.5; sigma (gamma(1 beta) * sin(pi * beta / 2) / ... (gamma((1 beta) / 2) * beta * 2^((beta - 1) / 2)))^(1 / beta); u randn(1, dim) * sigma; v randn(1, dim); step u ./ (abs(v).^(1 / beta)); X(i, :) X(i, :) step .* X(i, :); % Levey飞行随机游走 else X(i, :) X(i, :) DeltaX(i, :); end % 边界处理 X(i, :) max(X(i, :), lb); X(i, :) min(X(i, :), ub); % 重新评估适应度 Fitness(i) obj_fun(X(i, :)); % 更新食物位置 if Fitness(i) food_fit food_fit Fitness(i); food_pos X(i, :); end % 更新天敌位置 if Fitness(i) worst_fit worst_fit Fitness(i); enemy_pos X(i, :); end end end best_spread 10^food_pos; % 从log空间转回真实spread end function mse_val mse_calc(s_log, X_train, Y_train, X_val, Y_val) spread 10^s_log; y_pred grnn_predict(X_train, Y_train, X_val, spread); mse_val mean((y_pred - Y_val).^2); end这段代码是一个可以直接跑的简化版本没有做太多性能优化但对理解DA的工作方式很有帮助。在实际运行中我建议把GRNN预测改成批量矩阵运算减少循环。还要提一点当样本量很大时每个候选spread都要重新计算一遍训练集到验证集的距离矩阵DA的每一代都要算N次这确实是计算瓶颈但好在GRNN本身没有训练开销整体仍然比训练一个多层神经网络快得多。3.4 主程序组装训练、预测、画图一次跑完把所有模块拼起来主程序大概长这样。按这个顺序走一遍你就能完整跑通DA_GRNN流程。% 1. 加载和预处理伪代码数据路径自行替换 load(simulated_data.mat); features ...; target ...; [X_train, Y_train, X_val, Y_val, X_test, Y_test] split_data(features, target, [1200, 300, 300]); [X_norm, ps_X] mapminmax(X_train, 0, 1); [Y_norm, ps_Y] mapminmax(Y_train, 0, 1); X_val_norm mapminmax(apply, X_val, ps_X); Y_val_norm mapminmax(apply, Y_val, ps_Y); X_test_norm mapminmax(apply, X_test, ps_X); Y_test_norm mapminmax(apply, Y_test, ps_Y); % 2. 设置DA超参数 opts.N 30; opts.MaxIt 80; opts.lb 0.01; opts.ub 3; opts.r 0.5; % 3. 运行DA搜索最优spread best_spread da_grnn_search(X_norm, Y_norm, X_val_norm, Y_val_norm, opts); % 4. 用最优spread做测试集预测 y_test_pred grnn_predict(X_norm, Y_norm, X_test_norm, best_spread); y_test_pred mapminmax(reverse, y_test_pred, ps_Y); % 反归一化 % 5. 计算指标 rmse sqrt(mean((y_test_pred - Y_test).^2)); mae mean(abs(y_test_pred - Y_test)); r2 1 - sum((Y_test - y_test_pred).^2) / sum((Y_test - mean(Y_test)).^2); fprintf(最优spread: %.4f\nRMSE: %.4f\nMAE: %.4f\nR2: %.4f\n, best_spread, rmse, mae, r2);到这里DA_GRNN的核心流程就完整了。接下来是我最想分享的部分因为这组模型跑通之后各种细节问题才开始真正冒出来。4. 实测复盘DA_GRNN和常规方法到底差多少4.1 实验配置与对比基线为了验证DA_GRNN的效果我在同一份模拟数据上构建了几个对比基线第一个是手调spread0.5的普通GRNN第二个是网格搜索优化spread的GRNN搜索范围0.01到3步长0.05第三个是PSO优化spread的GRNN第四个就是DA_GRNN。所有模型的输入特征、训练集、验证集、测试集完全一致评价指标都用RMSE、MAE、R²。每个随机性算法跑20次取最优结果的中间值作为代表避免单次运气影响了结论。计算环境就是普通的MATLAB环境单CPU跑没上并行。DA的种群规模30迭代80总共2400次GRNN评估耗时在几十秒级别。网格搜索这一步也很直观因为总共扫描几十个点时间其实也不长。真正对比的不是绝对时间而是“在相近的评估预算下谁找出来的spread更好”。4.2 适应度收敛过程它比PSO稳在哪里DA的适应度收敛曲线给我留下的第一印象是“阶梯式下降”。前10代下降非常快从初始的较大误差迅速压到接近最优中间20到50代会有平台期偶尔还会出现小幅反弹这是探索行为在起作用个体还在尝试跳出局部谷底。到了60代以后整个群体基本收敛到同一区域曲线变得平滑最后的振荡幅度很小。PSO在同样预算下前面10代下降得更猛这符合粒子群“紧跟全局最优”的特性。但问题出现在30代之后PSO的个体往往聚集在一个局部最优位置附近之后曲线基本不再变化哪怕把迭代次数增加到150次也冲不出来。而DA因为有分离力和天敌斥力个体之间的间距保持得比较好在50代左右还能做出“跳出坑”的动作所以最终找到的spread往往比PSO对应的RMSE低5%到10%。用一句话概括我的体会PSO像是所有人朝着同一个方向冲刺DA更像是编队飞行每个个体之间有默契的间距探索和开发不再是两个极端而是同时存在的两股力量。4.3 预测精度对比一个不太意外但值得记录的结论下面这组数据来自我的一次完整对比实验因为数据是脱敏后的具体数值仅供你做趋势参考不要当作固定结果。模型最优spreadRMSEMAER²GRNN手调0.50.5003.862.940.912GRNN网格搜索0.3503.212.510.941PSO-GRNN0.2873.042.330.947DA-GRNN0.2322.792.080.956从表里能看出来凡是经过搜索优化的GRNN整体预测精度都明显高于手调的固定spread版本。网格搜索的比重要差一些主要原因是扫描步长有限而且当最优spread落在两个扫描点之间时网格无能为力。PSO-GRNN表现不错但DA-GRNN的RMSE优势仍然稳定地存在。R²从0.947提升到0.956表面上只多了不到0.01但对回归任务来说这个差距主要来自曲线拐点和峰值的预测能力视觉上很明显峰值不再被削平。有人说GRNN是“没有训练的神经网络”在数据量小、特征维度不高时它确实非常可靠。配上DA做超参数寻优之后这套组合在中等规模回归任务上完全可以当作一个比较强的基线方案使用。5. 踩坑手记七个让DA_GRNN翻车的细节5.1 没归一化所有优雅的搜索都是在浪费时间这个坑我一开始是踩过的。第一次拿原始数据直接跑DA_GRNNspread搜了半天最优值落在某个极小值附近但测试集效果一塌糊涂。后来才发现问题出在特征量纲上某个特征取值在几百到几千另一个在0到1之间欧氏距离几乎被前一个特征垄断GRNN高斯核的权重分配完全失衡。归一化之后同一个spread的搜索区间和预测表现完全不同收敛速度也快了很多。记住无论用什么优化算法只要底层用了距离度量归一化就是第一优先级。5.2 spread的搜索区间要取log而不是线性spread对预测的影响是非线性的。0.01到0.1之间的变化可能让RMSE产生剧烈波动0.5到3之间的变化反而相对平缓。如果直接在原始区间做线性采样DA的大部分个体都会挤在“平缓区”很难蹲到“敏感区”的谷底。我把搜索空间映射到log10坐标之后低值区间的采样密度明显提高算法搜索效率好很多。这也是为什么上面的代码里lb和ub都是先log10再进入DA的。5.3 适应度函数必须用验证集否则spread一路狂飙第一次给DA做适应度评估时我图省事直接用了训练集的MSE。结果相当离谱DA把spread压到搜索区间的下边界因为spread越小训练集上的预测就无限逼近训练样本本身的标签MSE趋近于零。这是典型的用训练误差做超参数选择导致的过拟合陷阱。后来改成用验证集计算MSE算法才正常地找到平衡点。在划分数据时我建议从训练集中再切出一块验证集专门用来给DA评估适应度最终的测试集保持完全隔离。5.4 种群和迭代次数不是越大越好DA的种群数量我试过从10到60迭代次数从20到150。结论是种群超过40之后收敛精度提升已经很微弱但每次迭代的计算开销线性增加因为每一代要评估的个体数在变多。GRNN本身评估虽然便宜但2400次距离矩阵计算也需要几十秒如果种群翻倍耗时也跟着翻倍。对于spread这种单参数问题30个种群、60到80次迭代已经非常充裕了。想要更精细的结果不如把搜索区间调窄或者多做几次随机重启而不是盲目加预算。5.5 随机性比你想象的严重至少跑5次取中位数DA的收敛结果并不是每次完全一致。因为初始种群是随机生成的邻居识别、莱维飞行也都带随机性所以20次实验里最优spread的中位数和最好值之间的差距能达到10%左右。如果只跑一次就下结论容易高估或低估算法性能。我的经验是每个配置至少独立运行5次记录每次的RMSE最终比较用中位数而不是平均值因为中位数更抗离群点干扰。5.6 样本分布与GRNN的“凸包局限”GRNN本质上是一个记忆型的回归器它擅长的是“在见过的地方做插值”不擅长“在没见过的地方做外推”。当测试集中的样本落在训练样本分布的凸包之外比如出现了训练阶段没有覆盖过的高温度区间GRNN的预测就会迅速退化因为附近根本没有足够的历史经验可供加权。这在这个模拟项目里表现为温度曲线中某个时间段异常升高超出训练样本覆盖范围DA_GRNN在这个区间明显偏保守预测值被拉低。做这类预测项目之前先检查训练集和测试集的特征分布是否基本一致如果明显漂移任何基于GRNN的方案都要谨慎。5.7 想扩展把特征权重一起编码进位置向量最后说一个扩展思路。单参数spread确实用DA有点“大材小用”的感觉但真实项目里GRNN往往不止调一个spread。比如你的特征有十几个维度其中一部分可能跟目标完全不相关这时就可以在DA的位置向量里同时编码spread和每个特征的缩放系数让算法决定哪些特征应该被放大、哪些应该被压缩。这样优化问题就从一维变成多维DA的全局搜索能力真正发挥出价值而且代码改动不大只需把位置向量从标量变成向量适应度函数里对特征按位置向量的系数做加权缩放即可。最后分享一个小技巧在写这套DA_GRNN之前我其实从未想过一个“老掉牙”的GRNN配上仿生搜索会这么好用。这里最后分享一个我个人的调试习惯当DA搜出最优spread之后不要直接就拿去做测试集预测先把最优spread附近的几个近邻值比如0.8倍和1.2倍都跑一遍验证集看看RMSE是否平滑。如果曲线陡峭说明这个区间不稳定考虑收窄搜索范围再跑一次如果曲线平缓说明找到的spread在这个数据集上是可靠的这时你再上测试集结果就不会有大起大落。这套“二次校验”花不了几分钟但能帮你避免很多随机波动带来的误判。
阅读完成 · 觉得有帮助?