详解Python中4种超参自动优化算法的实现

目录
  • 一、网格搜索(Grid Search)
  • 二、随机搜索(Randomized Search)
  • 三、贝叶斯优化(Bayesian Optimization)
  • 四、Hyperband
  • 总结

大家好,要想模型效果好,每个算法工程师都应该了解的流行超参数调优技术。

今天我给大家总结超参自动优化方法:网格搜索、随机搜索、贝叶斯优化 和 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: 网格搜索和随机搜索的对比

解释图1,如果目前我们要搜索两个参数,但参数A重要而另一个参数B并没有想象中重要,网格搜索9个参数组合(A, B),而由于模型更依赖于重要参数A,所以只有3个参数值是真正参与到最优参数的搜索工作中。反观随机搜索,随机采样9种超参组合,在重要参数A上会有9个参数值参与到搜索工作中,所以,在某些参数对模型影响较小时,使用随机搜索能让我们有更多的探索空间

同样地,sklearn实现了随机搜索,样例代码如下:

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?》的回答。

调优的目的是要找到一组最优的超参组合,能使目标函数f达到全局最小值。

举个例子,若学习率设置过大,模型可能会在代价函数的全局最优点附近不断来回震荡,甚至跳出全局最优点,而设置过小,又可能会陷入局部最优,因此调学习率这一参数,是为了让模型能收敛到代价函数的全局最小值。可是在机器学习中,目标函数 常是被称作expensive blackbox function,计算开销大且不一定为凸函数。为此,贝叶斯优化出现了,它特别适合针对expensive blackbox function找到全局最优。

假设我们的真实的目标函数 长下图这样:

图2: 目标函数f(x)

注意: 目标函数 的 是指超参数,我们希望找到最优的超参 去得到最小的 。为什么用虚线表示 呢?因为它是黑箱函数(blackbox function)。

现在,我们怎么找到 全局最小值点呢?我们能不能先在 上随机采样10个点,然后取它们的最小值呢?

图3: 随机采样10个点的目标函数f(x)

图3里确实有个点挺靠近全局最优点的,那是不是在它附近再采样几个点,不断重复就行了?没那么简单,万一起始采样点在局部最小值附近,那这种方法会很容易陷入局部最优。关于“如何找到下一个合适的点”这个问题,我们先放一放,因为我们漏掉一个重点:每次尝试一种超参值 ,计算 的代价是昂贵的,为了减轻开销,贝叶斯优化采用了代理模型(surrogate model),代理模型可以被看作是一个简单模型去拟合原本复杂且不好理解的模型,简单来说,就是 计算太昂贵了,我们就用代理模型去代替它。

贝叶斯优化使用了**高斯过程(gasussian processes, GP)**去构建代理模型,高斯过程的细节这里暂时不讲,有兴趣的小伙伴可以自行查阅资料了解。基于给定的输入和输出,GP会推断出一个模型(这里为代理模型)。假设我们从昂贵的 采样了4个点,然后我们把这4个点交给GP,它会返回一个代理模型,如下图所示:

图4: 目标函数f(x)和代理模型

绿色实线就是GP猜的代理模型,绿色条带是输出分布的标准差(即为Uncertainty)。我们有了代理模型,后续我们去找下一个合适的超参值,就能带入到计算开销相对较小的代理模型中,评估给定超参值的情况。

现在,我们来思考回之前提到的问题:“如何找到下一个合适的点?”,这个问题本质是在问:“哪里有全局最小的点?”,为了解决这个问题,我们要关注两个地方:

(1) 已开发区域: 在绿色实线上最低的超参点。因为很可能它附近存在全局最优点。

(2) 未探索区域: 绿色实线上还未被探索的区域。比如图4,相比于0.15-0.25区间,0.65-0.75区间更具有探索价值(即该区间Uncertainty更大)。探索该区域有利于减少我们猜测的方差。

