文章目录
- 1. 贝叶斯优化方法
- 2. Python中的选择
- 3. 优化问题的四个部分
- 4. 代码演示
1. 贝叶斯优化方法
(注意是方法,是一种思想)
贝叶斯优化通过基于目标函数的过去评估结果建立替代函数(概率模型),来找到最小化目标函数的值。贝叶斯方法与随机或网格搜索的不同之处在于,它在尝试下一组超参数时,会参考之前的评估结果,因此可以省去很多无用功。超参数的评估代价很大,因为它要求使用待评估的超参数训练一遍模型,而许多深度学习模型动则几个小时几天才能完成训练,并评估模型,因此耗费巨大。贝叶斯调参发使用不断更新的概率模型,通过推断过去的结果来“集中”有希望的超参数。
2. Python中的选择
Python中有几个贝叶斯优化库,它们目标函数的替代函数不一样。在本文中,我们将使用Hyperopt,它使用Tree Parzen Estimator(TPE)。其他Python库包括Spearmint(高斯过程代理)和SMAC(随机森林回归)。
3. 优化问题的四个部分
贝叶斯优化问题有四个部分:
- 目标函数:我们想要最小化的内容,在这里,目标函数是机器学习模型使用该组超参数在验证集上的损失。
- 域空间:要搜索的超参数的取值范围
- 优化算法:构造替代函数并选择下一个超参数值进行评估的方法。
- 结果历史记录:来自目标函数评估的存储结果,包括超参数和验证集上的损失
4. 代码演示
import datetime
import numpy as np
import pandas as pd
import lightgbm as lgb
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score,f1_score
import matplotlib.pyplot as plt
from hyperopt import fmin,hp,Trials,space_eval,rand,tpe,anneal
import warnings
warnings.filterwarnings('ignore')
def printlog(info):
nowtime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print("\n"+"=========="*8 + "%s"%nowtime)
print(info+'...\n\n')
#================================================================================
# 一,读取数据
#================================================================================
printlog("step1: reading data...")
# 读取dftrain,dftest
breast = datasets.load_breast_cancer()
df = pd.DataFrame(breast.data,columns = [x.replace(' ','_') for x in breast.feature_names])
df['label'] = breast.target
df['mean_radius'] = df['mean_radius'].apply(lambda x:int(x))
df['mean_texture'] = df['mean_texture'].apply(lambda x:int(x))
dftrain,dftest = train_test_split(df)
categorical_features = ['mean_radius','mean_texture']
lgb_train = lgb.Dataset(dftrain.drop(['label'],axis = 1),label=dftrain['label'],
categorical_feature = categorical_features,free_raw_data=False)
lgb_valid = lgb.Dataset(dftest.drop(['label'],axis = 1),label=dftest['label'],
categorical_feature = categorical_features,
reference=lgb_train,free_raw_data=False)
#================================================================================
# 二,搜索超参
#================================================================================
printlog("step2: searching parameters...")
boost_round = 10
early_stop_rounds = 5
params = {
'learning_rate': 0.1,
'boosting_type': 'gbdt',#'dart','rf'
'objective':'binary',
'metric': ['auc'],
'num_leaves': 31,
'max_depth': 6,
'min_data_in_leaf': 5,
'min_gain_to_split': 0,
'reg_alpha':0,
'reg_lambda':0,
'feature_fraction': 0.9,
'bagging_fraction': 0.8,
'bagging_freq': 5,
'feature_pre_filter':False,
'verbose': -1
}
# 1,定义目标函数
def loss(config):
params.update(config)
gbm = lgb.train(params,
lgb_train,
num_boost_round= boost_round,
valid_sets=(lgb_valid, lgb_train),
valid_names=('validate','train'),
early_stopping_rounds = early_stop_rounds,
verbose_eval = False)
y_pred_test = gbm.predict(dftest.drop('label',axis = 1), num_iteration=gbm.best_iteration)
val_score = f1_score(dftest['label'],y_pred_test>0.5)
return -val_score
# 2,定义超参空间
#可以根据需要,注释掉偏后的一些不太重要的超参
spaces = {"learning_rate":hp.loguniform("learning_rate",np.log(0.001),np.log(0.5)),
"boosting_type":hp.choice("boosting_type",['gbdt','dart','rf']),
"num_leaves":hp.choice("num_leaves",range(15,128)),
#"max_depth":hp.choice("max_depth",range(3,11)),
#"min_data_in_leaf":hp.choice("min_data_in_leaf",range(1,50)),
#"min_gain_to_split":hp.uniform("min_gain_to_split",0.0,1.0),
#"reg_alpha": hp.uniform("reg_alpha", 0, 2),
#"reg_lambda": hp.uniform("reg_lambda", 0, 2),
#"feature_fraction":hp.uniform("feature_fraction",0.5,1.0),
#"bagging_fraction":hp.uniform("bagging_fraction",0.5,1.0),
#"bagging_freq":hp.choice("bagging_freq",range(1,20))
}
# 3,执行超参搜索
# hyperopt支持如下搜索算法
#随机搜索(hyperopt.rand.suggest)
#模拟退火(hyperopt.anneal.suggest)
#TPE算法(hyperopt.tpe.suggest,算法全称为Tree-structured Parzen Estimator Approach)
trials = Trials()
best = fmin(fn=loss, space=spaces, algo= tpe.suggest, max_evals=100, trials=trials)
# 4,获取最优参数
best_params = space_eval(spaces,best)
print("best_params = ",best_params)
# 5,绘制搜索过程
losses = [x["result"]["loss"] for x in trials.trials]
minlosses = [np.min(losses[0:i+1]) for i in range(len(losses))]
steps = range(len(losses))
fig,ax = plt.subplots(figsize=(6,3.7),dpi=144)
ax.scatter(x = steps, y = losses, alpha = 0.3)
ax.plot(steps,minlosses,color = "red",axes = ax)
plt.xlabel("step")
plt.ylabel("loss")
#================================================================================
# 三,训练模型
#================================================================================
printlog("step3: training model...")
params.update(best_params)
results = {}
gbm = lgb.train(params,
lgb_train,
num_boost_round= boost_round,
valid_sets=(lgb_valid, lgb_train),
valid_names=('validate','train'),
early_stopping_rounds = early_stop_rounds,
evals_result= results,
verbose_eval = True)
#================================================================================
# 四,评估模型
#================================================================================
printlog("step4: evaluating model ...")
y_pred_train = gbm.predict(dftrain.drop('label',axis = 1), num_iteration=gbm.best_iteration)
y_pred_test = gbm.predict(dftest.drop('label',axis = 1), num_iteration=gbm.best_iteration)
train_score = f1_score(dftrain['label'],y_pred_train>0.5)
val_score = f1_score(dftest['label'],y_pred_test>0.5)
print('train f1_score: {:.5} '.format(train_score))
print('valid f1_score: {:.5} \n'.format(val_score))
fig2,ax2 = plt.subplots(figsize=(6,3.7),dpi=144)
fig3,ax3 = plt.subplots(figsize=(6,3.7),dpi=144)
lgb.plot_metric(results,ax = ax2)
lgb.plot_importance(gbm,importance_type = "gain",ax=ax3)
#================================================================================
# 五,保存模型
#================================================================================
printlog("step5: saving model ...")
model_dir = "gbm.model"
print("model_dir: %s"%model_dir)
gbm.save_model("gbm.model",num_iteration=gbm.best_iteration)
printlog("task end...")