1. 程式人生 > >sklearn-交叉驗證

sklearn-交叉驗證

交叉驗證:評估模型的表現

如果我們訓練出的模型只在訓練集上表現極好,但在未知的資料上效果很差,說明出現了過擬合,為了避免這種現象的出現,我們需要驗證集來評估我們的模型。

當我們在訓練集上訓練好一個模型後,現在驗證集上對模型進行,如果驗證集上的效果比較好時,再到測試集上就行最後的評估。但是單純的將資料集分為三部分,會大大減少模型學習的資料量(因為有時資料是很難獲取的,數目可能會比較少),並且最後模型的效果也依賴於我們對於資料集的劃分。這時我們就可以使用交叉驗證,很好解決這個問題。

sklearn中的交叉驗證

利用 scikit-learn 包中的 train_test_split 輔助函式可以很快地將實驗資料集劃分為任何訓練集(training sets)和測試集(test sets)。

使用交叉驗證最簡單的方法是在估計器和資料集上呼叫 cross_val_score 輔助函式。

clf = svm.SVC(kernel='linear', C=1)
scores = cross_val_score(clf, iris.data, iris.target, cv=5)  #cv為迭代次數。
print(scores) 

當 cv 引數是一個整數時, cross_val_score 預設使用 KFold 或 StratifiedKFold 策略,後者會在估計器派生自 ClassifierMixin 時使用。

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

scaler = preprocessing.StandardScaler().fit(X_train) X_train_transformed = scaler.transform(X_train)
clf = svm.SVC(kernel='linear', C=1).fit(X_train_transformed, y_train)X_test_transformed = scaler.transform(X_test)
print('歸一化後測試集的準確度:',clf.score(X_test_transformed, y_test))

此外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)

cross_validate函式和多度量評估

此外還可以使用cross_validate函式進行評估,相比於cross_val_score有如下不同:
• 允許指定多個指標進行評估
• 除了測試得分之外,它還會返回一個包含訓練得分,擬合次數, score-times (得分次數)的一個字典。
對於單個度量評估,其中 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())
scores['test_recall_macro'] 

或作為一個字典mapping的分歧名稱預定義或自定義得分函式

scoring = {'prec_macro': 'precision_macro','rec_micro': make_scorer(recall_score, average='macro')}

也可使用單一的指標

scores = cross_validate(clf, iris.data, iris.target,scoring='precision_macro', cv=5,
 return_estimator=True)
sorted(scores.keys())

通過交叉驗證獲取預測

除了返回結果不同,函式 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) 

交叉驗證迭代器-迴圈遍歷資料
這裡我們假設一些資料是獨立的和相同分佈的 (i.i.d) 假定所有的樣本來源於相同的生成過程,並假設生成過程沒有記憶過去生成的樣本。

  1. K折交叉驗證: KFold 將所有的樣例劃分為 k 個組,稱為摺疊 (fold) (如果 k = n, 這等價於 Leave One Out(留一) 策略),都具有相同的大小(如果可能)。預測函式學習時使用 k - 1 個摺疊中的資料,最後一個剩下的摺疊會用於測試。K-折交叉驗證得出的效能指標是迴圈計算中每個值的平均值。 該方法雖然計算代價很高,但是它不會浪費太多的資料(如固定任意測試集的情況一樣), 在處理樣本資料集較少的問題(例如,逆向推理)時比較有優勢。

下面是交叉驗證行為的視覺化。注意,KFold不受類或組的影響。
在這裡插入圖片描述

  1. K折重複多次: RepeatedKFold 重複 K-Fold n 次。當需要執行時可以使用它 KFold n 次,在每次重複中產生不同的分割。類似地, RepeatedStratifiedKFold 在每個重複中以不同的隨機化重複 n 次分層的 K-Fold

  2. 留一交叉驗證: LeaveOneOut (或 LOO) 是一個簡單的交叉驗證。每個學習集都是通過除了一個樣本以外的所有樣本建立的,測試集是被留下的樣本。 因此,對於 n 個樣本,我們有 n 個不同的訓練集和 n 個不同的測試集。這種交叉驗證程式不會浪費太多資料,因為只有一個樣本是從訓練集中刪除掉的:

作為一般規則,大多數作者和經驗證據表明, 5- 或者 10- 交叉驗證應該優於 LOO

  1. 留P交叉驗證: LeavePOut 與 LeaveOneOut 非常相似,因為它通過從整個集合中刪除 p 個樣本來建立所有可能的 訓練/測試集。對於 n 個樣本,這產生了 {n \choose p} 個 訓練-測試 對。與 LeaveOneOut 和 KFold 不同,當 p > 1 時,測試集會重疊。

  2. 隨機排列交叉驗證: ShuffleSplit 迭代器將會生成一個使用者給定數量的獨立的訓練/測試資料劃分。樣例首先被打散然後劃分為一對訓練測試集合。 可以通過設定明確的 random_state ,使得偽隨機生成器的結果可以重複。

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