为了实现以上探索和开发的平衡(exploration-exploitation trade-off),贝叶斯优化使用了采集函数(acquisition function),它能平衡好全局最小值的探索和开发。采集函数有很多选择,其中最常见的是expectated of improvement(EI),我们先看一个utility function:

是目前观察到的最小值, 是超参值,我们希望上述utility function输出越大越好(即找到的 能获得比当前最小值还小),基于 ,EI采集函数如下所示:

具有最高的EI的超参值 会被选择。EI有两部分:

(1) 减少均值函数 ,提高EI。

(2) 增加方差 ,提高EI。

所以EI的提高是建立在均值和方差的trade-off,也是exploration和exploitation的trade-off。

图5: 采集函数A(x)

图5我们可以看到, 时EI最大,所以我们下一个超参值 应该选1。

讲到这里,我们来看下完整的贝叶斯优化步骤是怎样的:

图6: 贝叶斯优化-SMBO

SMBO是简洁版的贝叶斯优化,伪代码如图6所示,具体如下:

(1) 准备输入: expensive blackbox function ,超参搜索空间 ,采样数据集 (超参组合 ,对应 输出值),采集函数 和用数据集 拟合的代理模型M。

(2) 基于 和 ,采样得到数据集 。

(3) 循环选 次参数:

用当前数据集 拟合代理模型 ,实现模型更新。

选择使采集函数 最大的超参组合 。

将 带入 中,得到输出值 。(注意这里 的计算开销大)

将新的 加入到现有数据集 中。

目前,Hyperopt开源代码库已实现基于**TPE(Tree-structured Parzen Estimator Approach)**的贝叶斯优化,图6我们能看到GP构建的概率模型是 ,而TPE是 和 ,关于TPE和GP的对比讨论,建议阅读论文。TPE样例代码如下:

from sklearn import svm, datasets
from sklearn.model_selection import cross_val_score
from hyperopt import hp, fmin, tpe, space_eval
import pandas as pd

# 导入数据
iris = datasets.load_iris()

# step1: 定义目标函数
def objective(params):
      # 初始化模型并交叉验证
      svc = svm.SVC(**params)
      cv_scores = cross_val_score(svc, iris.data, iris.target, cv=5)
      # 返回loss = 1 - accuracy (loss必须被最小化)
      loss = 1 - cv_scores.mean()
      return loss

# step2: 定义超参搜索空间
space = {'kernel':hp.choice('kernel', ['linear', 'rbf']),
         'C':hp.uniform('C', 1, 10)}

# step3: 在给定超参搜索空间下,最小化目标函数
best = fmin(objective, space, algo=tpe.suggest, max_evals=100)
返回: best_loss: 0.013333333333333308(即accuracy为0.9866666666666667)

# step4: 打印结果
print(best)
返回:{'C': 6.136181888987526, 'kernel': 1}(PS:kernel为0-based index,这里1指rbf)

四、Hyperband

除了格子搜索、随机搜索和贝叶斯优化,还有其它自动调参方式。例如Hyperband optimization,Hyperband本质上是随机搜索的一种变种,它使用早停策略和Sccessive Halving算法去分配资源,结果是Hyperband能评估更多的超参组合,同时在给定的资源预算下,比贝叶斯方法收敛更快,下图展示了Hyperband的早停和资源分配:

图7: Hyperband的超参选择和评估

在Hyperband之后,还出现了BOHB,它混合了贝叶斯优化和Hyperband。Hyperband和BOHB的开源代码可参考HpBandSter库,这里不展开细讲。

总结

上面我们用Iris鸢尾花数据集试了不同的超参自动调优方法,发现贝叶斯优化和随机搜索都比格子搜索好。从一些论文反映,贝叶斯优化是更香的,但是贝叶斯优化好像在实践中用的不是那么多,网上也有很多分享者,像Nagesh Singh Chauhan,说的那样:

As a general rule of thumb, any time you want to optimize tuning hyperparameters, think Grid Search and Randomized Search! [10]

Hyperparameter Optimization for Machine Learning Models - Nagesh Singh Chauhan

