在训练玩具 Xgboost 模型时手动复制 cross_val_score 会导致奇怪的结果

Manually replicating cross_val_score leads to strange resutls when training a toy Xgboost model

我在超调 XGboost 玩具模型时尝试复制 cross_val_score() 的结果。

我使用代码 NO.1 进行交叉验证,其结果用作基准,然后使用代码 NO.2 和 NO.3 通过手动编程交叉验证循环来复制 CV 结果。

2号代码和3号代码的主要区别是我把XGboost分类器的初始化放在了3号代码的for循环之外但是inside 代码 NO.2 中的 for 循环。我预计只有代码 NO.2(循环内版本)产生与自动 cross_val_score 得到的结果相同的结果。令我惊讶的是,所有三个版本的代码共享相同的结果。

我的问题是:我们不应该像 cross_val_score 的源代码中提到的那样为每个验证克隆模型吗?在代码 3 中,经过训练的 Xgboost 模型在验证中不是独立的,对吗?本着交叉验证的精神,非独立性 NOT 不是吗?但是为什么我从他们那里得到了相同的结果?

代码NO.1

params = {
    'objective': 'binary:logistic',
    'eval_metric': 'auc'
}
model = XGBClassifier(**params)
kfold = StratifiedKFold(n_splits=N_SPLITS, random_state=SEED)
results = cross_val_score(model, X, Y, scoring='accuracy', cv=kfold) # only a single metric is permitted. model is cloned not relay across folds.
print(f'Accuracy: {results.mean()*100:.4f}% ({results.std()*100:.3f})')

代码2

x_train = all_df.drop('Survived', axis=1).iloc[:train_rows].values 
y_train = train_label.iloc[:train_rows].values
y_oof = np.zeros(x_train.shape[0])
acc_scores = []
kfold = StratifiedKFold(n_splits=N_SPLITS, random_state=SEED)
for i, (train_index, valid_index) in enumerate(kfold.split(x_train, y_train)):
    model = XGBClassifier(**params) # <=======================================
    X_A, X_B = x_train[train_index, :], x_train[valid_index, :]
    y_A, y_B = y_train[train_index], y_train[valid_index]
    model.fit(X_A, y_A, eval_set=[(X_B, y_B)])
    y_oof[valid_index] = model.predict(X_B)
    acc_scores.append(accuracy_score(y_B, y_oof[valid_index]))

代码3

x_train = all_df.drop('Survived', axis=1).iloc[:train_rows].values 
y_train = train_label.iloc[:train_rows].values
y_oof = np.zeros(x_train.shape[0])
acc_scores = []
kfold = StratifiedKFold(n_splits=N_SPLITS, random_state=SEED)
model = XGBClassifier(**params) # <=======================================
for i, (train_index, valid_index) in enumerate(kfold.split(x_train, y_train)):
    X_A, X_B = x_train[train_index, :], x_train[valid_index, :]
    y_A, y_B = y_train[train_index], y_train[valid_index]
    model.fit(X_A, y_A, eval_set=[(X_B, y_B)])
    y_oof[valid_index] = model.predict(X_B)
    acc_scores.append(accuracy_score(y_B, y_oof[valid_index]))

当您在 XGBClassifier 实例(或理想情况下,任何与 sklearn 兼容的估计器)上调用 fit 时,学习 从头开始​​ ,因此这些模型在验证中确实是独立的。

当然,重新初始化或克隆模型会稍微安全一些,尤其是当您不确定实施是否保留任何信息以供使用时。 cross_val_scorecross_validate 的包装器,实际上需要克隆,以防 return_estimator=True,因此需要保存模型的各种副本。