超参数优化

Bayesian Optimization使用Hyperopt进行参数调优

1. 前言

本文将介绍一种快速有效的方法用于实现机器学习模型的调参。有两种常用的调参方法:网格搜索和随机搜索。每一种都有自己的优点和缺点。网格搜索速度慢,但在搜索整个搜索空间方面效果很好,而随机搜索很快,但可能会错过搜索空间中的重要点。幸运的是,还有第三种选择:贝叶斯优化。本文我们将重点介绍贝叶斯优化的一个实现,一个名为hyperopt的Python模块

使用贝叶斯优化进行调参可以让我们获得给定模型的最佳参数,例如逻辑回归模型。这也使我们能够执行最佳的模型选择。通常机器学习工程师或数据科学家将为少数模型(如决策树,支持向量机和K近邻)执行某种形式(网格搜索或随机搜索)的手动调参,然后比较准确率并选择最佳的一个来使用。该方法可能比较的是次优模型。也许数据科学家找到了决策树的最优参数,但却错过了SVM的最优参数。这意味着他们的模型比较是有缺陷的。如果SVM参数调整得很差,K 近邻可能每次都会击败SVM。贝叶斯优化允许数据科学家找到所有模型的最佳参数,并因此比较最佳模型。这会得到更好的模型选择,因为你比较的是最佳的k近邻和最佳的决策树。只有这样你才能非常自信地进行模型选择,确保选择并使用的是实际最佳的模型。

本文涵盖的主题有:

  1. 目标函数
  2. 搜索空间
  3. 存储评估试验
  4. 可视化
  5. 经典数据集上的完整示例:Iris

2. 目标函数 - 一个启发性例子

假设你有一个定义在某个范围内的函数,并且想把它最小化。也就是说,你想找到产生最低输出值的输入值。下面的简单例子找到\(x\)的值用于最小化线性函数\(y(x)=x\)

from hyperopt import fmin, tpe, hp
best = fmin(
fn=lambda x: x,
space=hp.uniform('x', 0, 1),
algo=tpe.suggest,
max_evals=100)
print(best)

输出结果:

{'x': 0.000269455723739237}

  1. \(fmin\)首先接受一个函数来最小化,记为\(fn\),在这里用一个匿名函数\(lambda \ x:x\)来指定。该函数可以是任何有效的值返回函数,例如回归中的平均绝对误差。
  2. \(space\)是指定搜索空间,在本例中,它是0到1之间的连续数字范围,\(hp.uniform('x', 0, 1)\)指定。\(hp.uniform\)是一个内置的hyperopt函数,它有三个参数:名称\(x\),范围的下限和上限0和1。
  3. \(algo\)参数指定搜索算法,本例中tpe表示tree of Parzen estimators。该主题超出了本文的范围,但有数学背景的读者可以细读这篇文章。algo参数也可以设置为\(hyperopt.random\),但是这里我们没有涉及,因为它是众所周知的搜索策略。但在未来的文章中我们可能会涉及。
  4. 最后\(max\_evals\)是最大评估次数。这个fmin函数将返回一个python字典。

2.1 稍微复杂的例子

这有一个更复杂的目标函数:

\(lambda\ x: (x-1)^2\)。这次我们试图最小化一个二次方程\(y(x)=(x-1)^2\)。所以我们改变搜索空间以包括我们已知的最优值\((x=1)\)加上两边的一些次优范围:\(hp.uniform('x', -2, 2)\)。

best = fmin(
fn=lambda x: (x-1)**2,
space=hp.uniform('x', -2, 2),
algo=tpe.suggest,
max_evals=100)
print(best)

输出结果:

{'x': 0.997369045274755}

3. 搜索空间

hyperopt模块包含一些方便的函数来指定输入参数的范围。我们已经见过\(hp.uniform\)。最初,这些是随机搜索空间,但随着hyperopt更多的学习(因为它从目标函数获得更多反馈),通过它认为提供给它最有意义的反馈,会调整并采样初始搜索空间的不同部分。

以下内容将在本文中使用:

  1. \(hp.choice(label, options)\)其中options应是python列表或元组。
  2. \(hp.normal(label, mu, sigma)\)其中mu和sigma分别是均值和标准差。
  3. \(hp.uniform(label, low, high)\)其中low和high是范围的下限和上限。
import hyperopt.pyll.stochastic

