使用LOGO/LOO交叉验证时Grid Search的best_score为nan问题排查
Grid Search搭配LOGO/LOO交叉验证时R2评分为NaN的问题排查与解决
问题重现
使用XGBoost结合Grid Search做回归任务时,10折交叉验证可正常输出R2分数,但切换为**LeaveOneGroupOut (LOGO)或LeaveOneOut (LOO)**时,所有R2评分均为NaN。已确认训练集无缺失值,核心代码及运行输出如下:
核心代码
FDODB=pd.read_excel('Final Training Set for LOGO.xlsx') array = FDODB.values X = array[:,2:126] Y = array[:,1] Compd = array[:,0] scaler = StandardScaler() X = scaler.fit_transform(X) params = { 'max_depth': [5,7,9], 'learning_rate': [0.03,0.05,0.07], 'n_estimators': [200,300,400], 'min_child_weight': [5,7,9], 'subsample': [0.3, 0.5, 0.7], 'base_score': [0.4, 0.5, 0.6] } xgb_reg = XGBRegressor(tree_method='hist', device='cuda') logo = LeaveOneGroupOut() grid_search = GridSearchCV( estimator=xgb_reg, param_grid=params, scoring='r2', error_score="raise", cv=logo, verbose=2, n_jobs=-1, return_train_score=True ) grid_search.fit(X, Y, groups=Compd) best_params = grid_search.best_params_ best_score = grid_search.best_score_ print('Best hyperparameters:', best_params) print('Best R2 score:', best_score)
运行输出
c:\Anaconda\envs\machinelearning\Lib\site-packages\sklearn\model_selection_search.py:1102: UserWarning: One or more of the test scores are non-finite: [nan] warnings.warn( Best hyperparameters: {'base_score': 0.5, 'learning_rate': 0.03, 'max_depth': 8, 'min_child_weight': 6, 'n_estimators': 200, 'subsample': 0.3} Best R2 score: nan
原因分析
- 测试集标签方差为0:LOGO/LOO的某一轮验证中,测试组的Y值完全相同(方差为0)。R2计算依赖分母
var(y_true),当分母为0时会直接返回NaN,这种情况在分组后单组样本量极小(如单样本组)时极易出现。 - 分组逻辑异常:
Compd分组可能存在仅含1个样本的组,或部分组内所有样本的Y值完全一致,导致验证时触发R2计算的无效场景。
解决方法
检查并修正分组数据:
统计每个分组的样本量及Y值方差,定位问题分组:import pandas as pd FDODB['Compd'] = FDODB.iloc[:,0] FDODB['Y'] = FDODB.iloc[:,1] group_stats = FDODB.groupby('Compd')['Y'].agg(['count', 'var']) # 输出方差为0的分组 print(group_stats[group_stats['var'] == 0])对这类分组可选择合并同类型组,或直接从数据集中移除。
替换为鲁棒性评分指标:
改用对无方差样本更友好的指标,比如MSE、MAE,或自定义鲁棒版R2:from sklearn.metrics import make_scorer import numpy as np def robust_r2(y_true, y_pred): var_true = np.var(y_true) if var_true == 0: # 若真实值无波动,预测完全匹配则得1分,否则0分 return 1.0 if np.allclose(y_true, y_pred) else 0.0 # 加极小值避免分母为0 return 1 - np.sum((y_true - y_pred)**2) / (np.sum((y_true - np.mean(y_true))**2) + 1e-10) # 在GridSearch中使用自定义评分器 grid_search = GridSearchCV( estimator=xgb_reg, param_grid=params, scoring=make_scorer(robust_r2), error_score="raise", cv=logo, verbose=2, n_jobs=-1, return_train_score=True )调整交叉验证策略:
若分组样本量差异过大,可改用GroupKFold,将每组拆分为多份参与验证,避免单组全量作为测试集导致的方差为0问题。
内容的提问来源于stack exchange,提问作者Nakyung Lee
相关产品推荐
相关产品推荐