下面是交叉驗證行為的視覺化。注意,ShuffleSplit不受類或組的影響。
在這裡插入圖片描述

基於類標籤、具有分層的交叉驗證迭代器

使用StratifiedKFold和StratifiedShuffleSplit 分層抽樣。 一些分類問題在目標類別的分佈上可能表現出很大的不平衡性:例如,可能會出現比正樣本多數倍的負樣本。在這種情況下,建議採用如 StratifiedKFold 和 StratifiedShuffleSplit 中實現的分層抽樣方法,確保相對的類別頻率在每個訓練和驗證 摺疊 中大致保留。

  1. 分層K折:StratifiedKFold 是 k-fold 的變種,會返回 stratified(分層) 的摺疊:每個小集合中, 各個類別的樣例比例大致和完整資料集中相同。

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

下面是交叉驗證行為的視覺化。
在這裡插入圖片描述

  1. 分成隨機split:StratifiedShuffleSplit 是 ShuffleSplit 的一個變種,會返回直接的劃分,比如: 建立一個劃分,但是劃分中每個類的比例和完整資料集中的相同。

下面是交叉驗證行為的視覺化。
在這裡插入圖片描述

用於分組資料的交叉驗證迭代器
如何進一步測試模型的泛化能力? 留出一組特定的不屬於測試集和訓練集的資料。有時我們想知道在一組特定的 groups 上訓練的模型是否能很好地適用於看不見的 group 。為了衡量這一點,我們需要確保驗證物件中的所有樣本來自配對訓練摺疊中完全沒有表示的組。下面的交叉驗證分離器可以用來做到這一點。 樣本的 grouping identifier (分組識別符號) 通過 groups 引數指定。

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

下面是交叉驗證行為的視覺化。
在這裡插入圖片描述
2. LeaveOneGroupOut 是一個交叉驗證方案,它根據第三方提供的 array of integer groups (整陣列的陣列)來提供樣本。這個組資訊可以用來編碼任意域特定的預定義交叉驗證摺疊。

每個訓練集都是由除特定組別以外的所有樣本構成的。

  1. LeavePGroupsOut 類似於 LeaveOneGroupOut ,但為每個訓練/測試集刪除與 P 組有關的樣本。

  2. GroupShuffleSplit 迭代器是 ShuffleSplit 和 LeavePGroupsOut 的組合,它生成一個隨機劃分分割槽的序列,其中為每個分組提供了一個組子集。

下面是交叉驗證行為的視覺化。
在這裡插入圖片描述

交叉驗證再時間序列資料中的應用

時間序列資料的特點是時間 (autocorrelation(自相關性)) 附近的觀測之間的相關性。 然而,傳統的交叉驗證技術,例如 KFold 和 ShuffleSplit 假設樣本是獨立的且分佈相同的,並且在時間序列資料上會導致訓練和測試例項之間不合理的相關性(產生廣義誤差的估計較差)。 因此,對未來觀測的時間序列資料模型的評估至少與用於訓練模型的觀測模型非常重要。為了達到這個目的,一個解決方案是由 TimeSeriesSplit 提供的。

TimeSeriesSplit 是 k-fold 的一個變體,它首先返回 k 折作為訓練資料集,並且 (k+1) 折作為測試資料集。 請注意,與標準的交叉驗證方法不同,連續的訓練集是超越前者的超集。 另外,它將所有的剩餘資料新增到第一個訓練分割槽,它總是用來訓練模型。這個類可以用來交叉驗證以固定時間間隔觀察到的時間序列資料樣本。

下面是交叉驗證行為的視覺化。
在這裡插入圖片描述

一個綜合的例子:

from sklearn.model_selection import train_test_split,cross_val_score,cross_validate from sklearn.model_selection import KFold,LeaveOneOut,LeavePOut,ShuffleSplitfrom sklearn.model_selection import StratifiedKFold,StratifiedShuffleSplit
from sklearn.model_selection import GroupKFold,LeaveOneGroupOut,LeavePGroupsOut,GroupShuffleSplit 
from sklearn.model_selection import TimeSeriesSplit # 時間序列分割
from sklearn import datasetsfrom sklearn import svm  
from sklearn import preprocessing
from sklearn.metrics import recall_score

