超参数是估计器的参数中不能通过学习得到的参数。在scikit-learn中,他们作为参数传递给估计器不同类的构造函数。典型的例子有支持向量分类器的参数C,kernel和gamma,Lasso的参数alpha等。
在超参数集中搜索以获得最佳cross validation交叉验证分数的方法是可实现并且推荐的。
当构建一个估计器是,任意参数的选取通过这种方式可能会获得最佳参数。尤其是,要想知道参数名字和其对应的当前值,使用:
estimator.get_params()
搜索包括:
- 一个估计器(回归或分类,如sklearn.svm.SVC());
- 一个参数空间;
- 一个搜索的方法或可选参数集;
- 一个交叉验证的方案;
- 一个评分函数。
在scikit-learn中,超参数优化(optimization)或调优(tuning)是为学习算法选择一组最优超参数(Hyper-parameter),找到全局最小值;的方法一般包括以下四种:
- 传统或手动调参
- 网格搜索
- 随机搜索
- 贝叶斯搜索
一、 传统或手动调参
机器学习模型调优算法中的“传统或手动调参”是一种重要的优化方法,它主要依赖于模型训练者的经验和判断,对模型的超参数进行调整以达到最佳性能。以下是关于传统或手动调参的详细介绍:
原理:
手动调参的原理在于通过调整模型的超参数,来权衡模型的偏差和方差,从而提高模型的预测性能。超参数是机器学习算法在训练之前需要设定的参数,它们对模型的训练过程和结果有重要影响。
步骤:
- 理解数据和模型: 首先,模型训练者需要深入了解数据的特性以及所使用的- 机器学习模型的原理。这有助于确定哪些超参数可能对模型的性能有重要影响。
- 选择初始参数: 可以使用算法的默认参数作为起点,或者根据经验选择一组初始参数进行训练。
- 调整重要参数: 根据模型的训练效果和性能评估指标(如准确率、召回率、F1分数等),逐步调整对模型性能影响较大的超参数。
- 评估和调整: 使用验证集或交叉验证来评估不同超参数组合下的模型性能,并根据评估结果调整超参数。
- **迭代优化:**重复上述步骤,直到找到一组较优的超参数组合,使得模型在验证集上的性能达到最佳。
优点:
- 灵活性: 手动调参可以根据具体的数据和模型特点进行灵活调整,没有固定的规则限制。
- 可解释性: 由于调参过程是基于人的经验和判断,因此可以更容易地理解和解释模型的性能变化。
缺点:
- 依赖经验: 手动调参的效果很大程度上取决于模型训练者的经验和技能水平,对于初学者来说可能难以掌握。
- 耗时费力: 手动调参通常需要尝试多种不同的超参数组合,并进行多次训练和评估,因此过程可能比较耗时和繁琐。
- 容易陷入局部最优: 由于手动调参通常是基于当前状态的局部调整,因此可能容易陷入局部最优解,而非全局最优解。
让我们看看如下代码:
#importing required libraries
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold , cross_val_score
from sklearn.datasets import load_wine
wine = load_wine()
X = wine.data
y = wine.target
#splitting the data into train and test set
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size = 0.3,random_state = 14)
#declaring parameters grid
k_value = list(range(2,11))
algorithm = ['auto','ball_tree','kd_tree','brute']
scores = []
best_comb = []
kfold = KFold(n_splits=5)
#hyperparameter tunning
for algo in algorithm:
for k in k_value:
knn = KNeighborsClassifier(n_neighbors=k,algorithm=algo)
results = cross_val_score(knn,X_train,y_train,cv = kfold)
print(f'Score:{round(results.mean(),4)} with algo = {algo} , K = {k}')
scores.append(results.mean())
best_comb.append((k,algo))
best_param = best_comb[scores.index(max(scores))]
print(f'\nThe Best Score : {max(scores)}')
print(f"['algorithm': {best_param[1]} ,'n_neighbors': {best_param[0]}]")
二、网格搜索
机器学习模型调优算法中的“网格搜索”是一种自动化调参方法,它通过穷举搜索的方式,在指定的参数空间内寻找最优的超参数组合,从而优化模型的性能。以下是对网格搜索的详细介绍:
原理:
网格搜索的原理是基于暴力穷尽搜索,它会在指定的参数范围内,按照设定的步长,生成一个参数网格。然后,网格搜索会遍历这个参数网格中的每一组参数组合,对模型进行训练和评估,以找到在验证集上性能最佳的超参数组合。
步骤:
- 确定参数范围和步长: 首先,需要确定要搜索的超参数的范围和步长。这通常基于经验、算法的要求或数据的特性来确定。
- 生成参数网格: 根据确定的参数范围和步长,生成一个参数网格,其中包含了所有可能的参数组合。
- 遍历参数网格: 对于参数网格中的每一组参数组合,使用这些参数来训练模型,并在验证集上评估模型的性能。
- 记录最佳参数组合: 在遍历过程中,记录每一组参数对应的模型性能评估结果,并找到性能最佳的一组参数。
- 使用最佳参数组合: 最后,使用找到的最佳参数组合来重新训练模型,并在测试集上评估其性能。
优点:
- 简单直观: 网格搜索是一种简单直观的调参方法,易于理解和实现。
- 全局搜索: 通过遍历整个参数网格,网格搜索能够在全局范围内寻找最优参数组合,避免陷入局部最优。
缺点:
- 计算成本高: 当参数空间较大或参数范围较广时,网格搜索需要遍历的参数组合数量会急剧增加,导致计算成本非常高昂,可能需要大量的时间和计算资源。
- 可能不是最优解: 由于网格搜索是基于固定步长的搜索,可能无法找到真正的全局最优解,特别是当最优解位于两个搜索点之间时。
- 缺乏灵活性: 网格搜索无法根据模型性能的变化动态调整搜索范围和步长,因此对于一些复杂的问题可能不太适用。
让我们来了解一下 sklearn 的 GridSearchCV 是如何工作的,
from sklearn.model_selection import GridSearchCV
knn = KNeighborsClassifier()
grid_param = { 'n_neighbors' : list(range(2,11)) ,
'algorithm' : ['auto','ball_tree','kd_tree','brute'] }
grid = GridSearchCV(knn,grid_param,cv = 5)
grid.fit(X_train,y_train)
#best parameter combination
grid.best_params_
#Score achieved with best parameter combination
grid.best_score_
#all combinations of hyperparameters
grid.cv_results_['params']
#average scores of cross-validation
grid.cv_results_['mean_test_score']
三、随机搜索
机器学习模型调优算法中的“随机搜索”是一种高效且灵活的参数调优方法。它通过随机采样超参数空间中的参数组合,来寻找最优的超参数配置,从而优化模型的性能。以下是关于随机搜索的详细介绍:
原理:
随机搜索的原理在于,通过随机采样的方式,从整个超参数空间中挑选出一定数量的参数组合进行评估。与网格搜索不同,随机搜索并不遍历所有可能的参数组合,而是根据指定的采样策略(如均匀分布、正态分布等)在参数空间中进行随机抽样。这种方式可以在有限的计算资源下,快速找到性能较好的超参数组合。
步骤:
- 定义参数空间: 首先,需要确定要搜索的超参数及其取值范围。这些超参数可以是学习率、正则化系数、树的深度等,具体取决于所使用的机器学习算法。
- 设置采样策略: 选择合适的采样策略,确定如何从参数空间中随机抽取参数组合。常见的采样策略包括均匀采样、正态分布采样等。
- 随机采样与评估: 根据采样策略,在参数空间中随机抽取一定数量的参数组合,并使用这些参数组合来训练模型。然后,在验证集上评估模型的性能,并记录每个参数组合对应的性能指标。
- 选择最佳参数组合: 从所有评估过的参数组合中,选择性能最佳的一组作为最优超参数组合。
- 重新训练与评估: 使用最优超参数组合重新训练模型,并在测试集上评估其性能。
优点:
- 计算效率高: 由于随机搜索只需要评估一部分参数组合,因此相对于网格搜索来说,计算成本更低,能够在有限的时间内找到较好的超参数组合。
- 灵活性强: 随机搜索可以根据问题的特点和计算资源的情况,灵活调整采样策略和采样数量,以适应不同的应用场景。
- 全局搜索能力: 通过随机采样,随机搜索能够在一定程度上避免陷入局部最优解,具有更好的全局搜索能力。
缺点:
- 结果不稳定: 由于随机搜索是基于随机采样的,因此每次运行的结果可能会有所不同,存在一定的不确定性。
- 可能错过最优解: 虽然随机搜索可以在全局范围内搜索超参数组合,但由于其随机性,有时可能会错过真正的最优解。
让我们了解一下 sklearn 的 RandomizedSearchCV 是如何工作的,
from sklearn.model_selection import RandomizedSearchCV
knn = KNeighborsClassifier()
grid_param = { 'n_neighbors' : list(range(2,11)) ,
'algorithm' : ['auto','ball_tree','kd_tree','brute'] }
rand_ser = RandomizedSearchCV(knn,grid_param,n_iter=10)
rand_ser.fit(X_train,y_train)
#best parameter combination
rand_ser.best_params_
#score achieved with best parameter combination
rand_ser.best_score_
#all combinations of hyperparameters
rand_ser.cv_results_['params']
#average scores of cross-validation
rand_ser.cv_results_['mean_test_score']
四、贝叶斯搜索
之前在博客中也介绍过贝叶斯搜索和python实现,具体参考本人博客贝叶斯优化(Bayesian Optimization)介绍和python实现
机器学习模型调优算法中的“贝叶斯搜索”是一种基于贝叶斯定理的自动化调参方法,它通过不断地更新目标函数的后验分布来寻找最优的超参数组合。以下是对贝叶斯搜索的详细介绍:
原理:
贝叶斯搜索的主要思想是,在给定优化的目标函数(广义的函数,只需指定输入和输出即可,无需知道内部结构以及数学性质)的情况下,通过不断地添加样本点来更新目标函数的后验分布(通常是高斯过程),直到后验分布基本贴合于真实分布。每一次添加样本点都会考虑上一次参数的信息,以便更好地调整当前的参数。贝叶斯搜索的核心过程包括先验函数(Prior Function, PF)与采集函数(Acquisition Function, AC)。PF主要利用高斯过程回归(或其他PF函数)来建模目标函数的分布,而AC则用于确定下一个采样点,以最大化信息增益或最小化预期损失。
步骤:
- 定义先验分布: 基于问题的特性和经验知识,为超参数定义先验分布。
- 采集函数与样本选择: 使用采集函数(如EI、PI、UCB等)来评估不同超参数组合的潜在价值,并选择下一个要评估的样本点。
- 模型评估与更新: 在选定的超参数组合下训练模型,并在验证集上评估其性能。然后,使用这些评估结果来更新目标函数的后验分布。
- 迭代优化: 重复步骤2和3,直到满足停止条件(如达到最大迭代次数、性能提升不再显著等)。
- 选择最优参数: 从更新后的后验分布中选择性能最优的超参数组合。
优点:
- 高效性: 贝叶斯搜索能够平衡探索(在全局尚未探索的区域寻找更好的解)和利用(利用已知信息优化当前解),从而在较少的迭代次数内找到较好的超参数组合。
- 灵活性: 贝叶斯搜索可以适应各种复杂的目标函数和参数空间,对于非线性、非凸等问题也能表现出良好的性能。
- 自适应性: 随着迭代次数的增加,贝叶斯搜索会逐渐聚焦于性能更好的参数区域,从而提高搜索效率。
缺点:
- 计算成本: 尽管贝叶斯搜索相对于网格搜索和随机搜索更为高效,但在处理高维参数空间或需要精确建模的情况下,计算成本仍然可能较高。
- 实现复杂性: 贝叶斯搜索的实现相对复杂,需要深入理解贝叶斯定理和高斯过程等概念,以及如何选择和使用合适的采集函数。
- 对初始化的敏感性: 贝叶斯搜索的性能可能受到初始化参数和先验分布的影响,不合理的初始化可能导致搜索过程陷入局部最优解。
让我们用 scikit-optimize 的BayesSearchCV来理解这一点
安装: pip install scikit-optimize
实现贝叶斯搜索的另一个类似的库是 bayesian-optimization
from skopt import BayesSearchCV
import warnings
warnings.filterwarnings("ignore")
# parameter ranges are specified by one of below
from skopt.space import Real, Categorical, Integer
knn = KNeighborsClassifier()
#defining hyper-parameter grid
grid_param = { 'n_neighbors' : list(range(2,11)) ,
'algorithm' : ['auto','ball_tree','kd_tree','brute'] }
#initializing Bayesian Search
Bayes = BayesSearchCV(knn , grid_param , n_iter=30 , random_state=14)
Bayes.fit(X_train,y_train)
#best parameter combination
Bayes.best_params_
#score achieved with best parameter combination
Bayes.best_score_
#all combinations of hyperparameters
Bayes.cv_results_['params']
#average scores of cross-validation
Bayes.cv_results_['mean_test_score']
五、总结
在机器学习模型调优的过程中,找到参数的最佳组合与所需的计算时间之间始终存在一个权衡。当面对超参数空间庞大、维度众多时,选择适当的优化方式显得尤为重要。以下是针对网格搜索、随机搜索、手动调参和贝叶斯搜索这四种优化方式的总结:
-
网格搜索提供了一种全面而系统的搜索方法,通过遍历所有可能的参数组合来找到最优解。然而,当参数空间较大时,网格搜索的计算成本会急剧增加,可能导致优化过程耗时过长。
-
随机搜索则通过随机抽样来减少计算量,同时保持一定的全局搜索能力。它能够在有限的计算资源下快速找到性能较好的参数组合,尤其适用于超参数空间较大或计算资源有限的情况。
-
手动调参依赖于模型训练者的经验和判断,虽然灵活性较高,但耗时费力且容易陷入局部最优。它通常作为其他自动化调参方法的补充,用于对特定参数进行微调。
-
贝叶斯搜索则利用贝叶斯定理和高斯过程来不断更新目标函数的后验分布,以找到最优的超参数组合。它在平衡探索和利用方面表现出色,能够在较少的迭代次数内找到较好的解。然而,贝叶斯搜索的实现相对复杂,需要深入理解相关概念和技术。
在实际应用中,我们可以根据问题的特性和计算资源的情况选择合适的优化方式。对于超参数空间较大的情况,可以先使用随机搜索快速找到潜在的参数组合,然后对这些组合进行局部的网格搜索以选择最优特征。这样既能保证一定的全局搜索能力,又能减少计算成本。同时,结合手动调参对特定参数进行微调,可以进一步提高模型的性能。最终,在找到参数的最佳组合的保证和计算时间之间取得一个合理的权衡。