前言
网格调参(Grid Search)是一种通过在预定义的参数组合中搜索最佳模型超参数的方法,在进行网格调参时,首先为学习算法 定义一个超参数空间,然后在这个空间内进行搜索,以确定超参数的“最佳”取值,在这方面:
- 对于连续值超参数,基于先前的经验/知识/计算能力和资源,假设并离散化一个范围
- 对于离散/分类超参数,假设一个范围
- 将离散超参数搜索空间定义为每个超参数定义的值集的笛卡尔(Carte-sian)乘积
- 对于其中的每个点,估计相应模型的性能,并选择导致最佳性能的超参数值
上述过程可以扩展到多个学习算法,以确定学习算法-超参数值的最佳组合
使用验证集的网格搜索:在以往的数据处理中,通常将给定的样本分为训练集和测试集,分别用于训练和评估估算器。为了考虑模型选择阶段,一种常见的策略是将数据分为三个集合,分别称为训练集、验证集和测试集。额外的集合,即验证集,然后用于模型选择。在这方面,可以首先将整个数据分为两个较小的集合,例如,分别占给定样本的75%和25%的大小,并使用较小的集合作为测试集。然后,较大的集合(即75%大小的集合)被分为“训练集”和“验证集”(可以再次使用相同的比例,即75%和25%来生成训练和验证集)
Scikit-learn中的网格搜索模型选择实现:要实现使用交叉验证的网格搜索,可以编写一个代码,遍历搜索空间,并使用cross_val_score计算网格中每个点的得分。然而,scikit-learn有一种方便的方式来实现网格搜索,可以使用sklearn.model_selection模块中的GridSearchCV类。该类的一些重要参数包括:
- estimator: 这是来自估计器类的对象
- param_grid: 这可以是一个字典来定义一个网格,也可以是一个字典列表来定义多个网格。无论哪种方式,字典中的键应该是在模型选择中用于的估计器的参数。在搜索中使用的每个键的可能值都作为该键的值的值列表传递
- cv: 确定交叉验证策略(类似于cross_val_score中使用的相同参数)
- scoring: 在最常见的用例中,只传递评分函数的名称(一个字符串),也可以通过字符串列表或返回字典的可调用函数使用多个指标,如果在这里使用了多个指标,那么refit方法应该设置为用于找到训练最终估算器的最佳超参数值的指标
- refit: 默认情况下,该参数为True,意味着一旦确定了所有得分,它将使用找到的最佳超参数组合(导致最高的交叉验证分数)在整个数据集上训练(“refit”)估算器。如果在评分中使用了多个指标,该参数应该是一个字符串,用于标识用于重新拟合的指标
一旦创建了GridSearchCV类的实例,可以将其视为常规的分类器/回归器,因为它实现了fit、predict、score等多种方法。在这里,fit方法为每个给定的超参数组合训练一个估算器。predict和score方法可以分别用于使用找到的最佳估算器进行预测和评估。然而,GridSearchCV类的对象还具有一些重要的属性,例如:
- cv_results_: 这是一个字典,包含每个超参数组合在每个折叠上获得的分数,以及关于训练的其他一些信息(例如,训练时间)
- best_params_: 导致最高分数的超参数组合
- best_score_: 在给定网格上获得的最高分数
- best_estimator_: 如果refit=False,则不可用;否则,此属性提供对使用最佳超参数组合在整个数据集上训练的估算器的访问
案例
k折交叉验证下的网格调参
使用鸢尾花分类数据集,且使用GridSearchCV类来调整逻辑回归的超参数,在交叉验证期间监测准确率和加权ROC AUC,最后使用准确率进行超参数调整。一旦完成超参数调整,还会找到在整个训练数据上使用超参数导致CV准确性最高的超参数组合训练的最佳模型的测试准确性(在测试数据上的准确性)。至于交叉验证,使用分层的3折交叉验证,在拆分之前对数据进行洗牌
import pandas as pd
from sklearn import datasets
from sklearn.linear_model import LogisticRegression as LRR
from sklearn.model_selection import GridSearchCV, StratifiedKFold, train_test_split
iris = datasets.load_iris()
X_train = iris.data
y_train = iris.target
X_train, X_test, y_train, y_test= train_test_split(iris.data, iris.target, random_state=100, test_size=0.2, stratify=iris.target)
print('X_train_shape: ' + str(X_train.shape) + '\nX_test_shape: ' + str(X_test.shape)+ '\ny_train_shape: ' + str(y_train.shape) + '\ny_test_shape: ' + str(y_test.shape) + '\n')
# 创建 Logistic Regression 模型对象
lrr = LRR(max_iter=2000)
# 定义参数网格
grids = [
{'penalty': ['l2'], 'C': [0.01, 0.1]}, # l2正则化的参数组合
{'penalty': ['elasticnet'], 'C': [0.01, 0.1], 'l1_ratio': [0.2, 0.8], 'solver': ['saga']} # elasticnet正则化的参数组合
]
# 创建 StratifiedKFold 交叉验证对象
strkfold = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)
# 创建 GridSearchCV 对象
# n_jobs=-1 表示使用所有可用的CPU核心进行并行计算
# scoring 指定了评估指标,包括 accuracy 和 roc_auc_ovr_weighted
# refit='accuracy' 表示在搜索完成后,使用 accuracy 作为主要评价指标来重新拟合最佳模型
gscv = GridSearchCV(lrr, grids, cv=strkfold, n_jobs=-1, scoring=['accuracy', 'roc_auc_ovr_weighted'], refit='accuracy')
# 在训练数据上进行网格搜索和交叉验证,找到最佳模型
gscv.fit(X_train, y_train)
# 在测试数据上评估最佳模型的性能
score_best_estimator = gscv.score(X_test, y_test)
# 打印最佳模型的交叉验证最高得分、最佳超参数组合和测试数据上的准确性
print('the highest score is: {:.3f}'.format(gscv.best_score_))
print('the best hyperparameter combination is: {}'.format(gscv.best_params_))
print('the accuracy of the best estimator on the test data is: {:.3f}'.format(score_best_estimator))
# 将 GridSearchCV 的结果转为 DataFrame 方便查看
df = pd.DataFrame(gscv.cv_results_)
df
使用逻辑回归模型进行网格搜索调参,并利用交叉验证来评估模型性能。参数网格中包含了不同的正则化方式、正则化强度、以及 elasticnet 的相关参数。在搜索完成后,输出了交叉验证的最高得分、最佳超参数组合以及在测试数据上的准确性。最后,将 GridSearchCV 的结果转为 DataFrame 进行查看
验证集下的网格调参
使用验证集实现网格搜索,可以首先使用sklearn.model_selection中的train_test_split函数设置测试集(如果需要的话),然后再次使用它来设置验证集。然后,可以使用一些交叉验证生成器(例如sklearn.model_selection中的PredefinedSplit类)创建训练集和验证集的索引,并将其用作GridSearchCV的cv参数,该参数接受生成器。要使用PredefinedSplit,可以将验证集的索引设置为一个整数,如0,但应将训练集的索引设置为-1以排除它们不包含在验证集中
import pandas as pd
from sklearn import datasets
from sklearn.linear_model import LogisticRegression as LRR
from sklearn.model_selection import GridSearchCV, train_test_split, PredefinedSplit
import numpy as np
# 加载鸢尾花数据集
iris = datasets.load_iris()
# 将数据集划分为训练集和测试集
X, X_test, y, y_test = train_test_split(iris.data, iris.target, random_state=100, stratify=iris.target)
# 生成训练集的索引
indecis = np.arange(len(X))
# 将训练集进一步划分为训练集和验证集
X_train, X_val, y_train, y_val, indecis_train, indecis_val = train_test_split(X, y, indecis, random_state=100, stratify=y)
# 创建 PredefinedSplit 对象,用于指定训练集和验证集
ps_ind = np.zeros(len(indecis), dtype='int_')
ps_ind[indecis_train] = -1 # 将训练集的索引设置为 -1,验证集的索引保持为 0
print('indices used in CV splitter:\n', ps_ind)
pds = PredefinedSplit(ps_ind)
print('the split used as training and validation sets:\n', *pds.split())
# 创建 Logistic Regression 模型对象
lrr = LRR(max_iter=2000)
# 定义参数网格
grids = [
{'penalty': ['l2'], 'C': [0.01, 0.1]}, # l2正则化的参数组合
{'penalty': ['elasticnet'], 'C': [0.01, 0.1], 'l1_ratio': [0.2, 0.8], 'solver': ['saga']} # elasticnet正则化的参数组合
]
# 创建 GridSearchCV 对象
# cv=pds 指定了使用预定义的训练集和验证集划分进行交叉验证
gscv = GridSearchCV(lrr, grids, cv=pds, n_jobs=-1, scoring=['accuracy', 'roc_auc_ovr_weighted'], refit='accuracy')
# 在整个训练数据上进行网格搜索和交叉验证,找到最佳模型
gscv.fit(X, y) # 注意这里是在整个训练数据上进行训练,而不是仅在训练集上
# 在测试数据上评估最佳模型的性能
score_best_estimator = gscv.score(X_test, y_test)
# 打印最佳模型的交叉验证最高得分、最佳超参数组合和测试数据上的准确性
print('the highest score is: {:.3f}'.format(gscv.best_score_))
print('the best hyperparameter combination is: {}'.format(gscv.best_params_))
print('the accuracy of the best estimator on the test data is: {:.3f}'.format(score_best_estimator))
# 将 GridSearchCV 的结果转为 DataFrame 方便查看
df = pd.DataFrame(gscv.cv_results_)
df
使用预定义的训练集和验证集划分进行网格搜索调参,并在测试集上评估最佳模型的性能
import matplotlib.pyplot as plt
import seaborn as sns
# 提取不同参数组合下的评分数据
param_values = df['param_penalty'] + '_' + df['param_C'].astype(str)
scores_accuracy = df['mean_test_accuracy']
scores_roc_auc = df['mean_test_roc_auc_ovr_weighted']
# 创建两个子图,分别绘制 accuracy 和 roc_auc 的变化
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# 绘制 accuracy 的变化
sns.barplot(x=param_values, y=scores_accuracy, ax=ax1)
ax1.set_title('Grid Search Scores (Accuracy)')
ax1.set_xticklabels(ax1.get_xticklabels(), rotation=45, ha='right')
# 绘制 roc_auc 的变化
sns.barplot(x=param_values, y=scores_roc_auc, ax=ax2)
ax2.set_title('Grid Search Scores (ROC AUC)')
ax2.set_xticklabels(ax2.get_xticklabels(), rotation=45, ha='right')
# 调整子图之间的间距
plt.tight_layout()
# 显示图形
plt.show()
利用可视化工具来查看不同超参数组合下模型性能的变化
如果你对类似于这样的文章感兴趣。
欢迎关注、点赞、转发~