贝叶斯优化器
贝叶斯优化是一种用于全局优化的概率建模和数学优化技术。它的目标是在有限的迭代次数内找到一个未知目标函数的全局最优解。这一想法应用于超参数调整,就产生了贝叶斯优化器,它综合了数学建模、概率论和优化算法,成为一种强大的工具,特别适用于机器学习调参这类需要在参数空间中寻找最优解的问题。
相较于传统网格搜索,贝叶斯优化的优点在于其智能的参数探索策略,通过建立代理模型和概率模型,在有限的试验次数内更聪明地选择下一个尝试的参数组合,以更高效地逼近全局最优解。
对于机器学习工程师来说,贝叶斯优化器可以让调参过程变得轻松且高效:只需要设定好超参数搜索范围和迭代轮数,剩下的就可以交给优化器了。当然,若想更进一步提高模型性能,还是需要在贝叶斯优化器的基础上手动微调。
通过Optuna实现贝叶斯优化
Optuna(Optimization Tuning)是一个用于超参数优化的开源Python库,主要目标是提供一个灵活、轻量级且易于使用的工具,帮助机器学习工程师和研究人员在训练模型时有效地搜索最佳的超参数组合。
1. 定义超参数搜索空间
'''
以LightGBM模型为例
param_ranges表示需要搜索的超参数及其搜索范围
params为不需要优化的超参数
'''
param_ranges = {
'learning_rate' : (0.03, 0.2),
'lambda_l1' : (3, 6),
'lambda_l2' : (1, 5),
'max_depth' : (15, 35),
}
params = {
'random_state' : 6743,
'objective' : 'regression_l1',
'metric' : 'rmse',
'boosting_type' : 'gbdt',
'verbose' : -1,
'colsample_bytree' : 0.9,
'colsample_bynode' : 0.85,
'n_iter' : 2800,
"num_leaves" : 510,
"min_data_in_leaf" : 51,
'early_stopping_rounds': 50,
}
2. 解包超参数,创建模型
def get_model(trial):
'''
trial参数是从objective函数传递进来的,不可忽略
params是不需要搜索的参数,因此直接赋值给local_params
param_ranges的需要先经trial处理再赋值给local_params,是算法最核心的搜索过程
trial.suggest_uniform()表示此参数可以为浮点数,例如学习率
trial.suggest_int()表示此参数只能为整数,例如最大树深度
最后,被赋值给模型的参数包含了:不需要调整的参数值+经trial处理后被认为处于优化方向上的参数值
'''
local_params = params.copy()
local_params['learning_rate'] = trial.suggest_uniform('learning_rate', *param_ranges['learning_rate'])
local_params['lambda_l1'] = trial.suggest_uniform('lambda_l1', *param_ranges['lambda_l1'])
local_params['lambda_l2'] = trial.suggest_uniform('lambda_l2', *param_ranges['lambda_l2'])
local_params['max_depth'] = trial.suggest_int('max_depth', *param_ranges['max_depth'])
model=lgb.LGBMRegressor(**local_params)
return model
3. 定义objective函数(核心)
def objective(trial):
'''
objective函数是优化的核心,其返回值决定了优化目标,这里的返回值是测试集的平均预测误差MAE
此处建议把MAE_on_trian、MAE_on_test、adjusted_params编写为训练日志输出,
便于之后观察参数变化与目标值之间的关系,明确调优的方向
'''
# 获取模型
model = get_model(trial)
# 拟合模型
model.fit(X_train,y_train)
# 记录本轮训练的损失,MAE_on_test需要作为优化目标return回study
preds_on_train = model.predict(X_train)
preds_on_test = model.predict(X_test)
_,MAE_on_trian,_ = MAE(preds_on_train,y_train)
_,MAE_on_test,_ = MAE(preds_on_test,y_test)
# 此处可以通过trial.params.items()方法获取到当前正在被执行的超参数,可以用于编写训练日志
adjusted_params = {}
for key, value in trial.params.items():
adjusted_params[key] = value
return MAE_on_test
4. 创建一个study,开始优化
import optuna
'''
定义优化方向,即objective函数的返回值。
一般交叉熵和均方误差的方向都是minimize
N_TRIALS=50代表一共进行50次搜索
'''
N_TRIALS = 50
study = optuna.create_study(direction='minimize')
study.optimize(objective, n_trials=N_TRIALS)
# 至此50轮训练结束,打印一共进行了几轮、第几轮为最优轮
print('Number of finished trials: ', len(study.trials))
print('Best trial:')
trial = study.best_trial
# 输出最优轮对应的超参数
print('Value: ', trial.value)
print('Params: ')
for key, value in trial.params.items():
print(f' {key}: {value}')