DAY 11 常见调参方式

今天开始超参数调整的专题,回顾之前课程的核心知识点:

  1. 模型= 算法 + 实例化设置的外参(超参数) + 训练得到的内参
  2. 只要调参就需要考2次

        如果不做交叉验证,就需要划分验证集和测试集,但是很多调参方法中都默认有交叉验证,所以实际中可以省去划分验证集和测试集的步骤。

        每个模型都有自己的超参数,每个超参数都有一定的意义。为了精度和科研只需要用好调参工具即可。

数据预处理

import pandas as pd  # 用于数据处理和分析,可处理表格数据。
import matplotlib.pyplot as plt  # 用于绘制各种类型的图表。
import seaborn as sns  # 基于matplotlib的高级绘图库,绘制更美观的统计图
import numpy as np  # 用于数值计算,提供高效数组操作

# 设置中文字体(解决中文显示问题)
plt.rcParams['font.sans-serif'] = ['SimHei']  # Windows系统常用黑体字体
plt.rcParams['axes.unicode_minus'] = False  # 正常显示负号

# 读取数据
dt = pd.read_csv('data.csv')

# 定义嵌套映射字典,用于标签编码

mapping = {
  'Home Ownership':{
    'Home Mortgage': 3,    
    'Rent': 1,
    'Own Home': 0,
    'Have Mortgage': 2
  },
  'Years in current job':{
    '< 1 year': 0,
    '1 year': 1,
    '2 years': 2,
    '3 years': 3,
    '4 years': 4,
    '5 years': 5,
    '6 years': 6,
    '7 years': 7,
    '8 years': 8,
    '9 years': 9,
    '10+ years': 10
  },
  'Term':{
    'Short Term': 0,
    'Long Term': 1
  }
}
# map()方法进行映射
dt['Home Ownership'] = dt['Home Ownership'].map(mapping['Home Ownership'])
dt['Years in current job'] = dt['Years in current job'].map(mapping['Years in current job'])

# 对特征Purpose进行独热编码,get_dummies()
# pd.get_dummies(待处理数据集, columns= ['待处理列'])
dt = pd.get_dummies(dt, columns=['Purpose'])
# 接下来找到"独热编码"生成的新特征,将bool型转换为int型
data = pd.read_csv('data.csv')
for i in dt.columns:
  if i not in data.columns:
    dt[i] = dt[i].astype(int)

# Term 0-1映射
dt['Term'] = dt['Term'].map(mapping['Term'])
dt.rename(columns={'Term': 'Long Term'}, inplace=True) # 重命名列

# 填补缺失值
for i in dt.columns:
  if dt[i].isnull().sum() > 0:
    mode = dt[i].mode()[0] # 众数
    # median = dt[i].median() # 中位数
    dt[i].fillna(mode, inplace=True) # 缺失值填补   

例:划分训练集、验证集和测试集

