交叉验证中 RMSE 和 R 平方分数的可解释性

数据挖掘 回归 xgboost 交叉验证 过拟合 公制
2021-10-07 10:45:06

我正在研究数据集中有 30k 行的回归问题,决定使用 XGBoost 主要是为了避免为快速原始模型处理数据。而且我在进行交叉验证时注意到,火车的 R² 和 CV 的 R² 之间存在显着差异 => 明显的过度拟合迹象。这是我的 CV 代码:

oof_train = np.zeros((len(train_maisons)))
ind = 0
cv_scores = []
train_scores=[]
for ind,(ind_train,ind_val) in (enumerate (kfolds.split(X,y))):
    #ind=+1
    X_train,X_val = X.iloc[ind_train],X.iloc[ind_val] 
    y_train,y_val = y.iloc[ind_train],y.iloc[ind_val]
    xgb = XGBRegressor(colsample_bytree=0.6,gamma=0.3,learning_rate=0.1,max_depth=8,min_child_weight=3,subsample=0.9,n_estimators=1000,objective='reg:squarederror',eval_metric='rmse')
    xgb.fit(X_train,y_train)
    val_pred = xgb.predict(X_val)
    train_pred = xgb.predict(X_train)
    oof_train[ind_val] += val_pred
    score_fold_validation=np.sqrt(mean_squared_error(y_val, val_pred))
    score_fold_train=np.sqrt(mean_squared_error(y_train, train_pred))
    train_scores.append(score_fold_train)
    cv_scores.append(score_fold_validation)
    #r2_score(y_val, grid.best_estimator_.predict(X_val))
    print('Iteration : {} - CV Score : {} - R² Score CV : {} - Train Score : {} - R² Score train : {}'.format(str(ind+1),score_fold_validation,r2_score(y_val, val_pred),score_fold_train,r2_score(y_train,train_pred)))
end_train_score=np.mean(train_scores)
train_scores.append(end_train_score)
end_cv_score=np.mean(cv_scores)

使用 SquaredError 作为目标(损失函数),使用 RMSE 和 R² 进行评估,以下是指标的输出:

CV Score : 96416.84137549331 - R² Score CV : 0.6545903695464426 - Train Score : 30605.655815355676 - R² Score train : 0.9730563148067477

我的问题:这是否被认为是一个压倒性的过度拟合问题?还是温和?我应该做更多的特征工程还是更多地调整超参数(将 GridSearchCV 用于当前的超参数)。最后一件事,我在 X_train 上的结果是否表明我的特征信息量足以学习目标?还是火车分数有偏差

注意:在这段代码中,我对 CV 使用了10 折用过3 折给了我一个更好的简历结果,如果有人也能解释一下,那就太好了。

1个回答

最后一件事,我在 X_train 上的结果是否表明我的特征信息量足以学习目标?还是 R² 训练分数有偏差?

训练数据上的高分拟合并不一定表明您的特征信息量足以以一般方式学习目标。只有您的交叉验证分数可以这样做。

注意:在这段代码中,我对 CV 使用了 10 折。用过 3 折给了我一个更好的简历结果,如果有人也能解释一下,那就太好了。

我不认为会有很大的差异,所以这取决于它们有多好,但请记住,您是在为每个折叠随机选择一个数据子集。当您运行 3 次时,该模型巧合地能够更好地从选定的训练集中学习以预测验证集,这似乎是合理的。一般来说,你使用的折叠越多,你对分数的可靠性就越有信心。

我的问题:这是否被认为是一个压倒性的过度拟合问题?还是温和?我应该做更多的特征工程还是更多地调整超参数?(当前超参数使用 GridSearchCV )

我不是专业人士,但我认为这严重过度拟合。首先,我会返回到超参数调整以尝试使它们保持一致。此时我还将执行 RandomizedSearchCV 的强制插件:http ://www.jmlr.org/papers/volume13/bergstra12a/bergstra12a.pdf

编辑:

由于您已要求提供更多详细信息:

1) 对训练数据的高分是否表明这些特征的信息量足以学习目标?

考虑以下示例:

import numpy as np
from xgboost import XGBRegressor

x = np.random.rand(5000, 12)
y = np.random.randint(0, 200, 5000)

