学习目标

完成本课后,你将能够:

核心问题改变树的深度、正则化强度或学习率,结果可能发生明显变化。如何公平地选择设置,而不是不断尝试直到某个方案偶然在测试集上得分很高?

1. 1. 参数与超参数的不同角色

参数是根据训练数据估计的模型内部数值,例如线性模型权重、系数或决策树阈值。超参数是在训练之前或训练流程外围设定的,它控制模型如何构建,例如树的最大深度、正则化强度、邻居数量、学习率或训练轮数。

不要只根据名称判断。关键是数值的来源:如果优化过程根据训练数据估计它,它就是参数;如果实验者或搜索程序根据验证表现选择它,它就是超参数。

2. 2. 超参数控制什么

超参数会影响模型可表达的复杂度以及优化过程。更深的树可以表达更细致的规则,但也更容易拟合噪声。正则化限制系数大小,可能降低方差;限制过强则可能造成欠拟合。

对神经网络而言,学习率决定优化器每一步的大小:过大可能无法收敛,过小则会使训练变慢。训练轮数决定训练持续时间,batch size 会影响梯度更新方式和计算负载。

超参数配置示例配置:λ = (λ₁, λ₂, …, λₖ)
参数 — 训练前设置参数 → 训练 → 超参数 → 训练前设置参数训练超参数训练前设置
训练前设置

3. 3. 建立有意义的搜索空间

搜索空间定义超参数、允许取值和有效组合。不要一开始就设置数百个随意组合。应结合模型知识、数据规模、评价指标和资源。树深度可以使用较小的整数范围;正则化强度通常适合使用对数尺度,例如 0.001 到 100。

有些参数是有条件的:`max_depth` 属于树模型,而 `C` 适用于 scikit-learn 中某些 SVM 和逻辑回归模型。若未为不同模型定义独立空间,不应把不兼容的参数混在同一次搜索中。

4. 4. 搜索策略

手动调优适合初步诊断和验证假设。网格搜索会评估指定的所有组合,过程透明,但很快可能变得昂贵。随机搜索从指定分布中抽取组合;当只有少数维度真正重要时,它往往更高效。

贝叶斯优化利用以往试验结果选择下一批有希望的配置。它可能节省昂贵的运行,但需要合理的目标函数、约束和评估流程。有限预算下,没有任何方法能保证找到全局最优配置。

搜索空间 — 预算搜索空间 → 网格搜索 → 随机搜索 → 预算搜索空间网格搜索随机搜索预算
预算

5. 5. 在哪里调优:训练集、验证集与测试集

训练集用于拟合模型参数。验证集或交叉验证用于比较超参数。测试集应保持未使用状态,直到对最终选定流程进行评估。

如果反复依据测试集选择设置,决策就会逐渐适应测试集,它不再是独立检查。数据较少时可使用交叉验证:模型在部分折上训练,在留出的折上评估。对于时间或分组数据,折的划分必须尊重其结构。

选择准则选择:λ* = arg minλ CV(λ)
训练 — 最终检查训练 → 验证 → 测试 → 最终检查训练验证测试最终检查
最终检查

6. 6. Python 实践:Pipeline 与搜索

特征转换应放在 Pipeline 内部,并在每个训练折上拟合。否则缩放或特征选择可能提前看到验证数据,造成泄漏。下面的分类示例使用 RandomizedSearchCV:缩放器和估计器组成 Pipeline,搜索仅使用训练数据及其内部交叉验证。

这里 `C` 对应逻辑回归,因此搜索空间保持一致。真实项目还应记录评价指标、random_state、折数和计算预算。

代码示例

from scipy.stats import loguniform
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import RandomizedSearchCV, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)

pipeline = Pipeline([
    ("scale", StandardScaler()),
    ("model", LogisticRegression(max_iter=3000))
])

search = RandomizedSearchCV(
    pipeline,
    param_distributions={"model__C": loguniform(1e-3, 1e2)},
    n_iter=20,
    scoring="f1",
    cv=5,
    random_state=42,
    n_jobs=-1,
)
search.fit(X_train, y_train)

print("Best hyperparameters:", search.best_params_)
print("Mean CV F1:", search.best_score_)
print("Final test F1:", search.score(X_test, y_test))

7. 7. 调优是一种权衡,而不是神奇数字

除了平均指标,还应比较各折之间的波动、训练时间、模型大小、对随机种子的敏感性,以及重要数据分组上的表现。如果两个配置的差异小于估计的不确定性,更简单、更便宜的模型可能更合适。

选定后,记录搜索空间、库版本、随机种子、划分方案、指标和最佳超参数。选择结束后只进行一次最终测试评估。如果根据测试结果再次修改模型,就需要新的独立测试集或新的实验。

实践任务

选择一个分类数据集。固定评价指标,并创建分层 train/test 划分。在 train 上使用 Pipeline 和 5 折交叉验证比较三个 `C` 值。记录指标均值与标准差、耗时和选定配置。选择完成后,仅在 test 上评估一次。说明为什么测试指标没有参与搜索。

参考资料与延伸阅读

  1. scikit-learn:估计器超参数调优
  2. scikit-learn:RandomizedSearchCV
  3. scikit-learn:Pipeline 与组合估计器
  4. Bergstra & Bengio (2012):随机搜索超参数优化
  5. scikit-learn:交叉验证

课程术语表

参数根据训练数据估计的模型内部数值。
超参数控制模型结构或训练过程的设置。
搜索空间超参数允许取值及其组合的集合。
验证用于选择模型和设置的评估过程。
网格搜索评估预先定义网格中的所有组合。
随机搜索从定义的搜索空间中抽样组合。
贝叶斯优化利用先前试验结果进行的序贯搜索。
交叉验证在多个数据划分上重复评估。
Pipeline作为一个整体进行训练的转换与估计器序列。
计算预算对时间、内存或实验次数的限制。

自测

请选择一个答案。检查后将显示解释。

问题 1

线性模型中的哪一项属于参数?

问题 2

树模型的 `max_depth` 控制什么?

问题 3

通常何时使用验证集?

问题 4

随机搜索相对网格搜索的一个优势是什么?

问题 5

为什么不应反复使用测试集选择模型?

问题 6

为什么在交叉验证时把 scaler 放进 Pipeline?

问题 7

两个配置的表现几乎相同时,还应比较什么?

问题 8

是否应使用最终测试集选择超参数?