space = {
'x': hp.uniform('x', 0, 1),
'y': hp.normal('y', 0, 1),
'name': hp.choice('name', ['alice', 'bob']),
} print(hyperopt.pyll.stochastic.sample(space))

输出结果:

{'y': -1.4012610048810574, 'x': 0.7258615424906184, 'name': 'alice'}

4. Trials捕获信息

如果能看到hyperopt黑匣子内发生了什么是极好的。Trials对象使我们能够做到这一点。我们只需要导入一些东西。

from hyperopt import fmin, tpe, hp, STATUS_OK, Trials

fspace = {
'x': hp.uniform('x', -5, 5)
} def f(params):
x = params['x']
val = x**2
return {'loss': val, 'status': STATUS_OK} trials = Trials()
best = fmin(fn=f, space=fspace, algo=tpe.suggest, max_evals=50, trials=trials) print('best:', best) print 'trials:'
for trial in trials.trials[:2]:
print(trial)

\(STATUS_OK\)和Trials是新导入的。Trials对象允许我们在每个时间步存储信息。然后我们可以将它们打印出来,并在给定的时间步查看给定参数的函数评估值。

输出结果:

best: {'x': 0.014420181637303776}
trials:
{'refresh_time': None, 'book_time': None, 'misc': {'tid': 0, 'idxs': {'x': [0]}, 'cmd': ('domain_attachment', 'FMinIter_Domain'), 'vals': {'x': [1.9646918559786162]}, 'workdir': None}, 'state': 2, 'tid': 0, 'exp_key': None, 'version': 0, 'result': {'status': 'ok', 'loss': 3.8600140889486996}, 'owner': None, 'spec': None}
{'refresh_time': None, 'book_time': None, 'misc': {'tid': 1, 'idxs': {'x': [1]}, 'cmd': ('domain_attachment', 'FMinIter_Domain'), 'vals': {'x': [-3.9393509404526728]}, 'workdir': None}, 'state': 2, 'tid': 1, 'exp_key': None, 'version': 0, 'result': {'status': 'ok', 'loss': 15.518485832045357}, 'owner': None, 'spec': None}

Trials对象将数据存储为BSON对象,其工作方式与JSON对象相同。BSON来自pymongo模块。我们不会在这里讨论细节,这是对于需要使用MongoDB进行分布式计算的hyperopt的高级选项,因此需要导入pymongo。回到上面的输出。

  1. tid是时间 id,即时间步,其值从0到\(max\_evals-1\)。它随着迭代次数递增。
  2. \('x'\)是键\('vals'\)的值,其中存储的是每次迭代参数的值。
  3. \('loss'\)是键\('result'\)的值,其给出了该次迭代目标函数的值。

4.1 可视化

我们看看损失vs值的图

f, ax = plt.subplots(1)
xs = [t['misc']['vals']['x'] for t in trials.trials]
ys = [t['result']['loss'] for t in trials.trials]
ax.scatter(xs, ys, s=20, linewidth=0.01, alpha=0.75)
ax.set_title('$val$ $vs$ $x$ ', fontsize=18)
ax.set_xlabel('$x$', fontsize=16)
ax.set_ylabel('$val$', fontsize=16)

5. Iris 数据集

在本节中,我们将介绍4个使用hyperopt在经典数据集Iris上调参的完整示例。我们将涵盖K近邻(KNN),支持向量机(SVM),决策树和随机森林。需要注意的是,由于我们试图最大化交叉验证的准确率(acc请参见下面的代码),而hyperopt只知道如何最小化函数,所以必须对准确率取负。最小化函数f与最大化f的负数是相等的。

对于这项任务,我们将使用经典的Iris数据集,并进行一些有监督的机器学习。数据集有有4个输入特征和3个输出类别。数据被标记为属于类别0,1或2,其映射到不同种类的鸢尾花。输入有4列:萼片长度,萼片宽度,花瓣长度和花瓣宽度。输入的单位是厘米。我们将使用这4个特征来学习模型,预测三种输出类别之一。因为数据由sklearn提供,它有一个很好的DESCR属性,可以提供有关数据集的详细信息。尝试以下代码以获得更多细节信息。

5.1 K近邻

我们现在将使用hyperopt来找到K近邻(KNN)机器学习模型的最佳参数。KNN模型是基于训练数据集中k个最近数据点的大多数类别对来自测试集的数据点进行分类。下面的代码结合了我们所涵盖的一切。

