机器学习调参自动优化方法
Posted Datawhale
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了机器学习调参自动优化方法相关的知识,希望对你有一定的参考价值。
本文旨在介绍当前被大家广为所知的超参自动优化方法,像网格搜索、随机搜索、贝叶斯优化和Hyperband,并附有相关的样例代码供大家学习。
一、网格搜索(Grid Search)
网格搜索是暴力搜索,在给定超参搜索空间内,尝试所有超参组合,最后搜索出最优的超参组合。sklearn已实现该方法,使用样例如下:
from sklearn import svm, datasets
from sklearn.model_selection import GridSearchCV
import pandas as pd
# 导入数据
iris = datasets.load_iris()
# 定义超参搜索空间
parameters = {'kernel':('linear', 'rbf'), 'C':[1, 10]}
# 初始化模型
svc = svm.SVC()
# 网格搜索
clf = GridSearchCV(estimator = svc,
param_grid = parameters,
scoring = 'accuracy',
n_jobs = -1,
cv = 5)
clf.fit(iris.data, iris.target)
返回:GridSearchCV(cv=5, estimator=SVC(), n_jobs=-1,
param_grid={'C': [1, 10], 'kernel': ('linear', 'rbf')},
scoring='accuracy')
# 打印结果
print('详细结果:\\n', pd.DataFrame.from_dict(clf.cv_results_))
print('最佳分类器:\\n', clf.best_estimator_)
print('最佳分数:\\n', clf.best_score_)
print('最佳参数:\\n', clf.best_params_).
返回:
详细结果:
mean_fit_time std_fit_time mean_score_time std_score_time param_C ... split3_test_score split4_test_score mean_test_score std_test_score rank_test_score
0 0.000788 0.000394 0.000194 0.000389 1 ... 0.966667 1.0 0.980000 0.016330 1
1 0.000804 0.000402 0.000199 0.000399 1 ... 0.933333 1.0 0.966667 0.021082 4
2 0.000593 0.000484 0.000593 0.000484 10 ... 0.966667 1.0 0.973333 0.038873 3
3 0.000593 0.000484 0.000399 0.000489 10 ... 0.966667 1.0 0.980000 0.016330 1
[4 rows x 15 columns]
最佳分类器:
SVC(C=1, kernel='linear')
最佳分数:
0.9800000000000001
最佳参数:
{'C': 1, 'kernel': 'linear'}
sklearn.model_selection.GridSearchCV[1]的重要参数说明:
estimator: scikit-learn模型。
param_grid: 超参搜索空间,即超参数字典。
scoring: 在交叉验证中使用的评估策略。
n_jobs: 并行任务数,-1为使用所有CPU。
cv: 决定采用几折交叉验证。
二、随机搜索(Randomized Search)
随机搜索是在搜索空间中采样出超参组合,然后选出采样组合中最优的超参组合。随机搜索的好处如下图所示:
图1: 网格搜索和随机搜索的对比[2]
解释图1,如果目前我们要搜索两个参数,但参数A重要而另一个参数B并没有想象中重要,网格搜索9个参数组合(A, B),而由于模型更依赖于重要参数A,所以只有3个参数值是真正参与到最优参数的搜索工作中。反观随机搜索,随机采样9种超参组合,在重要参数A上会有9个参数值参与到搜索工作中,所以,在某些参数对模型影响较小时,使用随机搜索能让我们有更多的探索空间。
同样地,sklearn实现了随机搜索[3],样例代码如下:
from sklearn import svm, datasets
from sklearn.model_selection import RandomizedSearchCV
import pandas as pd
from scipy.stats import uniform
# 导入数据
iris = datasets.load_iris()
# 定义超参搜索空间
distributions = {'kernel':['linear', 'rbf'], 'C':uniform(loc=1, scale=9)}
# 初始化模型
svc = svm.SVC()
# 网格搜索
clf = RandomizedSearchCV(estimator = svc,
param_distributions = distributions,
n_iter = 4,
scoring = 'accuracy',
cv = 5,
n_jobs = -1,
random_state = 2021)
clf.fit(iris.data, iris.target)
返回:RandomizedSearchCV(cv=5, estimator=SVC(), n_iter=4, n_jobs=-1,
param_distributions={'C': <scipy.stats._distn_infrastructure.rv_frozen object at 0x000001F372F9A190>,
'kernel': ['linear', 'rbf']},
random_state=2021, scoring='accuracy')
# 打印结果
print('详细结果:\\n', pd.DataFrame.from_dict(clf.cv_results_))
print('最佳分类器:\\n', clf.best_estimator_)
print('最佳分数:\\n', clf.best_score_)
print('最佳参数:\\n', clf.best_params_)
返回:
详细结果:
mean_fit_time std_fit_time mean_score_time std_score_time param_C ... split3_test_score split4_test_score mean_test_score std_test_score rank_test_score
0 0.000598 0.000489 0.000200 0.000400 6.4538 ... 0.966667 1.0 0.986667 0.016330 1
1 0.000997 0.000002 0.000000 0.000000 4.99782 ... 0.966667 1.0 0.980000 0.026667 3
2 0.000798 0.000399 0.000399 0.000488 3.81406 ... 0.966667 1.0 0.980000 0.016330 3
3 0.000598 0.000488 0.000200 0.000399 5.36286 ... 0.966667 1.0 0.986667 0.016330 1
[4 rows x 15 columns]
最佳分类器:
SVC(C=6.453804509266643)
最佳分数:
0.9866666666666667
最佳参数:
{'C': 6.453804509266643, 'kernel': 'rbf'}
相比于网格搜索,sklearn随机搜索中主要改变的参数是param_distributions,负责提供超参值分布范围。
三、贝叶斯优化(Bayesian Optimization)
我写本文的目的主要是冲着贝叶斯优化来的,一直有所耳闻却未深入了解,所以我就来查漏补缺了。以下内容主要基于Duane Rich在《How does Bayesian optimization work?》[4]的回答。
调优的目的是要找到一组最优的超参组合,能使目标函数f达到全局最小值。
举个例子,若学习率设置过大,模型可能会在代价函数的全局最优点附近不断来回震荡,甚至跳出全局最优点,而设置过小,又可能会陷入局部最优,因此调学习率这一参数,是为了让模型能收敛到代价函数的全局最小值。可是在机器学习中,目标函数 常是被称作expensive blackbox function,计算开销大且不一定为凸函数。为此,贝叶斯优化出现了,它特别适合针对expensive blackbox function找到全局最优。
假设我们的真实的目标函数 长下图这样:
图2: 目标函数f(x)[4]
注意: 目标函数 的 是指超参数,我们希望找到最优的超参 去得到最小的 。为什么用虚线表示 呢?因为它是黑箱函数(blackbox function)。
现在,我们怎么找到 全局最小值点呢?我们能不能先在 上随机采样10个点,然后取它们的最小值呢?
图3: 随机采样10个点的目标函数f(x)[4]
图3里确实有个点挺靠近全局最优点的,那是不是在它附近再采样几个点,不断重复就行了?没那么简单,万一起始采样点在局部最小值附近,那这种方法会很容易陷入局部最优。关于“如何找到下一个合适的点”这个问题,我们先放一放,因为我们漏掉一个重点:每次尝试一种超参值 ,计算 的代价是昂贵的,为了减轻开销,贝叶斯优化采用了代理模型(surrogate model),代理模型可以被看作是一个简单模型去拟合原本复杂且不好理解的模型,简单来说,就是 计算太昂贵了,我们就用代理模型去代替它。
贝叶斯优化使用了高斯过程(gasussian processes, GP)去构建代理模型,高斯过程的细节这里暂时不讲,有兴趣的小伙伴可以自行查阅资料了解。基于给定的输入和输出,GP会推断出一个模型(这里为代理模型)。假设我们从昂贵的 采样了4个点,然后我们把这4个点交给GP,它会返回一个代理模型,如下图所示:
图4: 目标函数f(x)和代理模型[4]
绿色实线就是GP猜的代理模型,绿色条带是输出分布的标准差(即为Uncertainty)。我们有了代理模型,后续我们去找下一个合适的超参值,就能带入到计算开销相对较小的代理模型中,评估给定超参值的情况。
现在,我们来思考回之前提到的问题:"如何找到下一个合适的点?",这个问题本质是在问:“哪里有全局最小的点?”,为了解决这个问题,我们要关注两个地方:
(1) 已开发区域: 在绿色实线上最低的超参点。因为很可能它附近存在全局最优点。
(2) 未探索区域: 绿色实线上还未被探索的区域。比如图4,相比于0.15-0.25区间,0.65-0.75区间更具有探索价值(即该区间Uncertainty更大)。探索该区域有利于减少我们猜测的方差。
为了实现以上探索和开发的平衡(exploration-exploitation trade-off),贝叶斯优化使用了采集函数(acquisition function),它能平衡好全局最小值的探索和开发。采集函数有很多选择,其中最常见的是expectated of improvement(EI)[5],我们先看一个utility function:
是目前观察到的最小值, 是超参值,我们希望上述utility function输出越大越好(即找到的 能获得比当前最小值还小),基于 ,EI采集函数如下所示: