3.2. 调整估计器的超参数#

超参数是在估计器中没有直接学习的参数。在 scikit-learn 中,它们作为参数传递给估计器类的构造函数。典型的例子包括 Ckernelgamma 用于支持向量分类器,alpha 用于 Lasso 等。

可以并且建议搜索超参数空间以找到最佳的 交叉验证 分数。

在构建估计器时提供的任何参数都可以以这种方式进行优化。具体来说,要查找给定估计器的所有参数的名称和当前值,请使用

estimator.get_params()

搜索包括

  • 一个估计器(回归器或分类器,例如 sklearn.svm.SVC());

  • 一个参数空间;

  • 一种搜索或采样候选者的方法;

  • 一个交叉验证方案;以及

  • 一个 评分函数

scikit-learn 提供了两种通用的参数搜索方法:对于给定值,GridSearchCV 会穷举所有参数组合,而 RandomizedSearchCV 可以从参数空间中以指定分布采样给定数量的候选者。这两个工具都有相应的逐步缩减版本 HalvingGridSearchCVHalvingRandomSearchCV,它们在找到良好的参数组合方面可能快得多。

在描述这些工具之后,我们将详细介绍适用于这些方法的 最佳实践。一些模型允许使用专门的、高效的参数搜索策略,这些策略在 蛮力参数搜索的替代方案 中概述。

请注意,通常情况下,一小部分参数会对模型的预测或计算性能产生重大影响,而其他参数则可以保留其默认值。建议阅读估计器类的文档字符串,以更深入地了解其预期行为,可能需要阅读附带的文献参考。

3.2.2. 随机参数优化#

虽然使用参数设置网格是目前最广泛使用的参数优化方法,但其他搜索方法具有更有利的特性。 RandomizedSearchCV 在参数上实现随机搜索,其中每个设置都是从可能的参数值的分布中采样得到的。与穷举搜索相比,这有两个主要优点

  • 可以独立于参数数量和可能值选择预算。

  • 添加不影响性能的参数不会降低效率。

指定如何对参数进行采样是使用字典完成的,这与为 GridSearchCV 指定参数非常相似。此外,使用 n_iter 参数指定计算预算,即采样候选者的数量或采样迭代次数。对于每个参数,可以指定可能的值的分布或离散选择的列表(将从这些列表中均匀采样)

{'C': scipy.stats.expon(scale=100), 'gamma': scipy.stats.expon(scale=.1),
  'kernel': ['rbf'], 'class_weight':['balanced', None]}

此示例使用 scipy.stats 模块,该模块包含许多用于采样参数的有用分布,例如 expongammauniformloguniformrandint

原则上,可以传递任何提供 rvs(随机变量样本)方法来采样值的函数。对 rvs 函数的调用应在连续调用时提供来自可能的参数值的独立随机样本。

警告

在 scipy 0.16 版本之前的 scipy.stats 中的分布不允许指定随机状态。相反,它们使用全局 numpy 随机状态,可以通过 np.random.seed 进行播种,或使用 np.random.set_state 设置。但是,从 scikit-learn 0.18 开始,sklearn.model_selection 模块会设置用户提供的随机状态,如果 scipy >= 0.16 也可用。

对于连续参数,例如上面的 C,重要的是指定连续分布以充分利用随机化。这样,增加 n_iter 将始终导致更精细的搜索。

连续对数均匀随机变量是对数间隔参数的连续版本。例如,要指定上面 C 的等效项,可以使用 loguniform(1, 100) 而不是 [1, 10, 100]

镜像上面网格搜索中的示例,我们可以指定一个在 1e01e3 之间对数均匀分布的连续随机变量

from sklearn.utils.fixes import loguniform
{'C': loguniform(1e0, 1e3),
 'gamma': loguniform(1e-4, 1e-3),
 'kernel': ['rbf'],
 'class_weight':['balanced', None]}

示例

参考文献

  • Bergstra, J. 和 Bengio, Y.,随机搜索超参数优化,机器学习研究杂志 (2012)

3.2.3. 使用连续减半搜索最佳参数#

Scikit-learn 还提供了 HalvingGridSearchCVHalvingRandomSearchCV 估计器,可用于使用连续减半 [1] [2] 搜索参数空间。连续减半 (SH) 就像候选参数组合之间的锦标赛。SH 是一个迭代选择过程,其中所有候选者(参数组合)在第一次迭代中使用少量资源进行评估。只有其中一些候选者被选中用于下一轮迭代,下一轮迭代将分配更多资源。对于参数调整,资源通常是训练样本的数量,但它也可以是任意数值参数,例如随机森林中的 n_estimators

