简介:基于贝叶斯优化的卷积神经网络-双向长短期记忆网络(CNN-BiLSTM)回归预测Matlab实现,面向需要进行多输入单输出回归建模的科研与工程技术人员,适用于风速、负荷、交通流量、股价等连续值预测场景。模型借助贝叶斯优化算法自动寻优学习率、隐含层节点与正则化参数,避免手工调参的盲目性,同时结合CNN特征提取与BiLSTM时序建模能力,显著提升预测精度。资源共5个文件,包含4个MATLAB脚本和1个Excel数据集,脚本覆盖参数初始化、主程序运行与误差计算等完整环节,使用者只需替换数据即可运行,压缩包仅37KB,轻量便捷。评价指标包括R2、MAE、MSE、RMSE和MAPE,能够从误差与拟合优度多角度衡量模型表现。目前已有1962人学习下载,代码质量高、结构清晰,既适合深度学习回归预测初学者快速上手,也便于进阶研究者在此框架上扩展改进。
1. 贝叶斯优化的CNN-BiLSTM回归预测:一个不用手动调参的Matlab方案
做回归预测的人大概都有过这种体验:LSTM调参调到怀疑人生,学习率、隐含层节点数、正则化系数,每一项都能让验证集指标忽上忽下,网格搜索又贵得离谱。这套基于贝叶斯(bayes)优化卷积神经网络-双向长短期记忆网络(CNN-BiLSTM)的Matlab源码,把超参数搜索和模型训练打包成一个闭环,自动优化学习率、隐含层节点、正则化参数三个关键量,最后输出R2、MAE、MSE、RMSE、MAPE一套完整回归指标。适合有Matlab基础、不想手工调参的从业者,也适合拿它当模型骨架做毕业设计或横向项目的工程师。
2. 为什么是CNN-BiLSTM加贝叶斯优化:结构、时序与搜索策略
这一章先把模型的三个组成块讲透:CNN在回归预测里到底提取什么,BiLSTM比单向LSTM强在哪,贝叶斯优化为什么能在几十次评估内找到还不错的超参数。这三个问题想明白,后面改代码时你才知道动哪个旋钮会有什么后果。
2.1 CNN层提取局部特征:一维卷积如何压掉噪声
在纯LSTM网络里,每个时间步的输入是一整行特征,LSTM只能按顺序读,特征之间的局部关联完全靠记忆单元去隐式学习。CNN在这一步做的事是:用一个滑动的卷积核扫过输入序列,把相邻几个位置的特征组合成一个更高层的表示。比如卷积核大小为3时,输出特征图的每个点都聚合了原始输入里连续3个位置的信息,这就是局部感受野。
具体到Matlab实现,源码里用的是convolution1dLayer。常见的做法是先设3到5之间,核太大容易把短序列压得太快,太小又起不到平滑作用。卷积层后面一般接一个reluLayer做非线性激活,再接池化层降采样。池化不是必须的,如果输入序列本身不长,甚至可以考虑跳过池化层保留更多时序信息,效果反而更稳。很多新手在这里翻车,是因为把图像分类里的池化习惯带过来,序列数据池化过猛,训练指标会掉得很快。
% 一个典型的CNN-BiLSTM网络堆积示例 layers = [ sequenceInputLayer(featureDim) % 输入层:featureDim为特征维度 convolution1dLayer(3, 16, 'Padding', 'same') % 16个1维卷积核,核宽3 reluLayer bilstmLayer(hiddenUnits, 'OutputMode', 'last') % BiLSTM层,返回最后一个时间步 fullyConnectedLayer(1) regressionLayer ];代码里要注意的是convolution1dLayer的参数:第二个参数16表示卷积核个数,决定输出特征图通道数;'Padding','same'保证卷积后序列长度不变,这样BiLSTM看到的序列长度和原始输入一致。隐藏层节点数hiddenUnits是后面贝叶斯优化要搜索的变量,这里的值只是占位。CNN产出的是多通道特征序列,每个通道可以理解成从原始特征里提炼出来的一个子模式,BiLSTM在此基础上再做时序建模。卷积核个数和池化方式这两个参数,在源码里是固定值,不属于贝叶斯优化的搜索范围,我一般也建议保持固定,每次只动一个变量是调参时的铁律,多个变量一起动就成了玄学。
2.2 BiLSTM双向时序建模:前后文都要看
单向LSTM的隐藏状态只编码了过去的信息,这适合纯因果预测。但很多回归预测场景,比如风电功率、交通流量、电价序列,数据里既存在滞后效应,也存在相近时刻的联动。BiLSTM的做法是正向跑一遍、反向跑一遍,然后把两个方向的隐藏状态拼接起来,让输出同时携带前文和后文的信息。
在源码运行环境Matlab 2020b及以上,BiLSTM层用lstmLayer时把'Direction'设成'both'即可,不需要额外写两层再拼接。如果你改代码时发现旧版本不支持'Direction'参数,那就是环境版本问题,需要升级或手动写两个LSTM层再拼接。用LSTM做回归预测时,OutputMode是个容易被忽略的参数:后面接全连接层并输出单个预测值时,设'last';如果想输出整段序列再往后接层,设'sequence'。这边目标是一个样本对应一条预测值,设'last'最直接。
CNN和BiLSTM的连接顺序也有讲究:先CNN后BiLSTM,因为CNN先把特征压缩、降噪,BiLSTM面对的输入维度更小,训练稳定性更好。反过来不是不行,但参数量和训练时间都会明显上涨。项目数据如果每个样本只有一两百行,CNN部分用一两层就够了,堆太深反而会在小样本上过拟合。
2.3 贝叶斯优化为何比网格搜索划算:代理模型与采集函数
网格搜索在处理三个超参数时,哪怕每个参数只取10个候选,组合也有1000次训练。在Matlab里跑CNN-BiLSTM,一次训练从几十秒到几分钟不等,1000次是灾难级耗时。随机搜索虽然避免了全枚举,但每次采样都是独立事件,不参考已经跑过的点,碰到平坦区域会浪费大量评估。
贝叶斯优化的核心是用高斯过程做代理模型,把"超参数组合→损失值"拟合成一个带不确定性的函数,然后用采集函数决定下一个评估点。常用的采集函数是expected-improvement-plus,它天然在探索新区域和利用已知最优区域之间做折中。翻译成人话:代理模型猜出每个候选点的可能收益和不确定性,收益高、不确定性大的点优先被选中,所以头几次评估会跳得很远,后面越来越集中在一个盆地。
% 贝叶斯优化入口的常见写法 optimVars = [ optimizableVariable('LearnRate', [1e-3, 1e-1], 'Transform', 'log') optimizableVariable('NumHiddenUnits', [10, 200], 'Type', 'integer') optimizableVariable('L2Regularization', [1e-5, 1e-2], 'Transform', 'log') ]; results = bayesopt(@(params) cnnBiLstmObjective(params), optimVars, ... 'MaxObjectiveEvaluations', 30, ... 'AcquisitionFunctionName', 'expected-improvement-plus', ... 'IsObjectiveDeterministic', false);这里的优化变量是源码的核心配置,学习率、隐含层节点、L2正则化全部交给bayesopt自动搜索。'Transform','log'表示在log空间采样,原因是学习率和正则化通常在1e-3、1e-4这种量级才有区分度,线性空间会把大量评估浪费在大数值区间。'NumHiddenUnits'用'Type','integer'约束为整数,避免搜索到小数节点数导致训练报错。最后一行'IsObjectiveDeterministic', false告诉bayesopt目标函数是带随机性的,代理模型会把波动当作噪声处理,不会因为某一次评估值偏高就放弃整个区域。
3. 源码结构与数据格式:从main.m跑通到看懂每份文件
拿到压缩包解压后,不建议上来就点main.m运行。先把文件职责和数据格式弄清楚,能免掉后面至少一半的报错。下面按我在实际项目里拆这类源码的顺序来讲。
3.1 文件清单与调用关系
整个任务的核心文件有5个,main.m是唯一入口,其他都是被调用的子函数或配置脚本。
| 文件 | 职责 | 在流程中的位置 |
|---|---|---|
| main.m | 主程序:读数据、定义超参数搜索空间、调用bayesopt、输出最优模型与指标图 | 程序入口 |
| initialization.m | 参数初始化:设置训练轮数、验证集比例、归一化方式等全局配置 | 被main.m开头调用 |
| fical.m | 贝叶斯目标函数:内部完成数据划分、网络构建、训练、返回验证集损失 | 被bayesopt每次评估调用 |
| calulateE.m | 指标计算:计算R2、MAE、MSE、RMSE、MAPE并打印 | 训练结束后由main.m调用 |
| data.xlsx | 多输入单输出示例数据,行是样本,列是特征,最后一列是目标值 | 被main.m读取 |
这里容易忽略的是fical.m的地位。它本质上是一个黑匣子函数,贝叶斯优化每评估一组超参数,就调用fical.m训练一遍网络并返回一个标量损失。你如果自己换数据集,改的最多的除了数据文件,就是这个fical.m里网络结构的堆叠方式。很多人以为main.m才是核心,实际上main.m只负责调度,真正决定模型效果的是fical.m里的层结构、训练选项和数据划分方式。
3.2 data.xlsx的数据组织与归一化
data.xlsx是多输入单输出(MISO)格式,这个约定一定要看清楚:每一行是一个样本,前面若干列是输入特征,最后一列是待预测的目标值。不要把列顺序搞反,否则训练出来的模型指标会非常诡异,甚至出现训练集R2很高、测试集完全崩掉的情况。
%% 读取数据与归一化 data = readmatrix('data.xlsx'); X = data(:, 1:end-1); % 所有特征列 Y = data(:, end); % 最后一列是目标值 % mapminmax按行处理,所以先转置,让每个特征占一行 [X_norm, ps_x] = mapminmax(X', -1, 1); [Y_norm, ps_y] = mapminmax(Y', -1, 1); % 还原成 样本数×特征数 的格式 X_norm = X_norm'; Y_norm = Y_norm';这里有个很容易踩的坑:mapminmax是按行做归一化的,所以输入矩阵必须先转置,让每一行对应一个特征。归一化参数ps_x、ps_y要保留下来,等预测完成后用mapminmax('reverse')把输出还原回真实量纲。很多人在预测阶段忘记反归一化,得到的结果跟实际值差一个数量级,还以为是模型没收敛。归一化范围用[-1,1]还是[0,1]在回归任务里差别不大,源码里用-1到1是为了配合tanh类激活函数的输出区间。如果你的目标值有天然下界,比如功率、负荷都大于0,用[0,1]后MAPE的解释会更直观,这个可以按需改。
还有一个容易被忽略的点:data.xlsx里的样本顺序。如果数据是时间序列且按时间先后排列,划分训练集和验证集时不能随机打乱,否则模型会"看到未来"。源码里用cvpartition做的是随机划分,适合工业数据、传感器数据这类各样本相互独立的场景;如果明显带时间索引,我一般会改成按时间线截断:前70%训练,接着20%验证,最后10%测试。这一点直接决定测试集指标是真水平还是假繁荣。
3.3 跑通main.m的第一步:环境检查与路径配置
在双击运行之前先做两件事:确认工具箱齐全,确认当前目录在包根目录。CNN和BiLSTM层依赖Deep Learning Toolbox,bayesopt依赖Statistics and Machine Learning Toolbox,缺任何一个都会运行到一半时报错,前置检查能省不少排查时间。
%% 第一步:环境检查 ver('deep') % 检查Deep Learning Toolbox ver('stats') % 检查Statistics and Machine Learning Toolbox disp(pwd) % 确认当前目录是包根目录,而不是某个子目录 %% 第二步:把所有m文件加入搜索路径 addpath(pwd); savepath; % 保存路径设置,避免下次启动Matlab失效有两点需要注意。第一,addpath(pwd)只添加当前目录,子目录里的函数不会被递归加入,如果额外建了子文件夹放数据,要对子目录单独addpath。第二,Matlab函数名区分大小写,calulateE.m和CalulateE.m在部分平台上会被当成两个不同函数,源码文件名是固定拼写,改文件名时务必保持引用处一致,否则报"无法识别的函数或变量"。
路径没问题后,直接在命令行输入main回车。正常现象是控制台先出现贝叶斯优化的进度表,每一行显示一组超参数组合和对应的目标值;前几轮数值跳动很大是正常的,代理模型还在全局探索。等到进度完成,程序会打印最优超参数,然后训练最终模型输出测试集指标。第一次跑通的目标是"能出完整指标"而不是"指标非常高",先确认流程闭环,再谈调优。运行耗时方面,数据量在几百行、特征十几个维度时,单次训练大概几十秒,30次贝叶斯评估在主流台式机CPU上是半小时到一小时。如果你的数据量上了几千行,建议第一次先把MaxObjectiveEvaluations降到5,跑通之后再逐步放开。
4. 贝叶斯优化参数配置:学习率、隐含层节点与正则化怎么设
贝叶斯优化的效果,七分在目标函数封装,三分在搜索空间定义。这一章把fical.m和main.m里跟超参数相关的部分拆开,讲清楚每个变量范围怎么定、目标函数为什么返回RMSE、最终指标从哪里取。
4.1 搜索空间的定义:对数空间与整数约束
optimizableVariable是Matlab贝叶斯优化定义变量的标准方式,源码里三个变量的范围设置得很保守,直接拿来用多数数据集都能覆盖。
| 优化变量 | 范围 | Transform/Type | 说明 |
|---|---|---|---|
| LearnRate | [1e-3, 1e-1] | log | 学习率,对数空间更贴合收敛特性 |
| NumHiddenUnits | [10, 200] | integer | 隐含层节点数,必须是整数 |
| L2Regularization | [1e-5, 1e-2] | log | L2正则化系数,防止过拟合 |
学习率和L2正则化设成log变换,是因为这两种参数在1e-2和1e-3之间的差异,远比0.2和0.3之间大。线性空间会让代理模型把精力花在大数目的微小差异上,浪费评估次数。隐含层节点数用integer约束,否则搜索到50.7这种值,lstmLayer会直接报维度错误。我一般不建议把学习率下限再往下压到1e-4以下,除非数据量特别大且训练轮数特别多。学习率太小,前几十轮基本学不动,贝叶斯优化评估一组参数就要等几百轮训练,整体耗时会成倍上涨。上限设1e-1是因为Adam优化器在这个值上已经很震荡,再往上大概率出现NaN loss。
注意:换数据集时,搜索范围不要盲目扩大。空间太大,贝叶斯优化会在无关区域浪费大量评估;空间太小又会漏掉最优解。先按源码给的三个范围跑一轮,看最优解是否落在边界附近,再决定是否外扩。
4.2 目标函数封装:把训练过程变成一个可评估的黑匣子
贝叶斯优化要求目标函数接收一个参数结构体,返回一个标量损失,越小越好。所以fical.m内部要做的事情是:接收params字段、按参数构建网络、划分训练验证集、训练、在验证集上算RMSE、返回。这里贴一个简化版,对应源码里fical.m的整体结构:
function loss = fical(params) % 从params结构体中取出当前候选超参 learnRate = params.LearnRate; hiddenUnits = params.NumHiddenUnits; l2Reg = params.L2Regularization; % 载入数据,划分训练集与验证集(固定rng保证每次评估可复现) load('data.mat'); % 实际代码里可能是直接readmatrix data.xlsx rng(42); cvp = cvpartition(size(X,1), 'Holdout', 0.2); % 按当前超参构建CNN-BiLSTM网络 layers = [ sequenceInputLayer(size(X,2)) convolution1dLayer(3, 16, 'Padding', 'same') reluLayer bilstmLayer(hiddenUnits, 'OutputMode', 'last') fullyConnectedLayer(1) regressionLayer ]; % 训练选项:训练轮数、学习率、L2正则化 options = trainingOptions('adam', ... 'InitialLearnRate', learnRate, ... 'L2Regularization', l2Reg, ... 'MaxEpochs', 80, ... 'MiniBatchSize', 32, ... 'ValidationData', {XTest, YTest}, ... 'Verbose', false, ... 'Plots', 'none'); net = trainNetwork(XTrain, YTrain, layers, options); % 在验证集上预测并返回RMSE,作为贝叶斯优化的目标值 YPred = predict(net, XTest); loss = sqrt(mean((YPred - YTest).^2)); end这段代码有几个关键点值得反复看。第一,cvpartition的Holdout比例在0.15到0.3之间都可以,源码的典型做法是留20%。比例太小验证集噪声大,最优超参数会漂移;比例太大训练数据不足,学到的模型不稳定。第二,目标函数只返回验证集RMSE,不在函数内部打印R2或画图,因为bayesopt会调用这个函数几十次,每次训练都出图的话,内存和视觉体验都是灾难。第三,训练选项里Plots必须设'none',否则每轮评估都会弹出训练进度窗,Matlab界面会卡到几乎没法操作。
4.3 从贝叶斯结果到最终指标:R2、MAE、MSE、RMSE、MAPE
bayesopt跑完会返回一个BayesianOptimization对象,最优超参数在results.XBest,最优损失在results.MinObjective。真正对外报告指标时,要用这一组最优超参数重新训练一次,划分出一个测试集,由calulateE.m统一计算五个指标。
% calulateE.m 的核心计算逻辑 R2 = 1 - sum((YTest - YPred).^2) / sum((YTest - mean(YTest)).^2); MAE = mean(abs(YTest - YPred)); MSE = mean((YTest - YPred).^2); RMSE = sqrt(MSE); MAPE = mean(abs((YTest - YPred) ./ YTest)) * 100;逐个解释一下每个指标的使用场景。R2是最直观的拟合优度,接近1说明模型解释了大部分方差,但R2对离群点不敏感,一个样本预测偏很多时R2的跌幅远小于RMSE。MAE和RMSE一起看可以判断误差分布形态:两者接近,说明误差分布均匀;RMSE明显大于MAE,说明存在少数大误差样本,数据里可能有离群点。MAPE衡量相对误差,业务端最常问的就是"平均差几个百分点",但注意YTest为0时MAPE会算成inf,遇到含0的数据集要么过滤这些样本,要么换成SMAPE这类指标。调参时看验证集RMSE,出报告时看测试集五个指标,这两者别混。
5. 避坑与排查:跑bayes-CNN-BiLSTM最容易翻车的五个地方
这个源码整体流程是顺的,但用户在换数据、换环境时最容易在下面五个环节翻车。每一条都是会真实报错或结果异常的情况,按"现象→原因→解决"的思路写,你遇到了可以直接对号入座。
5.1 报错"无法识别的函数或变量"
现象:运行main.m后,第一行就提示某个函数名无法识别,常见的是fical或calulateE。原因有两个方向:一是m文件不在当前搜索路径里,二是文件名拼写或大小写和调用处不一致,Matlab在Windows下通常不区分大小写,但在某些部署环境或函数缓存里会出问题。 解决:先执行which fical,看Matlab能不能定位到这个文件。如果返回"未找到",先执行addpath(genpath(pwd))把整个目录树加进路径,再执行clear functions清掉旧函数缓存。如果which能找到但依然报错,检查文件名大小写是否与调用处完全一致,这是最隐蔽的一个坑。
5.2 ValidationData格式错误报错
现象:trainNetwork运行时报错类似"ValidationData must be a cell array",或者验证集数据的维度不匹配。 原因:trainNetwork的验证集必须用cell数组包起来,即'ValidationData', {XVal, YVal},而且XVal、YVal的行数要一致,如果数据划分时忘了重新索引,行数对不上就会报错。 解决:在fical.m训练之前强制加一段维度检查,用assert保证训练集和验证集样本数匹配、特征维度与sequenceInputLayer一致。另外注意,经过mapminmax归一化后X是样本×特征,而sequenceInputLayer期望的输入通常是特征×时间步,如果数据不是序列而是多特征单步,需要用permute调整维度,或者改用featureInputLayer,这点在换数据时特别容易忽略。
5.3 贝叶斯优化跑得太慢,一次就要几小时
现象:MaxObjectiveEvaluations设置成30,每次训练80轮,CPU环境下一次优化能跑一整个白天。 原因:这是预期行为,不是bug。贝叶斯优化的总耗时是评估次数乘以单次训练时间,数据量大、epochs多、又是CPU训练时,时间被一下子放大。 解决:第一轮验证流程时把MaxObjectiveEvaluations改成5到8,先把闭环跑通,指标大概符合预期后再加次数。训练选项里的MaxEpochs也可以先压到30轮,用"效果够看但时间减半"的思路快速验证。有Parallel Computing Toolbox时,在bayesopt里加'UseParallel', true,多核会把单个评估并行出去,通常能省一半以上的时间。
5.4 换了自己的数据后,预测结果全是常数或R2为负
现象:训练过程不报错,loss也在降,但预测输出几乎是一个常数,或者R2算出来是负的。 原因:这是回归预测里最经典的三个坑叠加。第一,特征里有NaN或常数列,CNN和BiLSTM从中学不到有效梯度;第二,归一化参数ps_x、ps_y用了全量数据统计,测试集信息提前泄漏进了训练过程,验证时指标虚高,真实测试时崩掉;第三,目标值YTest存在0值导致MAPE为inf,看起来像是整体指标炸了。 解决:数据进模型前先做清洗,用isnan(sum(data,2))找NaN行,用std(data(:,1:end-1))找出方差接近0的常量列并删掉。归一化必须拆成训练集fit、测试集transform两步,mapminmax做不到这一点,需要手工分别计算训练集的min和max再应用到测试集。
% 只统计训练集的归一化参数,测试集不参与 minX = min(XTrain, [], 1); maxX = max(XTrain, [], 1); XTrainNorm = (XTrain - minX) ./ (maxX - minX); XTestNorm = (XTest - minX) ./ (maxX - minX);这算是典型的血泪经验,凡是预测结果像一条水平直线的情况,先查这两处,比调任何超参数都管用。另外还有一种很隐蔽的情况:特征和目标值的量纲差别特别大,比如特征是温度、湿度这种O(1~100)的数值,目标是公斤这种O(10^4)的数值。即使归一化做了,如果各特征归一化后的尺度不统一,CNN卷积核学到的东西也会偏向大数值列。检查方式很简单,画出每个特征与目标的散点图,看看有没有明显线性关系的列,先把能做线性预测的列留着,再把其余列交给CNN和BiLSTM去学。
5.5 同样数据跑两次,最优超参数不一样
现象:同一份数据,不加任何改动,重复运行main.m,两次输出的最优学习率、节点数不一样,测试集指标也有波动。 原因:CNN和BiLSTM的权重初始化、训练过程中的MiniBatch采样都有随机性,而bayesopt在早期阶段本来就是在探索,随机性会在代理模型里放大。这个不叫bug,是这类模型的固有随机性。 解决:在main.m开头统一固定随机种子,rng(0)或rng(42)都行;数据划分用cvpartition固定一个划分方式。但要注意,固定种子只保证同一次运行内可复现,不同运行之间指标仍会有小波动,这是正常的。真正判断超参数好坏,至少看三次独立运行的中位数,不要被单次最好结果带偏。如果连续几次运行的最优超参数差别都很大,那通常说明数据量太小,模型本身处于欠定状态。这时候与其继续调参数,不如先增加样本量或做特征筛选。贝叶斯优化只能帮你把超参数选得更快,弥补不了数据本身的缺陷。
6. 换成你自己的数据集:数据清洗、K折验证与泛化检验
当流程跑通、指标符合预期,接下来的关键动作就是把自己的数据换进来。这一章讲三个进阶操作:替换数据时的格式约束,从单次划分升级到K折验证,以及一个每次必做的泛化检查。
6.1 替换数据的三个硬约束
自己准备数据时,保持"行=样本、列=特征、末列=目标值"的约定不变。换数据后第一件事不是直接跑,而是先做一次数据体检:检查NaN、删除常数列、统一单位。特征数量变了之后,fical.m里sequenceInputLayer的输入维度是自动从数据取的,不需要手动改,但要注意excel里如果存在文本列,readmatrix会读成NaN,这一条很多新手会漏。
6.2 从Holdout到K折交叉验证
源码默认用一次Holdout划分,数据量在几千行以内时,单次划分的验证指标波动不小。想更稳地评估超参数,可以把fical.m改成K折交叉验证:每轮评估对K个fold分别训练,取平均损失作为目标值。代价是评估时间乘以K,实操中K取3或5比较推荐,配合'UseParallel', true,并行能把额外时间抵消掉一部分。
6.3 测试集指标与验证集指标的分离习惯
提示:测试集指标必须来自完全没参与优化的数据。贝叶斯优化过程里打印的MinObjective是验证集RMSE,两者要分开记录。
贝叶斯优化过程中打印的最小目标值,只用来搜索超参数。最终对外汇报时,必须用完全没参与优化的测试集计算五个指标。我见过不少项目把优化过程中的MinObjective当最终指标写进报告,结果换数据后实际误差扩大30%以上,这就是验证集信息渗入超参数选择的典型后果。
从那以后我每次换数据集,都强制走一遍相同的流程:先查NaN和常量列,再固定rng和划分,用训练集拟合归一化参数,确认测试集完全隔离后,才交给贝叶斯优化。这套流程救过我至少三次把R2跑成负数的翻车现场,也帮你把调参时间从一天压缩到一顿饭的功夫。希望帮到你。
本文还有配套的精品资源,点击获取