PSO优化随机森林回归:MATLAB实现超参数自动搜索全攻略
最近有个回归预测的活儿数据维度不高但特征之间非线性关系明显我用默认参数的随机森林跑了一下效果始终差口气。后来把粒子群算法PSO和随机森林回归结合起来让粒子群自动搜索随机森林那几组关键超参数效果立刻上来了。这篇文章把我这段时间PSO-RF的MATLAB实现过程完整记录下来包括全部核心代码、参数设计思路以及这一路踩过的坑。如果你也是做回归预测的比如流量预测、房价预测、设备寿命预测这类任务手上又正好有MATLAB那这篇应该能帮你少走不少弯路。1. 项目概述与整体思路1.1 这个项目在解决什么问题先说结论这套PSO-RF的核心是用粒子群算法替代人工试参和网格搜索自动寻找随机森林回归模型的最优超参数组合。我最终优化的是三个参数——决策树数量、最小叶子节点样本数、每次分裂采样的特征数。这三个参数对随机森林的回归精度影响最大但手动调起来非常痛苦尤其是当特征数量较多、数据量较大时网格搜索的时间成本直接指数级上涨。随机森林回归本身是一种集成学习方法它通过自助采样Bootstrap生成多棵决策树每棵树在分裂时随机抽取一部分特征做最优分裂最后把多棵树的预测结果取平均作为回归输出。这个机制让RF对非线性关系、特征交互、缺失值都有很强的鲁棒性。但是随机性带来的好处是有前提的——森林里树的数量够不够多、每棵树的深度够不够深、每次分裂看的特征够不够这些超参数直接决定了模型的容量和泛化能力。默认参数能跑但远不是最优。不同数据集的最优超参数差异非常大比如数据量小时树数量设200就够了数据量大时可能得上千棵特征间相关性强的每次分裂采样特征数可以少一些相关性弱的则要多采一些。靠人工一个个实验一轮五折交叉验证跑下来就要十几分钟试个几十组参数半天就没了。粒子群算法在这里的价值就是把这几十次上百次的人工试参压缩成一次自动搜索用较小代价逼近全局较优的参数组合。1.2 为什么选PSO而不是网格搜索和贝叶斯优化如果你用过MATLAB自带的调参工具应该知道常见方案有三种网格搜索Grid Search、随机搜索Random Search和贝叶斯优化Bayesian Optimization。网格搜索最土但最直观可一旦参数维度上到三个以上每个参数给20个候选值组合数就是20^38000次模型训练这在回归任务里几乎跑不动。随机搜索虽然随机撒点能覆盖一些区域但说实话全凭运气结果稳定性差。贝叶斯优化在调参界口碑很好MATLAB的fitrgp、bayesopt函数可以直接用它的思路是用概率代理模型指导下一轮采样样本效率高。但我在实际项目里发现一个问题贝叶斯优化对连续参数的处理很优雅对离散整数参数却比较扭捏比如树数量这种整数直接搜索时得手动做round映射代理模型还容易在整数边界上失真。另外贝叶斯优化是串行迭代的每轮只出下一组参数想并行去评估多个候选组合比较费劲。粒子群算法的优势在于第一群体并行搜索种群里每个粒子都是独立的参数组合天然可以并行评估第二实现逻辑极其简单速度更新、位置更新两个公式就够没有复杂的概率模型要维护第三对整数参数的处理很自然位置向量先用连续量做搜索进适应度函数前取整就行搜索效率受影响很小。实际跑下来同样的二十次迭代、种群规模15PSO找到的参数组合基本能压过网格搜索上百次实验的结果而且代码量不过一百来行非常契合MATLAB这种工程优先的环境。1.3 PSO-RF的整体流程整个项目的执行链路可以拆成五步我按这个顺序搭的代码框架后面所有模块都是围绕它展开的。第一步数据准备。先把数据集拆成训练集和测试集用训练集做五折交叉验证来评估每组参数测试集只留到最后验证最终模型。第二步参数编码。把我们要优化的三个随机森林超参数映射成粒子位置向量的三维决策树数量NumLearningCycles取值区间我设为10到300最小叶子节点样本数MinLeafSize取值区间1到20每次分裂的特征采样数NumVariablesToSample取值区间1到总特征数第三步初始化粒子群。随机生成N个粒子的初始位置和速度每个粒子的位置对应一组RF超参数。这里有个小技巧我后面会细说可以额外把一组靠近默认参数的经验粒子放进初始种群让搜索起点不至于太离谱。第四步迭代搜索。每一轮迭代里每个粒子都用自己当前的位置参数去训练一次RF回归模型并以五折交叉验证的均方误差MSE作为适应度值然后更新个体最优pbest和全局最优gbest再按粒子群速度位置公式更新如此循环往复。第五步收敛输出。迭代结束后gbest对应的就是PSO找到的最优超参数组合。用它重新在完整训练集上训练一次RF最后用测试集做预测计算R2、RMSE、MAE等评价指标。这个流程看似简单但每一步都有不少细节坑尤其是适应度函数的写法和边界处理。下面把原理和代码逐步展开。2. 核心原理拆解随机森林与粒子群2.1 随机森林回归的超参数到底在优化什么很多初学者对超参数的理解比较模糊觉得树越多越好深度越深越好这是基本功不扎实的表现。我拆开说明一下。第一是树的数量。随机森林的误差可以分解成偏差、方差和噪声三部分。增加树的数量能显著降低方差这也是Bagging的核心作用。但树数量增加到一定程度后边际收益会趋近于零继续加树只会线性增加训练耗时。一般回归任务300到500棵就已经很稳了再多就是纯烧算力PSO搜索时这个边界必须卡住。第二是最小叶子节点样本数MinLeafSize。这个参数控制树的生长深度。取值越小树长得越深单棵树对训练数据拟合得越狠偏差低但方差高取值越大树越浅越保守方差低但偏差可能上来。回归问题里我一般把它限制在1到20之间默认值是1但真实业务数据噪声大用5到10往往效果更好PSO能在这个区间里找到平衡点。第三是每次分裂的特征采样数NumVariablesToSample。随机森林的随机很大程度体现在这里。每次分裂时只随机抽样一部分特征做最优分裂能有效降低树与树之间的相关性。回归问题中MATLAB默认取特征总数的三分之一但这是通用经验值。我遇到过特征高度冗余的场景采样数降到五分之一反而更强也遇到过特征互相正交的场景采样数得提到二分之一。不交给优化算法去搜的话很难摸到这个规律。补充一点随机森林对特征量纲不敏感数据预处理阶段不需要做标准化这跟神经网络完全不同。但特征里的极端异常值对树的分裂影响比较大建议提前用箱线图或者分位数法剔一下异常样本否则一棵树可能为了迎合一个异常样本分裂出不必要的路径拖累整片森林。2.2 粒子群算法的核心公式与本质粒子群优化算法的思想源自鸟群觅食行为1995年Kennedy和Eberhart提出。每个粒子就是一只鸟它知道自己当前的位置、当前飞行的速度、自己历史走过的最优位置pbest还知道整个鸟群目前发现的最优位置gbest。每轮迭代粒子就根据这三条信息调整自己的下一步速度和方向。速度更新公式是核心v_new w * v_old c1 * r1 * (pbest - x) c2 * r2 * (gbest - x)位置更新公式就是简单的累加x_new x_old v_new其中w是惯性权重控制上一轮速度对当前的影响程度c1、c2是学习因子分别控制向自己历史最优学习和向全局最优学习的比重r1、r2是0到1之间的随机数给搜索引入随机性。这个公式的本质可以理解成三个力的合成惯性让粒子保持原来的飞行方向避免来回震荡个体认知项把粒子拉向自己验证过的好区域社会认知项把粒子拉向整个群体验证过的好区域。三个力平衡得好粒子群就能在开发和探索之间走出一个舒适区。我实践下来惯性权重w从0.9线性递减到0.4效果最稳前期w大粒子飞得猛覆盖空间广后期w小粒子收着飞仔细在gbest附近打磨。c1和c2我一般取1.5不会出现一方过于强势把粒子拽进局部最优爬不出来的情况。还有一个容易被忽视的点速度必须限幅。如果某维速度设置得太大粒子可能直接飞出参数边界后面还得做边界反射徒增混乱。我通常把速度限制在参数区间宽度的十分之一范围比如树数量的搜索区间是[10, 300]宽度是290那速度范围就设为[-29, 29]这样粒子单步移动不会跨越半个搜索空间搜索轨迹更平滑。2.3 参数编码与适应度函数设计把PSO用到RF调参上最关键的设计决策有两个一是粒子位置向量怎么对应到超参数二是适应度函数怎么定义。位置编码我采用最直接的连续量映射整数量方案。粒子位置是一个1×3的连续向量三个维度分别对应树数量、最小叶子大小、特征采样数。计算适应度时先对位置向量做round取整再钳制到参数区间内传给随机森林训练函数。为什么不直接用整数型Particle呢因为粒子群的速度更新公式本质上是为连续空间设计的用整数粒子做加减会很快死板损失搜索灵活性。连续量取整虽然会让相邻两个位置映射到同一个整数上稍微牺牲了一点搜索精度但换来的是整个PSO框架的稳定和简洁这笔账是划算的。适应度函数我选择了五折交叉验证的均方误差MSE。为什么不直接在训练集上训练然后看训练误差因为那必然过拟合RF复杂度高的时候可以把训练集误差打到极小但你拿到测试集上立刻就现原形。交叉验证的均方误差能反映模型在同一分布不同子集上的稳定表现是更可信的适应度度量。注意这里选MSE而不是R2也不纯是习惯问题——PSO搜索的目标是让预测值与真实值的绝对偏差尽量小MSE作为优化目标与工程目标一致。R2更适合作为最终结果的报告指标不太适合直接当优化目标因为R2对预测值整体缩放不敏感优化它的含义不够直观。另外说一个细节适应度函数里的交叉验证折数并非固定不变。数据量小例如几百个样本时可以用五折甚至十折最大化利用数据数据量大几万样本时三折就够否则每评估一组参数都要训练三次完整模型粒子群几十个粒子跑下来非常煎熬。这个折数同样可以作为一个可调变量放进配置区别写死在代码里。3. MATLAB代码实现与实操记录3.1 环境准备与工具选择fitrensemble还是TreeBagger写代码之前先解决一个MATLAB版本选择问题。老版本里做随机森林回归常用的API是TreeBagger代码形如model TreeBagger(nTrees, X_train, Y_train, Method, regression, MinLeafSize, minLeaf, NumPredictorsToSample, numPred);但TreeBagger属于老一代接口新版本R2016b之后官方推荐用fitrensemble配合templateTree可控参数更丰富配合crossval做交叉验证也更顺手。我这个项目用的是fitrensemble具体的树结构参数全部通过templateTree传入。注意templateTree里对应三个关键参数的名称是NumVariablesToSample、MinLeafSize、MaxNumSplits和TreeBagger的名字略有出入抄旧代码的时候最容易在这里栽跟头。我提供两个版本的适应度函数写法。第一个是手写K折循环版灵活度高方便在每折之间加自己的预处理逻辑第二个是MATLAB内置crossval精简版代码短适合快速验证。先看手写版。3.2 粒子群主循环代码解析粒子群的主循环是整个程序的心脏。我先把完整代码贴出来再逐段解释关键设计%% PSO-RF 主程序 clear; clc; close all; rng(42); % 固定随机种子保证结果可复现 % ---------- 加载数据 ---------- % data: nSample x nFeature 的特征矩阵Y: nSample x 1 的目标向量 load(data.mat); X data(:, 1:end-1); Y data(:, end); nSample size(X, 1); nFeature size(X, 2); % ---------- 划分训练集与测试集 ---------- cvHoldout cvpartition(nSample, Holdout, 0.2); idxTrain cvHoldout.training; idxTest cvHoldout.test; X_train X(idxTrain, :); Y_train Y(idxTrain, :); X_test X(idxTest, :); Y_test Y(idxTest, :); % ---------- PSO 参数配置 ---------- nPop 15; % 种群规模 maxIter 30; % 最大迭代次数 c1 1.5; c2 1.5; % 学习因子 wMax 0.9; wMin 0.4;% 惯性权重上下限 dim 3; % 优化参数维度 % ---------- 参数边界 ---------- lb [10, 1, 1]; % [树数量, 最小叶子, 特征采样数] 下界 ub [300, 20, nFeature]; % 上界特征采样数不能超过总特征数 vMax (ub - lb) / 10; % 速度限幅 vMin -vMax; % ---------- 初始化粒子群 ---------- x zeros(nPop, dim); v zeros(nPop, dim); pbest zeros(nPop, dim); pbestFitness inf(nPop, 1); gbest zeros(1, dim); gbestFitness inf; for i 1:nPop % 在参数范围内均匀随机初始化 x(i, :) lb (ub - lb) .* rand(1, dim); v(i, :) vMin (vMax - vMin) .* rand(1, dim); fitness rfFitness(x(i, :), X_train, Y_train); pbest(i, :) x(i, :); pbestFitness(i) fitness; if fitness gbestFitness gbestFitness fitness; gbest x(i, :); end end % ---------- 迭代搜索 ---------- convergenceCurve zeros(maxIter, 1); for t 1:maxIter w wMax - (wMax - wMin) * t / maxIter; % 惯性权重线性递减 for i 1:nPop % 速度更新 v(i, :) w * v(i, :) ... c1 * rand(1, dim) .* (pbest(i, :) - x(i, :)) ... c2 * rand(1, dim) .* (gbest - x(i, :)); % 速度限幅 v(i, :) min(max(v(i, :), vMin), vMax); % 位置更新 x(i, :) x(i, :) v(i, :); % 位置越界处理钳制回边界 x(i, :) min(max(x(i, :), lb), ub); % 用新位置训练RF并计算适应度 fitness rfFitness(x(i, :), X_train, Y_train); % 更新个体最优 if fitness pbestFitness(i) pbestFitness(i) fitness; pbest(i, :) x(i, :); end % 更新全局最优 if fitness gbestFitness gbestFitness fitness; gbest x(i, :); end end convergenceCurve(t) gbestFitness; fprintf(迭代 %d/%d最优适应度(MSE) %.4f\n, t, maxIter, gbestFitness); end % ---------- 输出最优参数 ---------- bestParam round(gbest); % 取整得到最终超参数 fprintf(最优参数树数量%d最小叶子%d特征采样数%d\n, ... bestParam(1), bestParam(2), bestParam(3));代码里有两个地方我想单独拎出来讲。第一是边界处理。我是直接钳制Clamp也就是越界后把位置拉回到边界上。这个做法的缺点是粒子容易堆积在边界上尤其当最优参数就在边界附近时粒子会反复被拉回同一个位置多样性下降。更高级的做法是边界反射让粒子越界后以一种反弹的姿态回到搜索区间内部。但从工程实践看对于RF调参这种低维度连续空间钳制已经够用反射反而会引入不必要的速度方向突变。第二是随机种子。优化算法跑出来的结果天然有波动这个波动来自两部分PSO本身初始化粒子的随机性以及RF训练时Bagging采样的随机性。为了对比不同迭代次数的效果、或者复现论文结果必须在最开始设置rng(42)。虽然这个操作看起来不起眼但没它的话你跑两次程序得到的最优参数可能差得很远连你自己都说不清算法到底稳不稳定。3.3 随机森林适应度函数完整实现适应度函数负责评估一组超参数到底好不好。前面提到的两种写法这里都给出。先看手写五折交叉验证版function fitness rfFitness(params, X_train, Y_train) % 将连续位置向量转换为整数超参数 nTrees round(params(1)); minLeaf round(params(2)); numPred round(params(3)); % 确保特征采样数不越界 numPred min(max(numPred, 1), size(X_train, 2)); nFold 5; cv cvpartition(size(X_train, 1), KFold, nFold); mseSum 0; for i 1:nFold idx cv.test(i); mdl fitrensemble(X_train(~idx, :), Y_train(~idx), ... Method, Bag, ... NumLearningCycles, nTrees, ... Learners, templateTree(MinLeafSize, minLeaf, ... NumVariablesToSample, numPred)); yPred predict(mdl, X_train(idx, :)); mseSum mseSum mean((Y_train(idx) - yPred).^2); end fitness mseSum / nFold; end如果你不想手写循环MATLAB的crossval可以编译一个更短的版本function fitness rfFitnessFast(params, X_train, Y_train) nTrees round(params(1)); minLeaf round(params(2)); numPred min(max(round(params(3)), 1), size(X_train, 2)); mdl fitrensemble(X_train, Y_train, ... Method, Bag, ... NumLearningCycles, nTrees, ... Learners, templateTree(MinLeafSize, minLeaf, ... NumVariablesToSample, numPred)); cvmdl crossval(mdl, KFold, 5); fitness kfoldLoss(cvmdl, Mode, average); end两个版本我实际都在工程里用过说下取舍。手写循环版的优势在于你可以在每折训练前加入自己的归一化、PCA降维这些操作逻辑完全透明。crossval精简版代码短、不容易写错循环索引但如果你后续要在每折之间做数据变换它就不灵活了。对于标准流程我推荐精简版一个函数搞定清晰还快。还有一个容易被忽略的性能细节fitrensemble在训练Bag集成时树的数量由NumLearningCycles控制但这个参数默认的解释单位是学习循环次数在Bag方法里一棵树就是一个循环。也就是说你设300就真的训练300棵完整的决策树。五折交叉验证等效于训练300×51500棵树。如果粒子群有15个粒子、跑30代理论上最坏情况要训练1500×15×30675000棵树这个计算量绝对不是闹着玩的所以下面第四节的性能优化必须看。3.4 结果输出与模型评估粒子群搜索结束后拿到最优超参数bestParam下一步就是用全部训练数据重新训练最终模型并在测试集上评估。这一步的代码是%% 用PSO寻优得到的最优参数训练最终模型 bestParam round(gbest); mdlFinal fitrensemble(X_train, Y_train, ... Method, Bag, ... NumLearningCycles, bestParam(1), ... Learners, templateTree(MinLeafSize, bestParam(2), ... NumVariablesToSample, bestParam(3))); %% 测试集预测与指标计算 Y_pred predict(mdlFinal, X_test); 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; fprintf(RMSE %.4f\nMAE %.4f\nR2 %.4f\n, RMSE, MAE, R2);这里有一点要提醒最终模型的训练必须重新跑一遍完整的fitrensemble而且建议用全部训练数据而不是只用其中四折。因为交叉验证只是用来评估参数的好与坏确定参数之后我们要尽可能用更多数据来训练最终模型才能榨干数据的价值。也正因为如此最终模型的性能通常比适应度函数里评估的MSE更好因为训练数据多了20%相当于免费薅了一点数据羊毛。评估指标的选择上我习惯同时输出RMSE、MAE和R2三个指标。RMSE对较大误差更敏感适合衡量模型的失误严重程度MAE更稳健不会被个别极端预测值带偏R2则直观反映模型相对均值猜法的提升幅度。比如R20.93意思就是模型预测的残差方差比直接拿均值当预测值要少93%这个说法给非技术背景的项目方解释时非常实用。4. 常见问题与调参避坑实录4.1 收敛慢与训练耗时爆炸PSO-RF最现实的痛点就是训练耗时。我调试阶段第一次跑种群20、迭代50、树数量上限500结果一个晚上没跑完。后来做了三处调整时间砍掉了百分之七十。第一砍参数边界。树数量上限从500降到200因为回归任务200棵树和500棵树的精度差异微乎其微但训练时间差了2.5倍。第二降迭代次数。迭代30次配合线性递减的惯性权重已经完全够PSO收敛再往后跑只是在毫厘之间反复试探。第三把五折交叉验证降成三折代价是适应度的方差稍微变大但换来的是每组参数评估时间几乎减半。如果数据量确实大还有一个大招用并行计算。在粒子群主循环外面先开并行池然后粒子适应度评估用parfor替代for多个粒子的RF训练可以同时进行。不过要注意parfor里调用fitrensemble时要确认每个worker都能访问到数据变量否则矩阵传递开销会抵消并行收益。MATLAB的并行池如果内存不足反而会频繁触发数据拷贝非常难受建议先用数据量较小的一批任务做验证。4.2 整数参数与边界处理不当导致PSO失效粒子群本质是为连续空间设计的但我们的超参数全是整数。如果直接把位置向量当整数用速度更新公式里的小数项会在四舍五入中丢失粒子位置长期不变算法直接陷入停滞。我的处理是位置向量全程保持连续只在适应度函数内部取整。取整位置和原始位置之间的细微差别由速度和位置更新的惯性来吸收搜索照样能够迭代。边界问题也很典型。有一次我把特征采样数的上界设成nFeature但某个粒子的位置经过多轮更新后涨到了nFeature0.8适应度函数里没有钳制直接传给了templateTreeMATLAB报参数非法错误程序当场崩溃。从那以后我在适应度函数开头统一加了一行参数合法性保护numPred min(max(numPred, 1), size(X_train, 2));主循环和适应度函数两层都要做边界钳制主循环保证粒子不会飞得太远适应度函数兜底保证任何极端输入都不会报错。这个双保险思路在自动化调参脚本里非常重要因为你永远不知道粒子会飞到哪里去。4.3 过拟合与早熟收敛的判断自适应度函数用的是交叉验证MSE理论上已经能挡住明显的过拟合。但实际中我还是遇到过一次标志性的过拟合事件测试集R2只有0.81明显低于交叉验证阶段估算的水平诊断下来是最小叶子节点样本数被PSO搜到了1树长得又深又碎把训练集噪声都背下来了。解决办法有两招一是把MinLeafSize的下界从1提到3不给PSO太激进的空间二是在最终评估时对比训练集和测试集的RMSE倍数如果测试集RMSE超过训练集的1.5倍就要警惕参数是否过拟合。早熟收敛则是粒子群自己的问题当整个群体在迭代后期全部聚集到某个局部最优附近速度趋近于零位置不再更新算法实际上已经死了。一个简单的诊断指标是看每次迭代结束后种群位置的方差如果方差连续五轮都小于参数区间宽度的百分之一说明群体多样性已经枯竭。救急的办法是在这种情况下重新随机初始化一部分粒子或者把惯性权重w临时上调0.1给群体踹一脚。我用过几次效果立竿见影但只作为应急手段不要依赖它。4.4 常见问题速查表下面是我这段时间实操积累的速查表基本覆盖了PSO-RF常见问题的定位思路。现象可能原因排查与解决办法训练极慢迭代一周跑不完种群数、迭代数、树数量上限过大优先砍树数量上限再考虑降交叉验证折数每次运行结果差异巨大没有固定随机种子程序开头加rng(42)固定RF和PSO两处随机性适应度卡住不动粒子聚集PSO早熟收敛上调惯性权重或重新随机初始化部分粒子参数越界报错主循环或适应度函数缺钳制两层都加min(max(...))边界保护测试集R2明显低于交叉验证估计参数过拟合提高MinLeafSize下界检查树数量是否过大MATLAB报参数非法特征采样数超过总特征数适应度函数内部统一钳制到[1, nFeature]这张表也是我自己调试时常用的排查单建议你把自己的数据集套进去过一遍很多时候问题出在非常低级的地方但耗掉的时间是实打实的。5. 后续扩展与个人实操心法5.1 简单有效的改进方向如果标准PSO-RF在你的数据集上效果不够好可以从两个方向做低成本改进。一是改造粒子群本身。比如加一个线性递减的惯性权重这个代码里已经写了更进阶一点可以用自适应粒子群根据群体聚集程度动态调整权重和学习因子避免早熟。另一个方向是加入变异算子每轮迭代后随机挑一个粒子把它某一维的位置随机重置到搜索空间内类似于遗传算法的变异。这个操作能有效保持种群多样性实现成本只有几行代码收益却很明显。二是扩展优化参数的维度。不要只优化三个参数把MaxNumSplits、MinParentSize也编进粒子位置向量维度从3升到5。搜索空间变大的代价是收敛速度下降所以需要同步增加种群规模或迭代次数。我做过一次对比5维PSO-RF的平均精度比3维提高了约6%但耗时增加了约40%所以是否扩维要看你的实际瓶颈是精度还是算力。5.2 这套方案能迁移到什么场景PSO-RF这套思路绝对不仅限于回归预测。把适应度函数里的拟合目标换成分类准确率或者AUC随机森林回归换成随机森林分类就变成了PSO-RF分类调参。我顺手做过一个二分类不平衡数据集实验把MinLeafSize、特征采样数和树数量交给PSO搜索分类的F1分数比默认参数提高了差不多一成。再比如时间序列预测把滞后特征构造好的样本喂进来照样可以用这套框架滚动寻优。另外粒子群优化的思想也不限于搭配随机森林。你把适应度函数里的fitrensemble换成支持向量回归、高斯过程回归甚至轻量级神经网络就得到PSO-SVR、PSO-GPR这些变体。它们在学术论文里已经很常见原理上都跑不出本文这套框架。最后分享一点我个人的体会调参优化这件事功夫有一半在算法之外。数据质量不过关特征构造不合理再强的优化算法也只是在错误的地基上精装修。我第一次跑通PSO-RF时兴奋地把测试集R2从0.87刷到了0.94后来仔细一查是数据拆分时不小心把同一设备不同时刻的样本同时灌进了训练集和测试集数据泄露带来的虚假繁荣。做这行多质疑自己的结果比多跑几轮算法更重要。PSO-RF是个好工具但请一定先确保你的数据是干净的。