天天看點

模型評估—交叉驗證3. sklearn的交叉驗證

目錄

1.The Validation Set Approach

2.Cross-Validation

3. sklearn的交叉驗證

3.1. 計算交叉驗證的名額

3.1.1. cross_validate 函數和多度量評估

3.1.2. 通過交叉驗證擷取預測

3.2. 交叉驗證疊代器

3.3. 交叉驗證疊代器–循環周遊資料

3.3.1. K 折

3.1.3.2. 重複 K-折交叉驗證

3.3.3. 留一交叉驗證 (LOO)

3.3.4. 留 P 交叉驗證 (LPO)

3.3.5. 随機排列交叉驗證 a.k.a. Shuffle & Split

3.4. 基于類标簽、具有分層的交叉驗證疊代器

3.4.1. 分層 k 折

3.4.2. 分層随機 Split

3.5. 用于分組資料的交叉驗證疊代器

3.5.1. 組 k-fold

3.5.2. 留一組交叉驗證

3.5.3. 留 P 組交叉驗證

3.5.4. Group Shuffle Split

3.6. 預定義的折疊 / 驗證集

3.7. 交叉驗證在時間序列資料中應用

3.8. A note on shuffling

3.9. 交叉驗證和模型選擇

學習預測函數的參數,并在相同資料集上進行測試是一種錯誤的做法: 一個僅給出測試用例标簽的模型将會獲得極高的分數,但對于尚未出現過的資料它則無法預測出任何有用的資訊。 這種情況稱為 overfitting(過拟合). 為了避免這種情況,在進行(監督)機器學習實驗時,通常取出部分可利用資料作為 test set(測試資料集) x

_test, y_test

。需要強調的是這裡說的 “experiment(實驗)” 并不僅限于學術(academic),因為即使是在商業場景下機器學習也往往是從實驗開始的。

在機器學習裡,通常來說我們不能将全部資料用于訓練模型,否則我們将沒有資料集對該模型進行驗證,進而評估我們的模型的預測效果。為了解決這一問題,有如下常用的方法。

1.The Validation Set Approach

第一種是最簡單的,也是很容易就想到的。我們可以把整個資料集分成兩部分,一部分用于訓練,一部分用于驗證,這也就是我們經常提到的訓練集(training set)和測試集(test set)。

模型評估—交叉驗證3. sklearn的交叉驗證

例如,如上圖所示,我們可以将藍色部分的資料作為訓練集(包含7、22、13等資料),将右側的資料作為測試集(包含91等),這樣通過在藍色的訓練集上訓練模型,在測試集上觀察不同模型不同參數對應的MSE的大小,就可以合适選擇模型和參數了。不過,這個簡單的方法存在兩個弊端:

1.最終模型與參數的選取将極大程度依賴于你對訓練集和測試集的劃分方法。什麼意思呢?我們再看一張圖:

模型評估—交叉驗證3. sklearn的交叉驗證

右邊是十種不同的訓練集和測試集劃分方法得到的test MSE,可以看到,在不同的劃分方法下,test MSE的變動是很大的,而且對應的最優degree也不一樣。是以如果我們的訓練集和測試集的劃分方法不夠好,很有可能無法選擇到最好的模型與參數。

2.該方法隻用了部分資料進行模型的訓練

我們都知道,當用于模型訓練的資料量越大時,訓練出來的模型通常效果會越好。是以訓練集和測試集的劃分意味着我們無法充分利用我們手頭已有的資料,是以得到的模型效果也會受到一定的影響。

基于這樣的背景,有人就提出了Cross-Validation方法,也就是交叉驗證。

2.Cross-Validation

2.1 LOOCV

首先,我們先介紹LOOCV方法,即(Leave-one-out cross-validation)。像Test set approach一樣,LOOCV方法也包含将資料集分為訓練集和測試集這一步驟。但是不同的是,我們現在隻用一個資料作為測試集,其他的資料都作為訓練集,并将此步驟重複N次(N為資料集的資料數量)。

模型評估—交叉驗證3. sklearn的交叉驗證

如上圖所示,假設我們現在有n個資料組成的資料集,那麼LOOCV的方法就是每次取出一個資料作為測試集的唯一進制素,而其他n-1個資料都作為訓練集用于訓練模型和調參。結果就是我們最終訓練了n個模型,每次都能得到一個MSE。而計算最終test MSE則就是将這n個MSE取平均。

模型評估—交叉驗證3. sklearn的交叉驗證
模型評估—交叉驗證3. sklearn的交叉驗證

