GridSearchCV简介

GridSearchCV 是 scikit-learn 中用于超参数调优的工具,通过穷举搜索指定的参数组合,结合交叉验证来找到模型的最优参数。

什么是超参数?

超参数是模型训练前需要手动设置的参数,模型不会自动学习它们。

# KNN 的超参数示例
knn = KNeighborsClassifier(
    n_neighbors=5,      # 超参数:K值
    weights='uniform',  # 超参数:权重策略
    p=2                 # 超参数:距离度量
)

GridSearchCV 的核心思想

穷举搜索 + 交叉验证

  1. 你提供一个参数网格(可能的参数值组合)
  2. GridSearchCV 尝试每一种组合
  3. 对每种组合进行交叉验证,计算平均性能
  4. 返回性能最好的参数组合

基本用法

1. 最简单的例子

from sklearn.model_selection import GridSearchCV
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

# 加载数据
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, test_size=0.2, random_state=42
)

# 1. 定义模型
knn = KNeighborsClassifier()

# 2. 定义参数网格
param_grid = {
    'n_neighbors': [3, 5, 7, 9],      # 尝试4个不同的K值
    'weights': ['uniform', 'distance'] # 尝试2种权重策略
}
# 总共会尝试 4 × 2 = 8 种组合

# 3. 创建 GridSearchCV 对象
grid_search = GridSearchCV(
    estimator=knn,           # 模型
    param_grid=param_grid,   # 参数网格
    cv=5,                    # 5折交叉验证
    scoring='accuracy',      # 评估指标
    verbose=1                # 显示进度
)

# 4. 执行搜索(训练)
grid_search.fit(X_train, y_train)

# 5. 查看结果
print(f"最佳参数: {grid_search.best_params_}")
print(f"最佳交叉验证得分: {grid_search.best_score_:.4f}")
print(f"测试集得分: {grid_search.score(X_test, y_test):.4f}")

参数详解

参数说明示例
estimator要调优的模型KNeighborsClassifier()
param_grid参数网格(字典或列表){'n_neighbors': [3,5,7]}
cv交叉验证折数5(5折)、3(3折)
scoring评估指标'accuracy''f1''roc_auc'
n_jobs并行计算核心数-1(使用所有核心)
verbose详细程度(0,1,2)1(显示进度)
refit用最佳参数重新训练True(默认)

复杂参数网格

1. 字典形式(笛卡尔积)

# 会尝试所有组合:3×2×2 = 12 种
param_grid = {
    'n_neighbors': [3, 5, 7],
    'weights': ['uniform', 'distance'],
    'p': [1, 2]
}

2. 列表形式(不同参数集)

# 第一组参数
param_grid = [
    {'n_neighbors': [3, 5], 'weights': ['uniform']},     # KNN with uniform
    {'n_neighbors': [7, 9], 'weights': ['distance']},    # KNN with distance
    {'kernel': ['linear', 'rbf'], 'C': [0.1, 1]}         # SVM 的不同参数
]

完整实战示例

示例1:KNN 参数调优

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline

# 1. 准备数据
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, test_size=0.2, random_state=42
)

# 2. 创建管道(标准化 + KNN)
pipeline = make_pipeline(
    StandardScaler(),
    KNeighborsClassifier()
)

# 3. 定义参数网格(注意参数名前要加模型名)
param_grid = {
    'kneighborsclassifier__n_neighbors': [3, 5, 7, 9, 11],
    'kneighborsclassifier__weights': ['uniform', 'distance'],
    'kneighborsclassifier__p': [1, 2]
}

# 4. GridSearchCV
grid_search = GridSearchCV(
    pipeline,
    param_grid,
    cv=5,                  # 5折交叉验证
    scoring='accuracy',
    n_jobs=-1,             # 使用所有CPU核心
    verbose=1
)

# 5. 训练
grid_search.fit(X_train, y_train)

# 6. 结果分析
print("="*50)
print(f"最佳参数: {grid_search.best_params_}")
print(f"最佳交叉验证得分: {grid_search.best_score_:.4f}")
print(f"测试集得分: {grid_search.score(X_test, y_test):.4f}")
print("="*50)

# 7. 查看所有结果
results = pd.DataFrame(grid_search.cv_results_)
print(results[['param_kneighborsclassifier__n_neighbors', 
               'param_kneighborsclassifier__weights',
               'mean_test_score', 'std_test_score']].head())

示例2:SVM 参数调优

from sklearn.svm import SVC

# SVM 有更多需要调优的超参数
param_grid = {
    'C': [0.1, 1, 10, 100],           # 正则化强度
    'gamma': [0.001, 0.01, 0.1, 1],   # 核函数系数
    'kernel': ['rbf', 'linear']        # 核函数类型
}

svm = SVC()
grid_search = GridSearchCV(svm, param_grid, cv=5, scoring='accuracy', n_jobs=-1)
grid_search.fit(X_train, y_train)

