使用随机搜索来优化超参数
有很多方法可以优化模型的超参数。我们之前看了网格搜索和它的一个基本实施例子。
这一次,我们将寻求了解另一种被称为随机搜索的超参数优化方法。在本教程中,我们将介绍随机搜索,并通过一个简单的方法在Python中实现它。
前提条件
-
[本文]的一部分对网格搜索方法进行了简要介绍。
-
[VSCode]是我在本教程中选择的代码编辑器。我们将使用的语言是Python。
-
我们将使用[scikit-learn]库中的一个被称为[RandomizedSearchCV]的工具来进行交叉验证的随机搜索。
-
与网格搜索教程一样,我们将使用虹膜数据集。
随机搜索
随机搜索是一种选择超参数的随机组合并用于训练模型的方法。最好的随机超参数组合被使用。随机搜索与网格搜索有一些相似之处。
然而,一个关键的区别是,我们没有为每个超参数指定一组可能的值。相反,我们从每个超参数的统计分布中抽样取值。为每个超参数定义一个抽样分布,以进行随机搜索。
这种技术使我们能够控制尝试的超参数组合的数量。与网格搜索不同的是,在网格搜索中,每一个可能的组合都会被尝试,随机搜索允许我们指定要训练的模型数量。我们可以根据我们的计算资源或每次迭代所花费的时间来确定我们的搜索迭代。下面的图片显示了一个随机布局。

