做电力负荷预测这几年,我最大的体会不是模型要多高级,而是选对方法、处理好细节。这篇文章记录一个用MATLAB实现岭回归(Ridge)做电力负荷预测的完整项目,从问题背景、数据处理、核心代码到GUI设计,全部展开讲清楚。如果你刚接触负荷预测,或者正被多重共线性折腾得头疼,这篇应该能帮上忙。整个项目里的程序我都整理成可以复现的脚本,你按步骤替换成自己的负荷数据就能跑起来。
先说清楚这个项目到底在做什么:针对某地区台区的小时级负荷数据,构建以温度、湿度、日历特征、历史负荷为核心的特征体系,通过岭回归建立预测模型,预测未来24小时的逐时负荷曲线,最后用MATLAB App Designer做一个可交互的桌面工具,让不熟悉代码的人也能一键导入数据、训练模型、查看预测结果和误差指标。
1. 电力负荷预测的真实痛点:为什么普通回归容易翻车
1.1 负荷数据是多变量强耦合的
电力负荷数据表面看只是一串随时间变化的数字,实际上背后叠加了很多层规律。日内有早晚高峰,工作日和周末的曲线形态完全不同,夏季高温制冷负荷猛增,冬季寒潮取暖负荷飙高,节假日期间工业负荷下降但商业和居民负荷又有自己的节奏。所以可以说,负荷是一个典型的多变量耦合问题,只用单一特征去建模一定不够。
刚开始做这个项目时,我习惯性地把所有可能相关的特征一股脑塞进去:温度、湿度、风速、气压、星期几、是否节假日、上一小时负荷、前一天同一时刻负荷等等。结果普通最小二乘回归(OLS)训练时表现还凑合,一到测试集就原形毕露,预测值忽高忽低,甚至出现温度系数为负这种明显违背常识的情况。问题就出在特征之间的高度相关性上。
温度、湿度、体感温度高度相关,星期编码和周末标记高度相关,历史负荷的多个滞后项之间也天然存在相关性。这就好比同时用一把尺子、一根绳子和一只脚去量同一段距离,工具之间互相打架,最后量出来的结果当然不稳定。统计学上把这种现象叫作多重共线性。
1.2 多重共线性让最小二乘系数失去稳定性
普通最小二乘的核心是最小化残差平方和,当特征之间强烈相关时,设计矩阵的列向量接近线性相关,(X^TX) 接近奇异矩阵。求逆之后系数估计的方差会被放大到难以接受的程度,导致回归系数对训练数据的微小变化极其敏感。
可以看一组直观现象:换一个随机种子,或者删掉几个训练样本,普通的回归系数数值可能变化很大,甚至符号翻转。这种模型在训练集上笑呵呵,在测试集上哭唧唧,完全是过拟合的表现。负荷预测场景里,我们希望模型在未见过的日期上也能稳定输出合理曲线,所以必须对系数做约束,让模型不要那么“敏感”。
1.3 本篇项目场景定一个明确的小目标
为了避免内容空泛,我先给这个项目定一个具体目标:用过去80天的小时级负荷数据训练模型,预测第81到第82天的未来24小时负荷曲线。数据采样间隔为1小时,每天24个点。预测目标变量是整点负荷值,输入特征是温度、湿度、日历属性以及不超过168小时的历史负荷滞后项。
这个场景定下来之后,所有代码都围绕它展开。你完全可以把它改成15分钟粒度的96点预测,或者改成日峰值预测,原理完全一致。
2. 岭回归原理拆解:L2惩罚项到底在做什么
2.1 从最小二乘到岭估计的公式变化
最小二乘的优化目标是让预测值与真实值的残差平方和最小:
[ \min_{\beta} |y - X\beta|^2 ]
岭回归在目标函数后面加了一个L2惩罚项:
[ \min_{\beta} |y - X\beta|^2 + \lambda |\beta|^2 ]
其中 (\lambda) 是岭参数,也叫正则化系数。它的闭式解是:
[ \hat{\beta}_{\text{ridge}} = (X^TX + \lambda I)^{-1}X^Ty ]
和普通最小二乘的 ((X^TX)^{-1}X^Ty) 对比,区别就是在 (X^TX) 的对角线上加了一个 (\lambda)。这个加法让原本可能接近奇异、求逆不稳定的矩阵变得“稳当”起来。
在MATLAB里写这个解不需要手动求逆,用左除运算更稳定:
beta = (X' * X + lambda * eye(size(X,2))) \ (X' * y);这里有一个细节:通常不对截距项做惩罚,因为惩罚截距会让整个基线偏移。处理办法是把单位矩阵第一个对角元素置为0,表示常数项不参与正则化。
2.2 岭参数λ的物理含义:偏差和方差的权衡
岭回归本质上是在偏差和方差之间做交易。(\lambda) 越大,系数被压缩得越狠,模型方差快速下降,但偏差会慢慢增大。(\lambda) 太小,惩罚作用微弱,退化成普通最小二乘,老问题又回来了。
可以这么理解:(\lambda) 就像一个音量旋钮,拧小了噪声听得很清楚,拧大了连正常声音都压没了,合适的位置刚好让背景噪声降到不干扰主旋律。实际项目中,(\lambda) 从0变化到较大值的过程中,测试误差通常先下降再上升,呈现一条U形曲线,谷底附近就是值得选择的参数区间。
还有一个容易忽略的问题:惩罚项对每个特征施加同样的力度,如果特征量纲差异巨大,比如温度是几十这个量级,历史负荷是几千这个量级,那么惩罚实际作用就不均匀。所以做岭回归前,对特征做标准化处理几乎是必须的。这也是不少新手直接拿原始数据套ridge函数效果不好的原因之一。
2.3 岭迹图与交叉验证:两种实用的选λ方法
第一种方法是岭迹图。把所有特征在不同 (\lambda) 下的系数画成曲线,横轴是 (\lambda),纵轴是标准化后的系数值。当 (\lambda) 从小到大变化时,系数会从剧烈摆动逐渐走向稳定。选一个曲线基本平稳、不再大起大落的 (\lambda) 区间,再在区间里取偏小的值,这样既能控制方差,又不会过度压缩信号。
第二种方法是交叉验证。把训练数据按时间顺序分成若干段,轮流拿一段做验证集,其余段做训练集,比较不同 (\lambda) 下的验证误差。注意负荷数据是时间序列,切分时不能随机打乱,否则会引入未来信息,让验证结果虚高。这一点后面第7章还会专门说。
如果时间紧张,直接按80%和20%切分一次验证也够用。数据量大的时候,K折交叉验证更稳妥,一般K取5或10。
3. 数据准备与特征工程:决定预测上限的第一步
3.1 构建特征体系:日历、气象、历史负荷三层
特征工程决定预测能力的上限,模型只是逼近这个上限。在这个项目里,我把特征分成三类:
| 特征类别 | 具体特征 | 对应含义 |
|---|---|---|
| 日历特征 | 小时、是否周末、星期几 | 刻画日内高峰、周内规律 |
| 气象特征 | 温度、湿度、前3小时滑动平均温度 | 反映制冷制热需求和建筑热惯性 |
| 历史负荷 | 前1小时负荷、前24小时负荷、前168小时负荷 | 刻画负荷的自相关性和周期性 |
为什么要有前168小时(前一周同一时刻)的负荷?因为负荷有很强的周周期性,上周同一天的负荷形态对本周同一天有很强的参考价值。类似地,前24小时负荷负责抓日周期性,前1小时负荷负责抓短时惯性。
3.2 模拟出一份可复现的负荷数据
为了让你能完整跑通流程,我先生成一份模拟负荷数据。它包含基础负荷曲线、早晚双高峰、高温制冷负荷、周末效应和随机扰动,尽量贴近真实数据的典型特征。
rng(42); % 固定随机种子,保证结果可复现 totalHours = 100 * 24; % 100天,小时级 hourOfDay = repmat((0:23)', 100, 1); dayIdx = floor((0:totalHours-1)' / 24) + 1; % 基础负荷:700kW基线 + 早上9点高峰 + 晚上18点高峰 baseProfile = 700 + 80 * exp(-(hourOfDay - 9).^2 / 8) + 90 * exp(-(hourOfDay - 18).^2 / 6); % 温度序列:30天周期波动 + 随机扰动 tempTrend = 10 * sin(2 * pi * dayIdx / 30); temp = 22 + tempTrend + 3 * randn(totalHours, 1); % 高温时段制冷负荷:温度超过28度后线性增加 coolLoad = 6 * max(0, temp - 28) .* (hourOfDay > 7 & hourOfDay < 22); % 周末效应:负荷下降8% isWeekend = double(ismember(mod(dayIdx, 7), [0, 6])); weekFactor = 1 - 0.08 * isWeekend; % AR(1)随机噪声,让序列更接近真实负荷 noise = filter(1, [1 -0.75], 5 * randn(totalHours, 1)); load = (baseProfile + coolLoad) .* weekFactor + 250 + noise; load = max(150, load);真实项目中,你只需要用readtable或readmatrix读取自己的Excel或CSV数据,替换掉上面这段生成逻辑,后续所有步骤完全通用。
3.3 缺失值、异常值和归一化处理
负荷数据从现场采集回来往往有脏数据。缺失值最简单的处理方式是取前一天同一时刻的值填补;连续缺失较多时,可以用插值法。异常值一般用3σ原则识别,也就是偏离均值超过三倍标准差的点先标记出来,再结合相邻时刻做平滑修正。
处理完缺失值和异常值后,最关键的步骤是归一化。我推荐对特征矩阵做z-score标准化,也就是减去均值除以标准差。注意:标准化时用的均值和标准差必须在训练集上计算,然后应用到测试集,绝不能直接对全部数据统一标准化,否则测试集的信息会渗入训练过程,这叫信息泄漏,会让评估结果虚高。
% 仅以训练集统计量做标准化 muX = mean(Xtrain(:, 2:end)); % 第一列是常数项,不参与标准化 sdX = std(Xtrain(:, 2:end)); XtrainStd = [ones(size(Xtrain,1), 1), (Xtrain(:, 2:end) - muX) ./ sdX]; XtestStd = [ones(size(Xtest,1), 1), (Xtest(:, 2:end) - muX) ./ sdX];标签值y也可以做标准化,预测完再还原回去。如果y不标准化,训练也没问题,但统一处理之后数值更稳定。切记还原的时候用训练集的均值标准差。
4. 核心代码实现:从最小二乘到岭回归的训练闭环
4.1 特征矩阵构建与训练/测试集划分
先构造滞后特征。注意生成滞后特征时,前168小时没有历史数据,会产生NaN,需要用rmmissing删掉这些无效行。
lag1 = [nan; load(1:end-1)]; lag24 = [nan(24, 1); load(1:end-24)]; lag168 = [nan(168, 1); load(1:end-168)]; tempRoll = movmean(temp, [3 0]); % 当前时刻与前3小时平均温度 % 拼成表格,删除NaN行 data = table(hourOfDay, isWeekend, temp, tempRoll, lag1, lag24, lag168, load); data = rmmissing(data);接下来构建设计矩阵。第一列加入全1向量作为截距项。
X = [ones(height(data), 1), data.hourOfDay, data.isWeekend, ... data.temp, data.tempRoll, data.lag1, data.lag24, data.lag168]; y = data.load;按时间顺序切分训练集和测试集,前80%做训练,后20%做测试。
n = size(X, 1); nTrain = floor(n * 0.8); Xtrain = X(1:nTrain, :); ytrain = y(1:nTrain); Xtest = X(nTrain+1:end, :); ytest = y(nTrain+1:end);4.2 实现一个岭回归训练函数
虽然MATLAB自带ridge函数,但我更喜欢自己写核心训练逻辑,这样每一步都清清楚楚,也方便扩展成自适应选λ。下面的函数实现了岭估计:
function beta = ridgeFit(X, y, lambda) % X: 设计矩阵,第一列为常数项 % y: 标准化后的负荷值 % lambda: 岭参数 p = size(X, 2); I = eye(p); I(1, 1) = 0; % 截距项不惩罚 beta = (X' * X + lambda * I) \ (X' * y); end这段代码就是公式的直接翻译。用左除而不是显式求逆,数值稳定性和速度都更好。我自己在测试集上跑下来,左除和inv结果基本一致,但当特征维度增加时,左除的稳定性优势会越来越明显。
4.3 岭迹图与交叉验证寻找最优λ
先对特征和标签做标准化,再画岭迹图:
muX = mean(Xtrain(:, 2:end)); sdX = std(Xtrain(:, 2:end)); XtrainStd = [ones(nTrain, 1), (Xtrain(:, 2:end) - muX) ./ sdX]; muY = mean(ytrain); sdY = std(ytrain); ytrainStd = (ytrain - muY) / sdY; lambdas = logspace(-3, 3, 30); betaPath = zeros(size(XtrainStd, 2), numel(lambdas)); for i = 1:numel(lambdas) betaPath(:, i) = ridgeFit(XtrainStd, ytrainStd, lambdas(i)); end figure; plot(lambdas, betaPath(2:end, :)', 'LineWidth', 1.2); set(gca, 'XScale', 'log'); xlabel('lambda'); ylabel('标准化系数'); title('岭迹图');从岭迹图上可以看出,当λ较小时系数波动剧烈,λ增大后曲线逐渐收敛稳定。接着用简单的时序验证集选λ:
nVal = floor(nTrain * 0.2); Xval = XtrainStd(nTrain-nVal+1:end, :); yval = ytrainStd(nTrain-nVal+1:end); Xtr = XtrainStd(1:nTrain-nVal, :); ytr = ytrainStd(1:nTrain-nVal); cvRmse = zeros(size(lambdas)); for i = 1:numel(lambdas) beta = ridgeFit(Xtr, ytr, lambdas(i)); predVal = Xval * beta; cvRmse(i) = sqrt(mean((yval - predVal).^2)); end [~, bestIdx] = min(cvRmse); lambdaBest = lambdas(bestIdx); fprintf('最优lambda = %.4f\n', lambdaBest);4.4 测试集预测与结果还原
用选定的λ在完整训练集上重新训练,再预测测试集。因为标签y做了标准化,预测结果要还原回实际负荷值:
beta = ridgeFit(XtrainStd, ytrainStd, lambdaBest); XtestStd = [ones(size(Xtest,1), 1), (Xtest(:, 2:end) - muX) ./ sdX]; ytestStd = (ytest - muY) / sdY; predStd = XtestStd * beta; predLoad = predStd * sdY + muY; trueLoad = ytestStd * sdY + muY;这里有个容易踩坑的地方:对测试集做标准化时,必须继续使用训练集算出的muX和sdX,而不是重新计算测试集的均值方差。一旦重新计算,相当于测试集信息提前泄露,预测误差会被低估。
5. 结果评估:用三个指标看清模型真实水平
5.1 评估指标定义与MATLAB实现
负荷预测常用的三个指标是均方根误差(RMSE)、平均绝对百分比误差(MAPE)和决定系数R²。
| 指标 | 公式 | 说明 |
|---|---|---|
| RMSE | (\sqrt{\frac{1}{n}\sum (y_i - \hat{y}_i)^2}) | 误差绝对水平,量纲和负荷一致 |
| MAPE | (\frac{1}{n}\sum \left|\frac{y_i - \hat{y}_i}{y_i}\right| \times 100%) | 相对误差百分比,直观 |
| R² | (1 - \frac{\sum (y_i - \hat{y}_i)^2}{\sum (y_i - \bar{y})^2}) | 模型解释力,越接近1越好 |
对应的MATLAB代码:
rmseVal = sqrt(mean((trueLoad - predLoad).^2)); mapeVal = mean(abs((trueLoad - predLoad) ./ trueLoad)) * 100; ssRes = sum((trueLoad - predLoad).^2); ssTot = sum((trueLoad - mean(trueLoad)).^2); r2Val = 1 - ssRes / ssTot; fprintf('RMSE = %.2f kW\n', rmseVal); fprintf('MAPE = %.2f%%\n', mapeVal); fprintf('R2 = %.4f\n', r2Val);MAPE要注意负荷值很低的夜间时段,如果实际负荷只有150kW,哪怕误差只有30kW,百分比误差也会飙到20%,所以看MAPE的同时一定要结合RMSE,不能只看单一指标。
5.2 岭回归与普通最小二乘的对比
以本文模拟数据集某次运行为例,对比结果大致如下:
| λ取值 | 训练RMSE(kW) | 测试RMSE(kW) | 测试MAPE(%) |
|---|---|---|---|
| 0(OLS) | 42.3 | 58.7 | 6.2 |
| 10 | 45.1 | 51.9 | 5.4 |
| 50 | 52.8 | 53.6 | 6.0 |
数值在不同随机种子下会有浮动,但趋势稳定:λ从0增大后,训练误差小幅上升,测试误差明显下降,说明过拟合得到缓解。λ再继续增大,两边误差一起变大,模型被过度压缩,表达力不足。
这个对比也解释了为什么项目里坚持要调λ而不是默认λ=0。岭回归的价值不在训练集上表现更好,而在于泛化能力更稳。
5.3 可视化:预测值对比曲线和误差分布
检查预测效果时,我会画两张图。第一张是连续48小时的真实负荷和预测负荷曲线对比,直观观察峰谷是否跟得上。第二张是预测误差直方图,看误差是否近似正态分布,如果出现明显的偏态或有规律波动,往往说明某些特征没有建模到位。
figure; t = 1:48; plot(t, trueLoad(1:48), 'b-', 'LineWidth', 1.5); hold on; plot(t, predLoad(1:48), 'r--', 'LineWidth', 1.5); legend('真实负荷', '预测负荷'); xlabel('小时'); ylabel('负荷/kW'); title('连续48小时负荷预测对比'); figure; histogram(trueLoad - predLoad, 30); xlabel('预测误差/kW'); ylabel('频数'); title('预测误差分布');如果曲线整体滞后半小时到一小时,说明滞后特征权重过大;如果峰值系统性偏低,说明温度或时段特征的交互项不足,这些都可以通过观察图快速定位。
6. GUI设计:把训练流程变成可交互的桌面工具
6.1 界面布局:从用户角度拆分功能
只写脚本文件,自己用还行,但要交给不懂代码的同学或同事操作,就必须做一个界面。MATLAB当前推荐用App Designer,而不是老旧的GUIDE。App Designer的组件拖拽体验、回调函数管理和代码结构都比GUIDE清晰。
我设计的界面分成三个区域:
- 左侧控制面板:数据导入按钮、训练模型按钮、预测按钮
- 中间参数区:λ输入框(留空则自动交叉验证)、结果显示文本框
- 右侧绘图区:两个坐标轴,左轴显示岭迹图,右轴显示预测对比图
控件清单如下:
| 控件类型 | 控件名 | 功能 |
|---|---|---|
| 按钮 | ImportButton | 选择CSV数据文件 |
| 按钮 | TrainButton | 训练岭回归模型 |
| 按钮 | PredictButton | 预测选定时段并出图 |
| 编辑框 | LambdaEditField | 手动输入岭参数 |
| 文本区 | MetricsTextArea | 输出RMSE、MAPE、R² |
| 坐标轴 | TraceAxes | 绘制岭迹图 |
| 坐标轴 | PredAxes | 绘制真实/预测对比曲线 |
6.2 回调函数如何串联整个流程
App Designer是事件驱动的,点击按钮就会触发对应的回调函数。整个流程像一条流水线:导入按钮负责读数据并存储到app.Data;训练按钮从app.Data取数做特征工程、标准化、训练,把模型存到app.Model;预测按钮从app.Model取模型做预测并更新两个坐标轴和指标文本框。
这种设计的好处是数据和状态都挂在App对象上,回调之间通过app.xxx共享,不用到处定义全局变量,代码结构清晰很多。
6.3 关键回调代码展示
下面是三个核心回调的骨架代码:
% 导入数据按钮回调 function ImportButtonPushed(app, event) [file, path] = uigetfile({'*.csv;*.xlsx'}, '选择负荷数据文件'); if isequal(file, 0) return; end app.DataTable = readtable(fullfile(path, file)); app.StatusLabel.Text = ['已加载: ', file]; end% 训练模型按钮回调 function TrainButtonPushed(app, event) % 从表格取已经物理量化的列 loadVec = app.DataTable.load; tempVec = app.DataTable.temp; hourVec = app.DataTable.hour; weekVec = app.DataTable.is_weekend; % 构造特征矩阵,具体逻辑参考第4章 [X, y] = app.buildFeatures(loadVec, tempVec, hourVec, weekVec); % 划分训练/测试集 n = size(X, 1); nTrain = floor(n * 0.8); Xtrain = X(1:nTrain, :); ytrain = y(1:nTrain); Xtest = X(nTrain+1:end, :); ytest = y(nTrain+1:end); % 自动寻优或手动指定λ lambdaInput = str2double(app.LambdaEditField.Value); if isnan(lambdaInput) lambdaBest = app.autoSelectLambda(Xtrain, ytrain); else lambdaBest = lambdaInput; end app.Model.beta = ridgeFit(Xtrain, ytrain, lambdaBest); app.Model.muX = muX; app.Model.sdX = sdX; app.Model.muY = muY; app.Model.sdY = sdY; % 更新岭迹图 plot(app.TraceAxes, lambdas, betaPath(2:end,:)', 'LineWidth', 1.2); set(app.TraceAxes, 'XScale', 'log'); app.StatusLabel.Text = ['训练完成,λ = ', num2str(lambdaBest)]; end% 预测按钮回调 function PredictButtonPushed(app, event) % 取测试集最后48小时做展示预测 [predLoad, trueLoad] = app.predictWrap(48); % 更新预测对比图 plot(app.PredAxes, 1:48, trueLoad, 'b-', 1:48, predLoad, 'r--'); legend(app.PredAxes, '真实负荷', '预测负荷'); xlabel(app.PredAxes, '小时'); % 更新指标 rmseVal = sqrt(mean((trueLoad - predLoad).^2)); mapeVal = mean(abs((trueLoad - predLoad) ./ trueLoad)) * 100; app.MetricsTextArea.Value = sprintf('RMSE = %.2f kW\nMAPE = %.2f%%', rmseVal, mapeVal); end把这些回调对应绑定到按钮的ButtonPushedFcn,界面就能完整跑起来。实际做的时候我建议先跑通脚本流程,再封装成回调,不要把调试和界面开发混在一起,否则会非常痛苦。
7. 实测中绕不开的五个坑与处理思路
7.1 时间序列不能随机打乱
第1次做交叉验证时,我直接用了通用的cvpartition随机划分,结果测试误差低得离谱,一度以为模型完美了。后来才发现,训练集里混入了测试时段附近的样本,模型相当于提前看到了未来信息,这叫数据泄漏。负荷预测是时间序列问题,训练集只能是测试集的过去,不能用未来。正确的做法是按时间先后切块,或者做滚动验证。
7.2 归一化的均值和标准差必须来自训练集
这个坑紧随其后。把全部数据放一起做标准化再划分训练测试,看似方便,实际测试集的统计信息已经被模型偷看到了。正确做法是先划分,再计算训练集的muX、sdX,用它们去标准化测试集。对y同理。项目里如果发现测试误差远低于训练误差,第一时间检查是不是这里出了问题。
7.3 注意MATLAB内置ridge函数的默认缩放行为
MATLAB自带的ridge函数默认会对预测变量做中心化和标准化,返回的系数是经过缩放处理的。如果不了解这个默认行为,直接把系数套到原始数据上预测,结果往往对不上。建议要么像我一样自己写闭式解函数,要么在调用时明确设置缩放参数为0,使用原始尺度系数。
% 使用内置函数时,指定scaled=0,更直观 betaBuiltIn = ridge(y, X, lambda, 0);7.4 多步预测时误差会不断累积
用前1小时负荷做特征时,预测未来第2小时就面临一个问题:第1小时的预测值还没被验证,该用真实值还是预测值作为特征?如果简单用预测值迭代下去,误差会像滚雪球一样越来越大。处理思路有两种:一是改特征,只用前24小时和前168小时这类较远的滞后项,减少对邻近时刻的依赖;二是采用滚动预测加定期修正,比如每预测1小时就与最新真实值对齐一次。实际系统中我倾向于后者。
7.5 节假日和极端天气会让模型瞬间失灵
纯日历特征只区分了工作日和周末,但法定节假日的负荷形态往往比周末更低,调休上班的周末反而和工作日类似。模型不感知这些特殊日期,预测就会出现明显的单点大误差。解决办法是在特征工程阶段加入节假日标记列,并对极端高温日单独训练一个修正模型,或者至少把节假日数据单独标注出来,不要让它污染正常模式的学习。
最后再分享一点实际体会
整套流程做完之后,我自己最受益的反而不是岭回归本身有多厉害,而是它逼着我把特征工程和验证环节做扎实。以前做普通回归时,模型“看似”能自动从一堆特征里学出关系,其实系数早被共线性搞乱了,我还一直以为是自己数据不够多。换成岭回归后,系数稳定了,特征的重要性也看得更清楚。如果你后面想继续深入,可以用贝叶斯优化替代网格搜索去自动选λ,也可以把岭回归换成弹性网络,再引入自动特征筛选,效果还能再上一个台阶。这篇就先到这,有问题欢迎留言交流。