Scikit-learn GridSearchCV交叉验证返回错误均值问题求助
问题分析与解决思路
我之前也碰到过一模一样的问题!核心原因是sklearn对分类任务的交叉验证结果默认采用加权平均(按各折测试集的样本数量加权),而不是你手动计算的简单算术平均。
为什么会出现这个差异?
当你使用分类器时,GridSearchCV底层调用的cross_validate函数会自动将每折测试集的样本数作为权重,计算加权平均的mean_test_*结果。举个例子,假设你的第一折测试集有100个样本,第二折有200个样本,那么加权平均的计算方式是:
(100 * 0.935603198 + 200 * 0.933665455) / (100 + 200) ≈ 0.9343
这正好和你给出的mean_test_f1数值完全匹配,而不是简单的(0.9356 + 0.9337)/2 ≈ 0.9346。
验证方法
你可以检查自定义train_test_iterable中每折的测试集大小,验证加权平均是否和cv_results_里的均值一致:
# 获取各折测试集的大小 test_sizes = [len(test_idx) for _, test_idx in train_test_iterable.split(X, Y)] # 对第一行结果计算加权平均 weighted_mean = (test_sizes[0] * 0.935603198 + test_sizes[1] * 0.933665455) / sum(test_sizes) print(weighted_mean) # 应该等于0.934310796
如何得到算术平均的结果?
如果你更倾向于使用简单算术平均,可以试试以下几种方法:
方法1:让各折测试集大小一致
使用StratifiedKFold(针对分类任务)这类默认会均分样本的交叉验证器,这样加权平均就等同于算术平均。如果必须使用自定义迭代器,调整逻辑让每折测试集的样本数尽量相等。
方法2:手动计算算术平均
在获取cv_results_之后,手动对各折的测试结果求均值:
import numpy as np # 提取所有折的f1测试结果 split_test_f1 = [gscv.cv_results_[f'split{i}_test_f1'] for i in range(2)] # 计算算术平均 mean_test_f1_manual = np.mean(split_test_f1, axis=0)
方法3:自定义交叉验证器,强制使用等权重
通过继承BaseCrossValidator自定义一个交叉验证器,返回全1的权重,让cross_validate计算算术平均:
from sklearn.model_selection import BaseCrossValidator class EqualWeightCV(BaseCrossValidator): def __init__(self, base_cv): self.base_cv = base_cv def split(self, X, y=None, groups=None): # 复用基础CV的split逻辑 yield from self.base_cv.split(X, y, groups) def get_n_splits(self, X=None, y=None, groups=None): return self.base_cv.get_n_splits(X, y, groups) @property def weights(self): # 返回全1的权重,强制算术平均 return [1] * self.get_n_splits() # 用自定义包装器包裹你的train_test_iterable cv = EqualWeightCV(train_test_iterable) # 传入GridSearchCV gscv = GridSearchCV( n_jobs=n_jobs, cv=cv, estimator=pipeline, param_grid=param_grid, verbose=10, scoring=['accuracy', 'precision','recall','f1'], refit='f1', return_train_score=return_train_score, error_score=error_score )
另外,注意到你有两行完全相同的拆分结果,这可能是param_grid里存在重复的参数组合,或者自定义迭代器生成了重复的折,建议检查一下参数网格的配置。
内容的提问来源于stack exchange,提问作者mlengg
相关产品推荐
相关产品推荐