如下图所示,只有一部分候选者在最后一次迭代中“存活”。这些是那些在所有迭代中始终排名在得分最高的候选者中的候选者。每次迭代都为每个候选者分配越来越多的资源,这里是指样本数量。

../_images/sphx_glr_plot_successive_halving_iterations_001.png

我们在这里简要介绍主要参数,但在下面的部分中将更详细地描述每个参数及其交互。该 factor (> 1) 参数控制资源增长的速度以及候选者数量减少的速度。在每次迭代中,每个候选者的资源数量乘以 factor,候选者的数量除以相同的因子。与 resourcemin_resources 一样,factor 是控制我们实现中搜索的最重要参数,尽管值 3 通常效果很好。 factor 有效地控制了 HalvingGridSearchCV 中的迭代次数以及 HalvingRandomSearchCV 中的候选者数量(默认情况下)和迭代次数。如果可用资源数量很少,也可以使用 aggressive_elimination=True。通过调整 min_resources 参数可以获得更多控制。

这些估计器仍然是实验性的:它们的预测和 API 可能会在没有任何弃用周期的情况下发生变化。要使用它们,您需要显式导入 enable_halving_search_cv

>>> # explicitly require this experimental feature
>>> from sklearn.experimental import enable_halving_search_cv  # noqa
>>> # now you can import normally from model_selection
>>> from sklearn.model_selection import HalvingGridSearchCV
>>> from sklearn.model_selection import HalvingRandomSearchCV

示例

3.2.3.1. 选择 min_resources 和候选者数量#

除了 factor 之外,影响逐次减半搜索行为的两个主要参数是 min_resources 参数和评估的候选者(或参数组合)数量。 min_resources 是在第一次迭代中为每个候选者分配的资源量。候选者的数量在 HalvingRandomSearchCV 中直接指定,并由 HalvingGridSearchCVparam_grid 参数确定。

考虑一个资源是样本数量的情况,我们有 1000 个样本。理论上,使用 min_resources=10factor=2,我们最多可以运行 7 次迭代,使用以下数量的样本: [10, 20, 40, 80, 160, 320, 640]

但根据候选者的数量,我们可能运行的迭代次数少于 7 次:如果我们从少量候选者开始,最后一次迭代可能使用少于 640 个样本,这意味着没有使用所有可用的资源(样本)。例如,如果我们从 5 个候选者开始,我们只需要 2 次迭代:第一次迭代有 5 个候选者,然后第二次迭代有 5 // 2 = 2 个候选者,之后我们就知道哪个候选者表现最好(所以我们不需要第三次迭代)。我们最多只使用 20 个样本,这是一种浪费,因为我们有 1000 个样本可用。另一方面,如果我们从大量候选者开始,我们可能在最后一次迭代中得到很多候选者,这可能并不总是理想的:这意味着许多候选者将使用全部资源运行,基本上将过程简化为标准搜索。

HalvingRandomSearchCV 的情况下,候选者的数量默认设置为最后一次迭代尽可能使用可用资源。对于 HalvingGridSearchCV,候选者的数量由 param_grid 参数决定。更改 min_resources 的值将影响可能的迭代次数,并因此影响理想的候选者数量。

在选择 min_resources 时,另一个需要考虑的是,使用少量资源是否容易区分好坏候选者。例如,如果你需要大量样本才能区分好坏参数,建议使用较高的 min_resources。另一方面,如果即使使用少量样本也能清楚地区分,那么使用较小的 min_resources 可能更可取,因为它可以加快计算速度。

请注意,在上面的示例中,最后一次迭代没有使用可用的最大资源量:有 1000 个样本可用,但最多只使用了 640 个。默认情况下,HalvingRandomSearchCVHalvingGridSearchCV 都尝试在最后一次迭代中尽可能使用更多资源,但前提是此资源量必须是 min_resourcesfactor 的倍数(此约束将在下一节中说明)。 HalvingRandomSearchCV 通过采样正确数量的候选者来实现这一点,而 HalvingGridSearchCV 通过正确设置 min_resources 来实现这一点。有关详细信息,请参阅 耗尽可用资源

3.2.3.2. 每次迭代的资源量和候选者数量#

在任何迭代 i 中,每个候选者都会分配给定的资源量,我们将其表示为 n_resources_i。此数量由参数 factormin_resources 控制,如下所示(factor 严格大于 1)

n_resources_i = factor**i * min_resources,

或等效地

n_resources_{i+1} = n_resources_i * factor

其中 min_resources == n_resources_0 是第一次迭代中使用的资源量。 factor 还定义了将在下一轮迭代中选择的候选者比例