比起test set approach,LOOCV有很多優點。首先它不受測試集合訓練集劃分方法的影響,因為每一個資料都單獨的做過測試集。同時,其用了n-1個資料訓練模型,也幾乎用到了所有的資料,保證了模型的bias更小。不過LOOCV的缺點也很明顯,那就是計算量過于大,是test set approach耗時的n-1倍。

為了解決計算成本太大的弊端,又有人提供了下面的式子,使得LOOCV計算成本和隻訓練一個模型一樣快。

模型評估—交叉驗證3. sklearn的交叉驗證

其中

模型評估—交叉驗證3. sklearn的交叉驗證

表示第i個拟合值,而

模型評估—交叉驗證3. sklearn的交叉驗證

則表示leverage。關于

模型評估—交叉驗證3. sklearn的交叉驗證

的計算方法詳見線性回歸的部分(以後會涉及)。

2.2 K-fold Cross Validation

另外一種折中的辦法叫做K折交叉驗證,和LOOCV的不同在于,我們每次的測試集将不再隻包含一個資料,而是多個,具體數目将根據K的選取決定。比如,如果K=5,那麼我們利用五折交叉驗證的步驟就是:

  1. 将所有資料集分成5份
  2. 不重複地每次取其中一份做測試集,用其他四份做訓練集訓練模型,之後計算該模型在測試集上的
    模型評估—交叉驗證3. sklearn的交叉驗證
  3. 将5次的
    模型評估—交叉驗證3. sklearn的交叉驗證
    ​​​​​​​取平均得到最後的MSE
模型評估—交叉驗證3. sklearn的交叉驗證

不難了解,其實LOOCV是一種特殊的K-fold Cross Validation(K=N)。再來看一組圖:

模型評估—交叉驗證3. sklearn的交叉驗證

每一幅圖中藍色表示的真實的test MSE,而黑色虛線和橙線則分貝表示的是LOOCV方法和10-fold CV方法得到的test MSE。我們可以看到事實上LOOCV和10-fold CV對test MSE的估計是很相似的,但是相比LOOCV,10-fold CV的計算成本卻小了很多,耗時更少。

2.3 Bias-Variance Trade-Off for k-Fold Cross-Validation

最後,我們要說說K的選取。事實上,和開頭給出的文章裡的部分内容一樣,K的選取是一個Bias和Variance的trade-off(權衡)。K越大,每次投入的訓練集的資料越多,模型的Bias越小。但是K越大,又意味着每一次選取的訓練集之前的相關性越大(考慮最極端的例子,當k=N,也就是在LOOCV裡,每次都訓練資料幾乎是一樣的)。而這種大相關性會導緻最終的test error具有更大的Variance。一般來說,根據經驗我們一般選擇k=5或10。

2.4 Cross-Validation on Classification Problems

上面我們講的都是回歸問題,是以用MSE來衡量test error。如果是分類問題,那麼我們可以用以下式子來衡量Cross-Validation的test error:

模型評估—交叉驗證3. sklearn的交叉驗證

其中Erri表示的是第i個模型在第i組測試集上的分類錯誤的個數。

3. sklearn的交叉驗證

利用 scikit-learn 包中的 

train_test_split

 輔助函數可以很快地将實驗資料集劃分為任何訓練集(training sets)和測試集(test sets)。 下面讓我們載入 iris 資料集,并在此資料集上訓練出線性支援向量機:

>>> import numpy as np
>>> from sklearn.model_selection import train_test_split
>>> from sklearn import datasets
>>> from sklearn import svm

>>> iris = datasets.load_iris()
>>> iris.data.shape, iris.target.shape
((150, 4), (150,))

           

我們能快速采樣到原資料集的 40% 作為測試集,進而測試(評估)我們的分類器:

>>> X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.4, random_state=0)

>>> X_train.shape, y_train.shape
((90, 4), (90,))
>>> X_test.shape, y_test.shape
((60, 4), (60,))

>>> clf = svm.SVC(kernel='linear', C=1).fit(X_train, y_train)
>>> clf.score(X_test, y_test)                           
0.96...

           

當評價估計器的不同設定(”hyperparameters(超參數)”)時,例如手動為 SVM 設定的 

C

 參數, 由于在訓練集上,通過調整參數設定使估計器的性能達到了最佳狀态;但 在測試集上 可能會出現過拟合的情況。 此時,測試集上的資訊回報足以颠覆訓練好的模型,評估的名額不再有效反映出模型的泛化性能。 為了解決此類問題,還應該準備另一部分被稱為 “validation set(驗證集)” 的資料集,模型訓練完成以後在驗證集上對模型進行評估。 當驗證集上的評估實驗比較成功時,在測試集上進行最後的評估。

