使用LeaveOneOut()的cross_val_score为何出现NaN验证分数?
问题原因分析与解决方案
核心原因
你遇到的全NaN问题,本质是LinearRegression默认的R²评分指标在单样本测试集上无法计算。
R²(决定系数)的计算公式是:
R² = 1 - (Σ(y_true - y_pred)² / Σ(y_true - y_mean)²)
其中y_mean是测试集真实值的平均值。当使用LeaveOneOut时,每次的测试集只有1个样本,此时y_mean就等于这个样本的真实值,导致分母Σ(y_true - y_mean)² = 0,除法运算直接返回NaN。不管是用cross_val_score还是手动实现交叉验证,只要测试集是单样本,用R²评分都会出现这个问题。
解决方案
1. 更换适合单样本的评分指标
放弃默认的R²,改用不需要计算样本均值的回归指标,比如均方误差(MSE)、平均绝对误差(MAE)。在cross_val_score中通过scoring参数指定即可:
from sklearn.model_selection import LeaveOneOut from sklearn.linear_model import LinearRegression from sklearn.datasets import load_iris from sklearn.model_selection import cross_val_score import numpy as np iris = load_iris() kfindex = LeaveOneOut() model = LinearRegression() # 使用负均方误差(sklearn中回归指标默认返回负值,方便统一最大化逻辑) scores = cross_val_score(model, iris.data, iris.target, cv=kfindex, scoring='neg_mean_squared_error') # 取绝对值后计算均值 print(np.abs(scores).mean(), scores.shape, scores)
手动实现的版本也可以替换成对应指标:
i = 0 scores = [] for train_x_indices, test_x_indeces in kfindex.split(iris.data): xtrain, xtest = iris.data[train_x_indices], iris.data[test_x_indices] ytrain, ytest = iris.target[train_x_indices], iris.target[test_x_indices] i += 1 model.fit(xtrain, ytrain) y_pred = model.predict(xtest) # 计算均方误差 mse = np.mean((y_pred - ytest)**2) scores.append(mse) print(i) print(scores, np.mean(scores))
2. 改用多样本的交叉验证策略
如果一定要用R²评分,可以换成K-Fold(比如k=5或10),这样每次测试集有多个样本,y_mean不会等于单个样本值,R²可以正常计算:
from sklearn.model_selection import KFold kfindex = KFold(n_splits=10, shuffle=True, random_state=42) scores = cross_val_score(model, iris.data, iris.target, cv=kfindex) print(scores.mean(), scores.shape, scores)
3. 自定义评分函数(可选)
如果坚持要在LeaveOneOut场景下用类似R²的逻辑,可以自定义评分函数处理单样本情况,比如当测试集只有1个样本时,直接比较预测值和真实值的差异:
from sklearn.metrics import make_scorer def custom_r2(y_true, y_pred): if len(y_true) == 1: # 单样本时,预测值等于真实值则返回1,否则返回0 return 1.0 if np.isclose(y_pred[0], y_true[0]) else 0.0 # 多样本时用默认R²计算 ss_res = np.sum((y_true - y_pred)**2) ss_tot = np.sum((y_true - np.mean(y_true))**2) return 1 - (ss_res / ss_tot) # 转换为sklearn可用的评分器 custom_scorer = make_scorer(custom_r2) scores = cross_val_score(model, iris.data, iris.target, cv=kfindex, scoring=custom_scorer) print(scores.mean(), scores.shape, scores)
内容的提问来源于stack exchange,提问作者Shilpa Ajith
相关产品推荐
相关产品推荐

