day11 python超参数调整

  • 模型组成:模型 = 算法 + 实例化设置的外参(超参数)+ 训练得到的内参
  • 调参评估:调参通常需要进行两次评估。若不使用交叉验证,需手动划分验证集和测试集;但许多调参方法自带交叉验证功能,实际中可省略该步骤
  • 超参数学习:每个模型都有对应超参数且各具意义。若仅为追求精度或完成科研,可直接使用调参工具,无需深入研究每个超参数原理

一、数据预处理

首先执行数据预处理代码,为后续模型训练做准备:

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) # 将独热编码后的特征转换为整数类型

# 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)  # 用众数填充缺失值,直接修改原数据

二、数据集划分

2.1 两次划分数据集(演示)

由于调参需两次评估,这里演示如何两次划分数据集(因相关函数一次只能划分一次):

from sklearn.model_selection import train_test_split
X = data.drop(['Credit Default'], axis=1)  # 提取特征,按列删除指定列
y = data['Credit Default']  # 提取标签

# 第一次划分:80%训练集,20%临时集
X_train, X_temp, y_train, y_temp = train_test_split(X, y, test_size=0.2, random_state=42)

# 第二次划分:临时集再分为50%验证集和50%测试集
X_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp, test_size=0.5, random_state=42)

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,)

2.2 一次划分数据集(实际使用)

考虑到多数调参函数自带交叉验证,实际中常进行一次划分(8:2 划分训练集和测试集):

from sklearn.model_selection import train_test_split
X = data.drop(['Credit Default'], axis=1)  # 提取特征
y = data['Credit Default'] # 提取标签

# 80%训练集,20%测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

三、超参数调整方法概述

常见的超参数调整方法包括随机搜索、网格搜索和贝叶斯优化。在正式调参前,先构建基线模型(使用默认参数的 RandomForestClassifier),作为性能对比基准。

3.1 网格搜索 (GridSearchCV)

  • 原理:定义参数网格(param_grid),穷举所有参数组合进行评估
  • 缺点:计算成本高,参数组合随参数数量呈指数级增长,通常需缩小搜索范围或结合预筛选

3.2 随机搜索 (RandomizedSearchCV)

  • 原理:定义参数分布,在指定次数内随机采样评估
  • 优势:在高维参数空间中效率更高,较少迭代次数即可找到较优参数

3.3 贝叶斯优化 (BayesSearchCV from skopt)

  • 原理:基于历史评估结果构建概率模型(如高斯过程),预测最优参数组合
  • 优势:尤其适用于模型训练耗时场景,能用更少迭代次数达到更好性能

选择建议:计算资源充足选网格搜索;资源有限选贝叶斯优化;随机搜索应用相对较少。

四、模型训练与调参实战

4.1 默认参数的随机森林(基线模型)

import time 
print("--- 1. 默认参数随机森林 (训练集 -> 测试集) ---")
start_time = time.time() # 记录开始时间

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") # 忽略所有警告

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]]

4.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_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]]

4.3 贝叶斯优化随机森林

print("\n--- 3. 贝叶斯优化随机森林 (训练集 -> 测试集) ---")
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折交叉验证
    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))

运行结果

--- 3. 贝叶斯优化随机森林 (训练集 -> 测试集) ---
贝叶斯优化耗时: 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]]

4.4 另一种贝叶斯优化实现(拓展)

此方法可自定义目标函数、灵活控制交叉验证,通过 verbose 参数输出中间过程:

print("\n--- 3. 贝叶斯优化随机森林 (训练集 -> 测试集) ---")
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))

通过实践掌握了随机森林模型超参数调整方法,网格搜索虽能找到较优解但计算耗时久,随机搜索效率高但稳定性不足,贝叶斯优化结合历史数据预测最优参数组合,在效率与效果间取得平衡,同时也意识到数据预处理和数据集划分对模型性能的重要影响。

@浙大疏锦行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值