【机器学习实战笔记 10】超参数优化(一):网格优化方法

发布于:2025-06-19 ⋅ 阅读:(14) ⋅ 点赞:(0)

《超参数优化》

一 超参数优化与枚举网格的理论极限

  • 超参数优化HPO(HyperParameter Optimization)

每一个机器学习算法都会有超参数,而超参数的设置很大程度上影响了算法实际的使用效果,因此调参是机器学习算法工程师最为基础和重要的任务。现代机器学习与深度学习算法的超参数量众多,不仅实现方法异常灵活、算法性能也受到更多的参数的复合影响,因此当人工智能浪潮来临时,可以自动选择超参数的超参数优化HPO领域也迎来了新一轮爆发。

在算法的世界中,我们渴望一切流程最终都走向完美自动化,专门研究机器学习自动化的学科被称为AutoML,而超参数自动优化是AutoML中最成熟、最深入、也是最知名的方向。理论上来说,当算力与数据足够时,HPO的性能一定是超过人类的。HPO能够降低人为工作量,并且HPO得出的结果比认为搜索的复现可能性更高,所以HPO可以极大程度提升科学研究的复现性和公平性。当代超参数优化算法主要可以分为:

基于网格的各类搜索(Grid)

基于贝叶斯优化的各类优化算法(Baysian)

基于梯度的各类优化(Gradient-based)

基于种群的各类优化(进化算法,遗传算法等)

其中,各类网格搜索方法与基于贝叶斯的优化方法是最为盛行的,贝叶斯优化方法甚至可以被称为是当代超参数优化中的SOTA模型。这些模型对于复杂集成算法的调整有极大的作用与意义。

但是,参数空间越大,网格搜索所需的算力和时间也会越大,当参数维度上升时,网格搜索所需的计算量更是程指数级上升的。以随机森林为例:

只有1个参数n_estimators,备选范围是[50,100,150,200,250,300],需要建模6次。

增加参数max_depth,且备选范围是[2,3,4,5,6],需要建模30次。

增加参数min_sample_split,且备选范围为[2,3,4,5],需要建模120次。

同时,参数优化的目标是找出令模型泛化能力最强的组合,因此需要交叉验证来体现模型的泛化能力,假设交叉验证次数为5,则三个参数就需要建模600次。在面对超参数众多、且超参数取值可能无限的人工神经网络、融合模型、集成模型时,伴随着数据和模型的复杂度提升,网格搜索所需要的时间会急剧增加,完成一次枚举网格搜索可能需要耗费几天几夜。考虑到后续实践过程中,算法和数据都将更加复杂,而建模过程中超参数调优是模型训练的必备环节,因此,我们急需寻找到一种更加高效的超参数搜索方法。在本节课中,我们将介绍三种基于网格进行改进的超参数优化方法,并将他们的结果与网格搜索进行时间/空间/效果上的对比。

二 随机网格搜索RandomizedSearchCV

  • 基本原理

在讲解网格搜索时我们提到,伴随着数据和模型的复杂度提升,网格搜索所需要的时间急剧增加。以随机森林算法为例,如果使用过万的数据,搜索时间则会立刻上升好几个小时。因此,我们急需寻找到一种更加高效的超参数搜索方法。

首先,当所使用的算法确定时,决定枚举网格搜索运算速度的因子一共有两个:

1 参数空间的大小:参数空间越大,需要建模的次数越多

2 数据量的大小:数据量越大,每次建模时需要的算力和时间越多

因此,sklearn中的网格搜索优化方法主要包括两类,其一是调整搜索空间,其二是调整每次训练的数据。其中,调整参数空间的具体方法,是放弃原本的搜索中必须使用的全域超参数空间,改为挑选出部分参数组合,构造超参数子空间,并只在子空间中进行搜索。

以下图的二维空间为例,在这个n_estimators与max_depth共同组成的参数空间中,n_estimators的取值假设为[50,100,150,200,250,300],max_depth的取值假设为[2,3,4,5,6],则枚举网格搜索必须对30种参数组合都进行搜索。当我们调整搜索空间,我们可以只抽样出橙色的参数组合作为“子空间”,并只对橙色参数组合进行搜索。如此一来,整体搜索所需的计算量就大大下降了,原本需要30次建模,现在只需要8次建模。
在这里插入图片描述
在sklearn中,随机抽取参数子空间并在子空间中进行搜索的方法叫做随机网格搜索RandomizedSearchCV。由于搜索空间的缩小,需要枚举和对比的参数组的数量也对应减少,整体搜索耗时也将随之减少,因此:

