bayesian optimization

栏目: 编程工具 · 发布时间: 5年前

内容简介:几乎所有的机器学习算法都包括一些超参数。这些参数和常规参数不同,它们不是模型的一部分,不会在模型拟合中被自动调整,需要在额外的步骤中进行调整。常见的超参数有逻辑回归模型中的正则项lambda、支持向量机中的C项、基于树的算法中树的数量(如,随机森林、梯度提升机)。一般常见的超参数优化方法主要有:在这4中方法之中,我们尝试了网格搜索,随机搜索和贝叶斯优化。我们发现贝叶斯优化是最高效的,可以自动达到最优。

几乎所有的机器学习算法都包括一些超参数。这些参数和常规参数不同,它们不是模型的一部分,不会在模型拟合中被自动调整,需要在额外的步骤中进行调整。常见的超参数有逻辑回归模型中的正则项lambda、支持向量机中的C项、基于树的算法中树的数量(如,随机森林、梯度提升机)。

一般常见的超参数优化方法主要有:

  1. 网格搜索

  2. 随机搜索

  3. 基于梯度的优化

  4. 贝叶斯优化

在这4中方法之中,我们尝试了网格搜索,随机搜索和贝叶斯优化。我们发现贝叶斯优化是最高效的,可以自动达到最优。

为什么贝叶斯优化比网格搜索和随机搜索更高效呢?

在寻找最优超参数值的时候,需要提前确定一些条件。首先,也是最重要的,任何算法都需要一个目标函数,目标是使得目标函数达到最大值;或者一个损失函数,目标是使得损失函数达到最小值。然后,需要确定搜索范围,一般通过上限和下限来确定。可能还有一些对于算法的参数,比如搜索的步长。

网格搜索可能是应用最广泛的超参数搜索算法了,因为它确实很简单。网格搜索通过查找搜索范围内的所有的点,来确定最优值。它返回目标函数的最大值或损失函数的最小值。给定较大的搜索范围,以及较小的步长,网格搜索是一定可以找到全局最大值或最小值的。但是,网格搜索存在一个比较大的问题是,它十分消耗计算资源,特别是,需要调优的超参数比较多的时候(例如,随机森林里有8个左右)。因此,当人们实际使用网格搜索来找到最佳超参数集的时候,一般会先使用较广的搜索范围,以及较大的步长,来找到全局最大值或者最小值可能的参数值。然后,不断缩小搜索范围和步长,来达到更精确的最值。尽管这样做可以降低所需的时间,但是由于目标参数一般是非凸的,如图1所示,所以人们常常就会错过了全局的最大值或最小值,因为他们在第一次测试的时候找到了一个局部的最值。

bayesian optimization

随机搜索的思想和网格搜索比较相似,只是不再测试上界和下界之间的所有值,只是在搜索范围中随机取样本点。它的理论依据是,如果随即样本点集足够大,那么也可以找到全局的最大或最小值,或它们的近似值。通过对搜索范围的随机取样,随机搜索一般会比网格搜索要快一些。但是和网格搜索的快速版(非自动版)相似,结果也是没法保证的。

贝叶斯优化寻找使全局达到最值的参数时,使用了和网格搜索、随机搜索完全不同的方法。网格搜索和随机搜索在测试一个新的点时,会忽略前一个点的信息。而贝叶斯优化充分利用了这个信息。贝叶斯优化的工作方式是通过对目标函数形状的学习,找到使结果向全局最大值提升的参数。它学习目标函数形状的方法是,根据先验分布,假设一个搜集函数。在每一次使用新的采样点来测试目标函数时,它使用这个信息来更新目标函数的先验分布。然后,算法测试由后验分布给出的,全局最值最可能出现的位置的点。

对于贝叶斯优化,一个主要需要注意的地方,是一旦它找到了一个局部最大值或最小值,它会在这个区域不断采样,所以它很容易陷入局部最值。为了减轻这个问题,贝叶斯优化算法会在勘探和开采(exploration and exploitation)中找到一个平衡点。

勘探(exploration),就是在还未取样的区域获取采样点。开采(exploitation),就是根据后验分布,在最可能出现全局最值的区域进行采样。

何时贝叶斯优化无法返回最优值?

贝叶斯优化,尽管比网格搜索和随机搜索要好一些,但是它也不是魔法,所以有些东西还是要好好考虑一下。根据我们的经验,迭代次数(也就是选取采样点的数量),和搜索范围的大小的比值,十分重要。让我们假想一个极端的例子,来说明这一点。想象你要调整两个超参数,每个参数的范围是从1到1000.然后你把迭代指数设置成了2,算法几乎肯定会返回一个错误结果,因为他还没充分学习到目标函数的形状。

实现

下面主要基于 python 的bayes_opt模块对xgboost模型进行调参。

首先,加载所需要的模块