随机化搜索CV
在实现网格搜索时,我们使用了 sci-kit learn(sklearn)库,特别是 GridSearchCV。从同一个库中,我们将使用 RandomizedSearchCV。与GridSearchCV类似,它的目的是寻找最佳参数来改进给定的模型。
一个关键的区别是,它不测试所有的参数。相反,搜索是随机进行的。正如我们将在后面指出的,两者之间的一个实际区别是,RandomizedSearchCV允许我们指定我们寻求测试的参数值的数量。这是通过使用n_iter.
随机森林
由于我们将在随机搜索的实施过程中使用随机森林调节器,因此介绍随机森林是有价值的。随机森林指的是能够完成回归和分类任务的未经训练的决策树的集合。
它们涉及到袋法的使用,即结合许多模型来给出一个概括的结果。
我们已经解释了为什么随机森林是一个 "森林",但没有解释为什么它被认为是随机的。随机森林的随机性可以归结为几个概念。
首先,在构建树木时,训练观察的样本是随机抽取的。第二,当涉及到节点的分割时,会使用随机的特征子集。
随机森林参数
随机森林使用许多参数。以下是主要的参数。介绍这些参数是很有必要的,因为我们在以后的代码中会遇到所有的参数。
-
n_estimators:这个参数表示一个集合/森林中树的最大数量。
-
max_features:这表示在分割节点时考虑的最大特征数。
-
max_depth:max_depth表示每个决策树中允许的最大层数。
-
min_samples_split:为了使一个节点分裂,一个节点中需要有最小数量的样本。这个最小的数据点数量就是min_samples_split所代表的。
-
min_samples_leaf:在一个叶子节点中可以存储的最小数据点的数量。
-
bootstrap:为了对数据点进行抽样,采用了引导抽样法。可以在有替换或无替换的情况下进行抽样。带替换的抽样可以描述为当从随机人口中选出一个样本,然后再返回到人口中。如果
bootstrap = True,,抽样是带替换的随机进行的。如果bootstrap = False,,抽样是没有替换的。
随机搜索的实施
让我们对随机搜索有一个直接的实现。我们的目标是在随机搜索后确定最佳参数。
下面的步骤将说明这个过程。
- 输入NumPy:NumPy简化了对向量、矩阵以及多维数组的处理。我们导入
numpy,如下图所示。
import numpy as np
- 加载数据集:正如我们在实现网格搜索时一样,我们加载虹膜数据集。
from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data
y = iris.target
- 导入随机森林回归器:然后我们使用
from sklearn.ensemble import RandomForestRegressor来导入随机森林回归器。
from sklearn.ensemble import RandomForestRegressor
- 设置随机状态:
random_state参数允许我们设置随机发生器的种子值。它允许我们将许多具有相同行数的数据集通过它。
反过来,它在相同的索引上分割数据集。如果我们未能提供一个随机状态值,那么每次运行代码时都会生成一个不同的测试集。我选择使用35作为随机状态值。
rf = RandomForestRegressor(random_state = 35)
- 导入:
RandomizedSearchCV我们导入RandomizedSearchCV,对超参数进行随机搜索。
from sklearn.model_selection import RandomizedSearchCV
- 为随机搜索提供超参数网格:在这里,我们为我们之前定义的随机森林参数指定几个值。
n_estimators = [int(x) for x in np.linspace(start = 1, stop = 20, num = 20)] # number of trees in the random forest
max_features = ['auto', 'sqrt'] # number of features in consideration at every split
max_depth = [int(x) for x in np.linspace(10, 120, num = 12)] # maximum number of levels allowed in each decision tree
min_samples_split = [2, 6, 10] # minimum sample number to split a node
min_samples_leaf = [1, 3, 4] # minimum sample number that can be stored in a leaf node
bootstrap = [True, False] # method used to sample data points
random_grid = {'n_estimators': n_estimators,
'max_features': max_features,
'max_depth': max_depth,
'min_samples_split': min_samples_split,
'min_samples_leaf': min_samples_leaf,
'bootstrap': bootstrap}
- 评估:与我们的网格搜索实现类似,我们将在随机搜索中进行交叉验证。这是由
RandomizedSearchCV.,通过指定cv=5,我们使用交叉验证来训练一个模型5次。
此外,当我们进行网格搜索时,我们有verbose=0 ,以避免减慢我们的算法。在这种情况下,我们可以使用verbose=2 ,对生成的日志信息一目了然。
我们有n_iter 参数,允许我们进行不同的迭代,正如之前的网格搜索教程中提到的,当n_jobs = -1 ,所有的CPU都被投入使用。
rf_random = RandomizedSearchCV(estimator = rf,
param_distributions = random_grid,
n_iter = 100, cv = 5, verbose=2, random_state=35, n_jobs = -1)
- 拟合数据:我们通过
rf_random.fit(X,y)来进行。
所有的代码
import numpy as np
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestRegressor
iris = load_iris()
rf = RandomForestRegressor(random_state = 35)
from sklearn.model_selection import RandomizedSearchCV
X = iris.data
y = iris.target
n_estimators = [int(x) for x in np.linspace(start = 1, stop = 20, num = 20)]
max_features = ['auto', 'sqrt']
max_depth = [int(x) for x in np.linspace(10, 120, num = 12)]
min_samples_split = [2, 6, 10]
min_samples_leaf = [1, 3, 4]
bootstrap = [True, False]
random_grid = {'n_estimators': n_estimators,
'max_features': max_features,
'max_depth': max_depth,
'min_samples_split': min_samples_split,
'min_samples_leaf': min_samples_leaf,
'bootstrap': bootstrap}
rf_random = RandomizedSearchCV(estimator = rf,
param_distributions = random_grid,
n_iter = 100, cv = 5, verbose=2, random_state=35, n_jobs = -1)
rf_random.fit(X,y)
# this prints the contents of the parameters in the random grid
print ('Random grid: ', random_grid, '\n')
# print the best parameters
print ('Best Parameters: ', rf_random.best_params_, ' \n')
结果。
[Parallel(n_jobs=-1)]: Done 500 out of 500 | elapsed: 3.6s finished
Random grid:
{'n_estimators': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], 'max_features': ['auto', 'sqrt'], 'max_depth': [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120], 'min_samples_split': [2, 6, 10], 'min_samples_leaf': [1, 3, 4], 'bootstrap': [True, False]}
Best Parameters:
{'n_estimators': 10, 'min_samples_split': 10, 'min_samples_leaf': 4, 'max_features': 'auto', 'max_depth': 70, 'bootstrap': True}
我们的输出结果显示,n_estimators和min_samples_split的最佳参数是10。它还给出了min_samples_leaf为4,max_features为自动,max_depth为70,bootstrap为true。
网格搜索与随机搜索
凭空想象,网格搜索似乎是更好的方法,因为每一个可能的超参数组合都被测试过。但情况并不总是如此。这两种策略可以在维度方面进行比较。
对于网格搜索,维度越大,需要搜索的超参数组合的数量就越多。因此,网格搜索不实用的可能性更大。
搜索所需的时间不能证明使用网格搜索是合理的。随着参数数量的增加,使用中的计算资源也会被证明是不可行的。
每一个额外的参数都会使评估的数量呈指数增长。在超参数数量较少的情况下,网格搜索可能会胜过随机搜索。
这是因为网格搜索会通过用尽所有可能的组合来保证准确性。与网格搜索类似,维度越高,找到正确的超参数集所需的时间就越长。更高的维度也意味着更多的迭代次数。
尽管如此,随机搜索可能提供了实现最优参数的更大机会。即使随机搜索可能不像网格搜索那样准确,我们也可以控制尝试的组合数量。
与使用网格搜索相比,随机搜索模型可以在更短的时间内完成对优化参数的训练。与网格搜索相比,这也导致了更有效的计算能力的使用。
总结
我们提到,网格搜索尝试了所有的超参数组合。随机搜索可以让我们指定要训练多少个模型,从而控制尝试的组合数量。
这在保证找到最佳参数和使用的计算资源/时间之间引入了一个权衡。
我们已经经历了随机搜索的基本实现。我相信这篇文章已经提供了随机搜索和网格搜索之间的区别。