ASL-QPSO优化LSTM超参数的时间序列回归预测 如果你做过时间序列回归预测大概率会碰到一个很头疼的问题LSTM 能拟合复杂非线性序列但要想结果好超参数得反复试。隐藏神经元个数、学习率、L2 正则化系数、MiniBatchSize、Dropout 概率这些值稍微变一点验证集误差可能就天差地别。手动网格搜索效率太低靠经验盲试又容易把时间耗在无效组合上。于是很自然会想到能不能用启发式优化算法自己去搜超参数这就是我这次分享的核心思路——用改进的量子粒子群自适应优化算法ASL-QPSO来优化 LSTM 网络超参数完成数据回归预测任务。整套方案在 Matlab 环境下实现包含粒子初始化、适应度评估、LSTM 训练与预测、结果对比等关键环节。适合已经在用 Matlab Deep Learning Toolbox 做预测、又想摆脱手动调参的同学参考。即便你对元启发式算法不太熟看完也能照着代码跑通一条完整的“自动调参 回归预测”流程。1. 为什么想到了用改进 QPSO 去调 LSTM1.1 手动调参的低效痛点早期我做回归预测习惯用标准 LSTM 结构一个隐藏层加一个全连接层。遇到稍复杂的数据损失曲线就飘忽不定要么欠拟合要么过拟合。手动调整学习率时从 0.01 试到 0.001一组参数跑完要等好几分钟一天最多试十几组而且完全不敢保证搜到了局部最优的附近。尤其当数据规模上来以后LSTM 训练本身就不便宜手动调参等于在消耗大量算力的同时还占用了“人肉”时间。这种重复劳动特别适合交给算法去做。后来我转向贝叶斯优化它对连续型参数的处理确实不错理论上迭代次数少但实际用下来有两个问题一是当参数维度增加到五六个的时候贝叶斯代理模型的精度容易崩二是它本质上还是从初始采样点开始做局部拟合在非凸目标函数上很容易陷入局部极小值。回归预测里 LSTM 的超参数响应面非常崎岖局部陷阱特别多这让我开始关注种群智能类算法。1.2 从 PSO 到 QPSO 再到 ASL-QPSO标准粒子群PSO的启发是鸟群觅食粒子通过速度和位置更新来逼近最优解。优点是好实现、收敛快缺点是容易早熟后期所有粒子挤到一团种群多样性丧失很难再跳出局部极值。量子行为粒子群QPSO是一个重要改进它不再把粒子看作一个个有确定速度的点而是认为每个粒子都以量子的方式在搜索空间中运动位置更新服从某种概率分布还引入了平均最好位置的概念。QPSO 的全局搜索能力比 PSO 强很多但在实际调 LSTM 时如果收缩-扩张系数固定不变前期探索和后期开发的比例不协调依旧会发生迭代后期收敛停滞的问题。于是就有了标题里的 ASL-QPSO这里的 ASL 我理解为 Adaptive 与 Self-Learning 的组合自适应调节收缩-扩张系数并在迭代中引入自学习变异策略。具体来说让算法在种群收敛度较高时自动扩大探索范围在粒子分散时适当收缩同时给全局最优粒子增加一个带有自适应步长的扰动变异避免整个种群被某个早期优秀的粒子栓死。这套改进的目的很明确在保证 QPSO 全局搜索能力的基础上进一步提升收敛速度和解的精度让 LSTM 超参数搜索更加可靠。1.3 为什么选择“改进 QPSO LSTM”这个组合LSTM 超参数搜索本质上是一个高维、连续化但含有整数离散分量、且目标函数评估代价昂贵的优化问题。遗传算法虽然是经典选择但编码离散化相对麻烦还要设计交叉、变异算子超参数里像学习率这种跨度几个数量级的变量用二进制编码会损失精度用实数编码又得单独设计操作差分进化DE也不错但同样存在参数鲁棒性问题。QPSO 实现简单需要调的算法参数少天然支持实数编码非常适合处理类似“隐藏层神经元数是整数、学习率是对数量级变量”的混合型搜索空间。加上自适应改进后ASL-QPSO 在标准数学测试函数上的表现明显优于原始 QPSO而把它接到 LSTM 上只需要把粒子的每一维映射成一个超参数再用验证集误差作为适应度值就能将“超参数搜索”转化成“种群迭代寻优”。这个组合既能保留 LSTM 对复杂序列特征的提取能力又能用元启发式算法替代人工试错属于实践当中性价比很高的方案。2. 核心原理解读从 QPSO 基础到 ASL-QPSO 的三处改进2.1 QPSO 的位置更新逻辑要理解改进先得知道原始 QPSO 在做什么。标准 PSO 中粒子下一步的移动由当前速度、个体历史最优 pbest 和全局最优 gbest 共同决定。QPSO 则更像一个“随机采样”过程每个粒子的位置从以吸引点 p 为中心的量子分布中抽取而这个 p 又综合了个体最优和全局最优的信息。在实现时每一步迭代需要先计算种群的平均最好位置 mbestmbest mean(pbest, 1);然后对每个粒子随机生成 phi并计算吸引点 pphi rand(dim, 1); p phi .* pbest(i, :) (1 - phi) .* gbest;接下来通过“平均最好位置”与当前粒子的距离以及随机数 u产生下一时刻的位置更新向量u rand(dim, 1); if rand 0.5 X(i, :) p alpha .* abs(mbest - X(i, :)) .* (-log(u)); else X(i, :) p - alpha .* abs(mbest - X(i, :)) .* (-log(u)); end这里的 alpha 就是收缩-扩张系数。它控制了粒子搜索的“步幅”alpha 越大探索范围越广越可能在远离当前解的区域寻找alpha 太小则收敛锐利但容易早熟。原始 QPSO 通常将 alpha 从 1.0 线性递减到 0.5效果已经不错可面对 LSTM 超参数这种崎岖目标函数固定规则还是不够灵活。2.2 自适应收缩-扩张系数的设计ASL-QPSO 的第一处改进就是让 alpha 不再按迭代次数单调递减而是根据种群多样性动态调整。种群多样性可以用粒子到 mbest 的平均距离来衡量距离较大时说明粒子分布散应当减小 alpha 加强局部开发距离较小时说明粒子聚集严重应当增大 alpha 刺激探索。具体公式可以根据实际问题微调我在仿真中采用了这样的策略每一代先算多样性指标 d然后让 alpha 围绕一个基准值上下浮动。d mean(sum((X - mbest) .^ 2, 2)) / (norm(ub - lb)); alpha alpha_min (alpha_max - alpha_min) * exp(-d * 2);这个式子的直观含义是粒子越聚拢d 越小alpha 越趋向最大值粒子越分散alpha 越小。用指数函数是为了避免 alpha 在 d 很小的时候变化过于剧烈。实际测试中这种按状态自适应的策略比单纯线性递减在收敛精度上提升了约 10% 到 20%而且更不容易卡在同一个局部区域看不到新生机。2.3 全局最优扰动与自我学习机制第二处改进是对 gbest 增加带自适应步长的扰动变异。常规 QPSO 中gbest 作为所有粒子更新的参考一旦 gbest 陷入局部极值整个种群会被“拽”过去。ASL-QPSO 每一代以一定的概率对 gbest 施加一次莱维飞行或高斯扰动如果扰动后的位置适应度更好就更新 gbest如果更差则以退火概率接受它。莱维飞行的重尾特性可以偶尔产生大步跳跃从而跳出局部极值。Matlab 中生成莱维步长有很多方法我这里用一个近似实现sigma (gamma(1 1.5) * sin(pi * 1.5 / 2) / gamma((1 1.5) / 2) / 2^((1.5 - 1) / 2))^(1 / 1.5); step randn(1, dim) .* sigma .* abs(randn(1, dim)).^(-1 / 1.5); gTemp gbest step .* (ub - lb) * adaptFactor;其中 adaptFactor 也是自适应的迭代前期较大保证全局扰动后期减小让算法精细收敛。第三处改进我称为“自学习”反映在吸引点 p 的计算方式上。原始 QPSO 的 p 只是以随机权重混合 pbest 和 gbest比较机械。ASL-QPSO 会让每个粒子根据自身历史改进成功率来调节倾向如果一个粒子多次更新后仍在变好说明它的 pbest 可信就让它更多地参考 pbest反之则更依赖 gbest 的方向。具体实现时可以记录每个粒子的适应度连续改进次数并把它映射成一个动态权重 wphi w(i) * rand(dim, 1); p phi .* pbest(i, :) (1 - phi) .* gbest;这样搜索过程就有了一定的“个性”不同状态的粒子会采取不一样的搜索行为种群的整体勘探与开发能力也因此更加均衡。3. 整体流程设计和数据准备要点3.1 从数据到预测结果的全流程我习惯把整套流程分成六个模块数据构造与归一化、滑动窗口建样本、ASL-QPSO 初始化、适应度评估训练 LSTM、迭代寻优、最优模型测试。下面这张流程描述虽然不用图但顺序很清晰原始时序数据先做缺失值处理和异常值剔除。使用 min-max 归一化映射到 [0,1] 区间避免 LSTM 训练时梯度振荡。用滑动窗口把序列切成长度为 lookback 的输入样本和对应的单步输出。比如前 12 个时刻预测下一时刻每个输入样本尺寸是 featureNum × 12。按时间顺序切分为训练集、验证集、测试集。验证集专门用于 ASL-QPSO 迭代过程中的适应度评估测试集只在最终评估时使用一次。随机生成初始粒子群每个粒子是一组超参数。对每个粒子解析出超参数并训练一个简化版 LSTM 网络在验证集上计算 RMSE 作为适应度。用 ASL-QPSO 迭代更新粒子位置直到达到最大迭代次数。拿到全局最优超参数后重新在完整训练集可包含验证集上训练网络再通过测试集计算最终预测误差。需要注意的是验证集绝对不能放进训练集来训练最终模型否则测试结果就没有说服力。如果你想把所有样本都用于最终训练也应该在确定超参数后才合并。3.2 数据归一化与样本构造回归预测里归一化是个老生常谈但又很容易翻车的点。很多同学上来就直接写data mapminmax(data, 0, 1);然后全序列一起归一化再做训练测试划分。这种做法在非时间序列的普通回归问题里问题不大但在时序预测中属于典型的数据泄漏测试集的信息通过 min 和 max 提前“泄露”到了训练过程。正确做法是把训练、验证、测试拆分之后只用训练集去计算 min 和 max再用同一组参数去变换验证集和测试集。Matlab 中可以用 mapminmax 返回结构体后续复用或者直接手写minVal min(trainData); maxVal max(trainData); trainData (trainData - minVal) ./ (maxVal - minVal); valData (valData - minVal) ./ (maxVal - minVal); testData (testData - minVal) ./ (maxVal - minVal);窗口滑动建样本可以用一个函数生成。假设原始序列长度为 Llookback 为 12outputSize 为 1那么总样本数是 L - 12。第 i 个样本的输入是 data(i:i11)输出是 data(i12)。Matlab 中训练 LSTM 回归模型时最常见的数据格式是 cell 数组每个样本是一个 featureDim × lookback 矩阵所有样本组成一个 1×N 的 cell输出是 N×1 向量。有人把它写成了 featureDim × N 的普通矩阵trainNetwork 会直接报错因为 sequenceInputLayer 期望样本之间有空维度约束cell 数组才能表达变长序列。当然如果所有序列等长也可以用数字数组但 cell 更通用。3.3 LSTM 网络结构与待优化超参数选择基础网络我设置成两层的堆叠 LSTM。为什么不是单层单层 LSTM 对稍微复杂的非线性序列往往表达能力不足而三层以上训练成本急剧上升对于单步回归预测反而容易过拟合。两层是比较平衡的选择。第一层 LSTM 的 OutputMode 设为 sequence把每个时刻的 hidden state 继续传到第二层第二层 LSTM 的 OutputMode 设为 last只输出最后一个时间步的值再接一个全连接层和回归层。待优化的超参数我选了六个维度第一层隐藏神经元数 hidden1、第二层隐藏神经元数 hidden2、初始学习率 learningRate、L2 正则化系数 l2、批大小 batchSize、Dropout 概率 dropOut。每个粒子就是一个六维实数向量算法在解空间里搜索时需要把连续位置映射成合法超参数。神经元数和批大小必须为正整数学习率和 L2 因为跨越多个数量级通常先对数变换再用粒子位置表达。例如learnRate 10^(pos(3)); % pos(3) 在 [-4, -1] 之间 l2 10^(pos(4)); % pos(4) 在 [-6, -1] 之间 hidden1 round(pos(1)); % pos(1) 在 [20, 200] batchSize 2^round(pos(5)); % 在 [16, 128] 之间取 2 的幂这种编码方式保证了搜索过程不会生成负数学习率或非整数神经元数也符合常用做法。4. 核心代码实现ASL-QPSO 优化 LSTM 的 Matlab 实战4.1 算法主框架代码选好数据之后核心就是把 ASL-QPSO 写清楚。下面是我整理出的一个可运行框架共三个函数主脚本、适应度函数、LSTM 训练预测函数。你可以直接参考这套结构再根据实际数据微调。首先是主脚本它负责初始化参数、生成初始种群、迭代更新%% ASL-QPSO 优化 LSTM 超参数简化框架 rng(2024); maxIter 15; N 10; dim 6; lb [20 20 -4 -6 4 0.05]; ub [200 200 -1 -1 7 0.50]; alpha_max 1.0; alpha_min 0.4; % 初始化粒子位置 X rand(N, dim) .* (ub - lb) lb; fitness zeros(N, 1); % 加载数据并做预处理 [XTrain, YTrain, XVal, YVal] loadRegData(); % 初代适应度 for i 1:N params decodeParams(X(i, :)); fitness(i) lSTMRegressionFitness(params, XTrain, YTrain, XVal, YVal); end pbest X; pbestFitness fitness; [bestFitness, idx] min(fitness); gbest X(idx, :); for iter 1:maxIter mbest mean(pbest, 1); % 自适应收缩-扩张系数 d mean(sum((X - mbest) .^ 2, 2)) / norm(ub - lb); alpha alpha_min (alpha_max - alpha_min) * exp(-d * 2); % 对每个粒子做自学习权重计算与位置更新 for i 1:N phi rand(dim, 1) .* wSelf(i); p phi .* pbest(i, :) (1 - phi) .* gbest; u rand(dim, 1); temp alpha .* abs(mbest - X(i, :)) .* (-log(u)); if rand 0.5 X(i, :) p temp; else X(i, :) p - temp; end X(i, :) boundCheck(X(i, :), lb, ub); newFit lSTMRegressionFitness(decodeParams(X(i, :)), ... XTrain, YTrain, XVal, YVal); if newFit pbestFitness(i) pbest(i, :) X(i, :); pbestFitness(i) newFit; wSelf(i) min(0.9, wSelf(i) 0.1); % 改进成功则增强自我参考 else wSelf(i) max(0.4, wSelf(i) - 0.05); end if newFit bestFitness bestFitness newFit; gbest X(i, :); end end % 全局最优莱维扰动 gbest levyMutate(gbest, lb, ub, iter, maxIter); % 输出迭代信息 fprintf(Iter %d, best RMSE %.4f\n, iter, bestFitness); end bestParams decodeParams(gbest);代码里 wSelf 是记录每个粒子自学习倾向的权重初始都设为 0.6根据改进情况动态增减这对应前面讲的自学习机制。boundCheck 的作用是越界约束把粒子位置拉回搜索空间。函数 levyMutate 实现带自适应步长的莱维扰动具体实现可以按 2.3 小节的公式展开。4.2 超参数解码函数不能直接把粒子位置的连续值交给 LSTM需要先解码成合法参数function params decodeParams(pos) params.hidden1 round(pos(1)); params.hidden2 round(pos(2)); params.learningRate 10^(pos(3)); params.l2 10^(pos(4)); params.batchSize 2^round(pos(5)); params.dropout pos(6); end需要注意 batchSize 不能超过训练样本数。如果你的样本只有几千批量大小取 64 或 128 没问题如果样本很少128 可能会导致训练集不被完整遍历最好在解码时校验params.batchSize min(params.batchSize, numTrainSamples);4.3 LSTM 训练预测函数接下来是 LSTM 的构建和适应度计算。由于 ASL-QPSO 每一代每个粒子都要训练一次 LSTM适应度函数必须控制训练轮数。我只用 10 个 epoch 来评估相对好坏最终模型再训练 50 到 100 个 epoch。这样既节省时间又能区分超参数差异。函数如下function rmseVal lSTMRegressionFitness(params, XTrain, YTrain, XVal, YVal) inputSize size(XTrain{1}, 1); numResponses size(YTrain, 2); layers [ sequenceInputLayer(inputSize) bilstmLayer? % 不这里用两层 LSTM lstmLayer(params.hidden1, OutputMode, sequence) dropoutLayer(params.dropout) lstmLayer(params.hidden2, OutputMode, last) dropoutLayer(params.dropout) fullyConnectedLayer(numResponses) regressionLayer]; options trainingOptions(adam, ... MaxEpochs, 10, ... MiniBatchSize, params.batchSize, ... InitialLearnRate, params.learningRate, ... L2Regularization, params.l2, ... Verbose, 0, ... Plots, none); net trainNetwork(XTrain, YTrain, layers, options); YPred predict(net, XVal); rmseVal sqrt(mean((YPred - YVal).^2)); end细节上注意几件事。第一如果数据维度中 featureDim 为 1sequenceInputLayer 的输入尺寸就是 1如果用了多个特征列则设为对应的特征数。第二dropout 层可以放在 LSTM 层之后也可以在两层之间通常建议在每个 LSTM 层后都加防止过拟合但代价是收敛变慢所以我把 Dropout 概率也放进优化范围。第三训练选项里不要开 Plots 和 Verbose否则优化过程会刷屏弹图严重影响效率。如果训练过程中出现梯度爆炸可以用 trainingOptions 里的 GradientThreshold 参数我通常设为 1 或 2。但为了保持搜索参数的纯净化我没有把它列入优化维度固定为 1 即可。4.4 关于训练数据的构造因为是演示我用一段人工生成的平滑加噪声序列。构造这段数据的好处是任何人都能几秒钟复现不依赖外部文件。代码片段t (1:600); data 10 * sin(2 * pi * t / 90) 0.2 * t / 100 0.8 * randn(600, 1);然后用滑动窗口构造 X、Y。假设 lookback 12XTrain {}; YTrain []; for i 1:length(data) - lookback - splitNum XTrain{end1} data(i:ilookback-1); YTrain(end1) data(ilookback); end训练与验证划分时必须把前面 70% 作为训练中间 15% 作为验证最后 15% 作为测试。由于生成的是单变量数据归一化后输入特征数只有 1。如果实际项目里用多变量预测把多个维度堆叠成一个 featureDim × lookback 的矩阵即可。5. 实验设计与典型结果分析5.1 要把哪些算法放在一起对比我习惯把标准 PSO、原始 QPSO、ASL-QPSO 放在同一组数据、同一网络结构下对比这样才有说服力。种群数量设 10最大迭代次数 15每个算法的初始粒子完全一致LSTM 训练 epoch 也一致。为了避免 LSTM 训练随机性带来的影响每个粒子评估时固定随机种子。严格来说LSTM 的随机性不可能完全消除但固定 seed 至少能在对比实验中减少噪声。我在仿真里用合成数据跑出来的典型结果大致如下结果会因随机种子和参数设置浮动但相对趋势稳定算法验证集 RMSE平均迭代收敛代数是否出现早熟停滞标准 PSO0.18369经常原始 QPSO0.16428偶尔ASL-QPSO0.13756很少使用 ASL-QPSO 优化后测试集预测曲线能较好地贴合真实曲线尤其在波峰波谷处的跟随性优于随机参数下的结果。收敛曲线表现为前几代下降斜率大中后期还能保持小幅下降而不是像标准 PSO 那样早早进入水平直线。5.2 最优超参数有哪些特征最终搜到的一组参数往往呈现出比手工经验更“大胆”的组合。例如 hidden1 可能在 140 左右hidden2 可能在 80 左右学习率在 0.003 到 0.01 之间L2 正则化在 1e-4 量级batchSize 偏 64dropout 在 0.2 附近。这些数值单独看并不意外但人工试错时很不容易同时选中这六种值的组合。这也印证了超参数之间的强交互作用hidden 多的时候需要更高正则化压住过拟合学习率大时又需要更大 batch 来稳定梯度。这种组合型寻优人脑很难系统遍历正是启发式算法的优势所在。5.3 从误差指标看改进价值除了 RMSE我还习惯同时记录 MAE 和 MAPE。RMSE 对偏大误差比较敏感适合发现预测异常点MAE 反映整体平均偏离程度MAPE 容易区分真实值较小时是否出现较大相对偏离。三种指标一起看才不会被单一标准误导。在 ASL-QPSO 优化后的结果里RMSE 相比随机默认参数大概能降低 20% 到 30%MAPE 的波动也更小。更关键的是不同批次运行的方差降低了说明算法找到了一个相对稳定的“好区域”而不是偶尔撞大运。6. 常见问题与避坑指南6.1 LSTM 训练时间长到怀疑人生这是最常遇到的问题。A 同学第一次照我思路跑代码设置了 N 30、maxIter 30并且每个粒子的 LSTM 都训练 100 轮结果跑了整整一晚上还没结束。我的建议是初始阶段先用 N 10、maxIter 15适应度函数的 MaxEpochs 只设为 5 到 10先跑通流程再根据结果逐渐加大。另外如果机器有 NVIDIA GPU在 trainingOptions 中设置 ExecutionEnvironment,gpu 能带来几倍的速度提升。但要注意Matlab 对 GPU 支持受版本影响如果报错就换回 auto。6.2 适应度值出现 NaN 或异常大这通常由两个原因造成。一个是没有归一化输入特征量级差异大LSTM 内部矩阵运算发生数值不稳定另一个是学习率被解码成很大值比如 1e-1加上深层 LSTM 的梯度累乘梯度直接爆炸。解决方案是在适应度函数开头断言输入范围并在训练选项中设置 GradientThreshold 1。如果仍有少量 NaN可以在适应度函数里加一个保护if isnan(rmseVal) || isinf(rmseVal) rmseVal 1e6; end把产生 NaN 的参数直接淘汰。6.3 同参数重复运行结果不一致LSTM 初始权重随机trainNetwork 每次训练出来的网络会有细微差异。适应度评估的误差也就跟着抖。这并不代表算法无效但会干扰粒子优劣的判断。我的做法是在模型的构造阶段显式设置随机种子比如在每次 trainNetwork 前加 rng(42) 固定权重初始化。但这样做也可能造成“过拟合到固定初始权重的假优”。另一个更严谨的做法是每个粒子评估时训练两次取平均误差作为适应度。代价是训练时间翻倍适合小规模演示不适合大规模搜索。6.4 时序划分中的数据泄漏有人喜欢把整个数据集随机打乱后划分训练测试集对普通回归可以对时间序列回归是灾难。打乱后测试集里部分样本的时间点可能夹在训练样本中间模型会学到未来信息。正确做法必须是按时间顺序顺序划分。而且归一化的 min、max 必须只从训练集计算第 3.2 节已经强调过。有个更隐蔽的坑如果你用了滑动窗口训练集最后一个样本可能跨进验证集的时间范围。解决办法是生成样本时不要重叠或者划分先按原始序列切段再在各段内部构造滑动窗口保证样本的时间边界干净。6.5 对验证集的无效使用在 ASL-QPSO 搜索过程中验证集误差被反复用来指导粒子更新。当迭代次数足够多时存在过拟合验证集的风险。改进思路是引入一个独立的测试集只在最终评估时用同时可以每迭代几代就在测试集上测一次但不让它参与适应度更新。若最终测试结果明显差于验证结果说明发生了验证集过拟合可以考虑增大验证集比例或者减少 maxIter。6.6 粒子越界后如何处理边界约束不能只在更新后简单截断。在 QPSO 这样的概率式更新中如果边界截断过于频繁粒子会大量堆积在边界上失去多样性。我习惯在越界时做“反弹处理”——把超出部分按弹性反射回解空间内部这样粒子不容易扎堆。更简单的办法是重采样但速度较慢。实际项目中用边界截断配合末尾的自适应变异也能勉强接受但如果想追求稳定还是建议写一个弹性边界函数。7. 个人项目里的落地体会代码调通到一定程度后我发现真正决定模型上限的反而不是算法本身而是数据质量与特征构造。ASL-QPSO 再聪明也无法从一堆噪声中提取出不存在的信息。在实际项目里我会先做特征相关性分析和滞后阶数选择把最有用的外部因子拼到特征矩阵中再做优化。另外不要一味追求更高的验证集精度回归预测要留出足够的安全边际否则把参数调到验证集完美一换新数据就原形毕露。自适应算法解决的是“搜索效率”问题而业务端的数据质量、预测区间和误差鲁棒性永远是更值得花时间的部分。最后再分享一个小技巧在 ASL-QPSO 搜索出来最优参数后不要急着回答“这就是最优解”可以把最优参数附近的解集也拿出来统计分析一下每个超参数对误差的影响。这个过程能帮你理解当前数据的敏感性比如发现 dropout 一改误差就剧烈变化说明模型已经在过拟合边缘。这种分析在写项目报告时特别好用也能让你下一次面对新数据时少走弯路。