机器学习中总是会碰见调参这种枯燥无味且消耗时间的事情,所幸,有很多可以帮助你自动调参的库以及相应的方法,在这里统一总结一下吧。
一、随机森林超参数优化- RandomSearch和GridSearch
(1)RandomSearch
原理:
超参数优化也就是常说的调参,python-sklearn里常用的有GridSearchCV和RandomizedSearchCV可以用。其中GridSearchCV的原理很简明,就是程序去挨个尝试每一组超参数,然后选取最好的那一组。可以想象,这个是比较费时间的,面临着维度灾难。因此James Bergstra和Yoshua Bengio在2012年提出了超参数优化的RandomSearch方法。
RandomizedSearchCV是在论文的基础上加入了cross-validation
RandomSearchCV是如何"随机搜索"的:
考察其源代码,其搜索策略如下:
(a)对于搜索范围是distribution的超参数,根据给定的distribution随机采样;
(b)对于搜索范围是lit的超参数,在给定的list中等概率采样;
(c)对a、b两步中得到的n_iter组采样结果,进行遍历。
(补充)如果给定的搜索范围均为list,则不放回抽样n_iter次。
更详细的可以参考sklearn-RandomizedSearchCV的ParameterSampler类的代码。
为什么RandomSearchCV会有效?
(a)目标函数为 f(x,y)=g(x)+h(y),其中绿色为g(x),黄色为h(y),目的是求f的最大值。
(b)其中由于g(x)数值上要明显大于h(y),因此有f(x,y)=g(x)+h(y)≈g(x),也就是说在整体求解f(x,y)最大值的过程中,g(x)的影响明显大于h(y)。
(c)两个图都进行9次实验(搜索),可以看到左图实际探索了各三个点(在横轴和纵轴上的投影均为3个),而右图探索了9个不同的点(横轴纵轴均是,不过实际上横轴影响更大)。
(d)右图更可能找到目标函数的最大值。
因此引入随机因素在某些情况下可以提高寻优效率。
下面是具体代码
(2)GridSearch网格搜索
二、 Hyperopt自动化超参数调优- 贝叶斯优化
网格搜索和随机搜索则对ml模型超参数的优化能取得不错的效果,但是需要大量运行时间去评估搜索空间中并不太可能找到最优点的区域。因此越来越多的的超参数调优过程都是通过自动化的方法完成的,它们旨在使用带有策略的启发式搜索(informed search)在更短的时间内找到最优超参数。
贝叶斯优化是一种基于模型的用于寻找函数最小值的方法。近段时间以来,贝叶斯优化开始被用于机器学习超参数调优,结果表明,该方法在测试集上的表现更加优异,并且需要的迭代次数小于随机搜索。
Python 环境下有一些贝叶斯优化程序库,它们目标函数的代理算法有所区别。本部分主要介绍「Hyperopt」库,它使用树形 Parzen 评估器(TPE,https://papers.nips.cc/paper/4443-algorithms-for-hyper-parameter-optimization.pdf)作为搜索算法,其他的 Python 库还包含「Spearmint」(高斯过程代理)和「SMAC」(随即森林回归)。
贝叶斯优化问题有四个组成部分:
1)目标函数:我们想要最小化的对象,这里指带超参数的机器学习模型的验证误差
2)域空间:待搜索的超参数值
3)优化算法:构造代理模型和选择接下来要评估的超参数值的方法
4)结果的历史数据:存储下来的目标函数评估结果,包含超参数和验证损失
通过以上四个步骤,我们可以对任意实值函数进行优化(找到最小值)。
详解:
1)目标函数
模型训练目的是最小化目标函数,所以输出为需要最小化的实值——交叉验证损失。Hyperopt 将目标函数作为黑盒处理,因为这个库只关心输入和输出是什么。为了找到使损失最小的输入值。
cross_val_score
对衡量的estimator,它默认返回的是一个array,包含K folder情况下的各次的评分,一般采用mean()。 需要确定这个estimator默认的 scoring 是什么,它的值是越大越匹配还是越小越匹配。如果自己指定了scoring,一定要确定这个scoring值的意义,切记切记! 而如果用户不指定,一般对于Classification类的estimator,使用accuracy,它是越大越好,那么,hyperopt里面的loss的值就应该是对这个值取负数,因为hyperopt通过loss最小取找最佳匹配。 可以把feature的normalize或者scale作为一个choice,然后看看是否更合适。如果更合适,best里面就会显示 normalize 为1。
例二,也是取负
实际GBM完整的目标函数
2)域空间
贝叶斯优化中,域空间对每个超参数来说是一个概率分布而不是离散的值。因为很难确定不同数据集之间的最佳模型设定区间,此处主要采用贝叶斯算法进行推理。
此外,模型中有些参数是不需要调优的。以GBM为例,除了n_estimator之外,还有10个左右的参数需要调整。因此我们采用不同的分布来定义每个参数的域空间
不同分布名称含义:
choice:类别变量
quniform:离散均匀分布(在整数空间上均匀分布)
uniform:连续均匀分布(在浮点数空间上均匀分布)
loguniform:连续对数均匀分布(在浮点数空间中的对数尺度上均匀分布)
- hp.pchoice(label,p_options)以一定的概率返回一个p_options的一个选项。这个选项使得函数在搜索过程中对每个选项的可能性不均匀。
- hp.uniform(label,low,high)参数在low和high之间均匀分布。
- hp.quniform(label,low,high,q),参数的取值round(uniform(low,high)/q)*q,适用于那些离散的取值。
- hp.loguniform(label,low,high) 返回根据 exp(uniform(low,high)) 绘制的值,以便返回值的对数是均匀分布的。
优化时,该变量被限制在[exp(low),exp(high)]区间内。 - hp.randint(label,upper) 返回一个在[0,upper)前闭后开的区间内的随机整数。
- hp.normal(label, mu, sigma) where mu and sigma are the mean and standard deviation σ , respectively. 正态分布,返回值范围没法限制。
- hp.qnormal(label, mu, sigma, q)
- hp.lognormal(label, mu, sigma)
- hp.qlognormal(label, mu, sigma, q)
定义与空间后,可以选择一个样本来查看典型样本形式
3)搜索算法
algo指定搜索算法,目前支持以下算法:
①随机搜索(hyperopt.rand.suggest)
②模拟退火(hyperopt.anneal.suggest)
③TPE算法(hyperopt.tpe.suggest,算法全称为Tree-structured Parzen Estimator Approach)
尽管从概念上来说,这是贝叶斯优化最难的一部分,但在 Hyperopt 中创建优化算法只需一行代码。使用树形 Parzen 评估器(Tree Parzen Estimation,以下简称 TPE)的代码如下:
4)结果历史数据
想知道背后的发展进程,可以使用「Trials」对象,它将存储基本的训练信息,还可以使用目标函数返回的字典(包含损失「loss」和参数「params」)
Trials只是用来记录每次eval的时候,具体使用了什么参数以及相关的返回值。这时候,fn的返回值变为dict,除了loss,还有一个status。Trials对象将数据存储为一个BSON对象,可以利用MongoDB做分布式运算。
对于STATUS_OK的返回,会统计它的loss值,而对于STATUS_FAIL的返回,则会忽略。
可以通过这里面的值,把一些变量与loss的点绘图,来看匹配度。或者tid与变量绘图,看它搜索的位置收敛(非数学意义上的收敛)情况。
trials有这几种:
- trials.trials - a list of dictionaries representing everything about the search
- trials.results - a list of dictionaries returned by ‘objective’ during the search
- trials.losses() - a list of losses (float for each ‘ok’ trial) trials.statuses() - a list of status strings
5)优化算法
输出结果如下:
三、Optuna
有关这个库的文献好少,不过看代码的话,形式和Hyperopt差不太多