1. 程式人生 > >2.機器學習之 為什麼要用交叉驗證

2.機器學習之 為什麼要用交叉驗證

本文結構:

  • 什麼是交叉驗證法?
  • 為什麼用交叉驗證法?
  • 主要有哪些方法?優缺點?
  • 各方法應用舉例?

什麼是交叉驗證法?

它的基本思想就是將原始資料(dataset)進行分組,一部分做為訓練集來訓練模型,另一部分做為測試集來評價模型。

為什麼用交叉驗證法?

  1. 交叉驗證用於評估模型的預測效能,尤其是訓練好的模型在新資料上的表現,可以在一定程度上減小過擬合。
  2. 還可以從有限的資料中獲取儘可能多的有效資訊。

主要有哪些方法?

1. 留出法 (holdout cross validation)

在機器學習任務中,拿到資料後,我們首先會將原始資料集分為三部分:訓練集、驗證集和測試集。 
訓練集用於訓練模型,驗證集用於模型的引數選擇配置,測試集對於模型來說是未知資料,用於評估模型的泛化能力。

這個方法操作簡單,只需隨機把原始資料分為三組即可。 
不過如果只做一次分割,它對訓練集、驗證集和測試集的樣本數比例,還有分割後資料的分佈是否和原始資料集的分佈相同等因素比較敏感,不同的劃分會得到不同的最優模型,而且分成三個集合後,用於訓練的資料更少了。

於是有了 2. k 折交叉驗證(k-fold cross validation)加以改進:

k 折交叉驗證通過對 k 個不同分組訓練的結果進行平均來減少方差,因此模型的效能對資料的劃分就不那麼敏感。

  • 第一步,不重複抽樣將原始資料隨機分為 k 份。
  • 第二步,每一次挑選其中 1 份作為測試集,剩餘 k-1 份作為訓練集用於模型訓練。
  • 第三步,重複第二步 k 次,這樣每個子集都有一次機會作為測試集,其餘機會作為訓練集。
  • 在每個訓練集上訓練後得到一個模型,
  • 用這個模型在相應的測試集上測試,計算並儲存模型的評估指標,
  • 第四步,計算 k 組測試結果的平均值作為模型精度的估計,並作為當前 k 折交叉驗證下模型的效能指標。

k 一般取 10, 
資料量小的時候,k 可以設大一點,這樣訓練集佔整體比例就比較大,不過同時訓練的模型個數也增多。 
資料量大的時候,k 可以設小一點。

當 k=m 即樣本總數時,叫做 3. 留一法(Leave one out cross validation),每次的測試集都只有一個樣本,要進行 m 次訓練和預測。 
這個方法用於訓練的資料只比整體資料集少了一個樣本,因此最接近原始樣本的分佈。 
但是訓練複雜度增加了,因為模型的數量與原始資料樣本數量相同。 
一般在資料缺乏時使用。

此外:

  1. 多次 k 折交叉驗證再求均值,例如:10 次 10 折交叉驗證,以求更精確一點。
  2. 劃分時有多種方法,例如對非平衡資料可以用分層取樣,就是在每一份子集中都保持和原始資料集相同的類別比例。
  3. 模型訓練過程的所有步驟,包括模型選擇,特徵選擇等都是在單個摺疊 fold 中獨立執行的。

還有一種比較特殊的交叉驗證方式,Bootstrapping: 通過自助取樣法,即在含有 m 個樣本的資料集中,每次隨機挑選一個樣本,再放回到資料集中,再隨機挑選一個樣本,這樣有放回地進行抽樣 m 次,組成了新的資料集作為訓練集。

這裡會有重複多次的樣本,也會有一次都沒有出現的樣本,原資料集中大概有 36.8% 的樣本不會出現在新組資料集中。

優點是訓練集的樣本總數和原資料集一樣都是 m,並且仍有約 1/3 的資料不被訓練而可以作為測試集。 
缺點是這樣產生的訓練集的資料分佈和原資料集的不一樣了,會引入估計偏差。 
此種方法不是很常用,除非資料量真的很少。

各方法應用舉例?

1. 留出法 (holdout cross validation)

下面例子,一共有 150 條資料:

>>> 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,))
  • 用 train_test_split 來隨機劃分資料集,其中 40% 用於測試集,有 60 條資料,60% 為訓練集,有 90 條資料:
>>> 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,))

用 train 來訓練,用 test 來評價模型的分數。

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

2. k 折交叉驗證(k-fold cross validation)

最簡單的方法是直接呼叫 cross_val_score,這裡用了 5 折交叉驗證:

>>> 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.        ])

得到最後平均分為 0.98,以及它的 95% 置信區間:

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

我們可以直接看一下 K-fold 是怎樣劃分資料的: 
X 有四個資料,把它分成 2 折, 
結果中最後一個集合是測試集,前面的是訓練集, 
每一行為 1 折:

  • 樣的資料 X,我們看 LeaveOneOut 後是什麼樣子, 

那就是把它分成 4 折, 
結果中最後一個集合是測試集,只有一個元素,前面的是訓練集, 
每一行為 1 折:

>>> 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]