背景
在机器学习调参过程中,Optuna是一个强大的自动化超参数优化工具,它通过智能搜索算法在定义的参数空间中寻找最佳参数组合,从而提升模型性能。Optuna-dashboard虽然提供了一定的可视化功能,让用户能够观察到调参过程的变化,但其局限性也较为明显:
- 数据无法导出:Optuna-dashboard上的数据仅供简单查看,无法直接导出进行进一步的分析和记录
- 图表展示单一:默认的展示效果和配色较为有限,难以满足个性化展示的需求,尤其在需要深度分析超参数与目标值(object value)或超参数与试验次数关系时显得不够灵活
因此,为了更好地分析调参过程,本文选择使用自定义的可视化方式,通过清晰、灵活的图表呈现调参效果。通过自定义绘制,可以深入探索不同超参数组合对模型表现的影响,从而更有效地优化和展示模型调参结果,这种自定义可视化不仅能够在调参过程中更好地理解模型,还能生成直观、美观的图表,适用于展示和分析,当然还不了解Optuna的读者建议先阅读文章——可视化自动调参工具Optuna在机器学习中的应用:以 XGBoost 回归模型为例
代码实现
基于Optuna的自动化XGBoost超参数优化
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore")
plt.rcParams['font.family'] = 'Times New Roman'
plt.rcParams['axes.unicode_minus'] = False
df = pd.read_excel('2024-11-10-公众号Python机器学习AI.xlsx')
from sklearn.model_selection import train_test_split
X = df.drop(['待预测变量Y'],axis=1)
y = df['待预测变量Y']
# 划分训练集和测试集
X_temp, X_test, y_temp, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 然后将训练集进一步划分为训练集和验证集
X_train, X_val, y_train, y_val = train_test_split(X_temp, y_temp, test_size=0.125, random_state=42) # 0.125 x 0.8 = 0.1
import optuna # 导入Optuna库,用于超参数优化
from xgboost import XGBRegressor
from sklearn.metrics import mean_squared_error
from optuna.visualization import plot_optimization_history, plot_contour, plot_param_importances, plot_parallel_coordinate, plot_edf # 导入Optuna的可视化工具,用于绘制优化历史、参数重要性等
# 定义目标函数,用于Optuna的优化
def objective(trial):
# 定义模型的超参数搜索空间,Optuna会在此范围内进行参数采样
params = {
# 'n_estimators':模型中要构建的决策树的数量
# 值越高,模型拟合越充分,但计算开销也更大。一般较低的值适合快速迭代和避免过拟合
'n_estimators': trial.suggest_categorical('n_estimators', [50, 100, 200, 300]), #离散的类别选择范围
# 'max_depth':控制每棵树的深度,以防止过拟合。较高的深度会增加模型复杂性
'max_depth': trial.suggest_int('max_depth', 3, 15, step=1), #整数范围
# 'learning_rate':学习率决定每棵树对最终预测的贡献。较小的学习率通常需要更大的'n_estimators'来充分学习
'learning_rate': trial.suggest_loguniform('learning_rate', 0.01, 0.3), # 对数均匀分布的连续浮点数范围
# 'subsample':表示每棵树随机选择的样本比例,通常用于减少过拟合
'subsample': trial.suggest_uniform('subsample', 0.5, 1.0), # 线性均匀分布的连续浮点数范围
# 'colsample_bytree':控制每棵树使用的特征比例,以增强模型的鲁棒性
'colsample_bytree': trial.suggest_uniform('colsample_bytree', 0.5, 1.0),
# 'gamma':控制是否允许分裂,越高的值会使算法更保守,帮助避免过拟合
'gamma': trial.suggest_uniform('gamma', 0, 5)
}
# 使用采样的参数创建XGBRegressor模型
model = XGBRegressor(**params, random_state=42)
# 使用训练数据拟合模型
model.fit(X_train, y_train)
# 使用验证集进行预测
y_pred = model.predict(X_val)
# 计算并返回均方误差(MSE),作为优化的目标
return mean_squared_error(y_val, y_pred)
# 创建Optuna的Study对象,并设置优化方向为“minimize”表示最小化均方误差
# 默认情况下,Optuna使用的优化算法是 TPE (Tree-structured Parzen Estimator),
# 这是一种贝叶斯优化方法,适合高效地探索超参数空间
study = optuna.create_study(direction="minimize")
# 运行优化,进行100次试验
study.optimize(objective, n_trials=100)
print("Best parameters:", study.best_params)
# 使用最佳参数重新训练模型
best_model = XGBRegressor(**study.best_params, random_state=42)
best_model.fit(X_train, y_train)
使用Optuna库对XGBoost回归模型进行自动化超参数优化。首先,代码将数据集划分为训练集、验证集和测试集,然后定义目标函数(objective)来指定要优化的超参数范围,包括n_estimators、max_depth、learning_rate、subsample、colsample_bytree和gamma。Optuna在此范围内自动采样超参数组合,通过最小化均方误差(MSE)来找到最优参数,运行100次试验后,代码输出最优参数并用其重新训练XGBoost模型,从而提高模型性能
记录与可视化调参优化历史
# 获取优化历史中的数据,每列的含义如下:
# 'trial_number': 每次试验的编号,用于区分不同的试验
# 'value': 每次试验的目标值(优化过程中的结果),通常是需要最小化或最大化的目标值
# 'datetime_start': 每次试验的开始时间,用于记录试验的开始时间点
# 'datetime_complete': 每次试验的完成时间,用于记录试验的结束时间点
optimization_history = {
'trial_number': [], # 试验编号
'value': [], # 目标值
'datetime_start': [], # 试验开始时间
'datetime_complete': [] # 试验完成时间
}
for trial in study.trials:
optimization_history['trial_number'].append(trial.number)
optimization_history['value'].append(trial.value)
optimization_history['datetime_start'].append(trial.datetime_start)
optimization_history['datetime_complete'].append(trial.datetime_complete)
# 将数据转换为 DataFrame
optimization_history = pd.DataFrame(optimization_history)
optimization_history
从Optuna的study对象中提取每次试验的编号、目标值、开始和结束时间,存入字典并转换为DataFrame,以便查看和分析优化历史
# 找到最优试验
best_trial_idx = optimization_history['value'].idxmin()
best_trial_number = optimization_history['trial_number'][best_trial_idx]
best_value = optimization_history['value'][best_trial_idx]
plt.figure(figsize=(10, 6), dpi=1200)
# 绘制非最佳点
plt.plot(
optimization_history['trial_number'],
optimization_history['value'],
marker='o',
linestyle='-',
color='skyblue',
alpha=0.4,
label='All Trials'
)
# 绘制最佳点
plt.plot(
best_trial_number,
best_value,
marker='*',
color='darkblue',
markersize=15,
label=f'Best Trial (#{best_trial_number})'
)
plt.title("Optimization History")
plt.xlabel("Trial Number")
plt.ylabel("MSE")
plt.grid(True)
plt.legend(loc='upper right', bbox_to_anchor=(1, 1), frameon=True)
plt.savefig('1.pdf', format='pdf', bbox_inches='tight')
plt.show()
从优化历史中找到具有最低目标值(MSE)的最佳试验,并提取其编号和对应的MSE值。然后,绘制优化历史折线图,其中所有试验结果以点的形式呈现,最佳试验点以星号标出,使得用户能够清晰地看到整个调参过程中的性能变化
超参数重要性分析与可视化
from optuna.importance import get_param_importances
# 获取超参数的重要性数据
param_importances = get_param_importances(study)
# 将数据转换为 DataFrame
Hyperparameter_Importances = pd.DataFrame(list(param_importances.items()), columns=['Parameter', 'Importance'])
Hyperparameter_Importances
使用Optuna的get_param_importances函数来计算超参数对优化目标的重要性,并将其结果转换为DataFrame便于查看和分析
# 对参数按重要性进行排序,便于图表的展示
Hyperparameter_Importances = Hyperparameter_Importances.sort_values(by='Importance', ascending=False)
# 绘制超参数重要性条形图
plt.figure(figsize=(10, 6), dpi=1200)
bars = plt.barh(Hyperparameter_Importances['Parameter'], Hyperparameter_Importances['Importance'], color='skyblue')
plt.gca().invert_yaxis() # 反转Y轴,使重要性最高的参数在顶部
# 为每个条形图显示数值
for bar, importance in zip(bars, Hyperparameter_Importances['Importance']):
plt.text(bar.get_width() + 0.01, bar.get_y() + bar.get_height() / 2,
f'{importance:.3f}', va='center')
plt.title("Hyperparameter Importances")
plt.xlabel("Importance")
plt.ylabel("Parameter")
# 设置x轴范围
plt.xlim(0, max(Hyperparameter_Importances['Importance']) * 1.1)
plt.savefig('2.pdf', format='pdf', bbox_inches='tight')
plt.show()
对超参数的重要性数据进行排序,然后使用水平条形图展示每个超参数对模型性能的重要性,确保最重要的参数位于顶部。代码还在每个条形上标出数值,提供了各参数对结果影响的精确度量,结果图显示了gamma和learning_rate是对模型性能影响最大的两个超参数,而max_depth和subsample的影响较小。通过这种可视化,用户可以更好地理解哪些超参数最值得关注和优化
多维参数组合与目标值的可视化分析
# 提取每次试验的参数组合和目标值
data = {
'trial_number': [],
'value': []
}
# 提取所有参数名并初始化列
for param_name in study.trials[0].params.keys():
data[param_name] = []
# 遍历所有试验,提取参数和目标值
for trial in study.trials:
data['trial_number'].append(trial.number)
data['value'].append(trial.value)
# 填充每个参数的值,如果该试验没有设置某个参数,则填充为 None
for param_name in data.keys():
if param_name not in ['trial_number', 'value']:
data[param_name].append(trial.params.get(param_name, None))
# 将数据转换为 DataFrame
Parameter_Combinations = pd.DataFrame(data)
Parameter_Combinations
从每次试验中提取超参数组合和目标值,将其整理成DataFrame,以便后续分析不同参数组合对目标值的影响
gamma和learning_rate对目标值的2D可视化分析
from mpl_toolkits.mplot3d import Axes3D
Parameter_Combinations['gamma'] = Parameter_Combinations['gamma'].astype(float)
Parameter_Combinations['learning_rate'] = Parameter_Combinations['learning_rate'].astype(float)
plt.figure(figsize=(10, 8))
# 绘制散点图,使用 'value' 进行颜色编码
sc = plt.scatter(
Parameter_Combinations['gamma'],
Parameter_Combinations['learning_rate'],
c=Parameter_Combinations['value'],
cmap='viridis',
s=50, # 设置散点大小
alpha=0.7 # 设置透明度
)
cbar = plt.colorbar(sc, format='%.3f')
cbar.set_label('Objective Value')
plt.xlabel("gamma")
plt.ylabel("learning_rate")
plt.title("2D Plot of gamma and learning_rate vs Objective Value")
plt.savefig('3.pdf', format='pdf', bbox_inches='tight')
plt.show()
绘制gamma和learning_rate两个超参数对目标值(Objective Value)的影响【排名影响前二 参数】,通过散点图展示每组参数组合的效果,并使用颜色编码表示目标值大小。结果显示了不同参数组合下的目标值分布,颜色越浅表示目标值越低,从而帮助识别表现较优的参数区域
colsample_bytree、gamma和learning_rate对目标值的3D可视化分析
绘制colsample_bytree、gamma和learning_rate三个超参数组合对目标值(Objective MSE)的三维散点图【排名影响前三参数】,使用颜色编码目标值大小。结果图展示了不同参数组合的目标值分布,颜色越浅表示目标值越低,便于识别在多维参数空间中表现最佳的区域,代码与数据集获取:如需获取本文的源代码和数据集,请添加作者微信联系
🎁 赠书活动来啦! 🎁
支持知识分享,畅享学习乐趣!特别感谢清华出版社 对本次赠书活动的鼎力支持!即日起,只需
点赞、在看、转发 此文章,作者将从后台随机抽取一位幸运儿,免费包邮赠送清华出版社提供的《MATLAB科技绘图与数据分析》这本精彩书籍📚!
💡 赶快参与,一键三连,说不定你就是那位幸运读者哦!
往期推荐
复现SCI文章 SHAP 依赖图可视化以增强机器学习模型的可解释性
复现 Nature 图表——基于PCA的高维数据降维与可视化实践及其扩展
复现Nature图表——基于PCA降维与模型预测概率的分类效果可视化
SCI图表复现:如何直观展示机器学习模型预测结果的准确性和一致性
如果你对类似于这样的文章感兴趣。
欢迎关注、点赞、转发~
个人观点,仅供参考