当设置相同的全域空间时,随机搜索的运算速度比枚举网格搜索很多。

当设置相同的训练次数时,随机搜索可以覆盖的空间比枚举网格搜索很多。

同时,绝妙的是,随机网格搜索得出的最小损失与枚举网格搜索得出的最小损失很接近

可以说,是提升了运算速度,又没有过多地伤害搜索的精度。

不过,需要注意的是,随机网格搜索在实际运行时,并不是先抽样出子空间,再对子空间进行搜索,而是仿佛“循环迭代”一般,在这一次迭代中随机抽取1组参数进行建模,下一次迭代再随机抽取1组参数进行建模,由于这种随机抽样是不放回的,因此不会出现两次抽中同一组参数的问题。我们可以控制随机网格搜索的迭代次数,来控制整体被抽出的参数子空间的大小,这种做法往往被称为“赋予随机网格搜索固定的计算量,当全部计算量被消耗完毕之后,随机网格搜索就停止”。

  • 随机网格搜索的实现
from sklearn.model_selection import RandomizedSearchCV

class sklearn.model_selection.RandomizedSearchCV(estimator, param_distributions, *, n_iter=10, scoring=None, n_jobs=None, refit=True, cv=None, verbose=0, pre_dispatch=‘2*n_jobs’, random_state=None, error_score=nan, return_train_score=False)

全部参数解读如下,其中加粗的是随机网格搜索独有的参数:

Name Description
estimator 调参对象,某评估器
param_distributions 全域参数空间,可以是字典或者字典构成的列表
n_iter 迭代次数,迭代次数越多,抽取的子参数空间越大
scoring 评估指标,支持同时输出多个参数
n_jobs 设置工作时参与计算的线程数
refit 挑选评估指标和最佳参数,在完整数据集上进行训练
cv 交叉验证的折数
verbose 输出工作日志形式
pre_dispatch 多任务并行时任务划分数量
random_state 随机数种子
error_score 当网格搜索报错时返回结果,选择’raise’时将直接报错并中断训练过程,其他情况会显示警告信息后继续完成训练
return_train_score 在交叉验证中是否显示训练集中参数得分
#定义随机搜索
search = RandomizedSearchCV(estimator=reg
                            ,param_distributions=param_grid_simple
                            ,n_iter = 800 #子空间的大小是全域空间的一半左右
                            ,scoring = "neg_mean_squared_error"
                            ,verbose = True
                            ,cv = cv
                            ,random_state=1412
                            ,n_jobs=-1
                           )
#查看模型结果
search.best_estimator_
abs(search.best_score_)**0.5
#根据最优参数重建模型
ad_reg = RFR(max_depth=24, max_features=16, min_impurity_decrease=0,
                      n_estimators=85, n_jobs=-1, random_state=1412,
                      verbose=True)
                      
rebuild_on_best_param(ad_reg)

结果:

HPO方法 默认参数 网格搜索 随机搜索
搜索空间/全域空间 - 1536/1536 800/1536
运行时间(分钟) - 6.36 2.83(↓)
搜索最优(RMSE) 30571.266 29179.698 29251.284
重建最优(RMSE) - 28572.070 28639.969(↑)

很明显,在相同参数空间、相同模型的情况下,随机网格搜索的运算速度是普通网格搜索的一半,当然,这与子空间是全域空间的一半有很大的联系。由于随机搜索只是降低搜索的次数,并非影响搜索过程本身,因此其运行时间基本就等于n_iter/全域空间组合数 * 网格搜索的运行时间。

  • 随机网格搜索的理论极限

虽然通过缩小子空间可以提升搜索的速度,但是随机网格搜索的精度看起来并没有削减太多,随机网格搜索可以得到和网格搜索一样好的结果吗?它也像网格搜索一样,可以得到最优的参数组合吗?为什么缩小参数空间之后,随机网格搜索的结果还与网格搜索一致?

理论上来说,枚举网格搜索的上限和随机网格搜索的上限哪个高?

从直觉上来说,我们很难回答这些问题,但我们可以从数学的随机过程的角度来理解这个问题。在机器学习算法当中,有非常多通过随机来提升运算速度(比如Kmeans,随机挑选样本构建簇心,小批量随机梯度下降,通过随机来减少每次迭代需要的样本)、或通过随机来提升模型效果的操作(比如随机森林,比如极度随机树)。两种随机背后的原理完全不同,而随机网格搜索属于前者,这一类机器学习方法总是伴随着“从某个全数据集/全域中进行抽样”的操作,而这种操作能够有效的根本原因在于:

  • 抽样出的子空间可以一定程度上反馈出全域空间的分布,且子空间相对越大(含有的参数组合数越多),子空间的分布越接近全域空间的分布

  • 当全域空间本身足够密集时,很小的子空间也能获得与全域空间相似的分布

  • 如果全域空间包括了理论上的损失函数最小值,那一个与全域空间分布高度相似的子空间很可能也包括损失函数的最小值,或包括非常接近最小值的一系列次小值

