机器学习中的交叉验证
總第100篇
本篇講講機器學習中的交叉驗證問題,并利用sklearn實現。
前言
在說交叉驗證以前,我們先想一下我們在搭建模型時的關于數據切分的常規做法[直接利用train_test_split把所有的數據集分成兩部分:train_data和test_data,先在train_data上進行訓練,然后再在test_data上進行測試評估模型效果的好壞]。
因為我們訓練模型時,不是直接把數丟進去就好了,而是需要對模型的不斷進行調整(比如參數),使模型在測試集上的表現足夠好,但是即使模型在測試集上效果好,不一定在其他數據集上效果好,因為這個模型是“專門”針對這個測試集而調整的,你為了測試這個模型真正的效果,你就得找另外的一部分數據,看模型在這些數據上的效果怎么樣,只有模型在另外的數據上效果也好,那才可以說明的模型的效果是真的好(泛化能力不錯,也可以理解成是舉一反三的能力)。
其實這個和我們上學時候的上課—做練習題—考試的關系很像,我們在每上完每一節課的時候,就會去做練習題來檢驗這節課的知識點掌握的怎么樣,你的練習題幾乎全部能做對,可能是因為剛上完這個章節,你一看到題就知道用這節課的知識點,直接把知識點套進去就可以做出來,每章如此,但是,期末考試的時候是把所有的章節結合起來考,不會告訴你用哪個知識點,這個時候就是檢驗你是否真的把這個知識點學會了的時候,你只有在期末考試的時候考的好,才能說明你是真的學習好。
這樣就需要把數據分成三份,一份訓練、一份驗證、一份測試,先在訓練集上訓練模型,然后驗證模型結果,最后再在測試集上判斷模型的真正效果,但是這樣做的結果就是大幅降低了數據的使用率,因訓練數據不夠多而造成欠擬合,并且數據切分的隨機性也會對模型的效果有影響,這兩個問題可以通過交叉驗證(CV)的方式解決。
最基本的方法被稱之為:k-折交叉驗證。k-折交叉驗證將訓練集劃分為k個較小的集合(其他方法會在下面描述,主要原則基本相同)。每一個 k 折都會遵循下面的過程:
將 k-1 份訓練集子集作為 training data (訓練集)訓練模型,
將剩余的 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. ? ? ? ?])默認情況下,每個 CV 迭代計算的分數是估計器的score方法。可以通過使用scoring參數來改變,scoring參數可選的值有“f1-score,neg_log_loss,roc_auc”等指標,具體值可看:
http://sklearn.apachecn.org/cn/0.19.0/modules/model_evaluation.html
設置方式,如下:
>>> 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. ? ? ? ?])獲取交叉驗證預測結果
通過cross_val_predict方法得到交叉驗證模型的預測結果, 對于每一個輸入的元素,如果其在測試集合中,將會得到預測結果。交叉驗證策略會將可用的元素提交到測試集合有且僅有一次(否則會拋出一個異常)。
>>> 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...交叉驗證迭代器
接下來的部分列出了一些用于生成索引標號,用于在不同的交叉驗證策略中生成數據劃分的工具。
K折(KFold)
KFold 將所有的樣例劃分為 k 個組,稱為折疊 (fold) (如果 k = n, 這等價于 Leave One Out(留一) 策略),都具有相同的大小(如果可能)。預測函數學習時使用 k - 1 個折疊中的數據,最后一個剩下的折疊會用于測試。
>>> 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]留一交叉驗證 (LOO)
LeaveOneOut (或 LOO) 是一個簡單的交叉驗證。每個學習集都是通過除了一個樣本以外的所有樣本創建的,測試集是被留下的樣本。 因此,對于 n 個樣本,我們有 n 個不同的訓練集和 n 個不同的測試集。這種交叉驗證程序不會浪費太多數據,因為只有一個樣本是從訓練集中刪除掉的:
>>> 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]留 P 交叉驗證 (LPO)
LeavePOut 與 LeaveOneOut 非常相似,因為它通過從整個集合中刪除 p 個樣本來創建所有可能的 訓練/測試集。對于 n 個樣本,這產生了 {n \choose p} 個 訓練-測試 對。與 LeaveOneOut 和 KFold 不同,當 p > 1 時,測試集會重疊。
>>> 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]隨機排列交叉驗證(Shuffle)
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交叉驗證,因為其提供了細致的訓練 / 測試劃分的數量和樣例所占的比例等的控制。
基于類標簽的交叉驗證迭代器
一些分類問題在目標類別的分布上可能表現出很大的不平衡性:例如,可能會出現比正樣本多數倍的負樣本。可以采用?StratifiedKFold 和 StratifiedShuffleSplit中實現的分層抽樣方法,確保相對的類別頻率在每個訓練和驗證折疊中大致保留。
分層k折
StratifiedKFold是k-fold的變種,會返回stratified(分層)的折疊:每個小集合中,各個類別的樣例比例大致和完整數據集中相同。
>>> 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]分層隨機Split
StratifiedShuffleSplit是ShuffleSplit的一個變種,會返回直接的劃分,比如:創建一個劃分,但是劃分中每個類的比例和完整數據集中的相同。
>>> from sklearn.model_selection import StratifiedShuffleSplit >>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]]) >>> y = np.array([0, 0, 1, 1]) >>> sss = StratifiedShuffleSplit(n_splits=3, test_size=0.5, random_state=0) >>> sss.get_n_splits(X, y) 3 >>> print(sss) ? ? ? StratifiedShuffleSplit(n_splits=3, random_state=0, ...) >>> for train_index, test_index in sss.split(X, y): ... ? print("TRAIN:", train_index, "TEST:", test_index) ... ? X_train, X_test = X[train_index], X[test_index] ... ? y_train, y_test = y[train_index], y[test_index] TRAIN: [1 2] TEST: [3 0] TRAIN: [0 2] TEST: [1 3] TRAIN: [0 2] TEST: [3 1]交叉驗證在時間序列數據中應用
時間序列數據的特點是時間 (autocorrelation(自相關性))之間是具有相關性的。然而,傳統的交叉驗證技術,例如 KFold和 ShuffleSplit假設樣本是獨立的且分布相同的,并且在時間序列數據上會導致訓練和測試實例之間不合理的相關性(產生廣義誤差的估計較差)。 因此,對 “future(未來)” 觀測的時間序列數據模型的評估至少與用于訓練模型的觀測模型非常重要。為了達到這個目的,一個解決方案是由TimeSeriesSplit提供的。
時間序列分割
TimeSeriesSplit是k-fold的一個變體,它首先返回k折作為訓練數據集,并且 (k+1) 折作為測試數據集。請注意,與標準的交叉驗證方法不同,連續的訓練集是超越前者的超集。另外,它將所有的剩余數據添加到第一個訓練分區,它總是用來訓練模型。
這個類可以用來交叉驗證以固定時間間隔觀察到的時間序列數據樣本。
對具有 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]交叉驗證實例
上面提到的幾種方法都是用來生成train和test的索引編號,而不像train_test_split方法直接可以生成訓練集和數據集,我們只需要利用索引的方式去把對應的train和test索引出來即可,拿最簡單的Kfold為例,具體的實現方式如下:
#導入所需要的庫 >>> from sklearn import svm ? >>> from sklearn.model_selection import KFold>>> digits = datasets.load_digits() ? #load scikit-learn庫里的實驗數據集:digits >>> X_digits = digits.data ?#獲得樣本特征數據部分 >>> y_digits = digits.target ?#獲得樣本label >>> svc = svm.SVC(C=1, kernel='linear') ?#初始化svm分類器>>> kf = KFold(n_splits=3) >>> for train, test in kf.split(X): >>> ? ? #此處train、test里有交叉驗證對象中已經初始化好的3組訓練樣本和測試樣本所需的位置標號 >>> ? ?[svc.fit(X_digits[train], y_digits[train]).score(X_digits[test], y_digits[test]) for train, test in kfold] ?你還可以看:
機器學習模型效果評估
機器學習中非平衡數據處理
機器學習中的特征選擇
總結
以上是生活随笔為你收集整理的机器学习中的交叉验证的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阴阳师秘闻几级开启
- 下一篇: 抖音豪豪7890骂人事件原委介绍 豪豪7