本文将详细介绍用来提高机器学习效果的最常见的超参数优化方法。
译者 | 朱先忠
审校 | 孙淑娟
通常,在尝试改进机器学习模型时,人们首先想到的解决方案是添加更多的训练数据。额外的数据通常是有帮助(在某些情况下除外)的,但生成高质量的数据可能非常昂贵。通过使用现有数据获得最佳模型性能,超参数优化可以节省我们的时间和资源。
顾名思义,超参数优化是为机器学习模型确定最佳超参数组合以满足优化函数(即,给定研究中的数据集,最大化模型的性能)的过程。换句话说,每个模型都会提供多个有关选项的调整“按钮”,我们可以改变它们,直到我们模型的超参数达到最佳组合。在超参数优化过程中,我们可以更改的参数的一些示例可以是学习率、神经网络的架构(例如,隐藏层的数量)、正则化等。
在这篇文章中,我们将从概念上介绍三种最常见的超参数优化方法,即网格搜索、随机搜索和贝叶斯优化,然后对它们进行逐一实现。
我将在文章一开始提供一个高级别的比较表,以供读者参考,然后将在本文的其余部分进一步探讨、解释和实施比较表中的每一项。
表1:超参数优化方法比较
网格搜索可能是超参数优化的最简单和最直观的方法,它涉及在定义的搜索空间中彻底搜索超参数的最佳组合。在此上下文中的“搜索空间”是整个超参数以及在优化过程中考虑的此类超参数的值。让我们通过一个示例来更好地理解网格搜索。
假设我们有一个只有三个参数的机器学习模型,每个参数都可以取下表中提供的值:
parameter_1 = [1, 2, 3]
parameter_2 = [a, b, c]
parameter_3 = [x, y, z]
我们不知道这些参数的哪个组合将优化我们的模型的优化功能(即为我们的机器学习模型提供最佳输出)。在网格搜索中,我们只需尝试这些参数的每一个组合,测量每个参数的模型性能,然后简单地选择产生最佳性能的组合!在此示例中,参数1可以取3个值(即1、2或3),参数2可以取3种值(即a、b和c),参数3可以取3种值(即x、y和z)。换句话说,总共有3*3*3=27个组合。本例中的网格搜索将涉及27轮评估机器学习模型的性能,以找到性能最佳的组合。
如您所见,这种方法非常简单(类似于试错任务),但也有一些局限性。让我们一起总结一下此方法的优点和缺点。
其中,优势包括:
为了实现网格搜索,我们将使用scikit-learn中的Iris数据集创建一个随机森林分类模型。该数据集包括3种不同的鸢尾花瓣和萼片长度,将用于本次分类练习。在本文中,模型开发是次要的,因为目标是比较各种超参数优化策略的性能。我鼓励您关注模型评估结果,以及每种超参数优化方法达到所选超参数集所需的时间。我将描述运行结果,然后为本文中使用的三种方法提供一个汇总比较表。
包括所有超参数值的搜索空间,定义如下:
search_space = {'n_estimators': [10, 100, 500, 1000],
'max_depth': [2, 10, 25, 50, 100],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 5, 10]}
上述搜索空间由4*5*3*3=180个超参数的总组合组成。我们将使用网格搜索来找到优化目标函数的组合,如下所示:
# Import libraries
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
import time
# 加载Iris数据集
iris = load_iris()
X, y = iris.data, iris.target
#定义超参数搜索空间
search_space = {'n_estimators': [10, 100, 500, 1000],
'max_depth': [2, 10, 25, 50, 100],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 5, 10]}
#定义随机林分类器
clf = RandomForestClassifier(random_state=1234)
# 生成优化器对象
optimizer = GridSearchCV(clf, search_space, cv=5, scoring='accuracy')
#存储起始时间,以便用于计算总的耗时
start_time = time.time()
#拟合数据上的优化器
optimizer.fit(X, y)
# 存储结束时间,以便用于计算总的耗时
end_time = time.time()
# 打印最佳超参数集和相应分数
print(f"selected hyperparameters:")
print(optimizer.best_params_)
print("")
print(f"best_score: {optimizer.best_score_}")
print(f"elapsed_time: {round(end_time-start_time, 1)}")
上面代码的输出结果如下:
这里我们可以看到使用网格搜索选择的超参数值。其中,best_score描述了使用所选超参数集的评估结果,而elapsed_time描述了我的本地笔记本电脑执行此超参数优化策略所花费的时间。在进行下一种方法时,请记住评估结果和经过的时间,以便进行比较。现在,让我们进入随机搜索的讨论。
顾名思义,随机搜索是从定义的搜索空间中随机采样超参数的过程。与网格搜索不同,随机搜索只会在预定义的迭代次数(取决于可用资源,如时间、预算、目标等)中选择超参数值的随机子集,并计算每个超参数的机器学习模型的性能,然后选择最佳的超参数值。
根据上述方法,您可以想象,与完整的网格搜索相比,随机搜索成本更低,但仍有其自身的优势和劣势,如下所示:
优势:
在下一个方法中,我们将通过贝叶斯优化解决网格和随机搜索的“无记忆”缺点。但在讨论此方法之前,让我们先来实现随机搜索。
使用下面的代码片段,我们将针对网格搜索实现中描述的相同问题实现随机搜索超参数优化。
# 导入库
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint
# 创建一个RandomizedSearchCV对象
optimizer = RandomizedSearchCV(clf, param_distributinotallow=search_space,
n_iter=50, cv=5, scoring='accuracy',
random_state=1234)
# 存储开始时间以计算总运行时间
start_time = time.time()
# 拟合数据上的优化器
optimizer.fit(X, y)
# 存储结束时间以计算总运行时间
end_time = time.time()
# 打印最佳超参数集和相应分数
print(f"selected hyperparameters:")
print(optimizer.best_params_)
print("")
print(f"best_score: {optimizer.best_score_}")
print(f"elapsed_time: {round(end_time-start_time, 1)}")
上面代码的输出结果如下:
随机搜索结果
与网格搜索的结果相比,这些结果非常有趣。best_score保持不变,但elapsed_time从352.0秒减少到75.5秒!真是令人印象深刻!换句话说,随机搜索算法设法找到了一组超参数,在网格搜索所需时间的21%左右,其性能与网格搜索相同!但是,这里的效率高得多。
接下来,让我们继续我们的下一种方法,称为贝叶斯优化,它从优化过程中的每一次尝试中学习。
贝叶斯优化是一种超参数优化方法,它使用概率模型从以前的尝试中“学习”,并将搜索引向搜索空间中超参数的最佳组合,从而优化机器学习模型的目标函数。
贝叶斯优化方法可以分为4个步骤,我将在下面描述。我鼓励您通读这些步骤,以便更好地理解流程,但使用这种方法并不需要什么前提知识。
如果您有兴趣了解更多有关贝叶斯优化的详细信息,可以查看以下帖子:
《机器学习中的贝叶斯优化算法》,地址是:
https://medium.com/@fmnobar/conceptual-overview-of-bayesian-optimization-for-parameter-tuning-in-machine-learning-a3b1b4b9339f。
现在,既然我们已经了解了贝叶斯优化是如何工作的,那么让我们来看看它的优点和缺点。
优势:
在排除了细节之后,让我们实现贝叶斯优化并查看结果。
与上一节类似,我们将使用下面的代码片段为网格搜索实现中描述的相同问题实现贝叶斯超参数优化。
# 导入库
from skopt import BayesSearchCV
# 执行贝叶斯优化
optimizer = BayesSearchCV(estimator=RandomForestClassifier(),
search_spaces=search_space,
n_iter=10,
cv=5,
scoring='accuracy',
random_state=1234)
# 存储开始时间以计算总运行时间
start_time = time.time()
optimizer.fit(X, y)
# 存储结束时间以计算总运行时间
end_time = time.time()
# 打印最佳超参数集和相应分数
print(f"selected hyperparameters:")
print(optimizer.best_params_)
print("")
print(f"best_score: {optimizer.best_score_}")
print(f"elapsed_time: {round(end_time-start_time, 1)}")
上面代码的输出结果如下:
贝叶斯优化结果
另一组有趣的结果!best_score与我们通过网格和随机搜索获得的结果保持一致,但结果仅用了23.1秒,而随机搜索为75.5秒,网格搜索为352.0秒!换句话说,使用贝叶斯优化所需的时间比网格搜索所需的时间大约少93%。这是一个巨大的生产力提升,在更大、更复杂的模型和搜索空间中变得更有意义。
请注意,贝叶斯优化只使用了10次迭代就获得了这些结果,因为它可以从以前的迭代中学习(与随机和网格搜索相反)。
下表对目前所讨论的三种方法的结果进行了比较。“Methodology(方法论)”一栏描述了所使用的超参数优化方法。随后是使用每种方法选择的超参数。“Best Score”是使用特定方法获得的分数,然后是“Elapsed Time”,表示优化策略在我的本地笔记本电脑上运行所需的时间。最后一列“获得的效率(Gained Efficiency)”假设网格搜索为基线,然后计算与网格搜索相比,其他两种方法中每种方法获得的效率(使用经过的时间)。例如,由于随机搜索耗时75.5秒,而网格搜索耗时352.0秒,因此相对于网格搜索的基线,随机搜索的效率计算为1–75.5/352.0=78.5%。
表2——方法性能比较表
以上比较表中的两个主要结论:
在这篇文章中,我们讨论了什么是超参数优化,并介绍了用于此优化练习的三种最常见的方法。然后,我们详细介绍了这三种方法中的每一种,并在分类练习中实现了它们。最后,我们比较了实施这三种方法的结果。我们发现,从以前的尝试中学习的贝叶斯优化等方法可以显著提高效率,这可能是大型复杂模型(如深度神经网络)中的一个重要因素,其中效率可能是一个决定因素。
朱先忠,51CTO社区编辑,51CTO专家博客、讲师,潍坊一所高校计算机教师,自由编程界老兵一枚。
原文标题:Hyperparameter Optimization — Intro and Implementation of Grid Search, Random Search and Bayesian Optimization,作者:Farzad Mahmoodinobar
以上是超参数优化比较之网格搜索、随机搜索和贝叶斯优化的详细内容。更多信息请关注PHP中文网其他相关文章!