上述数学事实听起来比较抽象,但其实我们可以通过绘制图像来直观地呈现这些事实。许多在数学上比较抽象的概念都可以被可视化。在这里,我们借助matplotlib工具库mplot3d中的一组默认数据。
在这里插入图片描述
抽样子集:
在这里插入图片描述
我们可以在图像上验证如下事实:

  • 抽样出的子空间可以一定程度上反馈出全域空间的分布,且子空间相对越大(含有的参数组合数越多),子空间的分布越接近全域空间的分布

  • 当全域空间本身足够密集时,很小的子空间也能获得与全域空间相似的分布

  • 如果全域空间包括了理论上的损失函数最小值,那一个与全域空间分布高度相似的子空间很可能也包括损失函数的最小值,或包括非常接近最小值的一系列次小值

因此,只要子空间足够大,随机网格搜索的效果一定是高度逼近枚举网格搜索的。在全域参数空间固定时,随机网格搜索可以在效率与精度之间做权衡。子空间越大,精度越高,子空间越小,效率越高。

  • 更大/更密集的全域空间

不过,由于随机网格搜索计算更快,所以在相同计算资源的前提下,我们可以对随机网格搜索使用更大的全域空间,因此随机搜索可能得到比网格搜索更好的效果:

#创造参数空间 - 让整体参数空间变得更密
param_grid_simple = {'n_estimators': [*range(80,100,1)]
                     , 'max_depth': [*range(10,25,1)]
                     , "max_features": [*range(10,20,1)]
                     , "min_impurity_decrease": [*np.arange(0,5,10)]
                    }
#计算全域参数空间大小,这是我们能够抽样的最大值
count_space(param_grid_simple)                  
#建立回归器、交叉验证
reg = RFR(random_state=1412,verbose=True,n_jobs=-1)
cv = KFold(n_splits=5,shuffle=True,random_state=1412)

#定义随机搜索
search = RandomizedSearchCV(estimator=reg
                            ,param_distributions=param_grid_simple
                            ,n_iter = 1536 #使用与枚举网格搜索类似的拟合次数
                            ,scoring = "neg_mean_squared_error"
                            ,verbose = True
                            ,cv = cv
                            ,random_state=1412
                            ,n_jobs=-1)   
#训练随机搜索评估器
#=====【TIME WARNING: 5~10min】=====#
start = time.time()
search.fit(X,y)
end = time.time() - start
print(end/60)
#查看最佳评估器
search.best_estimator_           
#查看最终评估指标
abs(search.best_score_)**0.5
 rebuild_on_best_param(search.best_estimator_)   
HPO方法 默认参数 网格搜索 随机搜索 随机搜索
(大空间)
搜索空间/全域空间 - 1536/1536 800/1536 1536/3000
运行时间(分钟) - 6.36 2.83(↓) 3.86(↓)
搜索最优(RMSE) 30571.266 29179.698 29251.284 29012.905(↓)
重建最优(RMSE) - 28572.070 28639.969(↑) 28346.673(↓)

可以发现,当全域参数空间增大之后,随即网格搜索可以使用与小空间上的网格搜索相似或更少的时间,来探索更密集/更大的空间,从而获得更好的结果。除了可以容忍更大的参数空间之外,随机网格搜索还可以接受连续性变量作为参数空间的输入。

  • 连续型的参数空间

对于网格搜索来说,参数空间中的点是分布均匀、间隔一致的,因为网格搜索无法从某种“分布”中提取数据,只能使用组合好的参数组合点,而随机搜索却可以接受“分布”作为输入。如上图所示,对于网格搜索来说,损失函数的最低点很不幸的、位于两组参数之间,在这种情况下,枚举网格搜索是100%不可能找到最小值的。但对于随机网格搜索来说,由于是一段分布上随机选择参数点,因此在同样的参数空间中,取到更好的值的可能性更大。

