本章内容:
- 如何使用Bayes_opt实现参数优化,及案例?
- 如何使用HyperOpt实现参数优化,及案例?
- 如何使用Optuna实现参数优化,及案例?
HPO库 | 优劣评价 | 推荐指数 |
---|---|---|
bayes_opt | ✅实现基于高斯过程的贝叶斯优化 ✅当参数空间由大量连续型参数构成时⛔包含大量离散型参数时避免使用⛔算力/时间稀缺时避免使用 | ⭐⭐ |
hyperopt | ✅实现基于TPE的贝叶斯优化✅支持各类提效工具✅进度条清晰,展示美观,较少怪异警告或报错✅可推广/拓展至深度学习领域⛔不支持基于高斯过程的贝叶斯优化⛔代码限制多、较为复杂,灵活性较差 | ⭐⭐⭐⭐ |
optuna | ✅(可能需结合其他库)实现基于各类算法的贝叶斯优化✅代码最简洁,同时具备一定的灵活性✅可推广/拓展至深度学习领域⛔非关键性功能维护不佳,有怪异警告与报错 | ⭐⭐⭐⭐ |
以上三个库都不支持基于Python环境的并行或加速,大多数优化算法库只能够支持基于数据库(如MangoDB,mySQL)的并行或加速,但以上库都可以被部署在分布式计算平台。
关于贝叶斯参数优化实现方式,需要了解的几点:
- 贝叶斯优化需要自定义目标函数、参数空间、优化器,通常不直接调库;
- 不同的贝叶斯方式下,定义目标函数、参数空间、优化器的规则不同,各有自己的规则
基于以上两点,下面介绍三种HPO库时,主要内容为①介绍自定义的规则,②案例的整个流程;
另外以下案例中,弱评估器均采用随机森林。
使用Bayes_opt时,通常为以下情况:
因为bayes_opt对参数空间的处理方法较原始,缺乏相应的提升/监控供销,对算力的要求较高,往往不是调参的第一选择。
目标函数的输入必须是具体的超参数,而不能是整个超参数空间,更不能是数据、算法等超参数以外的元素。因此在定义目标函数时,我们需要让超参数作为目标函数的输入。
示例:括号内必须是弱评估器的超参数
def bayesopt_objective(n_estimators,max_depth):
超参数的输入值只能是浮点数,不支持整数与字符串。因此当算法的实际参数需要输入字符串时,该参数不能使用bayes_opt进行调整,当算法的实际参数需要输入整数时,则需要在目标函数中规定参数的类型。
示例:括号里的超参数只能是浮点数:比如随机森林的参数criterion输入内容为‘gini’,这就是字符串,所以criterion这个参数不能放到里面。
def bayesopt_objective(n_estimators,max_depth):
示例:整数参数需要在设定时改为浮点数格式,如红色部分所示:树的棵数只能是整数,所以需要在设定时使用int()改为浮点数。
def bayesopt_objective(n_estimators):
model=RFR(n_estimators=int(n_estimators)
bayes_opt只支持寻找()的最大值,不支持寻找最小值。因此当定义的目标函数是某种损失时,目标函数的输出需要取负(即,如果使用RMSE,则应该让目标函数输出负RMSE,这样最大化负RMSE后,才是最小化真正的RMSE。)当定义的目标函数是准确率,或者auc等指标,则可以让目标函数的输出保持原样。
由于以上规则,输入bayes_opt的参数空间天生会比其他贝叶斯优化库更大/更密,因此需要的迭代次数也更多。
# pip install bayesian-optimization
from bayes_opt import BayesianOptimization
from sklearn.ensemble import RandomForestRegressor as RFR
from sklearn.model_selection import KFold,cross_validate
# 自定义随机森林模型和交叉验证模型,返回测试集的根均方误差
def bayesopt_objective(n_estimators,max_depth,max_features,min_impurity_decrease):
model=RFR(n_estimators=int(n_estimators)
,max_depth=int(max_depth)
,max_features=int(max_features)
,min_impurity_decrease=min_impurity_decrease
,random_state=7
,n_jobs=-1
)
cv=KFold(n_splits=5,shuffle=True,random_state=7)
validation_loss=cross_validate(model
,X,y
,scoring='neg_root_mean_squared_error'
,cv=cv
,n_jobs=-1
,error_score='raise' # 出错时报错,但不停止,如果设置为nan,将停止迭代
)
return np.mean(validation_loss['test_score'])
# 自定义优化器
def param_bayes_opt(init_points,n_iter):
opt=BayesianOptimization(bayesopt_objective
,param_grid_simple
,random_state=7)
# 使用优化器
opt.maximize(init_points=init_points # 抽取多少个初始观测值
,n_iter=n_iter # 总共观测/迭代次数
)
# 返回优化结果
params_best=opt.max['params'] # 返回最佳参数
score_best=opt.max['target'] # 返回最佳分数
# 打印结果
print("\n","best params: ", params_best,
"\n","best cvscore: ", score_best)
return params_best,score_best
# 自定义验证函数,返回bayes_opt最优参数的RMSE
def bayes_opt_validation(params_best):
model=RFR(n_estimators=int(params_best['n_estimators'])
,max_depth=int(params_best['max_depth'])
,max_features=int(params_best['max_features'])
,min_impurity_decrease=int(params_best['min_impurity_decrease'])
,random_state=7
,n_jobs=-1
)
cv=KFold(n_splits=5,shuffle=True,random_state=7)
validation_loss=cross_validate(model
,X,y
,scoring='neg_root_mean_squared_error'
,cv=cv
,n_jobs=-1
)
return np.mean(validation_loss['test_score'])
data=pd.read_csv(r'C:\Users\EDZ\test\ML-2 courseware\Lesson 9.随机森林模型\datasets\House Price\train_encode.csv',index_col=0)
X=data.iloc[:,:-1]
y=data.iloc[:,-1]
param_grid_simple={'n_estimators':(80,100)
,'max_depth':(15,25)
,'max_features':(10,20)
,'min_impurity_decrease':(20,24)
}
params_best,score_best=param_bayes_opt(20,280)
params_best # 打印最优参数组合
score_best # 打印最优参数评分
validation_score=bayes_opt_validation(params_best) # 参数组合验证
validation_score # day
HyperOpt定义参数空间,有如下几种字典形式
如无特殊说明,hp中的参数空间定义方法应当都为前闭后开区间。
HyperOpt定义参数空间,选择字典形式的思路:
HyperOpt优化目标函数时,涉及的功能/库
# pip install hyperopt
# pip install optuna
import optuna
print(optuna.__version__)
import hyperopt
print(hyperopt.__version__)
from sklearn.ensemble import RandomForestRegressor as RFR
from sklearn.model_selection import KFold,cross_validate
from bayes_opt import BayesianOptimization
from hyperopt import hp,fmin,tpe,Trials,partial
from hyperopt.early_stop import no_progress_loss
# 设定参数空间
param_grid_simple={'n_estimators':hp.quniform('n_estimators',80,100,1)
,'max_depth':hp.quniform('max_depth',10,25,1)
,'max_features':hp.quniform('max_features',10,20,1)
,'min_impurity_decrease':hp.quniform('min_impurity_decrease',20,25,1)
}
# 计算参数空间的大小
len([*range(80,100,1)])*len([*range(10,25,1)])*\
len([*range(10,20,1)])*len([range(20,25,1)])
# 设定目标函数_基评估器选择随机森林
def hyperopt_objective(params):
model=RFR(n_estimators=int(params['n_estimators'])
,max_depth=int(params['max_depth'])
,max_features=int(params['max_features'])
,min_impurity_decrease=params['min_impurity_decrease']
,random_state=7
,n_jobs=4)
cv=KFold(n_splits=5,shuffle=True,random_state=7)
validate_loss=cross_validate(model,X,y
,cv=cv
,scoring='neg_root_mean_squared_error'
,n_jobs=-1
,error_score='raise')
return np.mean(abs(validate_loss['test_score']))
# 设定优化过程
def param_hyperopt(max_evals=100):
# 记录迭代过程
trials=Trials()
# 提前停止
early_stop_fn=no_progress_loss(100) # 当损失函数的连续迭代100次都没有下降时,则停止;正常10-50即可
# 定义代理模型
# algo=partial(tpe.suggest # 设置代理模型的算法
# ,n_sratup_jobs=20 # 设置初始样本量
# ,n_EI_candidates=50) # 设置使用多少样本点来计算采集函数
params_best=fmin(hyperopt_objective # 设定目标函数
,space=param_grid_simple # 设定参数空间
,algo=tpe.suggest # 设定代理模型,如果需要自定义代理模型,使用前面algo=……的代码
,max_evals=max_evals # 设定迭代次数
,trials=trials
,early_stop_fn=early_stop_fn # 控制提前停止
)
print('best parmas:',params_best)
return params_best,trials
# 设定验证函数(和设定目标函数的代码一致)
def hyperopt_validation(params):
model=RFR(n_estimators=int(params['n_estimators'])
,max_depth=int(params['max_depth'])
,max_features=int(params['max_features'])
,min_impurity_decrease=params['min_impurity_decrease']
,random_state=7
,n_jobs=4)
cv=KFold(n_splits=5,shuffle=True,random_state=7)
validate_loss=cross_validate(model,X,y
,cv=cv
,scoring='neg_root_mean_squared_error'
,n_jobs=-1
)
return np.mean(abs(validate_loss['test_score']))
# 执行实际优化流程
# 1. 计算1%空间时的优化过程,返回最佳参数组合和迭代过程
params_best,trials=param_hyperopt(30)
#2. 计算3%空间时的优化过程,返回最佳参数组合和迭代过程
params_best, trials = param_hyperopt(100)
#3. 计算10%空间时的优化过程,返回最佳参数组合和迭代过程
params_best, trials = param_hyperopt(300)
# 根据最佳参数组合验证模型,返回RMSE
hyperopt_validation(params_best)
#打印所有搜索相关的记录
trials.trials[0]
#打印全部搜索的目标函数值
trials.losses()[:10]
# 准备数据及库
# pip install optuna
# pip install scikit-optimize
import optuna
optuna.__version__
data=pd.read_csv(r'E:\jupyter_notebook\机器学习二期课程课件\Lesson 9.随机森林模型\datasets\House Price\train_encode.csv',index_col=0)
X=data.iloc[:,:-1]
y=data.iloc[:,-1]
X.head()
from sklearn.ensemble import RandomForestRegressor as RFR
from sklearn.model_selection import KFold,cross_validate
# 定义目标函数
def optuna_objective(trial):
n_estimators=trial.suggest_int('n_estimators',80,100,1) # 整数型:suggest_int('参数名称',下界,上界,步长)
max_depth=trial.suggest_int('max_depth',10,25,1)
max_features=trial.suggest_int('max_features',10,20,1)
# max_features=trial.suggest_categorical('max_features',['log2','sqrt','auto']) # 字符型
min_impurity_decrease=trial.suggest_int('min_impurity_decrease',20,25,1)
# min_impurity_decrease=trial.suggest_float('min_impurity_decrease',20,25,log=False) # 浮点型
model=RFR(n_estimators=n_estimators
,max_depth=max_depth
,max_features=max_features
,min_impurity_decrease=min_impurity_decrease
,random_state=7
,n_jobs=12
)
cv=KFold(n_splits=5,shuffle=True,random_state=7)
validate_loss=cross_validate(model,X,y
,cv=cv
,scoring='neg_root_mean_squared_error'
,n_jobs=12
,error_score='raise'
)
return np.mean(abs(validate_loss['test_score']))
# 定义优化过程
def optimizer_optuna(n_trials,algo):
if algo=='TPE':
algo=optuna.samplers.TPESampler(n_strarup_trials=10,n_ei_candidates=24)
elif algo=='GP':
from optuna.integration import SkoptSampler
import skopt
algo=SkoptSampler(skopt_kwargs={'base_estimator':'GP'
,'n_initial_points':10
,'acq_func':'EI'
}
)
study=optuna.create_study(sampler=algo # 定义样本抽样的算法
,direction='minimize' # 定义目标函数优化方向是最大值,还是最小值
)
study.optimize(optuna_objective # 目标函数
,n_trials=n_trials # 设定最大迭代次数(包括最初观测值)
,show_progress_bar=True # 要不要展示进度条
)
print('best parmas:',study.best_trial.params,
'\n','best score:',study.best_trial.values)
return study.best_trial.params,study.best_trial.values
# 执行流程
import warnings
warnings.filterwarnings('ignore',message='The objective has been evaluated at this point before.')
best_params,best_score=optimizer_optuna(10,'GP') # 小迭代次数代码测试
optuna.logging.set_verbosity(optuna.logging.ERROR) # 关闭打印迭代过程
best_params,best_score=optimizer_optuna(300,'GP')