6.3 正则化、交叉验证与预测区间:模型选择不能偷看答案
训练误差下降,新数据上的误差却可能上升,原因包括过拟合、分布偏移和泄漏。正则化限制模型自由度;预处理和系数在各训练折拟合,再用对应验证折比较正则强度。最终测试集则留到选择流程固定后再使用。
本课目标
- 区分 ridge、lasso 与 elastic net 的目标;
- 用 pipeline 把预处理放入交叉验证;
- 根据独立、分组或时间结构选择切分;
- 报告基线、预测误差和预测区间。
1. Ridge 与 Lasso 惩罚不同
Ridge:
Lasso:
- ridge 连续收缩相关特征系数,通常不精确归零;
- lasso 可产生稀疏解,但相关特征间选择可能不稳定;
- elastic net 混合 L1/L2,常在相关特征组中更稳健。
截距通常不惩罚,具体库行为需确认。惩罚改变了优化目标;正则化系数不能直接按普通 OLS p 值解释。
上式使用未归一化的平方误差和,库参数未必采用同一约定。scikit-learn Ridge 使用 SSE 加 L2 惩罚,Lasso则用 SSE 除以 alpha 乘 L1 范数。两个估计器的 alpha 数字相同,不表示惩罚强度相同。
2. 缩放决定惩罚公平性
一个以字节计、一个以比例计的特征尺度差异巨大。相同系数惩罚在原尺度上代表不同函数变化,所以正则化前通常标准化数值特征。
标准化参数只能在每个训练折拟合。scikit-learn pipeline 示例:
示例使用 scikit-learn 1.4 及以上版本,按顺序运行。四个完整队伍留作最终测试,其余 16 个队伍用于模型选择。数值缺失在折内填补;样本没有类别缺失,预测时未知类别会映射为全零 one-hot 块,这只是兜底表示,不代表模型学会了新类别。
import numpy as np
import pandas as pd
rng = np.random.default_rng(31)
frame = pd.DataFrame({
"team_id": np.repeat(np.arange(20), 5),
"team_size": rng.integers(2, 9, 100).astype(float),
"resources_used": rng.uniform(10, 50, 100),
"mission_type": rng.choice(["survey", "repair"], 100),
})
y = 30 - 2 * frame["team_size"] + .4 * frame["resources_used"] + rng.normal(0, 3, 100)
frame.loc[3, "team_size"] = np.nan
features = ["team_size", "resources_used", "mission_type"]
train_mask = frame["team_id"] < 16
X_train, y_train = frame.loc[train_mask, features], y.loc[train_mask]
X_test, y_test = frame.loc[~train_mask, features], y.loc[~train_mask]
team_ids_train = frame.loc[train_mask, "team_id"]
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.linear_model import Ridge
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
numeric = ["team_size", "resources_used"]
categorical = ["mission_type"]
preprocess = ColumnTransformer([
("num", Pipeline([
("impute", SimpleImputer(strategy="median")),
("scale", StandardScaler()),
]), numeric),
("cat", OneHotEncoder(handle_unknown="ignore", sparse_output=False), categorical),
])
pipeline = Pipeline([
("preprocess", preprocess),
("model", Ridge()),
])交叉验证前先在全数据拟合预处理,会让验证值影响缩放、填补或学习出的类别表。Pipeline 把这些拟合放进切分内部,但提前在 Pipeline 外做过的处理仍可能泄漏。
3. 三层数据职责
- training:拟合参数;
- validation/CV:选择特征、模型与超参数;
- test:只在最终方案固定后估计泛化表现。
反复查看 test 结果再调整模型,test 已成为验证集。需要保留新的独立数据,或诚实报告评估偏乐观。
嵌套交叉验证在内层选择超参数,在外层评估整套选择流程,减少用同一批折同时选择和评分带来的乐观偏差。切分仍要匹配部署人群与依赖结构,多套一层不等于自动无偏。
4. 切分单位必须匹配应用
独立样本
对独立、可交换的观测,打乱后的 K-fold 可作起点。仍要检查各折样本数和关键群体覆盖,普通 K-fold 不保证这些分布均衡。
重复实体
若目标是推广到新用户或新队伍,同一实体应完整地留在一折。若目标是预测已知实体的未来记录,保留这些实体可能合理,但仍须遵守时间与标签可用性边界。
时间预测
训练必须早于验证;随机打散会用未来分布预测过去。使用 rolling/expanding window,并在特征窗口与标签窗口之间设置必要 gap。
空间或站点泛化
若要部署到未见站点,应留出完整站点或空间块。随机拆分相邻记录,可能把共享环境信息同时留在两侧;留出单位应对应部署时真正的新单位。
5. 超参数选择包含不确定性
from sklearn.model_selection import GridSearchCV, GroupKFold
search = GridSearchCV(
pipeline,
param_grid={"model__alpha": [0.01, 0.1, 1.0, 10.0, 100.0]},
scoring="neg_mean_absolute_error",
cv=GroupKFold(n_splits=5),
)
search.fit(X_train, y_train, groups=team_ids_train)评分指标应与业务损失一致。搜索范围、折分随机性和候选数量都会影响“最佳”alpha。可使用 one-standard-error rule 选择性能接近但更简单的模型。
GroupKFold(n_splits=5) 至少需要五个不同训练组,并保证验证组不出现在对应训练折。GridSearchCV 默认将选定 Pipeline 在全部训练数据上重拟合。各折共享大量训练数据,因此 one-standard-error rule 是选择启发式,不是独立样本意义下的置信保证。
6. 指标与基线
- MAE:目标单位,较少放大极端误差;
- RMSE:平方惩罚,大误差权重高;
:相对均值基线的平方误差改善; - MAPE:零值附近不稳定,并对低值偏重。
至少比较:
- 训练集均值/中位数基线;
- 简单业务规则;
- 当前生产模型;
- 新候选模型。
总体平均还要按群体、时间和目标范围切分,防止某一大群体掩盖小群体严重误差。
from sklearn.dummy import DummyRegressor
from sklearn.metrics import mean_absolute_error, root_mean_squared_error
prediction = search.predict(X_test)
baseline = DummyRegressor(strategy="median").fit(X_train, y_train).predict(X_test)
print({
"test_mae": mean_absolute_error(y_test, prediction),
"test_rmse": root_mean_squared_error(y_test, prediction),
"baseline_mae": mean_absolute_error(y_test, baseline),
"selected_alpha": search.best_params_["model__alpha"],
})最终测试记录不参与 alpha 选择,也不参与中位数基线拟合;分组报告还应确认每个留出组都有记录。通常的
7. 预测区间不是均值区间
均值响应置信区间估计
同方差线性模型可给解析区间,但分布错设、异方差和分布漂移会破坏覆盖。可考虑:
- 条件方差模型;
- 分位数回归;
- conformal prediction(在交换性等条件下提供边际覆盖);
- 按群体检查实际覆盖率。
只报告点预测会让用户误以为模型精度相同。区间宽度本身也是决策信息。
另一个 split conformal 小例子说明校准步骤:先固定预测器,在独立校准样本上收集绝对残差,再取有限样本修正后的分位数。
from math import ceil
import numpy as np
# Toy absolute errors from a separate calibration set, with the predictor already fixed.
calibration_errors = np.arange(1., 20.)
alpha = 0.10
n_calibration = len(calibration_errors)
rank = ceil((n_calibration + 1) * (1 - alpha))
q = np.inf if rank > n_calibration else np.sort(calibration_errors)[rank - 1]
new_prediction = 30.0
prediction_interval = (new_prediction - q, new_prediction + q)
print(prediction_interval) # (12.0, 48.0)校准与未来分数可交换时,该构造提供边际覆盖,不保证每个特征值或每个群体都有 90% 覆盖。这里的 19 个示意残差只演示秩计算;不能再用校准标签调预测器。重复实体或分布偏移需要考虑相应结构的方法。参见 Angelopoulos 与 Bates 的介绍。
8. Lasso 选择不等于稳定解释
在高度相关特征中,lasso 可能随机保留一个、把另一个压零;换一个折或轻微扰动数据,选择就变化。零系数不证明特征“没有作用”,非零也不代表因果重要。
评估:
- bootstrap/折间选择频率;
- 系数路径;
- 相关特征组;
- out-of-sample 性能;
- 领域合理性。
若目标是确认性变量选择和推断,需要专门方法和预设分析,不能先用 lasso 筛选再把普通 OLS p 值当未选择过。
9. 模型交付记录
目标与使用范围
训练/验证/测试时间和实体边界
特征定义与可用时点
预处理参数和类别策略
候选模型与超参数搜索
总体与分组误差
预测区间覆盖
已知失效区间和 fallback
数据/代码/模型版本
监控与重训触发条件常见误区
- Lasso 归零表示特征无用:选择受尺度、相关性和 lambda 影响。
- 交叉验证前统一缩放没关系:验证折信息已泄漏。
- 随机 K-fold 适合所有数据:实体、时间和站点结构决定切分。
- 置信区间就是单个预测的范围:均值区间与预测区间不同。
练习
- 比较同一数据在缩放前后 ridge 系数与预测。
- 对重复队伍数据比较随机 K-fold 与 GroupKFold 分数。
- 用时间切分找出随机切分造成的未来泄漏。
- 报告 MAE、RMSE、基线和四个群体的误差与区间覆盖。
小结
正则化限制模型自由度,却不能替代独立评估。预处理必须进入 pipeline,切分必须反映真实泛化目标,测试集只能承担一次最终评估。模型输出还要连同区间、群体误差和失效范围交付。
下一章进入特征工程:特征不是越多越好,而是必须在预测时可获得、与实体和时间对齐,并由训练数据拟合转换参数。