超参数调整专题1
知识点回顾
- 网格搜索
- 随机搜索(简单介绍,非重点 实战中很少用到,可以不了解)
- 贝叶斯优化
- time库的计时模块,方便后人查看代码运行时长,用于记录时间
今日作业:
对于信贷数据的其他模型,如LightGBM和KNN尝试用下贝叶斯优化和网格搜索
昨天的内容只是对机器学习的几个模型进行不调参的实例化训练预测,今天开始调参。对于调参一般要划分两次数据集,即训练集、验证集、测试集,因为调参过程中需要评估两次:
- 第一次评估:用验证集评估不同超参数组合的表现
- 第二次评估:用测试集最终评估选定模型的表现
如果不做交叉验证,就需要划分验证集和测试集,但是很多调参方法中都默认有交叉验证,所以实际中可以省去划分验证集和测试集的步骤
二次划分数据集与交叉验证
直接上划分两次数据集的代码,调用两次 train_test_split() 函数即可,最终划分为8:1:1
from sklearn.model_selection import train_test_split
X = data.drop(['Credit Default'], axis=1) # 特征,axis=1表示按列删除
y = data['Credit Default'] # 标签
# 按照8:1:1划分训练集、验证集和测试集
X_train, X_temp, y_train, y_temp = train_test_split(X, y, test_size=0.2, random_state=42) # 80%训练集,20%临时集
X_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp, test_size=0.5, random_state=42) # 50%验证集,50%测试集
print("X_train:", X_train.shape)
print("y_train:", y_train.shape)
print("X_val:", X_val.shape)
print("y_val:", y_val.shape)
print("X_test:", X_test.shape)
print("y_test:", y_test.shape)
打印一下数据集规模看看划分比例正不正确
X_train: (6000, 31)
y_train: (6000,)
X_val: (750, 31)
y_val: (750,)
X_test: (750, 31)
y_test: (750,)
最开始也说了很多调参函数自带交叉验证,甚至是必选的参数,如果想要不交叉反而实现起来会麻烦很多,所以仍旧只需要划分一次数据集就好了,参照昨天内容
调参方法
正常情况下,计算资源够用网格,计算资源不够用贝叶斯优化,随机搜索没什么人用
一. 网格搜索 (GridSearchCV):------可以认为是遍历所有设定的参数组合
- 需要定义参数的网格(param_grid),包含所有你想要尝试的特定值的列表。它会尝试网格中所有可能的参数组合。
- 缺点: 计算成本非常高,参数和值的数量稍多,组合数就会呈指数级增长(维度灾难)。因此,网格通常设置得比较小或集中在认为最优参数可能存在的区域(可能基于随机搜索的初步结果)。
from sklearn.model_selection import GridSearchCV # 网格搜索
from sklearn.ensemble import RandomForestClassifier # 随机森林分类器
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 用于评估分类器性能的指标
from sklearn.metrics import classification_report, confusion_matrix #用于生成分类报告和混淆矩阵
import time
import warnings #用于忽略警告信息
warnings.filterwarnings("ignore") # 忽略所有警告信息
# 定义要搜索的参数网格
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [None, 10, 20, 30],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4]
}
# 创建网格搜索对象
grid_search = GridSearchCV(estimator=RandomForestClassifier(random_state=42), # 随机森林分类器
param_grid=param_grid, # 参数网格
cv=5, # 5折交叉验证,将训练集分成5份,4份训练,1份验证,轮流5次
n_jobs=-1, # 使用所有可用的CPU核心进行并行计算,减少调参时间
scoring='accuracy') # 使用准确率作为评分标准
start_time = time.time() # 记录开始时间
# 在训练集上进行网格搜索
grid_search.fit(X_train, y_train) # 在训练集上训练,模型实例化和训练的方法都被封装在这个网格搜索对象里了
end_time = time.time() # 记录结束时间
print(f"网格搜索耗时: {end_time - start_time:.4f} 秒")
print("最佳参数: ", grid_search.best_params_) #best_params_属性返回最佳参数组合
# 使用最佳参数的模型进行预测
best_model = grid_search.best_estimator_ # 获取最佳模型
best_pred = best_model.predict(X_test) # 在测试集上进行预测
print("\n网格搜索优化后的随机森林 在测试集上的分类报告:")
print(classification_report(y_test, best_pred))
print("网格搜索优化后的随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, best_pred))
要点:1.参数网格为字典格式,迄今为止学到用字典的除了这个还有标签编码里的映射关系
2.执行网格搜索时,模型的实例化和训练都被封装在这个网格搜索对象里面了,通过这个对象的 best_estimator_ 属性可直接获取训练好的最优模型,无需重新训练但要预测
3.网格搜索只用训练集(X_train)进行调参,此时交叉验证再自动划分出验证集,测试集(X_test)仅用于最终评估,搞清楚什么时候用什么数据集
二. 随机搜索 (RandomizedSearchCV):
- 需要定义参数的分布,而不是固定的列表。这是它与网格搜索的主要区别,它不会尝试所有组合,而是在指定次数内随机采样。通常,用相对较少的迭代次数(如 50-100)就能找到相当好的参数。
- 对于给定的计算预算,随机搜索通常比网格搜索更有效,尤其是在高维参数空间中。
三. 贝叶斯优化 (BayesSearchCV from skopt):
- 需要定义参数的搜索空间,与随机搜索类似,当搜索空间非常大时,它通常比网格搜索和随机搜索更有效。
- 核心优势: 它不是随机选择下一个点,而是根据先前评估的结果建立一个概率模型(通常是高斯过程),预测哪些参数组合可能产生更好的结果,并据此选择下一个评估点。这使得它在寻找最优解方面通常比随机搜索更高效(用更少的迭代次数达到相似或更好的性能),特别是当模型训练(单次评估)非常耗时的时候。
from skopt import BayesSearchCV
from skopt.space import Integer # 定义整数型参数空间(如 n_estimators 的取值范围)
from sklearn.ensemble import RandomForestClassifier # 随机森林分类器
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score # 用于评估分类器性能的指标
from sklearn.metrics import classification_report, confusion_matrix #用于生成分类报告和混淆矩阵
import time
import warnings #用于忽略警告信息
warnings.filterwarnings("ignore") # 忽略所有警告信息
# 定义要搜索的参数空间
search_space = {
'n_estimators': Integer(50, 200),
'max_depth': Integer(10, 30),
'min_samples_split': Integer(2, 10),
'min_samples_leaf': Integer(1, 4)
}
# 创建贝叶斯优化搜索对象
bayes_search = BayesSearchCV(
estimator=RandomForestClassifier(random_state=42),
search_spaces=search_space,
n_iter=32, # 迭代次数,值越大效果越好但耗时越长,可根据需要调整
cv=5, # 5折交叉验证,这个参数是必须的,不能设置为1,否则就是在训练集上做预测了
n_jobs=-1,
scoring='accuracy'
)
start_time = time.time()
# 在训练集上进行贝叶斯优化搜索
bayes_search.fit(X_train, y_train)
end_time = time.time()
print(f"贝叶斯优化耗时: {end_time - start_time:.4f} 秒")
print("最佳参数: ", bayes_search.best_params_)
# 使用最佳参数的模型进行预测
best_model = bayes_search.best_estimator_
best_pred = best_model.predict(X_test)
print("\n贝叶斯优化后的随机森林 在测试集上的分类报告:")
print(classification_report(y_test, best_pred))
print("贝叶斯优化后的随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, best_pred))
要点:1.贝叶斯优化不是 sklearn 的原生功能,而是由第三方库 scikit-optimize(简称 skopt)提供的,导入库的时候注意一下
2.和上面网格搜索的参数空间相比,贝叶斯优化需要参数分布范围,而非网格搜索那样的固定值列表,Integer() 表示在范围内连续采样整数,贝叶斯优化会动态调整采样点
收获心得:
其实贝叶斯优化还有其他写法,但这里的代码与网格搜索的写法比较一致,方便对比看。其次每个模型超参数都挺多的,不用学习具体原理,会调参就行,比如先问大模型给出一个比较合理的调参范围,自己再根据结果手动优化。