然而,通過将原始資料分為3個資料集合,我們就大大減少了可用于模型學習的樣本數量, 并且得到的結果依賴于集合對(訓練、驗證)的随機選擇。這個問題可以通過 交叉驗證(CV 縮寫) 來解決。 交叉驗證仍需要測試集做最後的模型評估,但不再需要驗證集。最基本的方法被稱之為k-折交叉驗證 。 k-折交叉驗證将訓練集劃分為 k 個較小的集合(其他方法會在下面描述,主要原則基本相同)。 每一個 k 折都會遵循下面的過程:

  • 将 
    模型評估—交叉驗證3. sklearn的交叉驗證
     份訓練集子集作為 training data (訓練集)訓練模型,
  • 将剩餘的 1 份訓練集子集作為驗證集用于模型驗證(也就是利用該資料集計算模型的性能名額,例如準确率)。

k-折交叉驗證得出的性能名額是循環計算中每個值的平均值。 該方法雖然計算代價很高,但是它不會浪費太多的資料(如固定任意測試集的情況一樣), 在處理樣本資料集較少的問題(例如逆向推理)時比較有優勢。

3.1. 計算交叉驗證的名額

使用交叉驗證最簡單的方法是在估計器和資料集上調用 

cross_val_score

 輔助函數。下面的例子展示了如何通過分割資料,拟合模型和計算連續 5 次的分數(每次不同分割)來估計 linear kernel 支援向量機在 iris 資料集上的精度:

>>> from sklearn.model_selection import cross_val_score
>>> clf = svm.SVC(kernel='linear', C=1)
>>> scores = cross_val_score(clf, iris.data, iris.target, cv=5)
>>> scores                                              
array([ 0.96...,  1\.  ...,  0.96...,  0.96...,  1\.        ])

           

評分估計的平均得分和 95% 置信區間由此給出:

>>> print("Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))
Accuracy: 0.98 (+/- 0.03)

           

預設情況下,每個 CV 疊代計算的分數是估計器的 

score

 方法。可以通過使用 scoring 參數來改變計算方式如下:

>>> from sklearn import metrics
>>> scores = cross_val_score( clf, iris.data, iris.target, cv=5, scoring='f1_macro')
>>> scores                                              
array([ 0.96...,  1\.  ...,  0.96...,  0.96...,  1\.        ])

           

詳情請參閱 scoring 參數: 定義模型評估規則 。 在 Iris 資料集的情形下,樣本在各個目标類别之間是平衡的,是以準确度和 F1-score 幾乎相等。當 

cv

 參數是一個整數時, 

cross_val_score

 預設使用 

KFold

 或 

StratifiedKFold

 政策,後者會在估計器派生自 

ClassifierMixin

 時使用。也可以通過傳入一個交叉驗證疊代器來使用其他交叉驗證政策,比如:

>>> from sklearn.model_selection import ShuffleSplit
>>> n_samples = iris.data.shape[0]
>>> cv = ShuffleSplit(n_splits=3, test_size=0.3, random_state=0)
>>> cross_val_score(clf, iris.data, iris.target, cv=cv)
...                                                     
array([ 0.97...,  0.97...,  1\.        ])

           

保留資料的資料轉換:正如在訓練集中保留的資料上測試一個 predictor (預測器)是很重要的一樣,預處理(如标準化,特征選擇等)和類似的 data transformations 也應該從訓練集中學習,并應用于預測資料以進行預測:

>>> from sklearn import preprocessing
>>> X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.4, random_state=0)
>>> scaler = preprocessing.StandardScaler().fit(X_train)
>>> X_train_transformed = scaler.transform(X_train)
>>> clf = svm.SVC(C=1).fit(X_train_transformed, y_train)
>>> X_test_transformed = scaler.transform(X_test)
>>> clf.score(X_test_transformed, y_test)  
0.9333...

           

Pipeline

 可以更容易地組合估計器,在交叉驗證下使用如下:

>>> from sklearn.pipeline import make_pipeline
>>> clf = make_pipeline(preprocessing.StandardScaler(), svm.SVC(C=1))
>>> cross_val_score(clf, iris.data, iris.target, cv=cv)
...                                                 
array([ 0.97...,  0.93...,  0.95...])

           

可以參閱 Pipeline(管道)和 FeatureUnion(特征聯合): 合并的評估器.

3.1.1. cross_validate 函數和多度量評估

cross_validate

 函數與 