print(f"最佳参数: {grid_search.best_params_}")
print(f"最佳得分: {grid_search.best_score_:.4f}")

示例3:随机森林参数调优

from sklearn.ensemble import RandomForestClassifier

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]        # 叶子节点最少样本数
}

rf = RandomForestClassifier(random_state=42)
grid_search = GridSearchCV(rf, param_grid, cv=3, n_jobs=-1, verbose=1)
grid_search.fit(X_train, y_train)

查看结果的属性

# 训练完成后,GridSearchCV 对象有以下重要属性:

# 1. 最佳参数
print(grid_search.best_params_)
# {'n_neighbors': 5, 'weights': 'uniform'}

# 2. 最佳交叉验证得分
print(grid_search.best_score_)
# 0.9583333333333334

# 3. 最佳模型(已经用最佳参数重新训练)
best_model = grid_search.best_estimator_

# 4. 所有参数组合的详细结果
results = grid_search.cv_results_
print(f"平均测试得分: {results['mean_test_score']}")
print(f"得分标准差: {results['std_test_score']}")
print(f"参数组合: {results['params']}")

# 5. 每个参数组合的排名
print(f"排名: {results['rank_test_score']}")

# 6. 最佳模型在测试集上的表现
test_score = grid_search.score(X_test, y_test)

可视化结果

import matplotlib.pyplot as plt
import seaborn as sns

# 将结果转换为 DataFrame
results_df = pd.DataFrame(grid_search.cv_results_)

# 绘制参数与得分的关系
plt.figure(figsize=(10, 6))
sns.lineplot(
    data=results_df,
    x='param_kneighborsclassifier__n_neighbors',
    y='mean_test_score',
    hue='param_kneighborsclassifier__weights',
    marker='o'
)
plt.title('Grid Search Results')
plt.xlabel('Number of Neighbors (K)')
plt.ylabel('Cross-Validation Accuracy')
plt.legend(title='Weights')
plt.grid(True)
plt.show()

不同评分指标

# 分类问题常用指标
scoring_options = {
    'accuracy': 'accuracy',           # 准确率
    'f1_macro': 'f1_macro',          # F1分数(宏平均)
    'roc_auc': 'roc_auc_ovr',        # ROC-AUC
    'precision': 'precision_macro',   # 精确率
    'recall': 'recall_macro'          # 召回率
}

for name, metric in scoring_options.items():
    grid_search = GridSearchCV(
        estimator, param_grid, 
        scoring=metric, cv=5
    )
    grid_search.fit(X_train, y_train)
    print(f"{name}: {grid_search.best_score_:.4f}")

注意事项

1. 计算成本高

# 假设:
# - 参数组合数: 20
# - 交叉验证折数: 5
# - 总训练次数: 20 × 5 = 100 次
# 
# 如果每次训练需要1秒,总耗时100秒

# 解决方案:
# - 减少参数组合
# - 降低 cv 值(如 cv=3)
# - 使用 n_jobs=-1 并行计算
# - 先用 RandomizedSearchCV 粗调,再用 GridSearchCV 细调

2. 数据泄露风险

# ❌ 错误:在 GridSearchCV 之前做预处理
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)  # 数据泄露!
grid_search.fit(X_scaled, y)

# ✅ 正确:使用 Pipeline
pipeline = make_pipeline(StandardScaler(), KNeighborsClassifier())
grid_search.fit(pipeline, X_train, y_train)

3. 参数命名规则

# 在 Pipeline 中,参数名需要加前缀
pipeline = make_pipeline(StandardScaler(), KNeighborsClassifier())

# 正确写法:'模型名__参数名'
param_grid = {
    'kneighborsclassifier__n_neighbors': [3, 5, 7]
}

# 或者使用 named_steps
param_grid = {
    pipeline.named_steps['kneighborsclassifier'].__class__.__name__.lower() 
    + '__n_neighbors': [3, 5, 7]
}

GridSearchCV vs RandomizedSearchCV

特性GridSearchCVRandomizedSearchCV
搜索方式穷举所有组合随机采样部分组合
参数空间离散值连续或离散
计算成本
适用场景参数空间小参数空间大
能否找到最优一定能(在给定网格内)不一定
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import uniform, randint

# 随机搜索示例
param_dist = {
    'n_neighbors': randint(3, 20),           # 3-20 之间的随机整数
    'weights': ['uniform', 'distance'],
    'p': [1, 2]
}

random_search = RandomizedSearchCV(
    knn, param_dist, 
    n_iter=20,          # 随机采样20组
    cv=5, 
    random_state=42
)

总结

要点说明
作用自动寻找模型的最优超参数
核心穷举搜索 + 交叉验证
优点能找到给定范围内的最优参数
缺点计算成本高,参数空间大时慢
最佳实践结合 Pipeline 使用,避免数据泄露
适用场景超参数数量少(<10个),每个参数取值少(<10个)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值