HPO方法 默认参数 网格搜索 随机搜索 随机搜索
(大空间)
随机搜索
(连续型)
搜索空间/全域空间 - 1536/1536 800/1536 1536/3000 1536/无限
运行时间(分钟) - 6.36 2.83(↓) 3.86(↓) 3.92
搜索最优(RMSE) 30571.266 29179.698 29251.284 29012.905(↓) 29148.381
重建最优(RMSE) - 28572.070 28639.969(↑) 28346.673(↓) 28495.682

理论上来说,当枚举网格搜索所使用的全域参数空间足够大/足够密集时,枚举网格搜索的最优解是随机网格搜索的上限,因此理论上随机网格搜索不会得到比枚举网格搜索更好的结果

但现实中的问题是,由于枚举网格搜索的速度太慢,因此枚举网格搜索的全域参数空间往往无法设置得很大,也无法设置得很密集,因此网格搜索的结果很难接近理论上的最优值。当随机网格搜索将空间设置更大、更密集时,就可以捕获更广空间的分布,也自然就可能捕获到理论上的最优值了。

三 对半网格搜索HalvingSearchCV

  • 基本原理

在讲解随机网格搜索之前,我们梳理了决定枚举网格搜索运算速度的因子:

1 参数空间的大小:参数空间越大,需要建模的次数越多

2 数据量的大小:数据量越大,每次建模时需要的算力和时间越多

面对枚举网格搜索过慢的问题,sklearn中呈现了两种优化方式:其一是调整搜索空间,其二是调整每次训练的数据。调整搜索空间的方法就是随机网格搜索,而调整每次训练数据的方法就是对半网格搜索。

假设现在存在数据集 D D D,我们从数据集 D D D中随机抽样出一个子集 d d d。如果一组参数在整个数据集 D D D上表现较差,那大概率这组参数在数据集的子集 d d d上表现也不会太好。反之,如果一组参数在子集 d d d上表现不好,我们也不会信任这组参数在全数据集 D D D上的表现。参数在子集与全数据集上反馈出的表现一致,如果这一假设成立,那在网格搜索中,比起每次都使用全部数据来验证一组参数,或许我们可以考虑只带入训练数据的子集来对超参数进行筛选,这样可以极大程度地加速我们的运算。

但在现实数据中,这一假设要成立是有条件的,即任意子集的分布都与全数据集D的分布类似。当子集的分布越接近全数据集的分布,同一组参数在子集与全数据集上的表现越有可能一致。根据之前在随机网格搜索中得出的结论,我们知道子集越大、其分布越接近全数据集的分布,但是大子集又会导致更长的训练时间,因此为了整体训练效率,我们不可能无限地增大子集。这就出现了一个矛盾:大子集上的结果更可靠,但大子集计算更缓慢。对半网格搜索算法设计了一个精妙的流程,可以很好的权衡子集的大小与计算效率问题,我们来看具体的流程:

1、首先从全数据集中无放回随机抽样出一个很小的子集 d 0 d_0 d0,并在 d 0 d_0 d0上验证全部参数组合的性能。根据 d 0 d_0 d0上的验证结果,淘汰评分排在后1/2的那一半参数组合


2、然后,从全数据集中再无放回抽样出一个比 d 0 d_0 d0大一倍的子集 d 1 d_1 d1,并在 d 1 d_1 d1上验证剩下的那一半参数组合的性能。根据 d 1 d_1 d1上的验证结果,淘汰评分排在后1/2的参数组合


3、再从全数据集中无放回抽样出一个比 d 1 d_1 d1大一倍的子集 d 2 d_2 d2,并在 d 2 d_2 d2上验证剩下1/4的参数组合的性能。根据 d 2 d_2 d2上的验证结果,淘汰评分排在后1/2的参数组合……

持续循环。如果使用S代表首次迭代时子集的样本量,C代表全部参数组合数,则在迭代过程中,用于验证参数的数据子集是越来越大的,而需要被验证的参数组合数量是越来越少的:

迭代次数 子集样本量 参数组合数
1 S C
2 2S 1 2 \frac{1}{2} 21C
3 4S 1 4 \frac{1}{4} 41C
4 8S 1 8 \frac{1}{8} 81C
……
(当C无法被除尽时,则向上取整)

备选参数组合只剩下一组,或剩余可用的数据不足,循环就会停下,具体地来说, 1 n \frac{1}{n} n1C <= 1或者nS > 总体样本量,搜索就会停止。在实际应用时,哪一种停止条件会先被触发,需要看实际样本量及参数空间地大小。同时,每次迭代时增加的样本量、以及每次迭代时不断减少的参数组合都是可以自由设定的。