from __future__ import print_function
import numpy as np
import pandas as pd
import gc
import warnings
from bayes_opt import BayesianOptimization
from sklearn.cross_validation import cross_val_score, StratifiedKFold, StratifiedShuffleSplit
from sklearn.metrics import log_loss, matthews_corrcoef, roc_auc_score
from sklearn.preprocessing import MinMaxScaler
import xgboost as xgb
import contextlib

定义一个capture stderr和stdout函数

@contextlib.contextmanager
def capture():
    import sys
    from cStringIO import StringIO
    olderr, oldout = sys.stderr, sys.stdout
    try:
        out = [StringIO(), StringIO()]
        sys.stderr, sys.stdout = out
        yield out
    finally:
        sys.stderr, sys.stdout = olderr, oldout
        out[0] = out[0].getvalue().splitlines()
        out[1] = out[1].getvalue().splitlines()

虽然scaleing对于xgboost而言没必要,但是可能其他模型需要使用到,比如线性模型,因此,这里定义一个scale函数

# 数据归一化处理
def scale_data(X, scaler=None):
    if not scaler:
        scaler = MinMaxScaler(feature_range=(-1, 1))
        scaler.fit(X)
    X = scaler.transform(X)
    return X, scaler

使用kaggle比赛的 数据案例 进行测试,首先加载数据集。

DATA_TRAIN_PATH = 'train.csv'
DATA_TEST_PATH = 'test.csv'
def load_data(path_train=DATA_TRAIN_PATH, path_test=DATA_TEST_PATH):
    train_loader = pd.read_csv(path_train, dtype={'target': np.int8, 'id': np.int32})
    train = train_loader.drop(['target', 'id'], axis=1)
    train_labels = train_loader['target'].values
    train_ids = train_loader['id'].values
    print('\n Shape of raw train data:', train.shape)
    test_loader = pd.read_csv(path_test, dtype={'id': np.int32})
    test = test_loader.drop(['id'], axis=1)
    test_ids = test_loader['id'].values
    print(' Shape of raw test data:', test.shape)
    return train, train_labels, test, train_ids, test_ids

接下来,定义一个用于参数搜索的交叉验证变量,需要注意的是cv函数里面的参数跟参数空间是一一对应的,下面,我们将学习率(‘eta’)参数设置为0.1,当然这个参数不是最优的,但是大的学习率会让搜索速度更快。而且你也可以在0.01~0.05之间进行测试,但是需要注意的是,这会增加训练时间因为我们需要更多的迭代次数来训练。对于交叉验证次数,10折交叉验证多多少少会比5折交叉验证提高微小的性能,但是很明显更加耗时。

xgboost的输出数据包含着很多有用的信息,后面我们将从这些数据中提取相关信息,并将每个cv的记录打印到日志文件中

虽然比赛中选择gini分数作为评估指标,但是下面我们选择AUC作为评估指标,因为AUC指标跟gini分数是直接相关的,当然也可以自定义评估指标令feval=gini。

def XGB_CV(
          max_depth,
          gamma,
          min_child_weight,
          max_delta_step,
          subsample,
          colsample_bytree
         ):

    global AUCbest
    global ITERbest
	# 定义xgboost参数
    paramt = {
              'booster' : 'gbtree',
              'max_depth' : int(max_depth),
              'gamma' : gamma,
              'eta' : 0.1,
              'objective' : 'binary:logistic',
              'nthread' : 8,
              'silent' : True,
              'eval_metric': 'auc',
              'subsample' : max(min(subsample, 1), 0),
              'colsample_bytree' : max(min(colsample_bytree, 1), 0),
              'min_child_weight' : min_child_weight,
              'max_delta_step' : int(max_delta_step),
              'seed' : 1001
              }

    folds = 5 # 5折交叉验证
    cv_score = 0
    print("\n Search parameters (%d-fold validation):\n %s" % (folds, paramt), file=log_file )
    log_file.flush()
    # 对于boosting rounds参数,一般不进行优化,而使用earlying stopping进行处理
    xgbc = xgb.cv(
                    paramt,
                    dtrain,
                    num_boost_round = 20000,
                    stratified = True,
                    nfold = folds,
#                    verbose_eval = 10,
                    early_stopping_rounds = 100,
                    metrics = 'auc',
                    show_stdv = True
               )
    print('', file=log_file)
    #    for line in result[0]:
    for line in result[1]:
        print(line, file=log_file)
        if str(line).find('cv-mean') != -1:
            #cv_score = float(re.split('[|]| |\t|:', line)[2])
    log_file.flush()

    val_score = xgbc['test-auc-mean'].iloc[-1]
    train_score = xgbc['train-auc-mean'].iloc[-1]
    print(' Stopped after %d iterations with train-auc = %f val-auc = %f ( diff = %f ) train-gini = %f val-gini = %f' % ( len(xgbc), train_score, val_score, (train_score - val_score), (train_score*2-1),
(val_score*2-1)) )
    if ( val_score > AUCbest ):
        AUCbest = val_score
        ITERbest = len(xgbc)
    # 这里需要注意的是我们需要计算最大化,因此类似于AUC,直接返回即可,对于logloss,需要在前面加上负号
    return (val_score*2) - 1 # auc越大越好