n_candidates_i = n_candidates // (factor ** i)

或等效地

n_candidates_0 = n_candidates
n_candidates_{i+1} = n_candidates_i // factor

因此,在第一次迭代中,我们使用 min_resources 资源 n_candidates 次。在第二次迭代中,我们使用 min_resources * factor 资源 n_candidates // factor 次。第三次再次将每个候选者的资源量相乘,并将候选者数量相除。当每个候选者的最大资源量达到,或者当我们已经确定了最佳候选者时,此过程停止。最佳候选者是在评估 factor 或更少候选者的迭代中确定的(有关解释,请参见下文)。

以下是一个使用 min_resources=3factor=2 从 70 个候选者开始的示例

n_resources_i

n_candidates_i

3 (=min_resources)

70 (=n_candidates)

3 * 2 = 6

70 // 2 = 35

6 * 2 = 12

35 // 2 = 17

12 * 2 = 24

17 // 2 = 8

24 * 2 = 48

8 // 2 = 4

48 * 2 = 96

4 // 2 = 2

我们可以注意到

  • 该过程在评估 factor=2 个候选者的第一次迭代中停止:最佳候选者是这 2 个候选者中最好的。没有必要运行额外的迭代,因为它只会评估一个候选者(即我们已经确定的最佳候选者)。因此,一般来说,我们希望最后一次迭代最多运行 factor 个候选者。如果最后一次迭代评估的候选者数量超过 factor 个,那么最后一次迭代将简化为常规搜索(如 RandomizedSearchCVGridSearchCV)。

  • 每个 n_resources_i 都是 factormin_resources 的倍数(这在其上面的定义中得到证实)。

每次迭代中使用的资源量可以在 n_resources_ 属性中找到。

3.2.3.3. 选择资源#

默认情况下,资源是根据样本数量定义的。也就是说,每次迭代都将使用越来越多的样本进行训练。但是,你可以使用 resource 参数手动指定要作为资源使用的参数。以下是一个资源根据随机森林的估计器数量定义的示例

>>> from sklearn.datasets import make_classification
>>> from sklearn.ensemble import RandomForestClassifier
>>> from sklearn.experimental import enable_halving_search_cv  # noqa
>>> from sklearn.model_selection import HalvingGridSearchCV
>>> import pandas as pd
>>>
>>> param_grid = {'max_depth': [3, 5, 10],
...               'min_samples_split': [2, 5, 10]}
>>> base_estimator = RandomForestClassifier(random_state=0)
>>> X, y = make_classification(n_samples=1000, random_state=0)
>>> sh = HalvingGridSearchCV(base_estimator, param_grid, cv=5,
...                          factor=2, resource='n_estimators',
...                          max_resources=30).fit(X, y)
>>> sh.best_estimator_
RandomForestClassifier(max_depth=5, n_estimators=24, random_state=0)

请注意,无法对参数网格中包含的参数进行预算。

3.2.3.4. 耗尽可用资源#

如上所述,每次迭代使用的资源数量取决于 min_resources 参数。如果你有很多资源可用,但从少量资源开始,其中一些资源可能会被浪费(即未使用)。

>>> from sklearn.datasets import make_classification
>>> from sklearn.svm import SVC
>>> from sklearn.experimental import enable_halving_search_cv  # noqa
>>> from sklearn.model_selection import HalvingGridSearchCV
>>> import pandas as pd
>>> param_grid= {'kernel': ('linear', 'rbf'),
...              'C': [1, 10, 100]}
>>> base_estimator = SVC(gamma='scale')
>>> X, y = make_classification(n_samples=1000)
>>> sh = HalvingGridSearchCV(base_estimator, param_grid, cv=5,
...                          factor=2, min_resources=20).fit(X, y)
>>> sh.n_resources_
[20, 40, 80]

搜索过程最多只会使用 80 个资源,而我们可用的最大资源数量是 n_samples=1000。在这里,我们有 min_resources = r_0 = 20

对于 HalvingGridSearchCV,默认情况下,min_resources 参数设置为 'exhaust'。这意味着 min_resources 会自动设置,以便最后一次迭代可以在 max_resources 限制内使用尽可能多的资源。

>>> sh = HalvingGridSearchCV(base_estimator, param_grid, cv=5,
...                          factor=2, min_resources='exhaust').fit(X, y)
>>> sh.n_resources_
[250, 500, 1000]

min_resources 在这里自动设置为 250,这导致最后一次迭代使用所有资源。使用的确切值取决于候选参数的数量、max_resourcesfactor

对于 HalvingRandomSearchCV,可以通过两种方式耗尽资源

  • 通过设置 min_resources='exhaust',就像 HalvingGridSearchCV 一样;

  • 通过设置 n_candidates='exhaust'