cross_val_score

 在下面的兩個方面有些不同 -

  • 它允許指定多個名額進行評估.
  • 除了測試得分之外,它還會傳回一個包含訓練得分,拟合次數, score-times (得分次數)的一個字典。 It returns a dict containing training scores, fit-times and score-times in addition to the test score.
  • 對于單個度量評估,其中 scoring 參數是一個字元串,可以調用或 None , keys 将是 - 

    ['test_score', 'fit_time', 'score_time']

  • 而對于多度量評估,傳回值是一個帶有以下的 keys 的字典 - 

    ['test_<scorer1_name>', 'test_<scorer2_name>','test_<scorer...>', 'fit_time', 'score_time']

return_train_score

 預設設定為 

True

 。 它增加了所有 scorers(得分器) 的訓練得分 keys 。如果不需要訓練 scores ,則應将其明确設定為 

False

 。

可以将多個名額指定為 predefined scorer names(預定義的得分器的名稱) list ,tuple 或者 set

>>> from sklearn.model_selection import cross_validate
>>> from sklearn.metrics import recall_score
>>> scoring = ['precision_macro', 'recall_macro']
>>> clf = svm.SVC(kernel='linear', C=1, random_state=0)
>>> scores = cross_validate(clf, iris.data, iris.target, scoring=scoring,cv=5, return_train_score=False)
>>> sorted(scores.keys())
['fit_time', 'score_time', 'test_precision_macro', 'test_recall_macro']
>>> scores['test_recall_macro']                       
array([ 0.96...,  1\.  ...,  0.96...,  0.96...,  1\.        ])

           

或作為一個字典 mapping 得分器名稱預定義或自定義的得分函數:

>>> from sklearn.metrics.scorer import make_scorer
>>> scoring = {'prec_macro': 'precision_macro','rec_micro': make_scorer(recall_score, average='macro')}
>>> scores = cross_validate(clf, iris.data, iris.target, scoring=scoring,cv=5, return_train_score=True)
>>> sorted(scores.keys())                 
['fit_time', 'score_time', 'test_prec_macro', 'test_rec_micro',
 'train_prec_macro', 'train_rec_micro']
>>> scores['train_rec_micro']                         
array([ 0.97...,  0.97...,  0.99...,  0.98...,  0.98...])

           

這裡是一個使用單一名額的 

cross_validate

 的例子:

>>> scores = cross_validate(clf, iris.data, iris.target,scoring='precision_macro')
>>> sorted(scores.keys())
['fit_time', 'score_time', 'test_score', 'train_score']

           

3.1.2. 通過交叉驗證擷取預測

除了傳回結果不同,函數 

cross_val_predict

 具有和 

cross_val_score

 相同的接口, 對于每一個輸入的元素,如果其在測試集合中,将會得到預測結果。交叉驗證政策會将可用的元素送出到測試集合有且僅有一次(否則會抛出一個異常)。這些預測可以用于評價分類器的效果:

>>> from sklearn.model_selection import cross_val_predict
>>> predicted = cross_val_predict(clf, iris.data, iris.target, cv=10)
>>> metrics.accuracy_score(iris.target, predicted) 
0.973...

           

注意,這個計算的結果和 

cross_val_score

 有輕微的差别,因為後者用另一種方式組織元素。可用的交叉驗證疊代器在下面的部分中。

示例

  • Receiver Operating Characteristic (ROC) with cross validation,
  • Recursive feature elimination with cross-validation,
  • Parameter estimation using grid search with cross-validation,
  • Sample pipeline for text feature extraction and evaluation,
  • 繪制交叉驗證預測圖,
  • Nested versus non-nested cross-validation.

3.2. 交叉驗證疊代器

接下來的部分列出了一些用于生成索引标号,用于在不同的交叉驗證政策中生成資料劃分的工具。

3.3. 交叉驗證疊代器–循環周遊資料

假設一些資料是獨立的和相同分布的 (i.i.d) 假定所有的樣本來源于相同的生成過程,并假設生成過程沒有記憶過去生成的樣本。在這種情況下可以使用下面的交叉驗證器。

注意:而 i.i.d 資料是機器學習理論中的一個常見假設,在實踐中很少成立。如果知道樣本是使用時間相關的過程生成的,則使用 time-series aware cross-validation scheme 更安全。 同樣,如果我們知道生成過程具有 group structure (群體結構)(從不同 subjects(主體) , experiments(實驗), measurement devices (測量裝置)收集的樣本),則使用 group-wise cross-validation 更安全。

3.3.1. K 折

KFold

 将所有的樣例劃分為 

模型評估—交叉驗證3. sklearn的交叉驗證

 個組,稱為折疊 (fold) (如果 

模型評估—交叉驗證3. sklearn的交叉驗證

, 這等價于 Leave One Out(留一) 政策),都具有相同的大小(如果可能)。預測函數學習時使用 