在这种模式下,只有在不同的子集上不断获得优秀结果的参数组合能够被留存到迭代的后期,最终选择出的参数组合一定是在所有子集上都表现优秀的参数组合。这样一个参数组合在全数据上表现优异的可能性是非常大的,同时也可能展现出比网格/随机搜索得出的参数更大的泛化能力。

  • 对半网格搜索的局限性

然而这个过程当中会存在一个问题:子集越大时,子集与全数据集D的分布会越相似,但整个对半搜索算法在开头的时候,就用最小的子集筛掉了最多的参数组合。如果最初的子集与全数据集的分布差异巨大的化,在对半搜索开头的前几次迭代中,就可能筛掉许多对全数据集D有效的参数,因此对半网格搜索最初的子集一定不能太小。

在对半网格搜索过程中,子集的样本量时呈指数级增长。

  • 对半网格搜索的实现

在sklearn当中,我们可以使用HalvingGridSearchCV类来实现对半网格搜索。Halving搜索是sklearn 1.0.1版本才新增的功能,因此现在该功能还处于实验阶段,在导入该类的时候需要同时导入用以开启对半网格搜索的辅助功能enable_halving_search_cv。当且仅当该功能被导入时,HalvingGridSearchCV才能够被导入和使用。

对半网格搜索的类如下所示:

class sklearn.model_selection.HalvingGridSearchCV(estimator, param_grid, *, factor=3, resource=‘n_samples’, max_resources=‘auto’, min_resources=‘exhaust’, aggressive_elimination=False, cv=5, scoring=None, refit=True, error_score=nan, return_train_score=True, random_state=None, n_jobs=None, verbose=0)

全部参数如下所示:

Name Description
estimator 调参对象,某评估器
param_grid 参数空间,可以是字典或者字典构成的列表
factor 每轮迭代中新增的样本量的比例,同时也是每轮迭代后留下的参数组合的比例
resource 设置每轮迭代中增加的验证资源的类型
max_resources 在一次迭代中,允许被用来验证任意参数组合的最大样本量
min_resources 首次迭代时,用于验证参数组合的样本量r0
aggressive_elimination 是否以全部数被使用完成作为停止搜索的指标,如果不是,则采取措施
cv 交叉验证的折数
scoring 评估指标,支持同时输出多个参数
refit 挑选评估指标和最佳参数,在完整数据集上进行训练
error_score 当网格搜索报错时返回结果,选择’raise’时将直接报错并中断训练过程
其他情况会显示警告信息后继续完成训练
return_train_score 在交叉验证中是否显示训练集中参数得分
random_state 控制随机抽样数据集的随机性
n_jobs 设置工作时参与计算的线程数
verbose 输出工作日志形式
  • factor

每轮迭代中新增的样本量的比例,同时也是每轮迭代后留下的参数组合的比例。例如,当factor=2时,下一轮迭代的样本量会是上一轮的2倍,每次迭代后有1/2的参数组合被留下。如果factor=3时,下一轮迭代的样本量会是上一轮的3倍,每次迭代后有1/3的参数组合被留下。该参数通常取3时效果比较好。

  • resource

设置每轮迭代中增加的验证资源的类型,输入为字符串。默认是样本量,输入为"n_samples",也可以是任意集成算法当中输入正整数的弱分类器,例如"n_estimators"或者"n_iteration"。

  • min_resource

首次迭代时,用于验证参数组合的样本量r0。可以输入正整数,或两种字符串"smallest",“exhaust”。

输入正整数n,表示首次迭代时使用n个样本。

输入"smallest",则根据规则计算r0:

当资源类型是样本量时,对回归类算法,r0 = 交叉验证折数n_splits * 2

当资源类型是样本量时,对分类算法,r0 = 类别数量n_classes_ * 交叉验证折数n_splits * 2

当资源类型不是样本量时,等于1

输入"exhaust",则根据迭代最后一轮的最大可用资源倒退r0。例如,factor=2, 样本量为1000时,一共迭代3次时,则最后一轮迭代的最大可用资源为1000,倒数第二轮为500,倒数第三轮(第一轮)为250。此时r0 = 250。"exhaust"模式下最有可能得到好的结果,不过计算量会略大,计算时间会略长。

如果我们无论如何都希望能够找到唯一最后的参数,那我们可以使用下面的参数:

  • aggressive_elimination

输入布尔值,默认False。当数据总样本量较小,不足以支撑循环直到只剩下最后一组备选参数时,可以打开该参数。

参数设置为True时,会重复使用首次迭代时的样本量,直到剩下的数据足以支撑样本量的增加直到只剩下最后一组备选参数

参数设置为False时,以全部样本被用完作为搜索结束的指标