这两个选项是互斥的:使用 min_resources='exhaust' 需要知道候选者的数量,反之亦然,n_candidates='exhaust' 需要知道 min_resources

一般来说,耗尽总资源数量会导致更好的最终候选参数,并且稍微更耗时。

3.2.3.5. 积极消除候选者#

理想情况下,我们希望最后一次迭代评估 factor 个候选者(参见 每次迭代的资源量和候选者数量)。然后我们只需要选择最好的一个。当可用资源数量相对于候选者数量较小时,最后一次迭代可能需要评估超过 factor 个候选者。

>>> from sklearn.datasets import make_classification
>>> from sklearn.svm import SVC
>>> from sklearn.experimental import enable_halving_search_cv  # noqa
>>> from sklearn.model_selection import HalvingGridSearchCV
>>> import pandas as pd
>>>
>>>
>>> param_grid = {'kernel': ('linear', 'rbf'),
...               'C': [1, 10, 100]}
>>> base_estimator = SVC(gamma='scale')
>>> X, y = make_classification(n_samples=1000)
>>> sh = HalvingGridSearchCV(base_estimator, param_grid, cv=5,
...                          factor=2, max_resources=40,
...                          aggressive_elimination=False).fit(X, y)
>>> sh.n_resources_
[20, 40]
>>> sh.n_candidates_
[6, 3]

由于我们不能使用超过 max_resources=40 个资源,因此该过程必须在评估超过 factor=2 个候选者的第二次迭代时停止。

使用 aggressive_elimination 参数,你可以强制搜索过程在最后一次迭代中以少于 factor 个候选者结束。为此,该过程将使用 min_resources 个资源消除尽可能多的候选者。

>>> sh = HalvingGridSearchCV(base_estimator, param_grid, cv=5,
...                            factor=2,
...                            max_resources=40,
...                            aggressive_elimination=True,
...                            ).fit(X, y)
>>> sh.n_resources_
[20, 20,  40]
>>> sh.n_candidates_
[6, 3, 2]

请注意,我们在最后一次迭代中以 2 个候选者结束,因为我们在前几次迭代中使用 n_resources = min_resources = 20 消除了足够的候选者。

3.2.3.6. 使用 cv_results_ 属性分析结果#

cv_results_ 属性包含用于分析搜索结果的有用信息。它可以使用 df = pd.DataFrame(est.cv_results_) 转换为 pandas 数据框。 HalvingGridSearchCVHalvingRandomSearchCVcv_results_ 属性类似于 GridSearchCVRandomizedSearchCV,并包含与连续减半过程相关的附加信息。

以下是一个包含 (截断) 数据框中某些列的示例

iter

n_resources

mean_test_score

params

0

0

125

0.983667

{‘criterion’: ‘log_loss’, ‘max_depth’: None, ‘max_features’: 9, ‘min_samples_split’: 5}

1

0

125

0.983667

{‘criterion’: ‘gini’, ‘max_depth’: None, ‘max_features’: 8, ‘min_samples_split’: 7}

2

0

125

0.983667

{‘criterion’: ‘gini’, ‘max_depth’: None, ‘max_features’: 10, ‘min_samples_split’: 10}

3

0

125

0.983667

{‘criterion’: ‘log_loss’, ‘max_depth’: None, ‘max_features’: 6, ‘min_samples_split’: 6}

15

2

500

0.951958

{‘criterion’: ‘log_loss’, ‘max_depth’: None, ‘max_features’: 9, ‘min_samples_split’: 10}

16

2

500

0.947958

{‘criterion’: ‘gini’, ‘max_depth’: None, ‘max_features’: 10, ‘min_samples_split’: 10}

17

2

500

0.951958

{‘criterion’: ‘gini’, ‘max_depth’: None, ‘max_features’: 10, ‘min_samples_split’: 4}

18

3

1000

0.961009

{‘criterion’: ‘log_loss’, ‘max_depth’: None, ‘max_features’: 9, ‘min_samples_split’: 10}

19

3

1000

0.955989

{‘criterion’: ‘gini’, ‘max_depth’: None, ‘max_features’: 10, ‘min_samples_split’: 4}

每行对应一个给定的参数组合(候选者)和一个给定的迭代。迭代由 iter 列给出。 n_resources 列告诉您使用了多少资源。

在上面的示例中,最佳参数组合是 {'criterion': 'log_loss', 'max_depth': None, 'max_features': 9, 'min_samples_split': 10},因为它以最高分数 0.96 达到最后一次迭代 (3)。

参考文献