为什么呢?我想原因是贝叶斯的开销太大了,前面有提到,在每次循环选超参值的时候,贝叶斯优化都需要将 带入昂贵的目标函数 中,去得到输出值y,当目标函数特别复杂时,这种情况的评估开销是很大的,更何况随着搜索空间和搜索次数的变大,计算会越发昂贵。

写这篇文章的过程中,我主要学到了2点,一是随机搜索在某些时候会比格子搜索好,二是了解贝叶斯优化的机理。这里,谈谈我比赛和个人实践中的体会,我很少会花过多时间在超参的调优上,因为它带来的收益是有限的,很多时候比起压榨模型来说,思考和挖掘数据特征能带来更多的收益,所以我想这也是为什么上面说:在任何想要调优超参时,先用格子搜索或随机搜索吧。

以上就是详解Python中4种超参自动优化算法的实现的详细内容,更多关于Python超参自动优化算法的资料请关注我们其它相关文章!

(0)

相关推荐

  • 使用TensorBoard进行超参数优化的实现

    在本文中,我们将介绍超参数优化,然后使用TensorBoard显示超参数优化的结果. 深度神经网络的超参数是什么? 深度学习神经网络的目标是找到节点的权重,这将帮助我们理解图像.文本或语音中的数据模式. 要做到这一点,可以使用为模型提供最佳准度和精度的值来设计神经网络参数. 那么,这些被称为超参数的参数是什么呢? 用于训练神经网络模型的不同参数称为超参数.这些超参数像旋钮一样被调优,以提高神经网络的性能,从而产生一个优化的模型.超参数的一个通俗的解释是:用来优化参数的参数. 神经网络中的一些超参

  • OpenCV python sklearn随机超参数搜索的实现

    本文介绍了OpenCV python sklearn随机超参数搜索的实现,分享给大家,具体如下: """ 房价预测数据集 使用sklearn执行超参数搜索 """ import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np import sklearn import pandas as pd import os import sys import tens

  • python超参数优化的具体方法

    1.手动调参,但这种方法依赖于大量的经验,而且比较费时. 许多情况下,工程师依靠试错法手工调整超参数进行优化,有经验的工程师可以在很大程度上判断如何设置超参数,从而提高模型的准确性. 2.网格化寻优,是最基本的超参数优化方法. 利用这种技术,我们只需要为所有超参数的可能性建立一个独立的模型,评估每个模型的性能,选择产生最佳结果的模型和超参数. from sklearn.datasets import load_iris from sklearn.svm import SVC iris = loa

  • 详解Python中4种超参自动优化算法的实现

    目录 一.网格搜索(Grid Search) 二.随机搜索(Randomized Search) 三.贝叶斯优化(Bayesian Optimization) 四.Hyperband 总结 大家好,要想模型效果好,每个算法工程师都应该了解的流行超参数调优技术. 今天我给大家总结超参自动优化方法:网格搜索.随机搜索.贝叶斯优化 和 Hyperband,并附有相关的样例代码供大家学习. 一.网格搜索(Grid Search) 网格搜索是暴力搜索,在给定超参搜索空间内,尝试所有超参组合,最后搜索出最优

  • 详解Python中四种关系图数据可视化的效果对比

    python关系图的可视化主要就是用来分析一堆数据中,每一条数据的节点之间的连接关系从而更好的分析出人物或其他场景中存在的关联关系. 这里使用的是networkx的python非标准库来测试效果展示,通过模拟出一组DataFrame数据实现四种关系图可视化. 其余还包含了pandas的数据分析模块以及matplotlib的画图模块. 若是没有安装这三个相关的非标准库使用pip的方式安装一下即可. pip install pandas -i https://pypi.tuna.tsinghua.e

  • 详解Python中list[::-1]的几种用法

    本文主要介绍了Python中list[::-1]的几种用法,分享给大家,具体如下: s = "abcde" list的[]中有三个参数,用冒号分割 list[param1:param2:param3] param1,相当于start_index,可以为空,默认是0 param2,相当于end_index,可以为空,默认是list.size param3,步长,默认为1.步长为-1时,返回倒序原序列 举例说明 param1 = -1,只有一个参数,作用是通过下标访问数据,-1为倒数第一个

  • 详解python中的三种命令行模块(sys.argv,argparse,click)

    Python作为一门脚本语言,经常作为脚本接受命令行传入参数,Python接受命令行参数大概有三种方式.因为在日常工作场景会经常使用到,这里对这几种方式进行总结. 命令行参数模块 这里命令行参数模块平时工作中用到最多就是这三种模块:sys.argv,argparse,click.sys.argv和argparse都是内置模块,click则是第三方模块. sys.argv模块(内置模块) 先看一个简单的示例: #!/usr/bin/python import sys def hello(name,

  • 详解Python中的GIL(全局解释器锁)详解及解决GIL的几种方案

    先看一道GIL面试题: 描述Python GIL的概念, 以及它对python多线程的影响?编写一个多线程抓取网页的程序,并阐明多线程抓取程序是否可比单线程性能有提升,并解释原因. GIL:又叫全局解释器锁,每个线程在执行的过程中都需要先获取GIL,保证同一时刻只有一个线程在运行,目的是解决多线程同时竞争程序中的全局变量而出现的线程安全问题.它并不是python语言的特性,仅仅是由于历史的原因在CPython解释器中难以移除,因为python语言运行环境大部分默认在CPython解释器中. 通过

  • 详解python中读取和查看图片的6种方法

    目录 1 OpenCV 2 imageio 3 PIL 4 scipy.misc 5 tensorflow 6 skimage 本文主要介绍了python中读取和查看图片的6种方法,分享给大家,具体如下: file_name1='test_imgs/spect/1.png' # 这是彩色图片 file_name2='test_imgs/mri/1.png' # 这是灰度图片 1 OpenCV 注:用cv2读取图片默认通道顺序是B.G.R,而不是通常的RGB顺序,所以读进去的彩色图直接显示会出现变

  • 一文详解Python中实现单例模式的几种常见方式

    目录 Python 中实现单例模式的几种常见方式 元类(Metaclass): 装饰器(Decorator): 模块(Module): new 方法: Python 中实现单例模式的几种常见方式 元类(Metaclass): class SingletonType(type): """ 单例元类.用于将普通类转换为单例类. """ _instances = {} # 存储单例实例的字典 def __call__(cls, *args, **kwa

  • 详解python中 os._exit() 和 sys.exit(), exit(0)和exit(1) 的用法和区别

    详解python中 os._exit() 和 sys.exit(), exit(0)和exit(1) 的用法和区别 os._exit() 和 sys.exit() os._exit() vs sys.exit() 概述 Python的程序有两中退出方式:os._exit(), sys.exit().本文介绍这两种方式的区别和选择. os._exit()会直接将python程序终止,之后的所有代码都不会继续执行. sys.exit()会引发一个异常:SystemExit,如果这个异常没有被捕获,那

  • 详解 Python中LEGB和闭包及装饰器

    详解 Python中LEGB和闭包及装饰器 LEGB L>E>G?B L:local函数内部作用域 E:enclosing函数内部与内嵌函数之间 G:global全局作用域 B:build-in内置作用域 python 闭包 1.Closure:内部函数中对enclosing作用域变量的引用 2.函数实质与属性 函数是一个对象 函数执行完成后内部变量回收 函数属性 函数返回值 passline = 60 def func(val): if val >= passline: print (

  • 详解python中的 is 操作符

    大家可以与Java中的 == 操作符相互印证一下,加深一下对引用和对象的理解.原问题: Python为什么直接运行和在命令行运行同样语句但结果却不同,他们的缓存机制不同吗? 其实,高票答案已经说得很详细了.我只是再补充一点而已. is 操作符是Python语言的一个内建的操作符.它的作用在于比较两个变量是否指向了同一个对象. 与 == 的区别 class A(): def __init__(self, v): self.value = v def __eq__(self, t): return

随机推荐