接下来,定义日志文件跟数据集

# 日志文件
log_file = open('Porto-AUC-5fold-XGB-run-01-v1-full.log', 'a')
AUCbest = -1.
ITERbest = 0
# 数据集
train, target, test, tr_ids, te_ids = load_data()
n_train = train.shape[0]
train_test = pd.concat((train, test)).reset_index(drop=True)
col_to_drop = train.columns[train.columns.str.endswith('_cat')]
col_to_dummify = train.columns[train.columns.str.endswith('_cat')].astype(str).tolist()
# 分类变量编码处理
for col in col_to_dummify:
    dummy = pd.get_dummies(train_test[col].astype('category'))
    columns = dummy.columns.astype(str).tolist()
    columns = [col + '_' + w for w in columns]
    dummy.columns = columns
    train_test = pd.concat((train_test, dummy), axis=1)
train_test.drop(col_to_dummify, axis=1, inplace=True)
train_test_scaled, scaler = scale_data(train_test)
train = train_test_scaled[:n_train, :]
test = train_test_scaled[n_train:, :]
print('\n Shape of processed train data:', train.shape)
print(' Shape of processed test data:', test.shape)

dtrain = xgb.DMatrix(train, label = target)
# 如果原始数据太大,则可以随机抽样进行测试
# sss = StratifiedShuffleSplit(target, random_state=1001, test_size=0.75)
# for train_index, test_index in sss:
#     break
# X_train, y_train = train[train_index], target[train_index]
# del train, target
# gc.collect()
dtrain = xgb.DMatrix(X_train, label = y_train)

接下来,定义xgboost的BO,需要注意的这里的参数搜索应与cv函数中的变量一一对应

XGB_BO = BayesianOptimization(XGB_CV, {
                                     'max_depth': (2, 12),
                                     'gamma': (0.001, 10.0),
                                     'min_child_weight': (0, 20),
                                     'max_delta_step': (0, 10),
                                     'subsample': (0.4, 1.0),
                                     'colsample_bytree' :(0.4, 1.0)
                                    })

通常,init_points参数设置在10~20之间,而n_iter设置在25~50之间。

print('-'*130)
print('-'*130, file=log_file)
log_file.flush()
with warnings.catch_warnings():
    warnings.filterwarnings('ignore') # 忽视一些警告,当然可以注释掉
    XGB_BO.maximize(init_points=2, n_iter=5, acq='ei', xi=0.0)
# XGB_BO.maximize(init_points=10, n_iter=50, acq='ei', xi=0.0)
# XGB_BO.maximize(init_points=10, n_iter=50, acq='ei', xi=0.01)
# XGB_BO.maximize(init_points=10, n_iter=50, acq='ucb', kappa=10)
# XGB_BO.maximize(init_points=10, n_iter=50, acq='ucb', kappa=1)

接下来,保存结果

print('-'*130)
print('Final Results')
print('Maximum XGBOOST value: %f' % XGB_BO.res['max']['max_val'])
print('Best XGBOOST parameters: ', XGB_BO.res['max']['max_params'])
print('-'*130, file=log_file)
print('Final Result:', file=log_file)
print('Maximum XGBOOST value: %f' % XGB_BO.res['max']['max_val'], file=log_file)
print('Best XGBOOST parameters: ', XGB_BO.res['max']['max_params'], file=log_file)
log_file.flush()
log_file.close()

history_df = pd.DataFrame(XGB_BO.res['all']['params'])
history_df2 = pd.DataFrame(XGB_BO.res['all']['values'])
history_df = pd.concat((history_df, history_df2), axis=1)
history_df.rename(columns = { 0 : 'gini'}, inplace=True)
history_df['AUC'] = ( history_df['gini'] + 1 ) / 2
history_df.to_csv('Porto-AUC-5fold-XGB-run-01-v1-grid.csv')

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

CSS精粹

CSS精粹

Rachel Andrew / 曹明伦 / 人民邮电出版社 / 2007-10 / 39.00元

本书采用问答的形式,为CSS使用过程中一些有价值的经典问题提供了精彩的实践解决方案。本书内容包括文本样式、CSS图像、导航、表格数据、注册表和用户界面、浏览器和设置支持、CSS定位和布局以及未来相关技术。 本书的目标读者是每一个需要使作CSS的Web设计人员和开发人员。本书通过经典的问题和精彩的解答将理论融于实践,使每一个带着问题阅读本书的读者都能找到自己满意的答案。一起来看看 《CSS精粹》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具