Skip to main content
 首页 » 编程设计

python之scikit-learn 中每个数据拆分的交叉验证指标

2025年05月04日20zhoujg

我需要为 (X_test, y_test) 数据的每个拆分明确获取交叉验证统计信息。

所以,为了尝试这样做,我做了:

kf = KFold(n_splits=n_splits) 
 
X_train_tmp = [] 
y_train_tmp = [] 
X_test_tmp = [] 
y_test_tmp = [] 
mae_train_cv_list = [] 
mae_test_cv_list = [] 
 
for train_index, test_index in kf.split(X_train): 
     
    for i in range(len(train_index)): 
        X_train_tmp.append(X_train[train_index[i]]) 
        y_train_tmp.append(y_train[train_index[i]]) 
 
    for i in range(len(test_index)): 
        X_test_tmp.append(X_train[test_index[i]]) 
        y_test_tmp.append(y_train[test_index[i]]) 
 
    model.fit(X_train_tmp, y_train_tmp) # FIT the model = SVR, NN, etc. 
 
    mae_train_cv_list.append( mean_absolute_error(y_train_tmp, model.predict(X_train_tmp)) # MAE of the train part of the KFold. 
 
    mae_test_cv_list.append( mean_absolute_error(y_test_tmp, model.predict(X_test_tmp)) ) # MAE of the test part of the KFold. 
 
    X_train_tmp = [] 
    y_train_tmp = [] 
    X_test_tmp = [] 
    y_test_tmp = [] 

这是通过使用例如 KFold 获取每个交叉验证拆分的平均绝对误差 (MAE) 的正确方法吗?

请您参考如下方法:

您的方法存在一些问题。

首先,您当然不必在训练和验证列表中手动地逐一附加数据(即您的 2 个内部 for 循环);简单的索引就可以完成这项工作。

此外,我们通常从不计算和报告训练 CV 折叠的错误 - 仅计算验证折叠的错误。

牢记这些,并将术语切换为“验证”而不是“测试”,这是一个使用波士顿数据的简单可重现示例,它应该直接适应您的情况:

from sklearn.model_selection import KFold 
from sklearn.datasets import load_boston 
from sklearn.metrics import mean_absolute_error 
from sklearn.tree import DecisionTreeRegressor 
 
X, y = load_boston(return_X_y=True) 
n_splits = 5 
kf = KFold(n_splits=n_splits, shuffle=True) 
model = DecisionTreeRegressor(criterion='mae') 
 
cv_mae = [] 
 
for train_index, val_index in kf.split(X): 
    model.fit(X[train_index], y[train_index]) 
    pred = model.predict(X[val_index]) 
    err = mean_absolute_error(y[val_index], pred) 
    cv_mae.append(err) 

之后,您的 cv_mae 应该是这样的(由于 CV 的随机性,细节会有所不同):

[3.5294117647058827, 
 3.3039603960396042, 
 3.5306930693069307, 
 2.6910891089108913, 
 3.0663366336633664] 

当然,所有这些明确的东西并不是真正必要的;你可以用 cross_val_score 更简单地完成这项工作.不过有一个小问题:

from sklearn.model_selection import cross_val_score 
cv_mae2 =cross_val_score(model, X, y, cv=n_splits, scoring="neg_mean_absolute_error") 
cv_mae2 
# result 
array([-2.94019608, -3.71980198, -4.92673267, -4.5990099 , -4.22574257]) 

除了不是真正问题的负号外,您会注意到结果的方差与上面的 cv_mae 相比看起来明显更高;原因是我们没有洗牌我们的数据。不幸的是,cross_val_score 不提供混洗选项,因此我们必须使用 shuffle 手动执行此操作。所以我们最终的代码应该是:

from sklearn.model_selection import cross_val_score 
from sklearn.utils import shuffle 
X_s, y_s =shuffle(X, y) 
cv_mae3 =cross_val_score(model, X_s, y_s, cv=n_splits, scoring="neg_mean_absolute_error") 
cv_mae3 
# result: 
array([-3.24117647, -3.57029703, -3.10891089, -3.45940594, -2.78316832]) 

折叠之间的差异明显较小,并且更接近我们最初的 cv_mae...