# 划分训练集、验证集和测试集,因为需要考2次
# 所以这里划分两次,因为这个函数只能划分一次所以需要调用两次才能划分成
from sklearn.model_selection import train_test_split
X = dt.drop(['Credit Default'], axis=1)  # 特征
y = dt['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
# X_val, y_val
# X_test, y_test

print("Data shapes:")
print(f"X_train:{X_train.shape}")
print(f"y_train:{y_train.shape}")
print(f"X_val:{X_val.shape}")
print(f"y_val{y_val.shape}")
print(f"X_test{X_test.shape}")
print(f"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,)

可以看出,训练集:验证集:测试集的大小是8:1:1

正片开始:

1、数据预处理(上边有)

        最开始也说了 很多调参函数自带交叉验证,甚至是必选的参数,想要不交叉验证反而会麻烦许多,所以这里还是只划分一次数据集即可。

2、只划分训练集、测试集

from sklearn.model_selection import train_test_split
X = dt.drop(['Credit Default'], axis=1)  # 特征,axis=1表示按列删除
y = dt['Credit Default']  # 标签

# 按照8:2划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 查看形状
print(X_train.shape)
print(X_test.shape)

输出:

(6000, 31)
(1500, 31)

3、训练和调参

简单的调参方法:

        基线模型(基准模型):首先运行一个使用默认参数的RandomForestClassifier,记录其性能作为比较的基准。

1. 网格搜索(GridSearchCV)

  • 需要定义参数的网格(param_grid),包含所有你想要尝试的特定值的列表。它会尝试网格中所有可能的参数组合。
  • 缺点: 计算成本非常高,参数和值的数量稍多,组合数就会呈指数级增长(维度灾难)。因此,网格通常设置得比较小或集中在认为最优参数可能存在的区域(可能基于随机搜索的初步结果)。

2. 随机搜索 (RandomizedSearchCV):

  • 需要定义参数的分布,而不是固定的列表。这是它与网格搜索的主要区别,它不会尝试所有组合,而是在指定次数内随机采样。通常,用相对较少的迭代次数(如 50-100)就能找到相当好的参数。
  • 对于给定的计算预算,随机搜索通常比网格搜索更有效,尤其是在高维参数空间中。

3. 贝叶斯优化(BayesSearchCV from skopt):

  • 需要定义参数的搜索空间,与随机搜索类似,当搜索空间非常大时,它通常比网格搜索和随机搜索更有效。
  • 核心优势: 它不是随机选择下一个点,而是根据先前评估的结果建立一个概率模型(通常是高斯过程),预测哪些参数组合可能产生更好的结果,并据此选择下一个评估点。这使得它在寻找最优解方面通常比随机搜索更高效(用更少的迭代次数达到相似或更好的性能),特别是当模型训练(单次评估)非常耗时的时候。

正常情况下,计算资源够用网格,计算资源不够用贝叶斯优化。随机搜索没什么人用。

(1)默认参数随机森林

# ---1. 默认参数随机森林---
from sklearn.ensemble import RandomForestClassifier  # 随机森林分类器
from sklearn.metrics import classification_report, confusion_matrix  # 分类报告、混淆矩阵

# 评估基准模型,这里不需要验证集
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("\n默认随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, rf_pred))

输出:

---1. 默认参数随机森林(训练集->测试集)---
训练与预测耗时1.5417秒。

默认随机森林 在测试集上的分类报告:
              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)网格搜索优化随机森林

# --- 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. 网格搜索优化随机森林(训练集 -> 测试集) ---
网格搜索耗时:46.3326秒
最佳参数: {'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]]

(3)贝叶斯优化随机森林

# --- 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_spaces = {
  'n_estimators': Integer(50, 200),
  'max_depth': Integer(10, 30),   # 决策树最大深度范围10-30
  'min_samples_split': Integer(2, 10),  # 节点分裂最小样本数范围2-10
  'min_samples_leaf': Integer(1, 4)  # 叶节点最小样本数范围1-4
}

# 创建贝叶斯优化搜索对象
bayes_search = BayesSearchCV(
  estimator=RandomForestClassifier(random_state=42),
  search_spaces=search_spaces,
  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_
bayes_pred = best_model.predict(X_test)

print("\n贝叶斯优化后的随机森林 在测试集上的分类报告:")
print(classification_report(y_test, bayes_pred))
print("\n贝叶斯优化后的随机森林 在测试集上的混淆矩阵:")
print(confusion_matrix(y_test, bayes_pred))

输出:

--- 2. 贝叶斯优化随机森林 (训练集 -> 测试集) ---
贝叶斯优化耗时:61.6268秒。
最佳参数: OrderedDict([('max_depth', 23), ('min_samples_leaf', 3), ('min_samples_split', 5), ('n_estimators', 125)])

贝叶斯优化后的随机森林 在测试集上的分类报告:
              precision    recall  f1-score   support

           0       0.76      0.97      0.85      1059
           1       0.80      0.27      0.40       441

    accuracy                           0.76      1500
   macro avg       0.78      0.62      0.63      1500
weighted avg       0.77      0.76      0.72      1500


贝叶斯优化后的随机森林 在测试集上的混淆矩阵:
[[1029   30]
 [ 324  117]]

注:Integer(10, 30)表示在[10 , 30]之间取值。

@浙大疏锦行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值