模型評估—交叉驗證3. sklearn的交叉驗證

 個折疊中的資料,最後一個剩下的折疊會用于測試。在 4 個樣例的資料集上使用 2-fold 交叉驗證的例子:

>>> import numpy as np
>>> from sklearn.model_selection import KFold

>>> X = ["a", "b", "c", "d"]
>>> kf = KFold(n_splits=2)
>>> for train, test in kf.split(X):
...     print("%s  %s" % (train, test))
[2 3] [0 1]
[0 1] [2 3]

           

每個折疊由兩個 arrays 組成,第一個作為 training set ,另一個作為 test set 。 由此,可以通過使用 numpy 的索引建立訓練/測試集合:

>>> X = np.array([[0., 0.], [1., 1.], [-1., -1.], [2., 2.]])
>>> y = np.array([0, 1, 0, 1])
>>> X_train, X_test, y_train, y_test = X[train], X[test], y[train], y[test]

           

3.1.3.2. 重複 K-折交叉驗證

RepeatedKFold

 重複 K-Fold n 次。當需要運作時可以使用它 

KFold

 n 次,在每次重複中産生不同的分割。2折 K-Fold 重複 2 次的示例:

>>> import numpy as np
>>> from sklearn.model_selection import RepeatedKFold
>>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
>>> random_state = 12883823
>>> rkf = RepeatedKFold(n_splits=2, n_repeats=2, random_state=random_state)
>>> for train, test in rkf.split(X):
...     print("%s  %s" % (train, test))
...
[2 3] [0 1]
[0 1] [2 3]
[0 2] [1 3]
[1 3] [0 2]

           

類似地, 

RepeatedStratifiedKFold

 在每個重複中以不同的随機化重複 n 次分層的 K-Fold 。

3.3.3. 留一交叉驗證 (LOO)

LeaveOneOut

 (或 LOO) 是一個簡單的交叉驗證。每個學習集都是通過除了一個樣本以外的所有樣本建立的,測試集是被留下的樣本。 是以,對于 

模型評估—交叉驗證3. sklearn的交叉驗證

 個樣本,我們有 

模型評估—交叉驗證3. sklearn的交叉驗證

 個不同的訓練集和 

模型評估—交叉驗證3. sklearn的交叉驗證

 個不同的測試集。這種交叉驗證程式不會浪費太多資料,因為隻有一個樣本是從訓練集中删除掉的:

>>> from sklearn.model_selection import LeaveOneOut

>>> X = [1, 2, 3, 4]
>>> loo = LeaveOneOut()
>>> for train, test in loo.split(X):
...     print("%s  %s" % (train, test))
[1 2 3] [0]
[0 2 3] [1]
[0 1 3] [2]
[0 1 2] [3]

           

LOO 潛在的使用者選擇模型應該權衡一些已知的警告。 當與 

模型評估—交叉驗證3. sklearn的交叉驗證

 折交叉驗證進行比較時,可以從 

模型評估—交叉驗證3. sklearn的交叉驗證

 樣本中建構 

模型評估—交叉驗證3. sklearn的交叉驗證

 模型,而不是 

模型評估—交叉驗證3. sklearn的交叉驗證

 模型,其中 

模型評估—交叉驗證3. sklearn的交叉驗證

 。 此外,每個在 

模型評估—交叉驗證3. sklearn的交叉驗證

 個樣本而不是在 

模型評估—交叉驗證3. sklearn的交叉驗證

 上進行訓練。在兩種方式中,假設 

模型評估—交叉驗證3. sklearn的交叉驗證

 不是太大,并且 

模型評估—交叉驗證3. sklearn的交叉驗證

, LOO 比 

模型評估—交叉驗證3. sklearn的交叉驗證

 折交叉驗證計算開銷更加昂貴。

就精度而言, LOO 經常導緻較高的方差作為測試誤差的估計器。直覺地說,因為 

模型評估—交叉驗證3. sklearn的交叉驗證

 個樣本中的 

模型評估—交叉驗證3. sklearn的交叉驗證

 被用來建構每個模型,折疊建構的模型實際上是相同的,并且是從整個訓練集建立的模型。但是,如果學習曲線對于所讨論的訓練大小是陡峭的,那麼 5- 或 10- 折交叉驗證可以泛化誤差增高。作為一般規則,大多數作者和經驗證據表明, 5- 或者 10- 交叉驗證應該優于 LOO 。

