前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Python进行超参数优化

使用Python进行超参数优化

作者头像
代码医生工作室
发布2020-11-13 11:59:08
1.7K0
发布2020-11-13 11:59:08
举报
文章被收录于专栏:相约机器人相约机器人

可以在此处找到本文随附的代码。

https://github.com/NMZivkovic/ml_optimizers_pt3_hyperparameter_optimization

到目前为止,在学习机器学习的整个过程中,涵盖了几个大主题。研究了一些回归算法,分类算法和可用于两种类型问题的算法(SVM, 决策树和随机森林)。除此之外,将toes浸入无监督的学习中,了解了如何使用这种类型的学习进行聚类,并了解了几种聚类技术。在所有这些文章中,使用Python进行“从头开始”的实现和TensorFlow, Pytorch和SciKit Learn之类的库。

担心AI会接手您的工作吗?确保是构建它的人。与崛起的AI行业保持相关!

超参数是每个机器学习和深度学习算法的组成部分。与算法本身学习的标准机器学习参数(例如线性回归中的w和b或神经网络中的连接权重)不同,工程师在训练过程之前会设置超参数。它们是控制工程师完全定义的学习算法行为的外部因素。需要一些例子吗?

该学习速率是最著名的超参数之一,C在SVM也是超参数,决策树的最大深度是一个超参数等,这些可以手动由工程师进行设置。但是如果要运行多个测试,可能会很麻烦。那就是使用超参数优化的地方。这些技术的主要目标是找到给定机器学习算法的超参数,该超参数可提供在验证集上测得的最佳性能。在本教程中,探索了可以提供最佳超参数的几种技术。

数据集和先决条件

本文中使用的数据来自PalmerPenguins数据集。最近引入了此数据集,以替代著名的Iris数据集。它是由Kristen Gorman博士和南极洲帕尔默站创建的。可以在此处或通过Kaggle获取此数据集。该数据集实质上由两个数据集组成,每个数据集包含344个企鹅的数据。就像在鸢尾花数据集中一样,帕尔默群岛的3个岛屿中有3种不同的企鹅。同样,这些数据集包含每个物种的标本维度。高门是鸟嘴的上脊。在简化的企鹅数据中,顶点长度和深度被重命名为culmen_length_mm和culmen_depth_mm变量。

https://github.com/allisonhorst/palmerpenguins

由于该数据集已被标记,因此将能够验证实验结果。但是通常不是这种情况,并且对聚类算法结果的验证通常是一个困难而复杂的过程。

就本文而言,请确保已安装以下Python 库:

  • NumPy
  • SciKit学习
  • SciPy
  • Sci-Kit优化

安装完成后,请确保已导入本教程中使用的所有必要模块。

代码语言:javascript
复制
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

from sklearn.model_selection import GridSearchCV, RandomizedSearchCV

from sklearn.svm import SVC
from sklearn.ensemble import RandomForestRegressor

from scipy import stats
from skopt import BayesSearchCV
from skopt.space import Real, Categorical

除此之外,至少要熟悉线性代数,微积分和概率的基础。

准备数据

加载并准备PalmerPenguins数据集。首先加载数据集,删除本文中未使用的功能:

代码语言:javascript
复制
data = pd.read_csv('./data/penguins_size.csv')

data = data.dropna()
data = data.drop(['sex', 'island', 'flipper_length_mm', 'body_mass_g'], axis=1)

然后分离输入数据并对其进行缩放:

代码语言:javascript
复制
X = data.drop(['species'], axis=1)

ss = StandardScaler()
X = ss.fit_transform(X)

y = data['species']
spicies = {'Adelie': 0, 'Chinstrap': 1, 'Gentoo': 2}
y = [spicies[item] for item in y]
y = np.array(y)

最后将数据分为训练和测试数据集:

代码语言:javascript
复制
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=33)

当绘制数据时,这里是它的样子:

