为了从监督学习模型中获得最佳性能和泛化能力,通常需要进行一个核心的优化步骤。大多数机器学习算法都有特定的设置,称为超参数,它们不是在训练过程中直接从数据中学习的,而是预先设定的。例子包括逻辑回归或支持向量机中的正则化强度 $C$,随机森林中的树的数量(n_estimators),或梯度提升中的学习率。超参数的选择能显著影响模型性能、泛化能力和训练时间。找到这些设置的良好组合对于构建高性能模型通常很重要。手动通过试错调整超参数是可行的,但随着超参数数量或可能值范围的增加,它会迅速变得低效且不可靠。很容易错过最优组合,或者花费过多时间在超参数空间的次优区域进行试探。因此,系统性的方法更受青睐。两种广泛用于自动化超参数调优的技术是网格搜索和随机搜索,它们常与交叉验证结合使用,以确保所选超参数具有良好的泛化能力。网格搜索网格搜索可能是最直接的自动化调优方法。它对超参数空间的指定子集执行穷尽式搜索。您为每个要调优的超参数定义一个“网格”的可能值,网格搜索会训练并评估这些值的每种可能组合对应的模型。例如,如果您正在调优一个随机森林,并想考虑以下参数:n_estimators: [100, 200, 300]max_depth: [5, 10, None]min_samples_split: [2, 4]网格搜索将训练并评估 $3 \times 3 \times 2 = 18$ 种不同的模型。通常,每种组合的评估都使用交叉验证进行。这为该超参数集提供了更稳定的性能估计,减少了对特定训练-测试划分过拟合的风险。产生最佳平均交叉验证分数的组合随后被选作最优超参数集。使用 Scikit-learn 实现Scikit-learn 提供了 GridSearchCV 类用于此目的。它接受一个估计器(如分类器或回归器)、一个参数网格(定义为字典)和交叉验证设置。import numpy as np from sklearn.model_selection import GridSearchCV from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import make_classification # 生成合成数据 X, y = make_classification(n_samples=1000, n_features=20, random_state=42) # 定义模型 rf = RandomForestClassifier(random_state=42) # 定义参数网格 param_grid = { 'n_estimators': [100, 200, 300], 'max_depth': [5, 10, 15], 'min_samples_split': [2, 5, 10] } # 实例化 GridSearchCV # cv=5 表示 5 折交叉验证 # n_jobs=-1 使用所有可用 CPU 核心 grid_search = GridSearchCV(estimator=rf, param_grid=param_grid, cv=5, n_jobs=-1, verbose=1, scoring='accuracy') # 将网格搜索拟合到数据 grid_search.fit(X, y) # 打印最佳参数和最佳分数 print(f"找到的最佳参数: {grid_search.best_params_}") print(f"最佳交叉验证分数: {grid_search.best_score_:.4f}") # 最佳估计器已在整个数据集上重新拟合 best_rf_model = grid_search.best_estimator_在此示例中,GridSearchCV 将评估 $3 \times 3 \times 3 = 27$ 种组合。verbose 参数控制搜索过程中打印的信息量。拟合后,grid_search.best_params_ 包含找到的最佳超参数组合的字典,grid_search.best_score_ 包含相应的平均交叉验证分数。grid_search.best_estimator_ 属性提供了使用这些最佳参数在整个数据集上重新拟合的模型。网格搜索的优缺点优点: 对指定网格进行穷尽式搜索。如果最优参数在网格内,网格搜索就能找到它们。易于理解和实现。缺点: 组合数量随超参数数量呈指数增长(“维度灾难”)。这会使其计算成本非常高,特别是当参数范围较大或包含连续值(需要离散化)时。它可能会花费大量时间评估搜索空间中不佳的区域。随机搜索随机搜索为网格搜索的穷尽式方法提供了一种更高效的替代方案。它不尝试所有组合,而是从指定的统计分布或列表中抽取固定数量(n_iter)的超参数组合。对于每个超参数,您可以提供一个值列表(如网格搜索中),或者,更强有力地,提供一个从中采样的分布(例如,用于学习率等连续参数的均匀分布,或用于 n_estimators 等整数参数的几何分布)。核心思想是,根据研究(Bergstra & Bengio, 2012)支持,对于许多问题,只有少数超参数显著影响性能。随机搜索将更多时间用于在不同超参数上检查具有潜在重要性的值,而不是穷尽地检查次要超参数的所有组合。在相同的计算预算下,随机搜索通常能覆盖更广的值范围,并找到比网格搜索更好或同样好的模型。{"data":[{"x":[1,1,1,2,2,2,3,3,3],"y":[1,2,3,1,2,3,1,2,3],"mode":"markers","type":"scatter","name":"网格搜索点","marker":{"color":"#1c7ed6","size":10}},{"x":[1.2,2.5,1.8,2.9,1.1,2.2,2.8,1.5,2.1],"y":[2.8,2.5,1.2,1.9,1.5,3.0,1.1,2.1,1.6],"mode":"markers","type":"scatter","name":"随机搜索点","marker":{"color":"#f03e3e","size":10},"xaxis":"x2","yaxis":"y2"}],"layout":{"grid":{"rows":1,"columns":2,"pattern":"independent"},"xaxis":{"title":"超参数 1","domain":[0,0.45],"range":[0.5, 3.5]},"yaxis":{"title":"超参数 2","range":[0.5, 3.5]},"xaxis2":{"title":"超参数 1","domain":[0.55,1],"range":[0.5, 3.5]},"yaxis2":{"anchor":"x2","title":"超参数 2","range":[0.5, 3.5]},"title":"网格搜索 vs. 随机搜索","showlegend":false,"margin":{"l":60,"r":60,"t":60,"b":60},"height":350}}此可视化图对比了网格搜索在二维超参数空间上的系统性点评估,与随机搜索的随机采样方法。在相同评估次数(本例中为 9 次)下,随机搜索覆盖了更多样化的值组合。使用 Scikit-learn 实现Scikit-learn 提供了 RandomizedSearchCV,其工作方式与 GridSearchCV 类似,但需要定义参数分布和迭代次数(n_iter)。import numpy as np from sklearn.model_selection import RandomizedSearchCV from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import make_classification from scipy.stats import randint, uniform # 生成合成数据(与之前相同) X, y = make_classification(n_samples=1000, n_features=20, random_state=42) # 定义模型 rf = RandomForestClassifier(random_state=42) # 定义要采样的参数分布或列表 # 对潜在的连续或宽范围参数使用分布 param_dist = { 'n_estimators': randint(100, 500), # 采样 100 到 499 之间的整数 'max_depth': [5, 10, 15, 20, None], # 从此列表中采样 'min_samples_split': randint(2, 11), # 采样 2 到 10 之间的整数 'min_samples_leaf': randint(1, 11), # 采样 1 到 10 之间的整数 'bootstrap': [True, False] # 从此列表中采样 } # 实例化 RandomizedSearchCV # n_iter 控制采样的参数设置数量 # 增加 n_iter 可进行更全面的搜索,减少则可提高速度 random_search = RandomizedSearchCV(estimator=rf, param_distributions=param_dist, n_iter=50, # 采样的参数设置数量 cv=5, n_jobs=-1, verbose=1, scoring='accuracy', random_state=42) # 用于可重现的结果 # 将随机搜索拟合到数据 random_search.fit(X, y) # 打印最佳参数和最佳分数 print(f"找到的最佳参数: {random_search.best_params_}") print(f"最佳交叉验证分数: {random_search.best_score_:.4f}") # 最佳估计器 best_rf_model_random = random_search.best_estimator_在这里,param_dist 使用 scipy.stats.randint 为 n_estimators、min_samples_split 和 min_samples_leaf 定义了整数范围上的均匀采样。max_depth 和 bootstrap 使用列表,从中均匀采样值。n_iter=50 意味着将采样 50 种不同组合,并使用 5 折交叉验证进行评估。随机搜索的优缺点优点: 在超参数数量较多时,比网格搜索高效得多。它通常能更快地找到非常好的参数组合。它允许从连续分布中采样。性能对是否包含不显著影响最终分数的超参数不那么敏感。缺点: 作为一种采样方法,它不保证能在搜索空间内找到绝对最佳组合。结果的质量取决于 n_iter 和随机采样过程。实际考虑范围和分布的选择: 为超参数选择合适的范围或分布需要对算法有一定了解,并可能需要先前的经验。可以从较宽的范围开始,然后根据初始结果进行调整(如果需要)。对数均匀分布(例如,使用 scipy.stats.loguniform)通常适用于学习率或正则化强度等跨多个数量级的参数。计算预算: 这两种方法都可能计算密集。调整网格大小(网格搜索)或 n_iter(随机搜索),以及交叉验证折数(cv),以适应您的时间限制。使用 n_jobs=-1 可以并行化跨可用 CPU 核心的处理过程,显著加快搜索速度。与管道集成: 超参数调优理想情况下应在包含预处理步骤(如缩放或编码)的 Pipeline 中进行。这可以防止在预处理步骤的超参数调优过程中,验证折叠数据泄露(例如,在交叉验证前在整个数据集上拟合缩放器)。您可以使用 stepname__parameter 语法为管道内的步骤定义超参数(例如,randomforestclassifier__n_estimators)。通过采用网格搜索或随机搜索,您可以从手动、可能带有偏差的调优,转向一种更系统、可重复的模型优化方法。尽管存在贝叶斯优化等更先进的技术,但网格搜索和随机搜索是广泛使用且易于获取的工具,它们能显著改进为监督学习模型寻找有效超参数配置的过程。