參考文獻:

  • http://www.faqs.org/faqs/ai-faq/neural-nets/part3/section-12.html;
  • T. Hastie, R. Tibshirani, J. Friedman, The Elements of Statistical Learning, Springer 2009
  • L. Breiman, P. Spector Submodel selection and evaluation in regression: The X-random case, International Statistical Review 1992;
  • R. Kohavi, A Study of Cross-Validation and Bootstrap for Accuracy Estimation and Model Selection, Intl. Jnt. Conf. AI
  • R. Bharat Rao, G. Fung, R. Rosales, On the Dangers of Cross-Validation. An Experimental Evaluation, SIAM 2008;
  • G. James, D. Witten, T. Hastie, R Tibshirani, An Introduction to Statistical Learning, Springer 2013.

3.3.4. 留 P 交叉驗證 (LPO)

LeavePOut

 與 

LeaveOneOut

 非常相似,因為它通過從整個集合中删除 

模型評估—交叉驗證3. sklearn的交叉驗證

 個樣本來建立所有可能的 訓練/測試集。對于 

模型評估—交叉驗證3. sklearn的交叉驗證

 個樣本,這産生了 

模型評估—交叉驗證3. sklearn的交叉驗證

 個 訓練-測試 對。與 

LeaveOneOut

 和 

KFold

 不同,當 

模型評估—交叉驗證3. sklearn的交叉驗證

 時,測試集會重疊。

在有 4 個樣例的資料集上使用 Leave-2-Out 的例子:

>>> from sklearn.model_selection import LeavePOut

>>> X = np.ones(4)
>>> lpo = LeavePOut(p=2)
>>> for train, test in lpo.split(X):
...     print("%s  %s" % (train, test))
[2 3] [0 1]
[1 3] [0 2]
[1 2] [0 3]
[0 3] [1 2]
[0 2] [1 3]
[0 1] [2 3]

           

3.3.5. 随機排列交叉驗證 a.k.a. Shuffle & Split

ShuffleSplit

 疊代器 将會生成一個使用者給定數量的獨立的訓練/測試資料劃分。樣例首先被打散然後劃分為一對訓練測試集合。可以通過設定明确的 

random_state

 ,使得僞随機生成器的結果可以重複。這是一個使用的小例子:

>>> from sklearn.model_selection import ShuffleSplit
>>> X = np.arange(5)
>>> ss = ShuffleSplit(n_splits=3, test_size=0.25,
...     random_state=0)
>>> for train_index, test_index in ss.split(X):
...     print("%s  %s" % (train_index, test_index))
...
[1 3 4] [2 0]
[1 4 3] [0 2]
[4 0 2] [1 3]

           

ShuffleSplit

 可以替代 

KFold

 交叉驗證,因為其提供了細緻的訓練 / 測試劃分的 數量和樣例所占的比例等的控制。

3.4. 基于類标簽、具有分層的交叉驗證疊代器

一些分類問題在目标類别的分布上可能表現出很大的不平衡性:例如,可能會出現比正樣本多數倍的負樣本。在這種情況下,建議采用如 

StratifiedKFold

 和 

StratifiedShuffleSplit

 中實作的分層抽樣方法,確定相對的類别頻率在每個訓練和驗證 折疊 中大緻保留。

3.4.1. 分層 k 折

StratifiedKFold

 是 k-fold 的變種,會傳回 stratified(分層) 的折疊:每個小集合中, 各個類别的樣例比例大緻和完整資料集中相同。在有 10 個樣例的,有兩個略不均衡類别的資料集上進行分層 3-fold 交叉驗證的例子:

>>> from sklearn.model_selection import StratifiedKFold

>>> X = np.ones(10)
>>> y = [0, 0, 0, 0, 1, 1, 1, 1, 1, 1]
>>> skf = StratifiedKFold(n_splits=3)
>>> for train, test in skf.split(X, y):
...     print("%s  %s" % (train, test))
[2 3 6 7 8 9] [0 1 4 5]
[0 1 3 4 5 8 9] [2 6 7]
[0 1 2 4 5 6 7] [3 8 9]

           

RepeatedStratifiedKFold

 可用于在每次重複中用不同的随機化重複分層 K-Fold n 次。

3.4.2. 分層随機 Split

StratifiedShuffleSplit

 是 ShuffleSplit 的一個變種,會傳回直接的劃分,比如: 建立一個劃分,但是劃分中每個類的比例和完整資料集中的相同。

3.5. 用于分組資料的交叉驗證疊代器