网格搜索

手动超参数调整缓慢且令人讨厌。这就是为什么探索第一个也是最简单的超参数优化技术–网格搜索的原因。该技术正在加速该过程,它是最常用的超参数优化技术之一。本质上,它使试错过程自动化。对于这项技术,提供了所有超参数值的列表,并且该算法为每种可能的组合建立了一个模型,对其进行评估,然后选择能够提供最佳结果的值。这是一种通用技术,可以应用于任何模型。

在示例中,使用SVM算法进行分类。考虑了三个超参数C,gamma和kernel。要更详细地了解它们,请查看本文。对于C,要检查以下值:0.1、1、100、1000;对于gamma,使用值:0.0001、0.001、0.005、0.1、1、3、5;对于kernel,使用值:'linear'和'rbf'。这是代码中的样子:

代码语言:javascript
复制
hyperparameters = {
    'C': [0.1, 1, 100, 1000],
    'gamma': [0.0001, 0.001, 0.005, 0.1, 1, 3, 5],
    'kernel': ('linear', 'rbf')
}

利用Sci-Kit Learn及其SVC类,其中包含用于分类的SVM实现。除此之外,使用GridSearchCV 类,该类用于网格搜索优化。结合起来看起来像这样:

代码语言:javascript
复制
grid = GridSearchCV(
        estimator=SVC(),
        param_grid=hyperparameters,
        cv=5,
scoring='f1_micro',
n_jobs=-1)

此类通过构造函数接收几个参数:

  • 估计器–实例机器学习算法本身。在那里传递了SVC类的新实例。
  • param_grid –包含超参数字典。
  • cv –确定交叉验证拆分策略。
  • 评分–用于评估预测的验证指标。使用F1分数。
  • n_jobs –表示要并行运行的作业数。值-1表示正在使用所有处理器。

剩下要做的唯一一件事就是通过使用fit方法来运行训练过程:

代码语言:javascript
复制
grid.fit(X_train, y_train)

训练完成后,可以检查最佳超参数和这些参数的分数:

代码语言:javascript
复制
print(f'Best parameters: {grid.best_params_}')
print(f'Best score: {grid.best_score_}')
Best parameters: {'C': 1000, 'gamma': 0.1, 'kernel': 'rbf'}
Best score: 0.9626834381551361

同样,可以打印出所有结果:

代码语言:javascript
复制
print(f'All results: {grid.cv_results_}')