对于对半网格搜索应用来说,最困难的部分就是决定搜索本身复杂的参数组合。在调参时,如果我们希望参数空间中的备选组合都能够被充分验证,则迭代次数不能太少(例如,只迭代3次),因此factor不能太大。但如果factor太小,又会加大迭代次数,同时拉长整个搜索的运行时间。同时,迭代次数还会影响我们最终能够使用的数据量,以及迭代完毕之后我们还需进一步验证的参数组合数量,两者都不能太少。因此,我们一般在使用对半网格搜索时,需考虑以下三个点:

1、min_resources的值不能太小,且在全部迭代过程结束之前,我们希望使用尽量多的数据

2、迭代完毕之后,剩余的验证参数组合不能太多,10以下最佳,如果无法实现,则30以下也可以接受

3、迭代次数不能太多,否则时间可能会太长

import re
import sklearn
import numpy as np
import pandas as pd
import matplotlib as mlp
import matplotlib.pyplot as plt
import time
from sklearn.ensemble import RandomForestRegressor as RFR
from sklearn.experimental import enable_halving_search_cv
from sklearn.model_selection import KFold, HalvingGridSearchCV, cross_validate, RandomizedSearchCV

# 现在,我们依然使用网格搜索最初的,空间大小为1536的参数空间:
param_grid_simple = {"criterion": ["squared_error","poisson"]
                     , 'n_estimators': [*range(20,100,5)]
                     , 'max_depth': [*range(10,25,2)]
                     , "max_features": ["log2","sqrt",16,32,64,"auto"]
                     , "min_impurity_decrease": [*np.arange(0,5,10)]
                    }

factor = 1.5
n_samples = X.shape[0]
min_resources = 500
space = 1536