如果潛在的生成過程産生依賴樣本的 groups ,那麼 i.i.d. 假設将會被打破。這樣的資料分組是特定于域的。一個例子是從多個患者收集醫學資料,從每個患者身上采集多個樣本。而這樣的資料很可能取決于個人群體。 在我們的例子中,每個樣本的患者 ID 将是其 group identifier (組辨別符)。在這種情況下,我們想知道在一組特定的 groups 上訓練的模型是否能很好地适用于看不見的 group 。為了衡量這一點,我們需要確定驗證對象中的所有樣本來自配對訓練折疊中完全沒有表示的組。下面的交叉驗證分離器可以用來做到這一點。 樣本的 grouping identifier (分組辨別符) 通過 

groups

 參數指定。

3.5.1. 組 k-fold

GroupKFold

 是 k-fold 的變體,它確定同一個 group 在測試和訓練集中都不被表示。 例如,如果資料是從不同的 subjects 獲得的,每個 subject 有多個樣本,并且如果模型足夠靈活以高度人物指定的特征中學習,則可能無法推廣到新的 subject 。 

GroupKFold

 可以檢測到這種過拟合的情況。 Imagine you have three subjects, each with an associated number from 1 to 3:

>>> from sklearn.model_selection import GroupKFold

>>> X = [0.1, 0.2, 2.2, 2.4, 2.3, 4.55, 5.8, 8.8, 9, 10]
>>> y = ["a", "b", "b", "b", "c", "c", "c", "d", "d", "d"]
>>> groups = [1, 1, 1, 2, 2, 2, 3, 3, 3, 3]

>>> gkf = GroupKFold(n_splits=3)
>>> for train, test in gkf.split(X, y, groups=groups):
...     print("%s  %s" % (train, test))
[0 1 2 3 4 5] [6 7 8 9]
[0 1 2 6 7 8 9] [3 4 5]
[3 4 5 6 7 8 9] [0 1 2]

           

每個 subject 都處于不同的測試階段,同一個科目從來沒有在測試和訓練過程中。請注意,由于資料不平衡,折疊的大小并不完全相同。

3.5.2. 留一組交叉驗證

LeaveOneGroupOut

 是一個交叉驗證方案,它根據第三方提供的 array of integer groups (整數組的數組)來提供樣本。這個組資訊可以用來編碼任意域特定的預定義交叉驗證折疊。每個訓練集都是由除特定組别以外的所有樣本構成的。例如,在多個實驗的情況下, 

LeaveOneGroupOut

 可以用來根據不同的實驗建立一個交叉驗證:我們使用除去一個實驗的所有實驗的樣本建立一個訓練集:

>>> from sklearn.model_selection import LeaveOneGroupOut

>>> X = [1, 5, 10, 50, 60, 70, 80]
>>> y = [0, 1, 1, 2, 2, 2, 2]
>>> groups = [1, 1, 2, 2, 3, 3, 3]
>>> logo = LeaveOneGroupOut()
>>> for train, test in logo.split(X, y, groups=groups):
...     print("%s  %s" % (train, test))
[2 3 4 5 6] [0 1]
[0 1 4 5 6] [2 3]
[0 1 2 3] [4 5 6]

           

另一個常見的應用是使用時間資訊:例如,組可以是收集樣本的年份,進而允許與基于時間的分割進行交叉驗證。

3.5.3. 留 P 組交叉驗證

LeavePGroupsOut

 類似于 

LeaveOneGroupOut

 ,但為每個訓練/測試集删除與 

模型評估—交叉驗證3. sklearn的交叉驗證

 組有關的樣本。Leave-2-Group Out 的示例:

>>> from sklearn.model_selection import LeavePGroupsOut

>>> X = np.arange(6)
>>> y = [1, 1, 1, 2, 2, 2]
>>> groups = [1, 1, 2, 2, 3, 3]
>>> lpgo = LeavePGroupsOut(n_groups=2)
>>> for train, test in lpgo.split(X, y, groups=groups):
...     print("%s  %s" % (train, test))
[4 5] [0 1 2 3]
[2 3] [0 1 4 5]
[0 1] [2 3 4 5]

           

3.5.4. Group Shuffle Split

GroupShuffleSplit

 疊代器是 

ShuffleSplit

 和 

LeavePGroupsOut

 的組合,它生成一個随機劃分分區的序列,其中為每個分組提供了一個組子集。這是使用的示例:

>>> from sklearn.model_selection import GroupShuffleSplit

