交叉验证中使用scoring='roc_auc'和roc_auc_score有什么区别?

16

我对cross_val_score中的'roc_auc'评分指标和可以直接导入并调用的roc_auc_score之间的差异感到困惑。

文档(http://scikit-learn.org/stable/modules/model_evaluation.html#scoring-parameter)表明,指定scoring ='roc_auc'将使用sklearn.metrics.roc_auc_score。但是,当我使用scoring ='roc_auc'实现GridSearchCV或cross_val_score时,与直接调用roc_auc_score时相比,我收到非常不同的数字。

以下是我的代码,以帮助演示我看到的内容:

# score the model using cross_val_score

rf = RandomForestClassifier(n_estimators=150,
                            min_samples_leaf=4,
                            min_samples_split=3,
                            n_jobs=-1)

scores = cross_val_score(rf, X, y, cv=3, scoring='roc_auc')

print scores
array([ 0.9649023 ,  0.96242235,  0.9503313 ])

# do a train_test_split, fit the model, and score with roc_auc_score

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)
rf.fit(X_train, y_train)

print roc_auc_score(y_test, rf.predict(X_test))
0.84634039111363313 # quite a bit different than the scores above!

我觉得我在这里缺少了一些非常简单的东西——很可能是我在实现/解释其中一个评分指标时犯了错误。

有人能否解释一下这两个评分指标之间差异的原因?


我也对这个差异感到非常困惑。我也尝试使用标准的make_scorer()函数,将一个得分函数转换为正确的Scorer对象,用于cross_val_score,但结果是相同的。make_scorer()给出的结果与我的手动实现相同,而'roc_auc'给出了更高的分数。幸运的是,在我的例子中,差异达到了几个百分点,不像你的例子那样大,但仍然存在疑问:我应该信任哪个函数? - Anton Fetisov
3个回答

13

这是因为您提供给 roc_auc_score 函数的是预测的 y 值,而不是概率值。该函数需要一个分数作为输入,而不是分类标签。请尝试使用以下代码:

print roc_auc_score(y_test, rf.predict_proba(X_test)[:,1])

它应该提供与cross_val_score先前结果类似的结果。有关更多信息,请参见此帖子


3
你说得完全正确!如果我能停止哭泣,我会笑出声的。谢谢! - MichaelHood

6
我刚遇到一个类似的问题here。关键是,cross_val_score使用KFold策略和默认参数进行训练集-测试集拆分,这意味着连续块的拆分而不是洗牌。另一方面,train_test_split做了一个洗牌拆分。
解决方法是让拆分策略明确,并指定洗牌,像这样:
shuffle = cross_validation.KFold(len(X), n_folds=3, shuffle=True)
scores = cross_val_score(rf, X, y, cv=shuffle, scoring='roc_auc')

1
Aniket,感谢您的回答。但是指定折叠并将它们传递到cross_val_score中并没有解决评分指标之间的差异。 - MichaelHood
我知道我来得很晚,但是当你使用cross_val_score方法并使用roc_auc评分时,为什么要传递预测类别标签而不是预测概率?既然是AUC,难道不需要概率值,以便可以测试不同的阈值吗? - NeonBlueHair

1

我自己遇到了这个问题,经过一番探索找到了答案。在此分享给大家。

实际上有两个半问题。

  1. 你需要使用相同的K折验证来比较分数(即相同的训练/测试划分);
  2. 你需要将概率传递到roc_auc_score中(使用predict_proba()方法)。但是,一些估计器(如SVC)没有predict_proba()方法,你可以使用decision_function()方法。

下面是一个完整的示例:

# Let's use the Digit dataset
digits = load_digits(n_class=4)
X,y = digits.data, digits.target
y[y==2] = 0 # Increase problem dificulty
y[y==3] = 1 # even more

使用两个估计器
LR = LogisticRegression()
SVM = LinearSVC()

将训练集和测试集进行分离。但要将其存储在可以重复使用的变量中。

fourfold = StratifiedKFold(n_splits=4, random_state=4)

将其传递给GridSearchCV并保存分数。请注意,我们正在传递fourfold

gs = GridSearchCV(LR, param_grid={}, cv=fourfold, scoring='roc_auc', return_train_score=True)
gs.fit(X,y)
gs_scores = np.array([gs.cv_results_[k][0] for k in gskeys])

将其输入到cross_val_score中并保存分数。
 cv_scores = cross_val_score(LR, X, y, cv=fourfold, scoring='roc_auc')

有时您想循环计算多个不同的分数,这就是您使用的方法。
loop_scores = list()
for idx_train, idx_test in fourfold.split(X, y):
  X_train, y_train, X_test, y_test = X[idx_train], y[idx_train], X[idx_test], y[idx_test]
  LR.fit(X_train, y_train)
  y_prob = LR.predict_proba(X_test)
  auc = roc_auc_score(y_test, y_prob[:,1])
  loop_scores.append(auc)

我们在各个方面的分数相同吗?

print [((a==b) and (b==c)) for a,b,c in zip(gs_scores,cv_scores,loop_scores)]
>>> [True, True, True, True]


但是,有时我们的估算器没有predict_proba()方法。因此,根据这个example,我们这样做:

for idx_train, idx_test in fourfold.split(X, y):
  X_train, y_train, X_test, y_test = X[idx_train], y[idx_train], X[idx_test], y[idx_test]
  SVM.fit(X_train, y_train)
  y_prob = SVM.decision_function(X_test)
  prob_pos = (y_prob - y_prob.min()) / (y_prob.max() - y_prob.min())
  auc = roc_auc_score(y_test, prob_pos)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接