iris = datasets.load_iris()  # 載入資料集
print('樣本集大小:',iris.data.shape,iris.target.shape)

# 我們使用鳶尾花資料集來看一下train_test_split函式的使用
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.4, random_state=0)  # 交叉驗證劃分訓練集和測試集.test_size為測試集所佔的比例
print('訓練集大小:',X_train.shape,y_train.shape)
print('測試集大小:',X_test.shape,y_test.shape)  
clf = svm.SVC(kernel='linear', C=1).fit(X_train, y_train) # 使用訓練集訓練模型,這裡使用支援向量機的方法
print('準確率:',clf.score(X_test, y_test))  # 計算測試集的度量值(準確率)


#  如果涉及到歸一化,則在測試集上也要使用訓練集模型提取的歸一化函式。
scaler = preprocessing.StandardScaler().fit(X_train)  # 通過訓練集獲得歸一化函式模型,在訓練集和測試集上都使用這個歸一化函式
X_train_transformed = scaler.transform(X_train)
clf = svm.SVC(kernel='linear', C=1).fit(X_train_transformed, y_train) # 使用訓練集訓練模型
X_test_transformed = scaler.transform(X_test)
print('歸一化後測試集的準確度:',clf.score(X_test_transformed, y_test))  # 計算測試集的度量值(準確度)

# ===================================直接呼叫交叉驗證評估模型==========================
clf = svm.SVC(kernel='linear', C=1)
scores = cross_val_score(clf, iris.data, iris.target, cv=5)  #cv為迭代次數。
print(scores)  # 列印輸出每次迭代的度量值(準確度)
print("Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))  # 獲取置信區間。(也就是均值和方差)

# ===================================多種度量結果======================================
scoring = ['precision_macro', 'recall_macro'] # precision_macro為精度,recall_macro為召回率
scores = cross_validate(clf, iris.data, iris.target, scoring=scoring,cv=5, return_train_score=True)
sorted(scores.keys())
print('測試結果:',scores)  # scores型別為字典。包含訓練得分,擬合次數, score-times (得分次數)


# ==================================K折交叉驗證、留一交叉驗證、留p交叉驗證、隨機排列交叉驗證==========================================
# k折劃分子集
kf = KFold(n_splits=2)
for train, test in kf.split(iris.data):
    print("k折劃分:%s %s" % (train.shape, test.shape))
    break

# 留一劃分子集
loo = LeaveOneOut()
for train, test in loo.split(iris.data):
    print("留一劃分:%s %s" % (train.shape, test.shape))
    break

# 留p劃分子集
lpo = LeavePOut(p=2)
for train, test in loo.split(iris.data):
    print("留p劃分:%s %s" % (train.shape, test.shape))
    break

# 隨機排列劃分子集
ss = ShuffleSplit(n_splits=3, test_size=0.25,random_state=0)
for train_index, test_index in ss.split(iris.data):
    print("隨機排列劃分:%s %s" % (train.shape, test.shape))
    break

# ==================================分層K折交叉驗證、分層隨機交叉驗證==========================================
skf = StratifiedKFold(n_splits=3)  #各個類別的比例大致和完整資料集中相同
for train, test in skf.split(iris.data, iris.target):
    print("分層K折劃分:%s %s" % (train.shape, test.shape))
    break

skf = StratifiedShuffleSplit(n_splits=3)  # 劃分中每個類的比例和完整資料集中的相同
for train, test in skf.split(iris.data, iris.target):
    print("分層隨機劃分:%s %s" % (train.shape, test.shape))
    break


# ==================================組 k-fold交叉驗證、留一組交叉驗證、留 P 組交叉驗證、Group Shuffle Split==========================================
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]

# k折分組
gkf = GroupKFold(n_splits=3)  # 訓練集和測試集屬於不同的組
for train, test in gkf.split(X, y, groups=groups):
    print("組 k-fold分割:%s %s" % (train, test))

# 留一分組
logo = LeaveOneGroupOut()
for train, test in logo.split(X, y, groups=groups):
    print("留一組分割:%s %s" % (train, test))

# 留p分組
lpgo = LeavePGroupsOut(n_groups=2)
for train, test in lpgo.split(X, y, groups=groups):
    print("留 P 組分割:%s %s" % (train, test))

# 隨機分組
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))


# ==================================時間序列分割==========================================
tscv = TimeSeriesSplit(n_splits=3)
TimeSeriesSplit(max_train_size=None, n_splits=3)
for train, test in tscv.split(iris.data):
    print("時間序列分割:%s %s" % (train, test))

結果
在這裡插入圖片描述

參見:
sklearn交叉驗證中文文件
cross_validation
交叉驗證