from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target def hyperopt_train_test(params):
clf = KNeighborsClassifier(**params)
return cross_val_score(clf, X, y).mean() space4knn = {
'n_neighbors': hp.choice('n_neighbors', range(1,100))
} def f(params):
acc = hyperopt_train_test(params)
return {'loss': -acc, 'status': STATUS_OK} trials = Trials()
best = fmin(f, space4knn, algo=tpe.suggest, max_evals=100, trials=trials)
print('best:',best)

现在让我们看看输出结果的图。y轴是交叉验证分数,x轴是k近邻个数。下面是代码和它的图像:

f, ax = plt.subplots(1)#, figsize=(10,10))
xs = [t['misc']['vals']['n'] for t in trials.trials]
ys = [-t['result']['loss'] for t in trials.trials]
ax.scatter(xs, ys, s=20, linewidth=0.01, alpha=0.5)
ax.set_title('Iris Dataset - KNN', fontsize=18)
ax.set_xlabel('n_neighbors', fontsize=12)
ax.set_ylabel('cross validation accuracy', fontsize=12)

k 大于63后,准确率急剧下降。这是因为数据集中每个类的数量。这三个类中每个类只有50个实例。所以让我们将\('n\_neighbors'\)的值限制为较小的值来进一步探索。

from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target def hyperopt_train_test(params):
clf = KNeighborsClassifier(**params)
return cross_val_score(clf, X, y).mean() space4knn = {
'n_neighbors': hp.choice('n_neighbors', range(1,50))
} def f(params):
acc = hyperopt_train_test(params)
return {'loss': -acc, 'status': STATUS_OK} trials = Trials()
best = fmin(f, space4knn, algo=tpe.suggest, max_evals=100, trials=trials)
print('best:',best)

现在我们可以清楚地看到k有一个最佳值,k=4。

上面的模型没有做任何预处理。所以我们来归一化和缩放特征,看看是否有帮助。用如下代码:

# now with scaling as an option
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target def hyperopt_train_test(params):
X_ = X[:] if 'normalize' in params:
if params['normalize'] == 1:
X_ = normalize(X_)
del params['normalize'] if 'scale' in params:
if params['scale'] == 1:
X_ = scale(X_)
del params['scale'] clf = KNeighborsClassifier(**params)
return cross_val_score(clf, X_, y).mean() space4knn = {
'n_neighbors': hp.choice('n_neighbors', range(1,50)),
'scale': hp.choice('scale', [0, 1]),
'normalize': hp.choice('normalize', [0, 1])
} def f(params):
acc = hyperopt_train_test(params)
return {'loss': -acc, 'status': STATUS_OK} trials = Trials()
best = fmin(f, space4knn, algo=tpe.suggest, max_evals=100, trials=trials)
print('best:',best)

并像这样绘制参数:

parameters = ['n_neighbors', 'scale', 'normalize']
cols = len(parameters)
f, axes = plt.subplots(nrows=1, ncols=cols, figsize=(15,5))
cmap = plt.cm.jet
for i, val in enumerate(parameters):
xs = np.array([t['misc']['vals'][val] for t in trials.trials]).ravel()
ys = [-t['result']['loss'] for t in trials.trials]
xs, ys = zip(\*sorted(zip(xs, ys)))
ys = np.array(ys)
axes[i].scatter(xs, ys, s=20, linewidth=0.01, alpha=0.75, c=cmap(float(i)/len(parameters)))
axes[i].set_title(val)

我们看到缩放和/或归一化数据并不会提高预测准确率。k的最佳值仍然为4,这得到98.6%的准确率。

所以这对于简单模型 KNN 调参很有用。让我们看看用支持向量机(SVM)能做什么。

5.2 支持向量机(SVM)

由于这是一个分类任务,我们将使用sklearn的SVC类。代码如下:

iris = datasets.load_iris()
X = iris.data
y = iris.target def hyperopt_train_test(params):
X_ = X[:] if 'normalize' in params:
if params['normalize'] == 1:
X_ = normalize(X_)
del params['normalize'] if 'scale' in params:
if params['scale'] == 1:
X_ = scale(X_)
del params['scale'] clf = SVC(**params)
return cross_val_score(clf, X_, y).mean() space4svm = {
'C': hp.uniform('C', 0, 20),
'kernel': hp.choice('kernel', ['linear', 'sigmoid', 'poly', 'rbf']),
'gamma': hp.uniform('gamma', 0, 20),
'scale': hp.choice('scale', [0, 1]),
'normalize': hp.choice('normalize', [0, 1])
} def f(params):
acc = hyperopt_train_test(params)
return {'loss': -acc, 'status': STATUS_OK} trials = Trials()
best = fmin(f, space4svm, algo=tpe.suggest, max_evals=100, trials=trials)
print('best:',best) parameters = ['C', 'kernel', 'gamma', 'scale', 'normalize']
cols = len(parameters)
f, axes = plt.subplots(nrows=1, ncols=cols, figsize=(20,5))
cmap = plt.cm.jet
for i, val in enumerate(parameters):
xs = np.array([t['misc']['vals'][val] for t in trials.trials]).ravel()
ys = [-t['result']['loss'] for t in trials.trials]
xs, ys = zip(\*sorted(zip(xs, ys)))
axes[i].scatter(xs, ys, s=20, linewidth=0.01, alpha=0.25, c=cmap(float(i)/len(parameters)))
axes[i].set_title(val)
axes[i].set_ylim([0.9, 1.0])

我们得到结果:

同样,缩放和归一化也没有帮助。核函数的首选是(linear),C的最佳值是1.4168540399911616,gamma的最佳值是15.04230279483486。这组参数得到了99.3%的分类准确率。

5.3 是时候把所有东西合为一体了

自动调整一个模型的参数(如SVM或KNN)非常有趣并且具有启发性,但同时调整它们并取得全局最佳模型则更有用。这使我们能够一次比较所有参数和所有模型,因此为我们提供了最佳模型。代码如下:

digits = datasets.load_digits()
X = digits.data
y = digits.target
print X.shape, y.shape def hyperopt_train_test(params):
t = params['type']
del params['type']
if t == 'naive_bayes':
clf = BernoulliNB(**params)
elif t == 'svm':
clf = SVC(**params)
elif t == 'dtree':
clf = DecisionTreeClassifier(**params)
elif t == 'knn':
clf = KNeighborsClassifier(**params)
else:
return 0
return cross_val_score(clf, X, y).mean() space = hp.choice('classifier_type', [
{
'type': 'naive_bayes',
'alpha': hp.uniform('alpha', 0.0, 2.0)
},
{
'type': 'svm',
'C': hp.uniform('C', 0, 10.0),
'kernel': hp.choice('kernel', ['linear', 'rbf']),
'gamma': hp.uniform('gamma', 0, 20.0)
},
{
'type': 'randomforest',
'max_depth': hp.choice('max_depth', range(1,20)),
'max_features': hp.choice('max_features', range(1,5)),
'n_estimators': hp.choice('n_estimators', range(1,20)),
'criterion': hp.choice('criterion', ["gini", "entropy"]),
'scale': hp.choice('scale', [0, 1]),
'normalize': hp.choice('normalize', [0, 1])
},
{
'type': 'knn',
'n_neighbors': hp.choice('knn_n_neighbors', range(1,50))
}
]) count = 0
best = 0
def f(params):
global best, count
count += 1
acc = hyperopt_train_test(params.copy())
if acc > best:
print 'new best:', acc, 'using', params['type']
best = acc
if count % 50 == 0:
print 'iters:', count, ', acc:', acc, 'using', params
return {'loss': -acc, 'status': STATUS_OK} trials = Trials()
best = fmin(f, space, algo=tpe.suggest, max_evals=1500, trials=trials)
print('best:',best)

由于我们增加了评估数量,此代码需要一段时间才能运行完:\(max\_evals=1500\)。当找到新的最佳准确率时,它还会添加到输出用于更新。好奇为什么使用这种方法没有找到前面的最佳模型:参数为kernel=linear,C=1.416,gamma=15.042的SVM。

6. 总结

我们已经介绍了简单的例子,如最小化确定的线性函数,以及复杂的例子,如调整SVM的参数。后面读者需要根据自己的需求再去调整选择的参数,也可以基于深度学习模型进行调参。

转载至https://www.jianshu.com/p/35eed1567463

