如何在scikit-learn中正确计算交叉验证分数?

3

我正在进行一个分类任务。然而,我得到的结果略有不同:

#First Approach
kf = KFold(n=len(y), n_folds=10, shuffle=True, random_state=False)
pipe= make_pipeline(SVC())
for train_index, test_index in kf:
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]

print ('Precision',np.mean(cross_val_score(pipe, X_train, y_train, scoring='precision')))



#Second Approach
clf.fit(X_train,y_train)
y_pred = clf.predict(X_test)
print ('Precision:', precision_score(y_test, y_pred,average='binary'))

#Third approach
pipe= make_pipeline(SCV())
print('Precision',np.mean(cross_val_score(pipe, X, y, cv=kf, scoring='precision')))

#Fourth approach

pipe= make_pipeline(SVC())
print('Precision',np.mean(cross_val_score(pipe, X_train, y_train, cv=kf, scoring='precision')))

输出:

Precision: 0.780422106837
Precision: 0.782051282051
Precision: 0.801544091998

/usr/local/lib/python3.5/site-packages/sklearn/cross_validation.py in cross_val_score(estimator, X, y, scoring, cv, n_jobs, verbose, fit_params, pre_dispatch)
   1431                                               train, test, verbose, None,
   1432                                               fit_params)
-> 1433                       for train, test in cv)
   1434     return np.array(scores)[:, 0]
   1435 

/usr/local/lib/python3.5/site-packages/sklearn/externals/joblib/parallel.py in __call__(self, iterable)
    798             # was dispatched. In particular this covers the edge
    799             # case of Parallel used with an exhausted iterator.
--> 800             while self.dispatch_one_batch(iterator):
    801                 self._iterating = True
    802             else:

/usr/local/lib/python3.5/site-packages/sklearn/externals/joblib/parallel.py in dispatch_one_batch(self, iterator)
    656                 return False
    657             else:
--> 658                 self._dispatch(tasks)
    659                 return True
    660 

/usr/local/lib/python3.5/site-packages/sklearn/externals/joblib/parallel.py in _dispatch(self, batch)
    564 
    565         if self._pool is None:
--> 566             job = ImmediateComputeBatch(batch)
    567             self._jobs.append(job)
    568             self.n_dispatched_batches += 1

/usr/local/lib/python3.5/site-packages/sklearn/externals/joblib/parallel.py in __init__(self, batch)
    178         # Don't delay the application, to avoid keeping the input
    179         # arguments in memory
--> 180         self.results = batch()
    181 
    182     def get(self):

/usr/local/lib/python3.5/site-packages/sklearn/externals/joblib/parallel.py in __call__(self)
     70 
     71     def __call__(self):
---> 72         return [func(*args, **kwargs) for func, args, kwargs in self.items]
     73 
     74     def __len__(self):

/usr/local/lib/python3.5/site-packages/sklearn/externals/joblib/parallel.py in <listcomp>(.0)
     70 
     71     def __call__(self):
---> 72         return [func(*args, **kwargs) for func, args, kwargs in self.items]
     73 
     74     def __len__(self):

/usr/local/lib/python3.5/site-packages/sklearn/cross_validation.py in _fit_and_score(estimator, X, y, scorer, train, test, verbose, parameters, fit_params, return_train_score, return_parameters, error_score)
   1522     start_time = time.time()
   1523 
-> 1524     X_train, y_train = _safe_split(estimator, X, y, train)
   1525     X_test, y_test = _safe_split(estimator, X, y, test, train)
   1526 

/usr/local/lib/python3.5/site-packages/sklearn/cross_validation.py in _safe_split(estimator, X, y, indices, train_indices)
   1589                 X_subset = X[np.ix_(indices, train_indices)]
   1590         else:
-> 1591             X_subset = safe_indexing(X, indices)
   1592 
   1593     if y is not None:

/usr/local/lib/python3.5/site-packages/sklearn/utils/__init__.py in safe_indexing(X, indices)
    161                                    indices.dtype.kind == 'i'):
    162             # This is often substantially faster than X[indices]
--> 163             return X.take(indices, axis=0)
    164         else:
    165             return X[indices]

IndexError: index 900 is out of bounds for size 900

所以,我的问题是上述哪种方法是正确计算交叉验证指标的方法?我相信我的分数已经受到污染,因为我不确定何时执行交叉验证。因此,有任何关于如何正确执行交叉验证得分的想法吗?
更新
在训练步骤中进行评估?
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = False)
clf = make_pipeline(SVC())
# However, fot clf, you can use whatever estimator you like
kf = StratifiedKFold(y = y_train, n_folds=10, shuffle=True, random_state=False)
scores = cross_val_score(clf, X_train, y_train, cv = kf, scoring='precision')
print('Mean score : ', np.mean(scores))
print('Score variance : ', np.var(scores))
2个回答

4

对于任何分类任务,使用StratifiedKFold交叉验证拆分总是很好的选择。在Stratified KFold中,您的分类问题从每个类别中具有相同数量的样本。

StratifiedKFold

然后这取决于你的分类问题类型。查看精确度和召回率得分总是很好的选择。在偏斜二元分类情况下,人们倾向于使用ROC AUC得分:

from sklearn import metrics
metrics.roc_auc_score(ytest, ypred)

让我们来看一下你的解决方案:
import numpy as np
from sklearn.cross_validation import cross_val_score
from sklearn.metrics import precision_score
from sklearn.cross_validation import KFold
from sklearn.pipeline import make_pipeline
from sklearn.svm import SVC

np.random.seed(1337)

X = np.random.rand(1000,5)

y = np.random.randint(0,2,1000)

kf = KFold(n=len(y), n_folds=10, shuffle=True, random_state=42)
pipe= make_pipeline(SVC(random_state=42))
for train_index, test_index in kf:
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]

print ('Precision',np.mean(cross_val_score(pipe, X_train, y_train, scoring='precision')))
# Here you are evaluating precision score on X_train.

#Second Approach
clf = SVC(random_state=42)
clf.fit(X_train,y_train)
y_pred = clf.predict(X_test)
print ('Precision:', precision_score(y_test, y_pred, average='binary'))

# here you are evaluating precision score on X_test

#Third approach
pipe= make_pipeline(SVC())
print('Precision',np.mean(cross_val_score(pipe, X, y, cv=kf, scoring='precision')))

# Here you are splitting the data again and evaluating mean on each fold

因此,结果是不同的。

谢谢你的帮助,关于cross_val_score,你知道为什么我得到了不同的结果,哪种方法是正确的计算方式吗? - john doe
1
可能是由于随机种子的原因。你尝试设置random_state参数看看会发生什么吗? - Abhishek Thakur
是的,在我开始之前,我执行了以下随机操作:np.random.seed(1337) - john doe
1
这与种子无关。这是因为每种方法都在做不同的事情 :) 然而,如果没有指定种子,你将在每次交叉验证中得到略有不同的结果,并且在交叉验证的平均值上也会有(非常)轻微的差异。如果使用相同的种子,每次运行实验时都会得到完全相同的结果 - 由于过拟合的原因,我不建议这样做。 - Igor OA
谢谢,两个答案都很好,虽然我想接受两个,但我会接受得票更多的那个。 - john doe

3
首先,如文档所解释并在一些示例中展示的那样,scikit-learn的交叉验证cross_val_score执行以下操作:
  1. 根据参数cv将数据集X分成N个折叠。它相应地拆分标签y
  2. 使用估计器(参数estimator)在N-1个前面的折叠上进行训练。
  3. 使用估计器预测最后一个折叠的标签。
  4. 通过比较预测和真实值返回得分(参数scoring
  5. 通过更改测试折叠来重复步骤2到步骤4。因此,您最终会得到一个包含N个分数的数组。

现在我们来看看你的每种方法。

第一种方法:

为什么在交叉验证之前要将训练集分割,因为scikit-learn函数已经帮你做好了?这样,你会在更少的数据上训练模型,最终得到一个不那么好的验证分数。
第二种方法:在数据上使用与cross_validation_score不同的度量标准。因此,您无法将其与其他验证分数进行比较,因为它们是两个不同的东西。一个是经典的错误百分比,而precision是用于校准二进制分类器(true或false)的度量标准。这是一个很好的指标(您可以检查ROC曲线、精度和召回率指标),但只比较这些指标。
第三种方法:这是更自然的方法。这个分数是“好”的(如果您想将其与其他分类器/估计器进行比较)。但是,我要警告您不要直接将平均值作为结果。因为有两件事情可以比较:平均值和方差。数组中的每个分数都不同,您可能想知道它与其他估计器相比有多少不同(您肯定希望方差尽可能小)。
第四种方法:

似乎存在一个问题,与Kfold有关但与cross_val_score无关。

最后:

只使用第二个第三种方法来比较估计器。但它们肯定不会估计相同的东西-精度与误差率。

clf = make_pipeline(SVC())
# However, fot clf, you can use whatever estimator you like
scores = cross_val_score(clf, X, y, cv = 10, scoring='precision')
print('Mean score : ', np.mean(scores))
print('Score variance : ', np.var(scores))

通过将clf更改为另一个估计器(或将其集成到循环中),您将能够为每个估计器获得得分并进行比较。

2
kf 在整个数据集上运行,即 len(y),并且您正在使用 X_train、y_train。 - Abhishek Thakur
1
是的,绝对没错 :) 我认为你想要做的是 cv = 10(而不是 cv = kf)。 - Igor OA
谢谢大家的帮助,我有点困惑...我阅读了文档,但并没有理解哪种是标准正确的方式...有人能给个例子吗? - john doe
1
文档说明:“对于整数/无输入,如果y是二进制或多类,则使用StratifiedKFold”。因此,默认情况下,如果“cv = 10”并且您的数据表示类,则默认使用StratifiedKFold。我不明白“我想在测试步骤中进行评估”的意思-测试步骤已集成在“cross_val_score”中,但如果您只想进行一次拆分,请使用“train_test_split”,这在交叉验证中可能不是一个好的做法。 - Igor OA
1
你为什么想要这样做呢?我认为如果你想比较估计器的性能,cross_val_score已经很好地设计了。 - Igor OA
显示剩余5条评论

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