for i in range(100):
    if (min_resources*factor**i > n_samples) or (space/factor**i < 1):
        break
    print(i+1,"本轮迭代样本:{}".format(min_resources*factor**i)
          ,"本轮验证参数组合:{}".format(space//factor**i + 1))

#建立回归器、交叉验证
reg = RFR(random_state=1412,verbose=True,n_jobs=-1)
cv = KFold(n_splits=5,shuffle=True,random_state=1412)

#定义对半搜索
search = HalvingGridSearchCV(estimator=reg
                            ,param_grid=param_grid_simple
                            ,factor=1.5
                            ,min_resources=500
                            ,scoring = "neg_mean_squared_error"
                            ,verbose = True
                            ,random_state=1412
                            ,cv = cv
                            ,n_jobs=-1)

#训练对半搜索评估器
#=====【TIME WARNING: 30~50min】=====#
start = time.time()
search.fit(X,y)
end = time.time() - start
print(end/60)
#查看最佳评估器
search.best_estimator_
#验证最佳参数组合的效力
rebuild_on_best_param(search.best_estimator_)
HPO方法 随机搜索 对半搜索
搜索空间/全域空间 800/1536 1536/1536
运行时间(分钟) 103.20 25.638(↓)
搜索最优(RMSE) 1055.555 1068.281
重建最优(RMSE) 1054.359 1082.916

可以看到,随机网格搜索的结果略微占优,但能够尝试的参数组合只有800个,且耗费的时间是对半搜索的4倍(1小时45分钟)。对于对半搜索,我们可以继续精细化调整整体的参数空间,进一步寻找更优的参数,但面对上万样本量的数据集,随机搜索的运算速度不足以支撑精细化调参,就更别提网格搜索了。之后,我们会给大家更详细地讲解更快速、更高效的优化方法。

四 【加餐】自动化机器学习AutoML

  • 什么是AutoML?

在过去十年内,机器学习领域获得了巨大的进展,得益于互联网(数据)与芯片(算力)行业的快速发展,各类机器学习算法终于化理论为实践,被应用当各行各业、千家万户。如今,越来越多的科研领域依赖机器学习算法,机器学习算法的实现也因sklearn等算法库而变得轻便简单,但真正高水平的应用却很大程度依赖于机器学习算法工程师的经验。

现代机器学习系统不止是关系到编程、数学等技能,更是伴随着众多基于经验和专业性的设计决策,包括如何构筑特征、选择模型,如何设置超参数空间(parameter space)、如何设置神经网络结构、如何排布和管理数据处理管道(pipeline)等等。在实际的机器学习系统搭建过程当中,有超过80%的时间都在处理基于经验的设计决策,而决策所需的专业性拉开了算法工程专家与普通编程者的距离,也拉开了算法专业人员与业务人员的距离。

虽然机器学习领域的高专业性保障了算法工程师的稀缺性与高待遇,但在实际商业场景中,还有众多不关注机器学习技术细节、只关注机器学习产出成果的公司与个人,对他们而言,机器学习领域对专业知识的要求成为了巨大的门槛。无论一门技术多么先进,最终的目的还是要围绕业务场景、解决业务问题,而机器学习算法的核心价值也在于能够一定程度上代替人脑完成决策、完成部分思考流程的自动化。为了机器学习能够被更广泛地应用、更快速地发展,研究者们产生了让机器学习完成机器学习建模流程中所需决策的想法,正是一想法催生了自动化机器学习AutoML这一研究领域。


AutoML旨在让算法完成机器学习建模流程中所需的决策

AutoML的最终目标是让机器学习建模流程不再需要人工干预
或至少不再需要富有经验的机器学习专家的干预


在整个建模流程中,可能自动化的步骤包括了:

  • 数据预处理
  • 特征构建/特征工程
  • 模型选择
  • 搭建神经网络架构
  • 模型调参
  • 模型融合
  • 模型可视化

其中某些领域在自动化之后,甚至可能完全替代算法工程师的作用。围绕所有可能的领域,学者们艰难探索了数十年。随着人工智能的兴起,AutoML也取得了极大的进展,终于有了较为成熟的方法与工具。现在,AutoML已经是近年来机器学习领域最受关注的研究领域之一:

  • 学术界
  • 2021年,google scholar上新增了2690篇autoML领域相关的论文
  • 光是在自动化神经网络架构领域,就有数百篇论文被发表
  • 学术研究中几乎不再出现手动调参方法
  • 自2014年以来,每两年会有一场AutoML或AutoDL大规模挑战赛
  • 第一届AutoML国际会议将在2022年夏季举办

从此AutoML不再是混迹在CVPR、ICML等大型会议上的小展台了。

  • 工业界
  • 2周之前,华为天才少年钟钊公布了他们的首个AutoML大规模商用的成果
  • 微软(Azune)、谷歌、亚马逊都已经提出了各自的AutoML解决方案
  • 2018年,自动化设计的神经网络架构首次胜过人为设计的神经网络架构
  • 自动化神经网络架构设计已经是许多实验室/高科技公司的标准化技能
  • 已实现的自动化框架
#示例代码,本段代码不提供运行

#安装
!pip install hpsklearn

#导入
from hpsklearn import HyperoptEstimator

#实例化
estim = HyperoptEstimator()

#训练
estim.fit(Xtrain, Ytrain)
prediction = estim.predict(Xtest)
score = estin.score(Xtest,Ytest)

#直接返回sklearn中存在的,选择出的最好模型
model = estim.best_model()

AutoML正在蓬勃发展,很可能即将迎来腾飞

但在发展过程中,AutoML应用逐渐偏离了“非专业人士”的领域


例如,业务人员或非专业人员恐怕很难在琳琅满目的第三方框架中选择适合当下项目的框架,同时非专业人员也无法辨别“自动化”究竟完成了哪些工作,还有哪些非自动化的流程需要完成,因此非专业人员只能够使用综合性自动化建模方案。然而:

  • AutoWEKA只支持Java

    AutoWEKA底层是基于WEKA所构建,而WEKA只有Java才能调用,因此AutoWEKA拓展性较差,不适用Java以外语言

  • Auto-sklearn不支持windows

    Auto-sklearn底层是基于linux中的resorce模块运行,因此不支持Windows系统,也不能完全支持Mac系统

  • hpsklearn缺乏维护,年久失修

    hpsklearn底层是基于sklearn,代码简单但缺乏维护,年久失修,时至今日一些基本的代码(如fit)运行还会报错

更糟糕的是,自动化流程是脆弱的,为了能够让用户简单地进行调用,自动化框架往往封装程度很深,一旦框架本身不那么完美,自动化流程就非常容易崩溃。即便真的能够进行调试,无论是最简单的网格搜索,还是复杂的贝叶斯优化,底层都是复杂的数学理论。即便对于机器学习算法工程师来说,AutoML算法背后的原理都令人望而生畏,更不要提完全不理解机器学习的非专业人员了。

AutoML的三大成熟研究领域

  • 超参数优化HPO(HyperParameter Optimization)

超参数优化是AutoML领域中最成熟、最深入、也是最知名的方向。每一个机器学习算法都会有超参数,而超参数的设置很大程度上影响了算法实际的使用效果,因此调参是机器学习算法工程师最为基础和重要的任务。理论上来说,当算力与数据足够时,HPO的性能一定是超过人类的。HPO能够降低人为工作量,并且HPO得出的结果比认为搜索的复现可能性更高,所以HPO可以极大程度提升科学研究的复现性和公平性。当代超参数优化算法主要可以分为:

基于网格的各类搜索(Grid)

基于贝叶斯优化的各类优化算法(Baysian)

基于梯度的各类优化(Gradient-based)

基于进化算法的各类优化(Evolutionary Optimzation)

  • 神经网络架构搜索NAS(Neural Architecture Search)

神经网络架构搜索解决深度学习世界当中最关键的问题之一:如何寻找性能良好的深度神经网络架构。与机器学习领域不同,深度学习领域没有一统天下的算法架构,面临任意学习任务,深度学习算法工程师都必须找出适应当前数据和业务的架构。自动化架构搜索可以大大加快新的深度学习应用程序的开发,因为开发人员终于不需要煞费苦心地评估不同的架构了。

  • 元学习(Meta-Learning)

元学习是一种基于其他任务的经验进行学习的方法。人类在学习新知识时,总是会或多或少地借助以前学习过的相关知识。例如,当你学习游泳时,你并不需要从“什么是水”开始,你已经自然地理解了“什么是水”,“水下不能呼吸”、“人下水会沉”等基本信息,然后就可以从“如何漂浮”以及“如何在水下换气”开始学习。

但对机器学习算法来说,每次学习都是从0开始,在接触训练数据之前,机器学习算法不会有任何“相关经验和理解”。元学习就是赋予机器学习算法“相关经验”的方法。在元学习中,我们一般会先让算法学习与目标任务相关的其他任务或其他信息,以获得能够知道检索新任务最优模型的相关知识,然后再在目标任务上进行训练。这一手段被广泛地应用于HPO和NAS领域,同时深度学习中的“迁移学习”也于元学习有异曲同工之妙。

在实际应用中,原学习可以被用于以下任务:

HPO&NAS

算法预测

模型选择

算法构建(L2L)

AutoML的新兴研究领域

  • 交互式可解释性机器学习ixAutoML(Interactive and Explainable AutoML)

大部分AutoML框架封装程度很深、许多AutoML算法还是黑箱算法,因此无论是机器学习工作者、还是非专业人士,都不太信任AutoML的结果。ixAutoML是研究自动化解释模型、自动化可视化的领域,旨在增强专业人员对AutoML的信任感,增强AutoML的透明程度。该领域已经发展了一段时间,目前为止最先进的研究能够可视化参数变化对模型整体的影响。

  • 绿色AI(Green AI)

AutoML最关键的问题之一是巨大的计算量。当人工神经网络、boosting算法等超参数众多的算法被应用当工业当中,巨量的数据、巨量的超参数让AutoML对算力的要求很高。绿色AI旨在自动研发低参数量、低超参量、但性能优越的算法。这个领域目前刚刚萌芽。

  • 非线性流程优化

无论我们如何“自动化”机器学习,在调参和模型选择中都会有一个巨大的争议:有的模型在没有调参之前结果不佳,但是调参之后可能得到很好的结果,有的模型在调参之前结果很好,但是调参之后未必能得到很好的结果。但在自动化过程中,我们一定是先选择模型,再调节参数,因此具有强大调参潜力的模型反而容易在自动化过程中被筛选掉。现在的技术已经支持对多个模型同时调参,并最终选择调参后结果最佳的模型,但这样又会需要大量计算资源与计算时间。如何快速解决模型选择与调参顺序所导致的问题,是现代autoML关键的课题。

AutoML真的会替代算法工程师吗?

短时间内一定不会。在爆发新一轮技术突破之前,AutoML还不能替代算法工程师。相对的,AutoML是辅助算法工程师的关键工具。有以下几个原因:

  • 适用于业务人员的AutoML框架还不成熟,并且全流程框架方面的研究也未见起色。
  • 在较大的、复杂的模型(如深度学习,boosting)或者较大的数据集上,AutoML求解虽然准确,但运算代价机器巨大。为节约成本,autoML过程必须经过算法工程师的优化和干涉。在NAS领域,神经网络架构一般也不会从0开始搜索,如何选择正确的备选项,依然依赖于算法工程师的专业知识。
  • 数据/业务问题的复杂程度目前依然超出AutoML的处理范围,AutoML只能够适用于较为固定的场景例如,损失函数不平滑、或损失函数不是凸函数时,AutoML的优化算法会失效。

网站公告

今日签到

点亮在社区的每一天
去签到