>>> X = [0.1, 0.2, 2.2, 2.4, 2.3, 4.55, 5.8, 0.001]
>>> y = ["a", "b", "b", "b", "c", "c", "c", "a"]
>>> groups = [1, 1, 2, 2, 3, 3, 4, 4]
>>> gss = GroupShuffleSplit(n_splits=4, test_size=0.5, random_state=0)
>>> for train, test in gss.split(X, y, groups=groups):
...     print("%s  %s" % (train, test))
...
[0 1 2 3] [4 5 6 7]
[2 3 6 7] [0 1 4 5]
[2 3 4 5] [0 1 6 7]
[4 5 6 7] [0 1 2 3]

           

當需要 

LeavePGroupsOut

 的操作時,這個類的資訊是很有必要的,但是 組 的數目足夠大,以至于用 

模型評估—交叉驗證3. sklearn的交叉驗證

 組生成所有可能的分區将會花費很大的代價。在這種情況下, 

GroupShuffleSplit

 通過 

LeavePGroupsOut

 提供了一個随機(可重複)的訓練 / 測試劃分采樣。

3.6. 預定義的折疊 / 驗證集

對一些資料集,一個預定義的,将資料劃分為訓練和驗證集合或者劃分為幾個交叉驗證集合的劃分已經存在。 可以使用 

PredefinedSplit

 來使用這些集合來搜尋超參數。比如,當使用驗證集合時,設定所有驗證集合中的樣例的 

test_fold

 為 0,而将其他樣例設定為 -1 。

3.7. 交叉驗證在時間序列資料中應用

時間序列資料的特點是時間 (autocorrelation(自相關性)) 附近的觀測之間的相關性。 然而,傳統的交叉驗證技術,例如 

KFold

 和 

ShuffleSplit

 假設樣本是獨立的且分布相同的,并且在時間序列資料上會導緻訓練和測試執行個體之間不合理的相關性(産生廣義誤差的估計較差)。 是以,對 “future(未來)” 觀測的時間序列資料模型的評估至少與用于訓練模型的觀測模型非常重要。為了達到這個目的,一個解決方案是由 

TimeSeriesSplit

 提供的。

3.7.1. 時間序列分割

TimeSeriesSplit

 是 k-fold 的一個變體,它首先傳回 

模型評估—交叉驗證3. sklearn的交叉驗證

 折作為訓練資料集,并且 

模型評估—交叉驗證3. sklearn的交叉驗證

 折作為測試資料集。 請注意,與标準的交叉驗證方法不同,連續的訓練集是超越前者的超集。 另外,它将所有的剩餘資料添加到第一個訓練分區,它總是用來訓練模型。這個類可以用來交叉驗證以固定時間間隔觀察到的時間序列資料樣本。對具有 6 個樣本的資料集進行 3-split 時間序列交叉驗證的示例:

>>> from sklearn.model_selection import TimeSeriesSplit

>>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [1, 2], [3, 4]])
>>> y = np.array([1, 2, 3, 4, 5, 6])
>>> tscv = TimeSeriesSplit(n_splits=3)
>>> print(tscv)  
TimeSeriesSplit(max_train_size=None, n_splits=3)
>>> for train, test in tscv.split(X):
...     print("%s  %s" % (train, test))
[0 1 2] [3]
[0 1 2 3] [4]
[0 1 2 3 4] [5]

           

3.8. A note on shuffling

(如果資料的順序不是任意的(比如說,相同标簽的樣例連續出現),為了獲得有意義的交叉驗證結果,首先對其進行 打散是很有必要的。然而,當樣例不是獨立同分布時打散則是不可行的。例如:樣例是相關的文章,以他們發表的時間進行排序,這時候如果對資料進行打散,将會導緻模型過拟合,得到一個過高的驗證分數:因為驗證樣例更加相似(在時間上更接近) 于訓練資料。一些交叉驗證疊代器, 比如 

KFold

 ,有一個内建的在劃分資料前進行資料索引打散的選項。注意:

  • 這種方式僅需要很少的記憶體就可以打散資料。
  • 預設不會進行打散,包括設定 

    cv=some_integer

     (直接)k 折疊交叉驗證的 

    cross_val_score

     , 表格搜尋等。注意 

    train_test_split

     會傳回一個随機的劃分。
  • 參數 

    random_state

     預設設定為 

    None

     ,這意為着每次進行 

    KFold(..., shuffle=True)

     時,打散都是不同的。 然而, 

    GridSearchCV

     通過調用 

    fit

     方法驗證時,将會使用相同的打散來訓練每一組參數。
  • 為了保證結果的可重複性(在相同的平台上),應該給 

    random_state

     設定一個固定的值。

3.9. 交叉驗證和模型選擇

交叉驗證疊代器可以通過網格搜尋得到最優的模型超參數,進而直接用于模型的選擇。 這是另一部分 調整估計器的超參數 的主要内容。