Allresults: {'mean_fit_time': array([0.00780015, 0.00280147, 0.00120015, 0.00219998, 0.0240006 ,

0.00739942, 0.00059962, 0.00600033, 0.0009994 , 0.00279789,

0.00099969, 0.00340114, 0.00059986, 0.00299864, 0.000597 ,

0.00340023, 0.00119658, 0.00280094, 0.00060058, 0.00179944,

0.00099964, 0.00079966, 0.00099916, 0.00100031, 0.00079999,

0.002 , 0.00080023, 0.00220037, 0.00119958, 0.00160012,

0.02939963, 0.00099955, 0.00119963, 0.00139995, 0.00100069,

0.00100017, 0.00140052, 0.00119977, 0.00099974, 0.00180006,

0.00100312, 0.00199976, 0.00220003, 0.00320096, 0.00240035,

0.001999 , 0.00319982, 0.00199995, 0.00299931, 0.00199928,

...

好的,现在建立这个模型,并检查它在测试数据集上的表现如何:

代码语言:javascript
复制
model = SVC(C=500, gamma = 0.1, kernel = 'rbf')
model.fit(X_train, y_train)

preditions = model.predict(X_test)
print(f1_score(preditions, y_test, average='micro'))

0.9701492537313433

太酷了,使用建议的超参数的模型的准确度约为97%。这是绘制模型时的样子:

随机搜寻

网格搜索非常简单。但是它也计算昂贵。特别是在深度学习领域,训练可能会花费很多时间。同样,某些超参数可能比其他一些更重要。这就是为什么的想法随机搜索出生在引入本文。实际上,这项研究表明,就计算成本而言,对于超参数优化,随机搜索比网格搜索更有效。该技术还允许更精确地发现重要超参数的良好值。

就像Grid Search一样,Random Search会创建一个超参数值的网格,并选择随机组合来训练模型。这种方法有可能错过最佳组合,但是,与Grid Search相比,它出人意料地更频繁地选择了最佳结果,而且花费的时间很少。看看它在代码中如何工作。同样=使用Sci-Kit Learn的SVC类,但是这次使用RandomSearchCV 类进行随机搜索优化。

代码语言:javascript
复制
hyperparameters = {
    "C": stats.uniform(500, 1500),
    "gamma": stats.uniform(0, 1),
    'kernel': ('linear', 'rbf')
}

random = RandomizedSearchCV(
                estimator = SVC(),
                param_distributions = hyperparameters,
                n_iter = 100,
                cv = 3,
                random_state=42,
                n_jobs = -1)

random.fit(X_train, y_train)

请注意,对C和gamma使用了均匀分布。同样可以打印出结果:

代码语言:javascript
复制
print(f'Best parameters: {random.best_params_}')
print(f'Best score: {random.best_score_}')

Best parameters: {'C': 510.5994578295761, 'gamma': 0.023062425041415757, 'kernel': 'linear'}

Best score: 0.9700374531835205

请注意接近但结果与使用Grid Search时不同。网格搜索的超参数C的值为500 ,而随机搜索的超参数C的值为510.59。仅此一项,就可以看到随机搜索的好处,因为不太可能将这个值放在网格搜索列表中。类似地,对于gamma来说,随机搜索得到0.23,而网格搜索得到0.1 。真正令人惊讶的是,Random Search选择了线性核而不是RBF,并且获得了更高的F1分数。要打印所有结果,使用cv_results_ 属性:

代码语言:javascript
复制
print(f'All results: {random.cv_results_}')

Allresults: {'mean_fit_time': array([0.00200065, 0.00233404, 0.00100454, 0.00233777, 0.00100009,

0.00033339, 0.00099715, 0.00132942, 0.00099921, 0.00066725,

0.00266568, 0.00233348, 0.00233301, 0.0006667 , 0.00233285,

0.00100001, 0.00099993, 0.00033331, 0.00166742, 0.00233364,

0.00199914, 0.00433286, 0.00399915, 0.00200049, 0.01033338,

0.00100342, 0.0029997 , 0.00166655, 0.00166726, 0.00133403,

0.00233293, 0.00133729, 0.00100009, 0.00066662, 0.00066646,

....

做与Grid Search相同的事情:使用建议的超参数创建模型,检查测试数据集的分数并绘制模型。

代码语言:javascript
复制
model = SVC(C=510.5994578295761, gamma = 0.023062425041415757, kernel = 'linear')
model.fit(X_train, y_train)

preditions = model.predict(X_test)
print(f1_score(preditions, y_test, average='micro'))

0.9701492537313433

测试数据集上的F1分数与使用网格搜索时的分数完全相同。查看模型:

贝叶斯优化

关于前两种算法的一个非常酷的事实是,所有具有不同超参数值的实验都可以并行运行。这样可以节省很多时间。但是,这也是他们最大的不足。意思是,由于每个实验都是独立进行的,因此无法在当前实验中使用过去实验的信息。整个领域都致力于解决序列优化问题-基于序列模型的优化(SMBO)。在该领域中探索的算法使用先前的实验和对损失函数的观察。基于它们,尝试确定下一个最佳点。贝叶斯优化是这种算法之一。

就像来自SMBO组的其他算法一样,使用先前评估的点(在这种情况下,它们是超参数值,但我们可以概括)来计算损失函数的后验期望。该算法使用两个重要的数学概念-高斯过程和获取函数。由于高斯分布是在随机变量上完成的,因此高斯过程就是其对函数的推广。就像高斯分布具有均值和协方差一样,高斯过程由均值函数和协方差函数来描述。

采集函数是用于评估当前损耗值的函数。观察它的一种方法是将其作为损失函数。它是损失函数的后验分布的函数,描述了超参数的所有值的效用。最受欢迎的采集功能有望改善:

其中f是损失函数,x'是当前最佳超参数集。将所有这些放在一起时,Byesian优化是通过3个步骤完成的:

  • 使用先前评估的损失函数点,使用高斯过程计算后验期望。
  • 选择了最大化预期改进效果的新点集
  • 计算新选定点的损失函数

将其引入代码的简便方法是使用Sci-Kit优化库,通常称为skopt。按照前面示例中使用的过程,可以执行以下操作:

代码语言:javascript
复制
hyperparameters = {
    "C": Real(1e-6, 1e+6, prior='log-uniform'),
    "gamma": Real(1e-6, 1e+1, prior='log-uniform'),
    "kernel": Categorical(['linear', 'rbf']),
}

bayesian = BayesSearchCV(
                estimator = SVC(),
                search_spaces = hyperparameters,
                n_iter = 100,
                cv = 5,
                random_state=42,
                n_jobs = -1)

bayesian.fit(X_train, y_train)

同样,为超参数集定义了字典。请注意使用了Sci-Kit Optimization库中的Real和Categorical类。然后以与使用GridSearchCV或RandomSearchCV相同的方式利用BayesSearchCV类。训练完成后,可以打印出最佳结果:

代码语言:javascript
复制
print(f'Best parameters: {bayesian.best_params_}')
print(f'Best score: {bayesian.best_score_}')

Best parameters:

OrderedDict([('C', 3932.2516133086), ('gamma', 0.0011646737978730447), ('kernel', 'rbf')])

Best score: 0.9625468164794008

使用此优化,得到了截然不同的结果。损失要比使用随机搜索时高。甚至可以打印所有结果:

代码语言:javascript
复制
print(f'All results: {bayesian.cv_results_}')

All results: defaultdict(<class 'list'>, {'split0_test_score': [0.9629629629629629,

0.9444444444444444, 0.9444444444444444, 0.9444444444444444, 0.9444444444444444,

0.9444444444444444, 0.9444444444444444, 0.9444444444444444, 0.46296296296296297,

0.9444444444444444, 0.8703703703703703, 0.9444444444444444, 0.9444444444444444,

0.9444444444444444, 0.9444444444444444, 0.9444444444444444, 0.9444444444444444,

.....

具有这些超参数的模型在测试数据集上的表现如何?

代码语言:javascript
复制
model = SVC(C=3932.2516133086, gamma = 0.0011646737978730447, kernel = 'rbf')
model.fit(X_train, y_train)
 
preditions = model.predict(X_test)
print(f1_score(preditions, y_test, average='micro'))

0.9850746268656716

这非常有趣。即使在验证数据集上得到的结果较差,在测试数据集上也得到了更好的分数。这是模型:

只是为了好玩,将所有这些模型放在一起:

备选方案

通常先前描述的方法是最流行和最常用的。但是如果以前的解决方案不适合,则可以考虑几种替代方法。其中之一是超参数值的基于梯度的优化。该技术计算有关超参数的梯度,然后使用梯度下降算法对其进行优化。这种方法的问题在于,要使梯度下降正常工作,需要凸且平滑的函数,而在谈论超参数时通常并非如此。另一种方法是使用进化算法进行优化。

结论

在本文中,介绍了几种众所周知的超参数优化和调整算法。了解了如何使用网格搜索,随机搜索和贝叶斯优化来获取超参数的最佳值。还看到了如何在代码中利用Sci-Kit Learn类和方法来实现。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 相约机器人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档