rfr = XGBRegressor(colsample_bytree=0.6,gamma=0.3,learning_rate=0.1,max_depth=8,min_child_weight=3,subsample=0.9,n_estimators=1000,eval_metric='rmse')
rfr.fit(x, y)
rfr.score(x, y)

输出[26]:0.999918392694166

完全随机的目标针对完全随机的输入进行训练,并且仍然几乎完美地得分。那是一个完全没用的模型;根据您的训练数据进行验证并不能让您确定您的特征信息量是否足以让您根据模型未见过的数据来预测目标。只有对看不见的数据进行交叉验证才能做到这一点。

2) 用了 3 折给了我更好的简历结果,如果有人也能解释一下,那就太好了。

RNG诸神对你微笑。再次考虑一个例子:

from xgboost import XGBClassifier, XGBRegressor
import pandas as pd
from sklearn.model_selection import KFold

df = pd.read_csv(r'https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv', index_col=None)

x = df.drop('species', axis=1)
y = df.species

xgb = XGBClassifier(max_depth=3)

kf = KFold(n_splits=10, shuffle=True)

for train_idx, test_idx in kf.split(x):
    xgb.fit(x.iloc[train_idx], y.iloc[train_idx])
    print(xgb.score(x.iloc[test_idx], y.iloc[test_idx]))

输出[17]: 0.9333333333333333
0.9333333333333333
0.9333333333333333
1.0
0.8666666666666667
1.0
1.0
0.9333333333333333
0.866666.666666

该代码将数据拆分 10 次,然后在每个选定切片上对模型进行训练和评分。如您所见,得分最高和最差的切片之间存在 13.4% 的差异。

您的 3-Fold 运行巧合地给了您 3 个“好”的弃牌。您拥有的折叠次数越多,您的交叉验证就越能代表真实结果。

3)这是否被认为是一个压倒性的过度拟合问题?还是温和?我应该做更多的特征工程还是更多地调整超参数?(当前超参数使用 GridSearchCV )

好吧,它是否压倒性是一个见仁见智的问题。然而,在我看来,对 300% 的验证集的损失确实是非常严重的过度拟合,但是如果您的验证RMSE 仍在您愿意接受的误差范围内,那么我想您可以继续使用无论如何。这真的是你的电话。

为了减少过度拟合,您需要更好地调整超参数。减少max_depth和增加min_samples_split是我通常使用树木的方法。如果您的修改后的模型(没有过度拟合或至少显着减少过度拟合)的交叉验证分数对您来说太低,您应该在此时返回特征工程。通过绘制调整超参数的效果,我们可以很容易地突出超参数对过拟合的影响:

from xgboost import XGBClassifier, XGBRegressor
import pandas as pd
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from sklearn.preprocessing import OneHotEncoder

df = pd.read_csv(r'https://raw.githubusercontent.com/mwaskom/seaborn-data/master/diamonds.csv', index_col=None)

ohe = OneHotEncoder(sparse=False)
x = pd.DataFrame(ohe.fit_transform(df[['cut', 'color', 'clarity']]))

df.drop(['cut', 'color', 'clarity'], axis=1, inplace=True)

df = df.merge(x, left_index=True, right_index=True)

x = df.drop('price', axis=1)
y = df.price

cv = {}
for i in range(20):
    xgb=XGBRegressor(max_depth=i+1)
    x_train, x_test, y_train,  y_test = train_test_split(x, y)
    xgb.fit(x_train, y_train)
    cv[i+1] = (xgb.score(x_train, y_train), xgb.score(x_test, y_test))

train = []
test = []
for i in cv.keys():
    train.append(cv[i][0])
    test.append(cv[i][1])

fig = plt.figure()
plt.plot(train)
plt.plot(test)
plt.legend(['train', 'test'])
plt.xlabel('max_depth')
plt.xlim(0, 20)
plt.ylabel('R^2 Score')
plt.show()

在此处输入图像描述

为愚蠢的 x 轴标签道歉:P。我们可以看到max_depth超参数对模型容易过拟合的趋势的影响;任何超过 3-ish 的东西和训练/测试分数开始出现分歧。

希望这些修订对您更有帮助:)