Bayesian Optimization使用Hyperopt进行参数调优的更多相关文章

  1. 【转载】AutoML--超参数调优之Bayesian Optimization

    原文:Auto Machine Learning笔记 - Bayesian Optimization 优化器是机器学习中很重要的一个环节.当确定损失函数时,你需要一个优化器使损失函数的参数能够快速有效 ...

  2. xgboost 参数调优指南

    一.XGBoost的优势 XGBoost算法可以给预测模型带来能力的提升.当我对它的表现有更多了解的时候,当我对它的高准确率背后的原理有更多了解的时候,我发现它具有很多优势: 1 正则化 标准GBDT ...

  3. Hbase记录-client访问zookeeper大量断开以及参数调优分析(转载)

    1.hbase client配置参数 超时时间.重试次数.重试时间间隔的配置也比较重要,因为默认的配置的值都较大,如果出现hbase集群或者RegionServer以及ZK关掉,则对应用程序是灾难性的 ...

  4. Spark Shuffle原理、Shuffle操作问题解决和参数调优

    摘要: 1 shuffle原理 1.1 mapreduce的shuffle原理 1.1.1 map task端操作 1.1.2 reduce task端操作 1.2 spark现在的SortShuff ...

  5. 搭建 windows(7)下Xgboost(0.4)环境 (python,java)以及使用介绍及参数调优

    摘要: 1.所需工具 2.详细过程 3.验证 4.使用指南 5.参数调优 内容: 1.所需工具 我用到了git(内含git bash),Visual Studio 2012(10及以上就可以),xgb ...

  6. 【转】Windows下使用libsvm中的grid.py和easy.py进行参数调优

    libsvm中有进行参数调优的工具grid.py和easy.py可以使用,这些工具可以帮助我们选择更好的参数,减少自己参数选优带来的烦扰. 所需工具:libsvm.gnuplot 本机环境:Windo ...

  7. spark参数调优

    摘要 1.num-executors 2.executor-memory 3.executor-cores 4.driver-memory 5.spark.default.parallelism 6. ...

  8. Linux内核 TCP/IP、Socket参数调优

    Linux内核 TCP/IP.Socket参数调优 2014-06-06  Harrison....   阅 9611  转 165 转藏到我的图书馆   微信分享:   Doc1: /proc/sy ...

  9. JVM参数调优

    JVM参数调优 JVM参数调优是一个很头痛的问题,可能和应用有关系,下面是本人一些调优的实践经验,希望对读者能有帮助,环境LinuxAS4,resin2.1.17,JDK6.0,2CPU,4G内存,d ...

随机推荐

  1. Web前端面试之HTML

    1. 对WEB标准以及W3C的理解与认识 web标准规范要求,书写标签闭合.小写.不乱嵌套,可提高搜索机器人对网页内容的搜索几率.--- SEO 使用外链css和js脚本,结构与行为.结构与表现分离, ...

  2. 读Windows核心编程-5-作业

    作业(Job) 有时候需要把一些进程集中管理,如终止一个进程以及它产生的子进程,但由于Windows并没有维护进程间父子关系,所以除非进程本身以某种方式记录这些信息,否则很难做到管理这种父子进程树.而 ...

  3. Java程序设计 实验三

    北京电子科技学院(BESTI) 实     验    报     告 课程:Java程序设计   班级:1353       姓名:李海空  学号:20135329 成绩:             指 ...

  4. csharp: NHibernate and Entity Framework (EF) (object-relational mapper)

    代码生成器: 1. http://www.codesmithtools.com/ 2.https://sourceforge.net/projects/mygeneration/ 3. http:// ...

  5. Linux(CentOS或RadHat)下MySQL源码安装

    安装环境: CentOS6.3 64位 软件: Mysql-5.6 所需包: gcc/g++ :MySQL 5.6开始,需要使用g++进行编译.cmake  :MySQL 5.5开始,使用cmake进 ...

  6. Java面向对象(封装性概论)

     Java面向对象(封装性概论) 知识概要:                   (1)面向对象概念 (2)类与对象的关系 (3)封装 (4)构造函数 (5)this关键字 (6)static关键 ...

  7. Android查缺补漏(线程篇)-- AsyncTask的使用及原理详细分析

    本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8515304.html 一.AsyncTask的使用 AsyncTask是一种轻 ...

  8. Excel下拉框多列显示,如何只显示一列

    小编最近接手一个项目,之于需要导数据,但是我们需要提前把表头什么的设置好,更方便其他小伙伴们帮助我们导入数据,小伙伴们都知道,在excel中设置下拉菜单很简单,直接用数据有效性-序列就可以实现,今天小 ...

  9. Java基础——Instanceof 运算符

    Instanceof 运算符 java 中的instanceof 运算符是用来在运行时指出对象是否为特定类的一个实例 instanceof运算返回值:boolean类型 用法 boolean resu ...

  10. java对图片进行透明化处理

    package utils; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; ...