使用scikit-learn的HalvingGridSearchCV时min_resources不符合预期
HalvingGridSearchCV(factor=2)迭代行为不符合预期的问题分析
问题场景概述
使用scikit-learn 1.4.0的HalvingGridSearchCV进行超参数调优时,设置factor=2、min_resources="exhaust"(期望最后一轮用全量训练集),针对8组超参数组合,出现以下异常,且仅factor=2时触发,factor=3/4结果正常:
- 1000样本生成数据:预期3轮迭代
n_candidates_=[8,4,2]、n_resources_=[250,500,1000],实际得到4轮n_candidates_=[8,4,2,1]、n_resources_=[125,250,500,1000],首轮资源减半且多一轮迭代 - 100样本生成数据:迭代轮数符合预期,但最后一轮
n_resources_=80未用全量100样本 - 数据依赖性差异:200样本Fashion-MNIST子集得到2轮
n_candidates_=[8,4]、n_resources_=[100,200];200样本生成数据得到4轮n_candidates_=[8,4,2,1]、n_resources_=[25,50,100,200],均不符合预期
核心原因分析
1. 迭代逻辑的边界处理问题
HalvingGridSearchCV的迭代停止与资源量计算受多重约束:
- 每轮候选数需≥1,且下一轮资源量不能超过总样本
- 当
cv=5时,每轮资源量需满足每个CV折至少有1个样本,源码会自动调整资源量,factor=2时这种调整可能打破预期的资源增长节奏 - 生成数据的均匀随机性可能导致模型性能差异极小,候选数筛选逻辑触发额外轮次;而结构化数据集(如Fashion-MNIST)性能差异更显著,候选数更早被筛选至
<factor,提前停止迭代
2. 对min_resources="exhaust"的理解偏差
该参数仅保证最后一轮使用全量资源,但迭代轮数由资源增长到全量所需的次数、候选数halving次数共同决定,并非严格按照总候选数 / factor^轮数的理想逻辑执行。
调试与验证建议
- 查看内部计算逻辑:打印
grid._n_required_iterations()和grid._compute_resources()的结果,确认轮数与资源量的计算过程 - 调整CV折数:尝试设置
cv=3或cv=1(不交叉验证),验证CV拆分是否为影响因素 - 升级版本:更新scikit-learn至1.5.x等最新稳定版,确认该问题是否已被官方修复
- 强制初始化资源:手动指定
initial_resources为预期首轮资源量(如1000样本时设为250),控制迭代起始节奏
临时解决方案
若需严格控制迭代行为:
- 当
factor=2时,手动计算并指定initial_resources和max_resources,确保最后一轮使用全量样本 - 临时切换至
factor=3或factor=4,规避该异常场景
复现代码
import numpy as np import pandas as pd from sklearn.datasets import fetch_openml, make_classification from sklearn.experimental import enable_halving_search_cv from sklearn.model_selection import train_test_split, HalvingGridSearchCV from sklearn.tree import DecisionTreeClassifier rng = np.random.RandomState(0) num_samples = 1000 # 可修改为100/200测试不同场景 # 切换数据集:注释下面一行,取消注释Fashion-MNIST代码块 X_train, y_train = make_classification(n_samples=num_samples, n_features=20, random_state=rng) # # Fashion-MNIST数据集测试 # data_fash = fetch_openml(name="Fashion-MNIST") # X_train, _, y_train, _ = train_test_split( # data_fash.data, data_fash.target, train_size=num_samples, random_state=rng # ) print("Train samples: ", len(X_train)) param_grid = {"criterion": ["gini", "entropy"], "min_samples_split": [2, 3, 4, 5]} cls = DecisionTreeClassifier() grid = HalvingGridSearchCV( cls, param_grid, factor=2, min_resources="exhaust", scoring="accuracy", cv=5 ) grid.fit(X_train, y_train) print("n_candidates_:", grid.n_candidates_) print("n_resources_:", grid.n_resources_)
内容的提问来源于stack exchange,提问作者JDups
相关产品推荐
相关产品推荐

