Leave-One-Group-Out交叉验证代码正确性及相关问题咨询
问题描述
代码可正常运行无报错,但得到的AUC值极高,怀疑是否正确实现了预期的Leave-One-Group-Out(LOGO)交叉验证。每组对应一名参与者的所有数据,预期每轮交叉验证中留出一名参与者的数据用于测试,用其余参与者的数据训练模型后再测试。已对每组内的任务顺序进行打乱(所有参与者的任务顺序一致),并做了归一化处理,使用scikit-learn库实现。
核心疑问
- 代码中是否存在错误或易导致过拟合的问题?这是否是LOGO的正确实现方式?(特征数据不包含目标值和任务编号)
- 多次修改
cross_validate的scoring参数运行代码,是否会导致模型过拟合?曾尝试一次性获取多指标(如AUC、准确率等)但未成功,能否通过多次运行代码来获取所需的不同指标?
代码实现
import numpy as np from sklearn.model_selection import LeaveOneGroupOut from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import cross_validate from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline from sklearn.metrics import f1_score from sklearn.ensemble import GradientBoostingClassifier # 设置随机种子保证可复现 random_seed = 200 np.random.seed(random_seed) # 从已定义的字典中提取变量 X = data_dict['data'] Y = data_dict['target'] groups = data_dict['participants'] # 实例化LeaveOneGroupOut交叉验证策略 logo = LeaveOneGroupOut() # 对每个参与者组内的任务顺序进行打乱 X_shuffled = [] Y_shuffled = [] groups_shuffled = [] unique_groups = np.unique(groups) # 每个组对应一名参与者 for group in unique_groups: group_indices = np.where(groups == group)[0] shuffled_indices = np.random.permutation(group_indices) X_shuffled.extend(X[shuffled_indices]) Y_shuffled.extend(Y[shuffled_indices]) groups_shuffled.extend(groups[shuffled_indices]) X_shuffled = np.array(X_shuffled) Y_shuffled = np.array(Y_shuffled) groups_shuffled = np.array(groups_shuffled) # 创建梯度提升分类器 clf = GradientBoostingClassifier(random_state=random_seed) # 构建预处理+模型的流水线:均值填充缺失值 → 标准化 → 分类器 pipeline = Pipeline([ ('imputer', SimpleImputer(strategy='mean')), ('scaler', StandardScaler()), # 归一化处理 ('clf', clf) ]) # 执行交叉验证,返回训练分数和模型实例 results_logo = cross_validate(pipeline, X_shuffled, Y_shuffled, cv=logo.split(X_shuffled, Y_shuffled, groups_shuffled), scoring='roc_auc', return_train_score=True, return_estimator=True) # 打印AUC结果 print('auc') print('training score: %.4f' % results_logo['train_score'].mean()) print('test score: %.4f' % results_logo['test_score'].mean()) print(results_logo['test_score']) print(np.mean(results_logo['test_score'])) # 打印每折的训练/测试分组情况 for i, (train_index, test_index) in enumerate(logo.split(X_shuffled, Y_shuffled, groups_shuffled)): print(f"Fold {i}:") print(f" Train: index={train_index}, group={groups_shuffled[train_index]}") print(f" Test: index={test_index}, group={groups_shuffled[test_index]}")
解答
一、LOGO交叉验证的正确性与潜在问题
1. LOGO实现是否正确?
你的代码基本正确实现了LOGO交叉验证:
- 正确使用
LeaveOneGroupOut定义了按参与者分组的交叉验证策略; - 组内打乱任务顺序的操作合理,仅重排同一参与者内部的数据,不会破坏训练/测试集的分组独立性;
- 用
Pipeline封装预处理和模型,确保了SimpleImputer和StandardScaler仅在训练集上拟合,避免了数据泄露。
可优化细节:cross_validate的cv参数可直接传入logo对象,同时指定groups=groups_shuffled,无需手动调用logo.split(),代码更简洁规范,效果完全一致:
results_logo = cross_validate(pipeline, X_shuffled, Y_shuffled, cv=logo, groups=groups_shuffled, scoring='roc_auc', return_train_score=True, return_estimator=True)
2. 高AUC值的可能原因及过拟合风险
高AUC不一定是代码错误,常见原因包括:
- 数据区分度高:特征与目标值的关联极强,模型泛化能力确实优秀;
- 样本不平衡:若目标类别严重倾斜(如负样本占比极高),AUC指标可能虚高;
- 模型拟合情况:对比训练集和测试集AUC,若两者差距小,说明无过拟合;若训练AUC远高于测试AUC,才是过拟合,可通过调小GradientBoosting的
n_estimators、增大learning_rate等参数缓解。
二、关于多指标计算的问题
1. 多次修改scoring参数是否会导致过拟合?
不会。过拟合是模型训练时过度拟合训练集导致泛化能力下降,而多次运行代码只是用同一交叉验证划分计算不同指标,模型训练过程完全独立,不会改变模型的泛化性能。
2. 如何一次性获取多指标?
之前尝试失败大概率是参数格式错误,sklearn的scoring支持传入字典指定多个指标,示例如下:
results_logo = cross_validate( pipeline, X_shuffled, Y_shuffled, cv=logo, groups=groups_shuffled, scoring={ 'roc_auc': 'roc_auc', 'accuracy': 'accuracy', 'f1': 'f1' }, return_train_score=True, return_estimator=True )
运行后results_logo会包含test_roc_auc、test_accuracy等键,对应各指标的测试分数。若仍报错,建议升级scikit-learn到较新版本。
若确实无法一次性获取,多次运行代码也可行,只要保持随机种子不变,交叉验证划分一致,各指标结果具备可比性。
内容的提问来源于stack exchange,提问作者user22409235
相关产品推荐
相关产品推荐

