目录
超参数调整专题
知识点回顾
- 网格搜索
- 随机搜索(简单介绍,非重点 实战中很少用到,可以不了解)
- 贝叶斯优化(2种实现逻辑,以及如何避开必须用交叉验证的问题)
- time库的计时模块,方便后人查看代码运行时长
-
模型 = 算法 + 实例化设置的外参(超参数)+训练得到的内参
-
只要调参就需要考2次
所以如果不做交叉验证,就需要划分验证集和测试集,但是很多调参方法中都默认有交叉验证,所以实际中可以省去划分验证集和测试集的步骤
每个模型都有自己的超参数,每个超参数都有一定的意义。但是如果为了精度和科研,我们完全无需学习只需要用好调参工具即可。
一、数据预处理
首先运行之前说过的预处理的代码
import pandas as pd #用于数据处理和分析,可处理表格数据。
import numpy as np #用于数值计算,提供了高效的数组操作。
import matplotlib.pyplot as plt #用于绘制各种类型的图表
import seaborn as sns #基于matplotlib的高级绘图库,能绘制更美观的统计图形。
# 设置中文字体(解决中文显示问题)
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统常用黑体字体
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
data = pd.read_csv('data.csv') #读取数据
# 先筛选字符串变量
discrete_features = data.select_dtypes(include=['object']).columns.tolist()
# Home Ownership 标签编码
home_ownership_mapping = {
'Own Home': 1,
'Rent': 2,
'Have Mortgage': 3,
'Home Mortgage': 4
}
data['Home Ownership'] = data['Home Ownership'].map(home_ownership_mapping)
# Years in current job 标签编码
years_in_job_mapping = {
'< 1 year': 1,
'1 year': 2,
'2 years': 3,
'3 years': 4,
'4 years': 5,
'5 years': 6,
'6 years': 7,
'7 years': 8,
'8 years': 9,
'9 years': 10,
'10+ years': 11
}
data['Years in current job'] = data['Years in current job'].map(years_in_job_mapping)
# Purpose 独热编码,记得需要将bool类型转换为数值
data = pd.get_dummies(data, columns=['Purpose'])
data2 = pd.read_csv("data.csv") # 重新读取数据,用来做列名对比
list_final = [] # 新建一个空列表,用于存放独热编码后新增的特征名
for i in data.columns:
if i not in data2.columns:
list_final.append(i) # 这里打印出来的就是独热编码后的特征名
for i in list_final:
data[i] = data[i].astype(int) # 这里的i就是独热编码后的特征名
# Term 0 - 1 映射
term_mapping = {
'Short Term': 0,
'Long Term': 1
}
data['Term'] = data['Term'].map(term_mapping)
data.rename(columns={'Term': 'Long Term'}, inplace=True) # 重命名列
continuous_features = data.select_dtypes(include=['int64', 'float64']).columns.tolist() #把筛选出来的列名转换成列表
# 连续特征用中位数补全
for feature in continuous_features:
mode_value = data[feature].mode()[0] #获取该列的众数。
data[feature].fillna(mode_value, inplace=True) #用众数填充该列的缺失值,inplace=True表示直接在原数据上修改。
# 划分训练集、验证集和测试集,因为需要考2次
# 这里演示一下如何2次划分数据集,因为这个函数只能划分一次,所以需要调用两次才能划分出训练集、验证集和测试集。
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%测试集
# X_train, y_train (80%)
# X_val, y_val (10%)
# X_test, y_test (10%)
print("Data shapes:")
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)
Data shapes: X_train: (6000, 31) y_train: (6000,) X_val: (750, 31) y_val: (750,) X_test: (750, 31) y_test: (750,)
# 最开始也说了 很多调参函数自带交叉验证,甚至是必选的参数,你如果想要不交叉反而实现起来会麻烦很多
# 所以这里我们还是只划分一次数据集
from sklearn.model_selection import train_test_split
X = data.drop(['Credit Default'], axis=1) # 特征,axis=1表示按列删除
y = data['Credit Default'] # 标签
# 按照8:2划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 80%训练集,20%测试集
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 warnings #用于忽略警告信息
warnings.filterwarnings("ignore") # 忽略所有警告信息
二、简单的调参方法
1. 网格搜索 (GridSearchCV):
- 需要定义参数的网格(param_grid),包含所有你想要尝试的特定值的列表。它会尝试网格中所有可能的参数组合。
- 缺点: 计算成本非常高,参数和值的数量稍多,组合数就会呈指数级增长(维度灾难)。因此,网格通常设置得比较小或集中在认为最优参数可能存在的区域(可能基于随机搜索的初步结果)。
2. 随机搜索 (RandomizedSearchCV):
- 需要定义参数的分布,而不是固定的列表。这是它与网格搜索的主要区别,它不会尝试所有组合,而是在指定次数内随机采样。通常,用相对较少的迭代次数(如 50-100)就能找到相当好的参数。
- 对于给定的计算预算,随机搜索通常比网格搜索更有效,尤其是在高维参数空间中。
3. 贝叶斯优化 (BayesSearchCV from skopt):
- 需要定义参数的搜索空间,与随机搜索类似,当搜索空间非常大时,它通常比网格搜索和随机搜索更有效。
- 核心优势: 它不是随机选择下一个点,而是根据先前评估的结果建立一个概率模型(通常是高斯过程),预测哪些参数组合可能产生更好的结果,并据此选择下一个评估点。这使得它在寻找最优解方面通常比随机搜索更高效(用更少的迭代次数达到相似或更好的性能),特别是当模型训练(单次评估)非常耗时的时候。
正常情况下,计算资源够用网格,计算资源不够用贝叶斯优化。
# --- 1. 默认参数的随机森林 ---
# 评估基准模型,这里确实不需要验证集
print("--- 1. 默认参数随机森林 (训练集 -> 测试集) ---")
import time # 这里介绍一个新的库,time库,主要用于时间相关的操作,因为调参需要很长时间,记录下会帮助后人知道大概的时长
start_time = time.time() # 记录开始时间
rf_model = RandomForestClassifier(random_state=42)
rf_model.fit(X_train, y_train) # 在训练集上训练
rf_pred = rf_model.predict(X_test) # 在测试集上预测
end_time = time.time() # 记录结束时间
print(f"训练与预测耗时: {end_time - start_time:.4f} 秒")
print("\n默认随机森林 在测试集上的分类报告:")
print(classification_report(y_test, rf_pred))
print("默认随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred))
--- 1. 默认参数随机森林 (训练集 -> 测试集) --- 训练与预测耗时: 0.9972 秒 默认随机森林 在测试集上的分类报告: precision recall f1-score support 0 0.77 0.97 0.86 1059 1 0.79 0.30 0.43 441 accuracy 0.77 1500 macro avg 0.78 0.63 0.64 1500 weighted avg 0.77 0.77 0.73 1500 默认随机森林 在测试集上的混淆矩阵: [[1023 36] [ 309 132]]
# 贝叶斯优化所需要安装scikit-optimize这个库
# pip install scikit-optimize -i https://pypi.tuna.tsinghua.edu.cn/simple
# --- 2. 网格搜索优化随机森林 ---
print("\n--- 2. 网格搜索优化随机森林 (训练集 -> 测试集) ---")
from sklearn.model_selection import GridSearchCV
# 定义要搜索的参数网格
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折交叉验证
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))
--- 2. 网格搜索优化随机森林 (训练集 -> 测试集) --- 网格搜索耗时: 56.7938 秒 最佳参数: {'max_depth': 20, 'min_samples_leaf': 1, 'min_samples_split': 2, 'n_estimators': 200} 网格搜索优化后的随机森林 在测试集上的分类报告: precision recall f1-score support 0 0.76 0.97 0.86 1059 1 0.80 0.28 0.42 441 accuracy 0.77 1500 macro avg 0.78 0.63 0.64 1500 weighted avg 0.77 0.77 0.73 1500 网格搜索优化后的随机森林 在测试集上的混淆矩阵: [[1028 31] [ 317 124]]
# --- 2. 贝叶斯优化随机森林 ---
print("\n--- 2. 贝叶斯优化随机森林 (训练集 -> 测试集) ---")
from skopt import BayesSearchCV
from skopt.space import Integer
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
import time
# 定义要搜索的参数空间
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))
--- 2. 贝叶斯优化随机森林 (训练集 -> 测试集) --- 贝叶斯优化耗时: 43.6849 秒 最佳参数: OrderedDict([('max_depth', 21), ('min_samples_leaf', 3), ('min_samples_split', 4), ('n_estimators', 85)]) 贝叶斯优化后的随机森林 在测试集上的分类报告: precision recall f1-score support 0 0.76 0.97 0.85 1059 1 0.78 0.27 0.40 441 accuracy 0.76 1500 macro avg 0.77 0.62 0.63 1500 weighted avg 0.77 0.76 0.72 1500 贝叶斯优化后的随机森林 在测试集上的混淆矩阵: [[1026 33] [ 321 120]]
实际上,有着非常多的方式可以实现贝叶斯优化,上面的代码比较简洁美观,贝叶斯优化和网格搜索的代码书写风格高度一致。
下面介绍一种贝叶斯优化方法的其他实现代码,他的优势在于
- 可以自己定义目标函数,也可以借助他不使用交叉验证,因为评估指标修改为不是交叉验证的结果即可,更加自由
- 有verbose参数,可以输出中间过程
下面的代码仅供参考和知识拓展:
# pip install bayesian-optimization -i https://mirrors.aliyun.com/pypi/simple/
# --- 2. 贝叶斯优化随机森林 ---
print("\n--- 2. 贝叶斯优化随机森林 (训练集 -> 测试集) ---")
from bayes_opt import BayesianOptimization
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
from sklearn.metrics import classification_report, confusion_matrix
import time
import numpy as np
# 假设 X_train, y_train, X_test, y_test 已经定义好
# 定义目标函数,这里使用交叉验证来评估模型性能
def rf_eval(n_estimators, max_depth, min_samples_split, min_samples_leaf):
n_estimators = int(n_estimators)
max_depth = int(max_depth)
min_samples_split = int(min_samples_split)
min_samples_leaf = int(min_samples_leaf)
model = RandomForestClassifier(
n_estimators=n_estimators,
max_depth=max_depth,
min_samples_split=min_samples_split,
min_samples_leaf=min_samples_leaf,
random_state=42
)
scores = cross_val_score(model, X_train, y_train, cv=5, scoring='accuracy')
return np.mean(scores)
# 定义要搜索的参数空间
pbounds_rf = {
'n_estimators': (50, 200),
'max_depth': (10, 30),
'min_samples_split': (2, 10),
'min_samples_leaf': (1, 4)
}
# 创建贝叶斯优化对象,设置 verbose=2 显示详细迭代信息
optimizer_rf = BayesianOptimization(
f=rf_eval, # 目标函数
pbounds=pbounds_rf, # 参数空间
random_state=42, # 随机种子
verbose=2 # 显示详细迭代信息
)
start_time = time.time()
# 开始贝叶斯优化
optimizer_rf.maximize(
init_points=5, # 初始随机采样点数
n_iter=32 # 迭代次数
)
end_time = time.time()
print(f"贝叶斯优化耗时: {end_time - start_time:.4f} 秒")
print("最佳参数: ", optimizer_rf.max['params'])
# 使用最佳参数的模型进行预测
best_params = optimizer_rf.max['params']
best_model = RandomForestClassifier(
n_estimators=int(best_params['n_estimators']),
max_depth=int(best_params['max_depth']),
min_samples_split=int(best_params['min_samples_split']),
min_samples_leaf=int(best_params['min_samples_leaf']),
random_state=42
)
best_model.fit(X_train, y_train)
best_pred = best_model.predict(X_test)
print("\n贝叶斯优化后的随机森林 在测试集上的分类报告:")
print(classification_report(y_test, best_pred))
print("贝叶斯优化后的随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, best_pred))
--- 2. 贝叶斯优化随机森林 (训练集 -> 测试集) --- | iter | target | max_depth | min_sa... | min_sa... | n_esti... | ------------------------------------------------------------------------- | 1 | 0.7828 | 17.49 | 3.852 | 7.856 | 139.8 | | 2 | 0.78 | 13.12 | 1.468 | 2.465 | 179.9 | | 3 | 0.7817 | 22.02 | 3.124 | 2.165 | 195.5 | | 4 | 0.7825 | 26.65 | 1.637 | 3.455 | 77.51 | | 5 | 0.7822 | 16.08 | 2.574 | 5.456 | 93.68 | | 6 | 0.7803 | 17.93 | 3.082 | 5.915 | 126.9 | | 7 | 0.7772 | 10.98 | 1.055 | 6.804 | 158.6 | | 8 | 0.7765 | 10.09 | 1.121 | 5.732 | 102.8 | | 9 | 0.78 | 29.42 | 3.129 | 9.951 | 106.1 | | 10 | 0.7822 | 19.43 | 1.481 | 8.416 | 64.48 | | 11 | 0.783 | 28.77 | 3.119 | 5.601 | 199.8 | | 12 | 0.7798 | 26.76 | 2.866 | 4.587 | 197.0 | | 13 | 0.78 | 13.87 | 2.864 | 3.986 | 165.2 | | 14 | 0.7782 | 10.13 | 2.378 | 9.813 | 129.0 | | 15 | 0.7787 | 24.85 | 2.575 | 4.403 | 52.95 | | 16 | 0.78 | 27.74 | 2.593 | 4.055 | 129.7 | | 17 | 0.7805 | 14.12 | 3.91 | 2.115 | 60.42 | | 18 | 0.7827 | 18.97 | 1.264 | 2.414 | 175.5 | | 19 | 0.7765 | 11.66 | 3.395 | 9.376 | 195.7 | | 20 | 0.7793 | 17.29 | 1.86 | 7.538 | 150.1 | | 21 | 0.7822 | 19.36 | 1.97 | 8.328 | 64.46 | | 22 | 0.781 | 29.96 | 3.562 | 7.921 | 199.0 | | 23 | 0.7808 | 18.14 | 2.653 | 5.696 | 138.2 | | 24 | 0.7818 | 15.49 | 3.068 | 9.806 | 140.5 | | 25 | 0.781 | 19.36 | 3.244 | 9.495 | 141.0 | | 26 | 0.7808 | 21.09 | 2.187 | 4.491 | 174.4 | | 27 | 0.7795 | 14.4 | 3.276 | 6.276 | 141.1 | | 28 | 0.7813 | 16.64 | 2.866 | 2.51 | 175.8 | | 29 | 0.7793 | 28.99 | 1.05 | 4.297 | 199.6 | | 30 | 0.7837 | 17.5 | 2.553 | 8.927 | 138.6 | | 31 | 0.7795 | 16.56 | 3.761 | 9.857 | 137.2 | | 32 | 0.7793 | 17.35 | 1.551 | 8.439 | 138.9 | | 33 | 0.7798 | 17.38 | 3.878 | 8.473 | 139.3 | | 34 | 0.779 | 24.33 | 1.157 | 5.795 | 76.37 | | 35 | 0.7825 | 26.71 | 1.621 | 3.293 | 77.47 | | 36 | 0.7825 | 29.04 | 3.905 | 5.19 | 199.9 | | 37 | 0.7825 | 18.26 | 2.861 | 9.151 | 138.7 | ========================================================================= 贝叶斯优化耗时: 197.6048 秒 最佳参数: {'max_depth': 17.502236740620297, 'min_samples_leaf': 2.5533082077180316, 'min_samples_split': 8.926771812562555, 'n_estimators': 138.5566475443472} 贝叶斯优化后的随机森林 在测试集上的分类报告: precision recall f1-score support 0 0.76 0.98 0.86 1059 1 0.83 0.26 0.40 441 accuracy 0.77 1500 macro avg 0.79 0.62 0.63 1500 weighted avg 0.78 0.77 0.72 1500 贝叶斯优化后的随机森林 在测试集上的混淆矩阵: [[1035 24] [ 325 116]]
三、作业
对于信贷数据的其他模型,如LightGBM和KNN 尝试用贝叶斯优化和网格搜索
1. 基础导入
# ============== 1. 基础导入 ==============
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.model_selection import GridSearchCV
from skopt import BayesSearchCV
from skopt.space import Integer, Real, Categorical
import time
import warnings
warnings.filterwarnings("ignore")
2. 数据预处理
与上述数据预处理内容一致
3. 随机森林优化
与上述随机森林优化内容一致
4. LightGBM模型
4.1 默认模型
# ============== 4. LightGBM模型 ==============
from lightgbm import LGBMClassifier
# 默认模型
print("\n=== LightGBM默认模型 ===")
start = time.time()
lgb_base = LGBMClassifier(random_state=42)
lgb_base.fit(X_train, y_train)
lgb_pred = lgb_base.predict(X_test)
print(f"耗时: {time.time()-start:.2f}s")
print(classification_report(y_test, lgb_pred))
输出结果:
=== LightGBM默认模型 ===
耗时: 0.15s
precision recall f1-score support
0 0.78 0.94 0.85 1059
1 0.70 0.36 0.47 441
accuracy 0.77 1500
macro avg 0.74 0.65 0.66 1500
weighted avg 0.75 0.77 0.74 1500
4.2 LightGBM网格搜索
# 网格搜索
print("\n=== LightGBM网格搜索 ===")
lgb_param_grid = {
'n_estimators': [50, 100, 200],
'learning_rate': [0.01, 0.1, 0.2],
'num_leaves': [31, 63, 127],
'max_depth': [5, 7, 9]
}
lgb_grid = GridSearchCV(LGBMClassifier(random_state=42), lgb_param_grid, cv=5, n_jobs=-1)
start = time.time()
lgb_grid.fit(X_train, y_train)
print(f"最佳参数: {lgb_grid.best_params_}")
print(f"耗时: {time.time()-start:.2f}s")
print(classification_report(y_test, lgb_grid.predict(X_test)))
输出结果:
=== LightGBM网格搜索 ===
最佳参数: {'learning_rate': 0.1, 'max_depth': 5, 'n_estimators': 50, 'num_leaves': 31}
耗时: 62.49s
precision recall f1-score support
0 0.76 0.98 0.86 1059
1 0.84 0.26 0.40 441
accuracy 0.77 1500
macro avg 0.80 0.62 0.63 1500
weighted avg 0.78 0.77 0.72 1500
4.3 LightGBM贝叶斯优化
# 贝叶斯优化
print("\n=== LightGBM贝叶斯优化 ===")
lgb_bayes_space = {
'n_estimators': Integer(50, 300),
'learning_rate': Real(0.01, 0.3, 'log-uniform'),
'num_leaves': Integer(20, 150),
'max_depth': Integer(3, 15)
}
lgb_bayes = BayesSearchCV(LGBMClassifier(random_state=42), lgb_bayes_space, n_iter=40, cv=5, n_jobs=-1)
start = time.time()
lgb_bayes.fit(X_train, y_train)
print(f"最佳参数: {lgb_bayes.best_params_}")
print(f"耗时: {time.time()-start:.2f}s")
print(classification_report(y_test, lgb_bayes.predict(X_test)))
输出结果:
=== LightGBM贝叶斯优化 ===
最佳参数: OrderedDict([('learning_rate', 0.012163837014962585), ('max_depth', 6), ('n_estimators', 296), ('num_leaves', 22)])
耗时: 90.40s
precision recall f1-score support
0 0.76 0.98 0.86 1059
1 0.83 0.26 0.40 441
accuracy 0.77 1500
macro avg 0.79 0.62 0.63 1500
weighted avg 0.78 0.77 0.72 1500
5. KNN模型
5.1 默认模型
# ============== 5. KNN模型 ==============
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
# 数据标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 默认模型
print("\n=== KNN默认模型 ===")
start = time.time()
knn_base = KNeighborsClassifier()
knn_base.fit(X_train_scaled, y_train)
knn_pred = knn_base.predict(X_test_scaled)
print(f"耗时: {time.time()-start:.2f}s")
print(classification_report(y_test, knn_pred))
输出结果:
=== KNN默认模型 ===
耗时: 0.08s
precision recall f1-score support
0 0.77 0.88 0.82 1059
1 0.56 0.36 0.44 441
accuracy 0.73 1500
macro avg 0.66 0.62 0.63 1500
weighted avg 0.71 0.73 0.71 1500
5.2 KNN网格搜索
# 网格搜索
print("\n=== KNN网格搜索 ===")
knn_param_grid = {
'n_neighbors': [3, 5, 7, 9],
'weights': ['uniform', 'distance'],
'p': [1, 2]
}
knn_grid = GridSearchCV(KNeighborsClassifier(), knn_param_grid, cv=5, n_jobs=-1)
start = time.time()
knn_grid.fit(X_train_scaled, y_train)
print(f"最佳参数: {knn_grid.best_params_}")
print(f"耗时: {time.time()-start:.2f}s")
print(classification_report(y_test, knn_grid.predict(X_test_scaled)))
输出结果:
=== KNN网格搜索 ===
最佳参数: {'n_neighbors': 9, 'p': 1, 'weights': 'uniform'}
耗时: 5.30s
precision recall f1-score support
0 0.76 0.93 0.83 1059
1 0.62 0.29 0.39 441
accuracy 0.74 1500
macro avg 0.69 0.61 0.61 1500
weighted avg 0.72 0.74 0.70 1500
5.3 KNN贝叶斯优化
# 贝叶斯优化
print("\n=== KNN贝叶斯优化 ===")
knn_bayes_space = {
'n_neighbors': Integer(3, 20),
'weights': Categorical(['uniform', 'distance']),
'p': Integer(1, 2)
}
knn_bayes = BayesSearchCV(KNeighborsClassifier(), knn_bayes_space, n_iter=40, cv=5, n_jobs=-1)
start = time.time()
knn_bayes.fit(X_train_scaled, y_train)
print(f"最佳参数: {knn_bayes.best_params_}")
print(f"耗时: {time.time()-start:.2f}s")
print(classification_report(y_test, knn_bayes.predict(X_test_scaled)))
输出结果:
# 贝叶斯优化
print("\n=== KNN贝叶斯优化 ===")
knn_bayes_space = {
'n_neighbors': Integer(3, 20),
'weights': Categorical(['uniform', 'distance']),
'p': Integer(1, 2)
}
knn_bayes = BayesSearchCV(KNeighborsClassifier(), knn_bayes_space, n_iter=40, cv=5, n_jobs=-1)
start = time.time()
knn_bayes.fit(X_train_scaled, y_train)
print(f"最佳参数: {knn_bayes.best_params_}")
print(f"耗时: {time.time()-start:.2f}s")
print(classification_report(y_test, knn_bayes.predict(X_test_scaled)))
6. 模型性能对比
# ============== 6. 性能对比 ==============
print("\n=== 最终模型对比 ===")
models = [
('Random Forest', rf_model),
('Optimized RF', grid_search.best_estimator_),
('Bayes RF', bayes_search.best_estimator_),
('LightGBM', lgb_base),
('Optimized LGB', lgb_grid.best_estimator_),
('Bayes LGB', lgb_bayes.best_estimator_),
('KNN', knn_base),
('Optimized KNN', knn_grid.best_estimator_),
('Bayes KNN', knn_bayes.best_estimator_)
]
for name, model in models:
if 'KNN' in name:
pred = model.predict(X_test_scaled)
else:
pred = model.predict(X_test)
print(f"\n{name} 分类报告:")
print(classification_report(y_test, pred))
输出结果(部分内容):
=== 最终模型对比 ===
Random Forest 分类报告:
precision recall f1-score support
0 0.77 0.97 0.86 1059
1 0.79 0.30 0.43 441
accuracy 0.77 1500
macro avg 0.78 0.63 0.64 1500
weighted avg 0.77 0.77 0.73 1500
Optimized RF 分类报告:
precision recall f1-score support
0 0.76 0.97 0.86 1059
1 0.80 0.28 0.42 441
accuracy 0.77 1500
macro avg 0.78 0.63 0.64 1500
weighted avg 0.77 0.77 0.73 1500
...
accuracy 0.75 1500
macro avg 0.73 0.60 0.60 1500
weighted avg 0.74 0.75 0.70 1500
Output is truncated. View as a scrollable element or open in a text editor. Adjust cell output settings...