首先需要搞明白,為什么需要交叉驗證?
交叉驗證是機器學習和統(tǒng)計學中常用的一種技術(shù),用于評估預(yù)測模型的性能和泛化能力,特別是在數(shù)據(jù)有限或評估模型對新的未見數(shù)據(jù)的泛化能力時,交叉驗證非常有價值。

那么具體在什么情況下會使用交叉驗證呢?
- 模型性能評估:交叉驗證有助于估計模型在未見數(shù)據(jù)上的表現(xiàn)。通過在多個數(shù)據(jù)子集上訓練和評估模型,交叉驗證提供了比單一訓練-測試分割更穩(wěn)健的模型性能估計。
- 數(shù)據(jù)效率:在數(shù)據(jù)有限的情況下,交叉驗證充分利用了所有可用樣本,通過同時使用所有數(shù)據(jù)進行訓練和評估,提供了對模型性能更可靠的評估。
- 超參數(shù)調(diào)優(yōu):交叉驗證通常用于選擇模型的最佳超參數(shù)。通過在不同數(shù)據(jù)子集上使用不同的超參數(shù)設(shè)置來評估模型的性能,可以確定在整體性能上表現(xiàn)最好的超參數(shù)值。
- 檢測過擬合:交叉驗證有助于檢測模型是否對訓練數(shù)據(jù)過擬合。如果模型在訓練集上的表現(xiàn)明顯優(yōu)于驗證集,可能表明存在過擬合的情況,需要進行調(diào)整,如正則化或選擇更簡單的模型。
- 泛化能力評估:交叉驗證提供了對模型對未見數(shù)據(jù)的泛化能力的評估。通過在多個數(shù)據(jù)分割上評估模型,它有助于評估模型捕捉數(shù)據(jù)中的潛在模式的能力,而不依賴于隨機性或特定的訓練-測試分割。
交叉驗證的大致思想可如圖5折交叉所示,在每次迭代中,新模型在四個子數(shù)據(jù)集上訓練,并在最后一個保留的子數(shù)據(jù)集上進行測試,確保所有數(shù)據(jù)得到利用。通過平均分數(shù)及標準差等指標,提供了對模型性能的真實度量

一切還得從K折交叉開始。
KFold
K折交叉在Sklearn中已經(jīng)集成,此處以7折為例:
from sklearn.datasets import make_regression
from sklearn.model_selection import KFold
x, y = make_regression(n_samples=100)
# Init the splitter
cross_validation = KFold(n_splits=7)

還有一個常用操作是在執(zhí)行拆分前進行Shuffle,通過破壞樣本的原始順序進一步最小化了過度擬合的風險:
cross_validation = KFold(n_splits=7, shuffle=True)

這樣,一個簡單的k折交叉驗證就實現(xiàn)了,記得看源碼看源碼看源碼!!
StratifiedKFold
StratifiedKFold是專門為分類問題而設(shè)計。
在有的分類問題中,即使將數(shù)據(jù)分成多個集合,目標分布也應(yīng)該保持不變。比如大多數(shù)情況下,具有30到70類別比例的二元目標在訓練集和測試集中仍應(yīng)保持相同的比例,在普通的KFold中,這個規(guī)則被打破了,因為在拆分之前對數(shù)據(jù)進行shuffle時,類別比例將無法保持。
為了解決這個問題,在Sklearn中使用了另一個專門用于分類的拆分器類——StratifiedKFold:
from sklearn.datasets import make_classification
from sklearn.model_selection import StratifiedKFold
x, y = make_classification(n_samples=100, n_classes=2)
cross_validation = StratifiedKFold(n_splits=7, shuffle=True, random_state=1121218)

雖然看起來與KFold相似,但現(xiàn)在類別比例在所有的split和迭代中都維持一致。
ShuffleSplit
有的時候只是多次重復(fù)進行訓練/測試集拆分過程,也是和交叉驗證很像的一種方式。
從邏輯上講,使用不同的隨機種子生成多個訓練/測試集應(yīng)該在足夠多的迭代中類似于一個穩(wěn)健的交叉驗證過程。
Sklearn中也有提供接口:
from sklearn.model_selection import ShuffleSplit
cross_validation = ShuffleSplit(n_splits=7, trAIn_size=0.75, test_size=0.25)

TimeSeriesSplit
當數(shù)據(jù)集為時間序列時,不能使用傳統(tǒng)的交叉驗證,這將完全打亂順序,為了解決這個問題,參考Sklearn提供了另一個拆分器——TimeSeriesSplit,
from sklearn.model_selection import TimeSeriesSplit
cross_validation = TimeSeriesSplit(n_splits=7)

如圖,驗證集始終位于訓練集的索引之后。由于索引是日期,不會意外地在未來的日期上訓練時間序列模型并對之前的日期進行預(yù)測。
非獨立同分布(non-IID)數(shù)據(jù)的交叉驗證
前面所述方法均在處理獨立同分布數(shù)據(jù)集,也就是說生成數(shù)據(jù)的過程不會受到其他樣本的影響。
然而,有些情況下,數(shù)據(jù)并不滿足IID的條件,即一些樣本組之間存在依賴關(guān)系,Kaggle上的競賽就有出現(xiàn),如google Brain Ventilator Pressure,該數(shù)據(jù)記錄了人工肺在數(shù)千個呼吸過程中(吸入和呼出)的氣壓值,并且對每次呼吸的每個時刻進行了記錄,每個呼吸過程大約有80行數(shù)據(jù),這些行之間是相互關(guān)聯(lián)的,在這種情況下,傳統(tǒng)的交叉驗證無法工作,因為拆分可能會“剛好發(fā)生在一個呼吸過程的中間”。
可以理解為需要對這些數(shù)據(jù)進行“分組”,因為組內(nèi)數(shù)據(jù)是有關(guān)聯(lián)的,比如當從多個患者收集醫(yī)療數(shù)據(jù)時,每個患者都有多個樣本,而這些數(shù)據(jù)很可能會受到患者個體差異的影響,所以也需要分組。
往往我們希望在一個特定組別上訓練的模型是否能夠很好地泛化到其他未見過的組別,所以在進行交差驗證時給這些組別數(shù)據(jù)打上“tag”,告訴他們?nèi)绾螀^(qū)分別瞎拆。
在Sklearn中提供了若干接口處理這些情況:
- GroupKFold
- StratifiedGroupKFold
- LeaveOneGroupOut
- LeavePGroupsOut
- GroupShuffleSplit
強烈建議搞清楚交叉驗證的思想,以及如何實現(xiàn),搭配看Sklearn源碼是一個肥腸不錯的方式。此外,需要對自己的數(shù)據(jù)集有著清晰的定義,數(shù)據(jù)預(yù)處理真的很重要。






