简介:这是一份面向具备Matlab与机器学习基础的研发人员、数据分析从业者的完整项目案例,聚焦麻雀搜索算法(SSA)优化广义回归神经网络(GRNN)的多变量回归预测,重点解决高维、噪声数据下传统模型易陷入局部最优、预测精度不足等问题。压缩包内仅含1个docx文档,整体大小56KB,文档以章节形式完整覆盖项目背景、目标与意义、挑战及解决方案、特点创新、应用领域、模型架构、代码示例、系统部署与应用、未来改进方向等模块,并配有GUI设计说明与关键代码详细注解。已有55人学习浏览。读者可从数据预处理、模型训练优化到实时预测与可视化界面对照实践,理解SSA-GRNN原理,并迁移到金融、气象、环境、制造等回归预测任务;还可参考增量学习、引入深度学习等扩展思路,适合作为课程设计或科研实验起点。
1. 机器学习里做多变量回归预测,Matlab实现麻雀算法(SSA)优化广义回归神经网络(GRNN),核心就一句话:GRNN只有一个超参数spread值得调,而SSA恰好擅长在连续空间里找全局最优,于是SSA-GRNN就成了很多项目里的默认组合。它不挑数据规模,几十个样本也能跑,特别适合做论文对比和仿真验证。
这篇文章写给正在写代码的人:不管你是课设、课题还是企业仿真,都希望拿到一份能直接复现的SSA-GRNN完整项目,而不是原理翻来覆去、代码缺胳膊少腿的帖子。我会把四个层面讲透——GRNN结构、SSA搜索逻辑、Matlab完整实现、GUI封装与踩坑。
读完你可以做两件事:按我的代码把整套流程跑通,然后替换成你自己的多变量数据,看着适应度曲线和误差指标判断模型到底行不行。
2. 先看懂GRNN的四层结构与唯一超参数spread:为什么不能随手乱设
2.1 输入层、模式层、求和层、输出层各做了什么
GRNN全称Generalized Regression Neural Network,是一种基于核回归的概率神经网络。它最吸引人的地方是不需要迭代训练,也不用反向传播,样本一次性进网络就完成了“建网”。Matlab里一条newgrnn命令就能把网络建好,省掉了大部分调参时间,代价是你得理解它内部那套非参数回归逻辑。
网络结构固定是四层。输入层的节点数等于你的特征维度,只负责把样本送入。模式层最特殊:每个训练样本对应一个神经元,神经元中心就是这个样本的输入向量。预测一个新点时,模式层逐个计算该点与所有样本中心的距离,再套一个径向基函数。求和层里有两类节点:一类对模式层输出做加权求和,另一类做普通求和。输出层把加权和与普求和相除,得到最终的回归估计值。
这里没有可学习的权重,也没有训练轮次,唯一影响预测形态的就是spread。spread是径向基函数的扩展速度,在newgrnn(P,T,spread)里直接作为第三个参数传入。spread越小,基函数越尖锐,新点只受周围极少数样本影响;spread越大,基函数越平缓,预测结果逐渐趋向所有训练样本输出的平均值。所以你会发现一个典型现象:spread设成0.001,训练集误差低到夸张,但测试集预测曲线震荡得像锯齿;spread设成100,预测曲线几乎变成一条平线。这不是玄学,是基函数宽度在起作用。
2.2 先用网格扫描摸清spread的脾气:一段可复现的Matlab代码
正式上SSA之前,我建议先做一次最原始的网格扫描。这一步有两个目的:一是确认你的数据和GRNN是否兼容,二是给后面SSA的搜索范围一个靠谱的上下界。代码里我按“每行一个样本、最后一列是输出”的常见格式处理。
% 固定随机种子,让结果可重复 rng(1); % 读数据:最后一列为因变量,前面所有列是自变量 data = xlsread('data.xlsx'); X = data(:, 1:end-1); Y = data(:, end); N = size(X, 1); % 划分训练集和测试集 idx = randperm(N); trainNum = floor(0.75 * N); X_train = X(idx(1:trainNum), :); Y_train = Y(idx(1:trainNum)); X_test = X(idx(trainNum+1:end), :); Y_test = Y(idx(trainNum+1:end)); % 归一化:newgrnn要求“每列一个样本”,所以需要转置 [Xn_train, psX] = mapminmax(X_train', 0, 1); [Yn_train, psY] = mapminmax(Y_train', 0, 1); Xn_test = mapminmax('apply', X_test', psX); % 对spread做网格扫描 spreads = 0.05:0.05:2; rmseTrain = zeros(size(spreads)); rmseTest = zeros(size(spreads)); for i = 1:length(spreads) net = newgrnn(Xn_train, Yn_train, spreads(i)); Yn_hat = sim(net, Xn_train); Y_hat = mapminmax('reverse', Yn_hat, psY); rmseTrain(i) = sqrt(mean((Y_hat - Y_train).^2)); Yn_hat_t = sim(net, Xn_test); Y_hat_t = mapminmax('reverse', Yn_hat_t, psY); rmseTest(i) = sqrt(mean((Y_hat_t - Y_test).^2)); end % 画出两条误差曲线 figure; plot(spreads, rmseTrain, 'o-', spreads, rmseTest, 's-'); legend('训练集RMSE', '测试集RMSE'); xlabel('spread'); ylabel('RMSE');这段代码里的关键点是mapminmax。它默认对矩阵的每一行做归一化,而newgrnn要求输入是“特征×样本”的矩阵,所以必须先把常见的数据表转置。后面mapminmax('apply', X_test', psX)表示用训练集的归一化参数去处理测试集,不能重新计算测试集自己的最小值和最大值。
网格扫描的结果通常会告诉你两件事:训练集RMSE随着spread增大而快速上升,因为网络变平滑后拟合能力下降;测试集RMSE则是一个U型曲线,最低点就是当前数据下的较优spread。如果最低点落在区间的边缘,说明你的扫描范围不对。比如最低点出现在0.05,那么真实最优可能在更小的位置,SSA搜索时就要把下界继续往下压。网格扫描的局限是只试了离散点,而且遇到多个局部低谷时会漏掉更优的位置,这时候才轮到麻雀算法出马。
3. 麻雀算法优化GRNN:三个角色、位置更新与适应度函数设计
3.1 发现者、加入者、侦察者如何在连续空间里合作搜索
麻雀算法是2020年前后提出的群智能优化算法,模拟麻雀觅食和反捕食的行为。整个种群分成三个角色,分工明确。
发现者占种群的一部分,通常设成20%。它们适应度好,离食物近,负责在当前位置附近精细搜索,同时引导其他麻雀飞过来。发现者更新时有一个安全阈值ST:当预警值小于ST时,说明周围安全,就在当前位置附近小步移动;当预警值大于等于ST时,说明有捕食者接近,整群麻雀需要迅速飞到安全区域,此时位置会进行一次较大幅度的随机跳跃。
加入者是剩余的大多数,跟着发现者走。适应度排在后面的一半加入者,会直接飞到一个远离当前最优的随机位置,保证种群不会过早聚在一起;适应度相对靠前的加入者,会向当前最优位置靠拢,并加入随机扰动避免原地打转。侦察者比例一般占10%,负责监视危险。如果自己不是最优,就向最优位置靠近;如果自己已经是最优,反而要远离最差位置,以此产生跳出局部最优的逃逸行为。
这三个角色的配合对一维spread搜索来说可能显得“杀鸡用牛刀”,但要注意GRNN的spread不是越接近某个固定值越好,它和数据分布、样本量、特征尺度都有关。SSA的优势在于它不依赖梯度,也不要求目标函数连续可导,给一个适应度函数就能搜,而且收敛速度在低维问题上明显快于网格扫描和随机搜索。
3.2 适应度函数:用验证集MSE而不是训练集MSE
SSA-GRNN里,麻雀个体的每一个位置就是一个候选的spread值。适应度函数负责回答“这个spread好不好”。最简单的写法是用训练集MSE,但这是很多新手的坑。因为spread越小,GRNN对训练样本的记忆越强,训练集MSE一定漂亮;可到了新样本上,预测曲线会剧烈震荡,掉进过拟合。
我通常会把原来的训练集再挖出一块验证集,用验证集的MSE做适应度。验证集不参与newgrnn建网,只用来评估“没见过”数据上的误差。这样SSA搜索出的spread会偏向光滑一些的位置,泛化能力更好。
function mse = fitness_fun(spread, Xn_tr, Yn_tr, Xn_va, Yn_va) % 用训练集建网,用验证集评估 net = newgrnn(Xn_tr, Yn_tr, spread); Yn_hat = sim(net, Xn_va); mse = mean((Yn_hat - Yn_va).^2); end这个函数保存为fitness_fun.m放在当前目录。注意newgrnn每次调用都会重新建网,所以SSA每评估一个个体就建一次网,种群数30、迭代50次就是1500次建网。听起来多,但GRNN建网只是复制样本,没有迭代训练,所以速度完全能接受。如果样本量过万,模式层神经元太多,可以考虑先对训练样本聚类减量,不过这是后话。
还有一个细节:newgrnn建网时如果spread恰好是0或负数会直接报错。所以不管SSA怎么飞,位置必须做边界限制。常见做法是给spread设上下界,比如[0.001, 10],每次位置更新后执行越界截断。
3.3 SSA主循环骨架:边界处理与收敛曲线
下面给一个麻雀算法主循环的核心骨架,一维搜索,方便你理解加入者、发现者、侦察者是怎么协同工作的。
% SSA参数 pop = 30; % 种群数量 M = 50; % 最大迭代次数 dim = 1; % 优化一个spread lb = 0.001; ub = 10; PD = 0.2; % 发现者比例 SD = 0.1; % 侦察者比例 ST = 0.8; % 预警安全阈值 % 初始化种群 X = lb + rand(pop, dim) * (ub - lb); fit = zeros(pop, 1); for i = 1:pop fit(i) = fitness_fun(X(i, 1), Xn_tr, Yn_tr, Xn_va, Yn_va); end [bestFit, bestIdx] = min(fit); bestX = X(bestIdx, :); curve = zeros(M, 1); for t = 1:M % 按适应度排序 [fitSorted, sortIdx] = sort(fit); Xsorted = X(sortIdx, :); pNum = round(pop * PD); % 发现者更新 R2 = rand; for j = 1:pNum if R2 < ST Xsorted(j) = Xsorted(j) * exp(-j / (pNum * M)); else Xsorted(j) = Xsorted(j) + randn; end end % 加入者更新 for j = pNum+1:pop if j > pop / 2 Xsorted(j) = randn * exp((Xsorted(end) - Xsorted(j)) / j^2); else % 一维下朝当前最优方向移动 Xsorted(j) = Xsorted(1) + 0.5 * (2*rand-1) * abs(Xsorted(j) - Xsorted(1)); end end % 侦察者更新 for j = 1:round(pop * SD) idx_s = randi(pop); if fitSorted(idx_s) > bestFit Xsorted(idx_s) = bestX + randn * abs(Xsorted(idx_s) - bestX); else Xsorted(idx_s) = Xsorted(idx_s) + rand * ... (Xsorted(idx_s) - Xsorted(end)) / (fitSorted(idx_s) - fitSorted(end) + eps); end end % 边界处理并重新计算适应度 Xsorted = max(min(Xsorted, ub), lb); for i = 1:pop fitSorted(i) = fitness_fun(Xsorted(i), Xn_tr, Yn_tr, Xn_va, Yn_va); end X = Xsorted; fit = fitSorted; [bestFit, bestIdx] = min(fit); bestX = X(bestIdx); curve(t) = bestFit; fprintf('迭代%d, 最优MSE=%.6f, spread=%.4f\n', t, bestFit, bestX); end这个骨架我把原始论文里的矩阵运算简化成了一维运算,实际效果完全够用。边界处理放在每个个体重新计算适应度之前,保证任何越界spread都不会传给newgrnn。收敛曲线curve记录每一代的全局最优MSE,后面画出来就能直观判断算法是否收敛。如果你发现曲线最后几个点还在明显下降,说明最大迭代次数太小,需要增加M。
4. Matlab完整项目实例:数据划分、SSA主程序、训练与回归评估
4.1 数据读取、训练/验证/测试三份划分与归一化
网上很多例程只分训练集和测试集,但SSA-GRNN建议分三份:训练集用来建网,验证集用来给SSA当适应度,测试集只做最终评估。这样能避免“用测试集调过参数再拿测试集报告成绩”的自欺欺人。
clear; clc; close all; rng(42); % 固定随机种子,保证结果可复现 % 读数据:每行一个样本,最后一列是输出 data = xlsread('data.xlsx'); X = data(:, 1:end-1); Y = data(:, end); N = size(X, 1); % 按 60% 训练 / 20% 验证 / 20% 测试 划分 idx = randperm(N); nTr = floor(0.6 * N); nVa = floor(0.2 * N); X_train = X(idx(1:nTr), :); Y_train = Y(idx(1:nTr)); X_val = X(idx(nTr+1:nTr+nVa), :); Y_val = Y(idx(nTr+1:nTr+nVa)); X_test = X(idx(nTr+nVa+1:end), :); Y_test = Y(idx(nTr+nVa+1:end)); % 归一化:newgrnn要求每列一个样本,所以转置 [Xn_tr, psX] = mapminmax(X_train', 0, 1); [Yn_tr, psY] = mapminmax(Y_train', 0, 1); Xn_va = mapminmax('apply', X_val', psX); Yn_va = mapminmax('apply', Y_val', psY); Xn_te = mapminmax('apply', X_test', psX); Yn_te = mapminmax('apply', Y_test', psY);这段代码里,mapminmax的第二个输入0,1表示把数据映射到[0,1]区间。为什么不直接用原始值?因为GRNN的径向基函数依赖样本欧氏距离,如果某个特征量纲很大、另一个量纲很小,距离就会被大量纲特征主导,spread怎么调都救不回来。归一化之后每个特征都落在一个量级,spread才具有全局可比性。
注意psX保存了训练集的最小值和最大值,验证集和测试集都用'apply'调用同一个变换。这是为了模拟真实预测场景。如果你的测试集里出现比训练集最大值还大的数,mapminmax会把它压到超出[0,1]范围,但这是正常现象,程序不会报错。
4.2 适应度函数与SSA主程序完整实现
先写适应度函数文件。把下面代码保存为fitness_fun.m。
function mse = fitness_fun(spread, Xn_tr, Yn_tr, Xn_va, Yn_va) net = newgrnn(Xn_tr, Yn_tr, spread); Yn_hat = sim(net, Xn_va); mse = mean((Yn_hat - Yn_va).^2); end然后写SSA主程序。这里给出一个可以直接执行的完整版本,使用的数据和上一节工作区中的Xn_tr等变量一致。
% SSA优化GRNN的spread pop = 30; M = 50; dim = 1; lb = 0.001; ub = 10; PD = 0.2; SD = 0.1; ST = 0.8; X = lb + rand(pop, dim) * (ub - lb); fit = zeros(pop, 1); for i = 1:pop fit(i) = fitness_fun(X(i), Xn_tr, Yn_tr, Xn_va, Yn_va); end [bestFit, bestIdx] = min(fit); bestX = X(bestIdx); curve = zeros(M, 1); for t = 1:M [fitSorted, sortIdx] = sort(fit); Xsorted = X(sortIdx); pNum = round(pop * PD); % 发现者 R2 = rand; for j = 1:pNum if R2 < ST Xsorted(j) = Xsorted(j) * exp(-j / (pNum * M)); else Xsorted(j) = Xsorted(j) + randn; end end % 加入者 for j = pNum+1:pop if j > pop / 2 Xsorted(j) = randn * exp((Xsorted(end) - Xsorted(j)) / j^2); else Xsorted(j) = Xsorted(1) + 0.5 * (2*rand-1) * abs(Xsorted(j) - Xsorted(1)); end end % 侦察者 for j = 1:round(pop * SD) idx_s = randi(pop); if fitSorted(idx_s) > bestFit Xsorted(idx_s) = bestX + randn * abs(Xsorted(idx_s) - bestX); else Xsorted(idx_s) = Xsorted(idx_s) + rand * ... (Xsorted(idx_s) - Xsorted(end)) / (fitSorted(idx_s) - fitSorted(end) + eps); end end % 边界处理 Xsorted = max(min(Xsorted, ub), lb); % 重新计算适应度 for i = 1:pop fitSorted(i) = fitness_fun(Xsorted(i), Xn_tr, Yn_tr, Xn_va, Yn_va); end X = Xsorted; fit = fitSorted; [bestFit, bestIdx] = min(fit); bestX = X(bestIdx); curve(t) = bestFit; fprintf('迭代%2d, 最优MSE=%.6f, spread=%.4f\n', t, bestFit, bestX); end % 画出收敛曲线 figure; plot(1:M, curve, 'b-', 'LineWidth', 1.5); xlabel('迭代次数'); ylabel('验证集MSE'); title('SSA优化GRNN收敛曲线'); grid on;这个版本的一个简化点是在加入者更新里用了带符号的随机系数,替代原始论文里对矩阵求伪逆的操作。原因是dim=1时,伪逆只有一个标量,符号正负不影响随机性。如果你想扩展成同时优化多个超参数,需要重新引入矩阵运算,一维版的随机系数就不适用了。
侦察者更新时,我用了fitSorted(idx_s)判断个体是否优于当前全局最优。注意这里可能会重复选中同一个个体,但不影响整体收敛,因为每次迭代都有随机扰动。如果希望更规范,可以遍历所有侦察者并保证不重复。实际测试区别很小。
4.3 用最优spread建网、预测并输出RMSE、MAE、R2
SSA搜索结束后,bestX就是当前数据下的最优spread。接下来要做两件事:一是把训练集和验证集合起来重新建网,尽可能多利用样本;二是在测试集上评估最终效果。
% 用训练集+验证集一起建网 X_all = [Xn_tr, Xn_va]; Y_all = [Yn_tr, Yn_va]; net = newgrnn(X_all, Y_all, bestX); % 测试集预测 Yn_pred = sim(net, Xn_te); Y_pred = mapminmax('reverse', Yn_pred, psY); Y_pred = Y_pred(:); Y_test = Y_test(:); % 计算回归指标 RMSE = sqrt(mean((Y_test - Y_pred).^2)); MAE = mean(abs(Y_test - Y_pred)); R2 = 1 - sum((Y_test - Y_pred).^2) / sum((Y_test - mean(Y_test)).^2); fprintf('\n最终结果:RMSE=%.4f, MAE=%.4f, R2=%.4f\n', RMSE, MAE, R2); % 测试集预测对比图 figure; plot(1:length(Y_test), Y_test, 'o-', 'LineWidth', 1); hold on; plot(1:length(Y_pred), 's--', 'LineWidth', 1); legend('真实值', 'SSA-GRNN预测值'); xlabel('测试样本序号'); ylabel('输出值'); grid on;这段代码里的mapminmax('reverse', Yn_pred, psY)是把归一化后的预测结果变回原始量纲,否则RMSE和MAE都是[0,1]尺度上的误差,没法直接理解。Y_pred = Y_pred(:)和Y_test = Y_test(:)是为了防止一个是行向量、一个是列向量导致维度不一致。R2的计算用的是标准的回归决定系数,越接近1说明模型越能解释数据波动。
一种更保守的做法是只用训练集建网,测试集评估。这样评估结果更真实,但会浪费验证集的样信息。我的选择是在最终模型里合并训练和验证集,因为SSA选spread用的是验证集MSE,没有让spread直接接触到测试集,所以测试集评估仍然相对可信。
5. SSA-GRNN排查指南:5个让人翻车的Matlab细节
5.1 每次跑出来的最优spread都不一样,是不是算法不稳定
现象:同样一份数据、同一套参数,每次运行得到的bestX都不同,有时候从0.3跳到2.1,最终测试集误差也忽高忽低。
原因:麻雀算法和其他群智能算法一样,初始化种群用的是随机数,位置更新里也有rand和randn。种群没有覆盖到足够空间或者迭代次数不够时,每次搜索的路径都会不一样。
解决:先在程序开头执行rng('default')或rng(42)固定随机种子,保证一键复现。但这只是掩盖问题,真实做法是让种群数量和迭代次数足够大。我常用pop=30, M=50起步,如果收敛曲线末尾还在下降,就把M加到100。如果spread仍然在两个极端之间跳,说明适应度函数有多个局部最优,数据可能有问题,见5.2。
5.2 预测曲线几乎是一条水平线,spread被搜到了上限附近
现象:SSA给出的最优spread等于10,也就是你设置的上边界。测试集的预测值全部落在训练集输出的平均值附近,RMSE虽然不高但没有任何趋势。
原因:spread过大,径向基函数变得太平,网络把所有训练样本的权重做成了一个几乎均匀的加权平均。另一种可能,是数据本身的特征与输出之间相关性很弱,GRNN学不到有效规律,于是最优选择就是输出一个接近均值的平滑值。
解决:先画散点图看每个自变量与输出之间有没有线性或单调关系。如果相关性太弱,该换特征而不是继续调spread。如果数据确有规律,把ub从10缩小到2或1,让SSA在合理区间里搜索。网格扫描的结果能直接帮你确定这个区间,别跳过。
5.3 newgrnn报错“Inputs and targets have different number of columns”
现象:代码写好一运行,newgrnn这一行直接抛错,提示输入和输出的列数不一致。
原因:newgrnn期望输入矩阵是R×Q,输出矩阵是S×Q,其中Q是样本数,R是特征数,S是输出维度。但大多数人读取Excel后得到的是“每行一个样本、每列一个特征”,直接把这样的矩阵传进去,矩阵方向正好相反。
解决:给训练数据加转置。例如Xn_tr = mapminmax(X_train', 0, 1),其中X_train是行样本、列特征。如果仍然报错,在命令窗口输入size(Xn_tr)和size(Yn_tr)确认第一维是不是特征数。另外,Y_train也要变成1×Q的行向量,newgrnn输出层节点数等于行数,这才能匹配单变量回归。
5.4 训练集误差很低,测试集却一塌糊涂
现象:SSA收敛曲线降到极低,比如MSE只有1e-6,但是最终测试集RMSE反而比网格扫描最差点还要高。
原因:这是一个典型的过拟合信号。你的适应度函数可能用了训练集MSE,而不是验证集MSE。spread被搜到了很小的值,比如0.001附近,此时GRNN退化成近似最近邻查表,训练样本被完全记住,但测试样本只要稍有偏移,预测就剧烈震荡。
解决:把适应度函数换成验证集MSE,也就是第3.2节给的那种写法。同时把lb从0.001提高到0.05或者更大一些,避免SSA进入极端尖峰区域。我通常从0.05开始,观察收敛曲线最低点有没有压在边界上,再决定是否继续下调。
5.5 GUI一点“开始训练”就转圈卡死,界面无响应
现象:在App Designer里放了按钮,把SSA主程序直接写进按钮回调。点下去整个窗口变成“未响应”,等很久之后才跳出结果。
原因:SSA-GRNN里的newgrnn和sim虽然是离线训练,但种群30、迭代50就对应上千次建网和预测,这一串操作放在GUI线程里同步执行,会阻塞界面的消息循环。drawnow只能刷新当前帧,没法解决整体阻塞。
解决:把SSA优化过程封装成独立函数,在按钮回调里使用parfeval或timer后台执行,执行完再更新界面。如果不想引入异步复杂度,至少要在回调开头把按钮设为禁用并加一个waitbar,循环里每算10个个体制更新一次进度条。实际项目里我发现waitbar结合drawnow能挽回不少体验,后台异步才是最终方案。
6. 把SSA-GRNN封装成GUI:App Designer关键回调与两个验证习惯
模型训练完只是第一步,交付时点按钮才能出结果的方式才勉强算个工具。Matlab里GUI我一般用App Designer,原因是从2020年以后新版对GUIDE的更新已经很少,App Designer的坐标轴、编辑框、标签组件布局更省事。你要做的核心是把第4章的SSA主程序包成一个函数,输入种群数和迭代次数,返回最优spread和收敛曲线:
function [bestSpread, curve] = runSSAGRNN(pop, M) % 内部加载数据、归一化、调用SSA主循环 bestSpread = ...; curve = ...; end然后在App Designer里放一个按钮、一个坐标轴、三个编辑框和几个标签。按钮回调这样写:
function RunButtonPushed(app, event) app.StatusLabel.Text = 'SSA正在搜索spread...'; drawnow; [bestSpread, curve] = runSSAGRNN(... app.PopEditField.Value, ... app.IterEditField.Value); plot(app.UIAxes, curve, 'b-'); app.SpreadEditField.Value = bestSpread; app.StatusLabel.Text = '训练完成'; end界面回显最优spread,坐标轴画出收敛曲线,这样用户至少能看到优化过程。训练完成后,别忘了把网络和归一化参数存成mat文件,不然下次关掉GUI又得重跑:
save('ssa_grnn_model.mat', 'net', 'psX', 'psY');以后预测新样本时,流程是mapminmax('apply', X_new', psX)、sim(net, ...)、mapminmax('reverse', ...)三步,缺一不可。
关于验证,我有两个一直保留的习惯。第一,每次看完收敛曲线才认结果:曲线末尾还在下降就加迭代次数,曲线平了但spread落在边界说明搜索范围有问题。第二,同一份数据用rng换三个种子各跑一遍,取spread的中位数作为最终值,能有效避开单次随机性。这两个习惯让我少翻了很多次车,至少遇到奇怪的预测结果时,能判断是算法没收敛还是数据本身不行。希望帮到你。
本文还有配套的精品资源,点击获取