單一驗證、k折交叉驗證(特例:留一法(LOOCV))、交叉驗證確定最優超參數

一、單一驗證

1、單一訓練集和測試集

最簡單的樣本集劃分就是只有訓練集測試集,而沒有驗證集,因此無法利用驗證集反過來對模型參數進行調整。
只能先給定一組超參數C,然后訓練得到訓練參數W,f(C,W)就算是最終模型,再利用測試集評估下泛化性能就結束了。
或者更復雜點的做法,先給定幾組超參數【C1,C2,……Cm】,然后分別在訓練集上訓練得到不同訓練參數【W1,W2,……Wm】,最后利用測試集得到泛化性能,比如準確率【P1,P2,……Pm】,最后挑選準確率最大值Pb對應的模型f(Cb,Wb)作為最終模型。如下圖所示:

圖片1.png

2、單一訓練集測、驗證集和測試集

將樣本集劃分為訓練集驗證集測試集,比例通常為8:1:1或6:2:2。
這種情況比第一種要好,多了一個驗證集,至少讓你可以根據驗證集的結果來重新調超參數了。
整個過程變成了下面這樣:
(1)先確定一組超參數C1,然后在訓練集上訓練得到參數W1,再用驗證集得到準確率P1;
(2)如果對P1不滿意,則更換超參數為C2,重新在訓練集上訓練得到參數W2;
……
(3)如此循環m次,最終得到了一系列超參數和訓練參數對【(C1,W1),(C2,W2,……,(Cm,Wm)】;
(4)最后找出準確率最大值Pmax對應模型f(Cb,Wb),到這一步,我們已經基本找到了相對最優的超參數Cb以及對應的模型f(Cb,Wb);
(5)接下去既可以將f(Cb,Wb)作為最終模型;也可以利用Cb在“訓練集+驗證集”上重新訓練一遍,得到一個新的訓練參數Wbb,此時f(Cb,Wbb)就是最終模型;
(6)最后用測試集檢驗最終模型f(Cb,Wb)(或f(Cb,Wbb)),得到模型準確率Pb(或Pbb),好與不好都不再調整模型參數了。
如下圖所示:

圖片2.png

3、單一劃分的缺陷

上述兩種方式都有兩個問題:
(1)把一個樣本集劃分成固定的訓練集、(驗證集)、測試集,由于每個樣本大概率是不同的,因此即使子集內樣本數每次都相同,最終的劃分結果也不同,特別是對于小樣本集。事實證明,在樣本集規模較小的情況下,對于不同的子集劃分(子集樣本數相同),即使是完全相同的超參數C0,訓練出來的模型也會出現很大波動(圖中不同的accuracy),如下圖所示:

圖片3.png

(2)固定的訓練集、(驗證集)、測試集劃分,模型的訓練只利用到了訓練集和驗證集,而沒有用到測試集,這是很浪費的,對于小樣本集而言甚至是致命的。其次,訓練好的模型在本來就不具代表性的測試集上做一次測試,就將其當做該模型的準確率是不合理的,只要測試集稍微發生變化,該模型的表現可能就變化很大,如下圖所示:

圖片4.png

出現上述情況的本質原因是樣本集分布不夠充分(不能有效表征全局特征),導致劃分后的子集更不充分,每一次劃分后子集內樣本的總體特征都會出現較大波動,自然訓練和測試的結果也會波動較大;如果樣本集足夠大足夠充分(足夠表征全局特征),那么無論劃分情況怎么變化,子集很大程度上也都足夠充分,訓練和測試出來的結果也就是全局結果,最優超參數和誤差也會比較穩定

二、交叉驗證

2.1 k折交叉驗證

針對前述“單一劃分的缺陷”,“交叉驗證”一定程度上能解決上述問題,其思路如下:
(1)先確定模型(例:SVM)和一組超參數(C0),然后在樣本集(測試集除外)上做k折交叉驗證,即獨立訓練了k個具體模型f(C0,W0)、f(C0,W1)……f(C0,Wk),并得到了每個模型在自己驗證集上的準確率(P1、P2……Pk),然后求他們的平均準確率Pa,Pa就可以代表這組超參數的綜合表現了(特別注意,交叉驗證后,這k個具體模型將被丟)。交叉驗證的唯一目的就是全面評價選定模型和超參數的綜合表現,即得到Pa,而不是用交叉驗證得到最終模型。相比于前面只固定一個驗證集或測試集的情況,在交叉驗證中,每個樣本都有機會成為訓練集和驗證集,最后用多個模型的平均準確率來評價這組超參數的表現,結果自然更合理
(2)第1步交叉驗證我們知道了超參數C0的綜合表現。然后利用C0在大訓練集(測試集除外)上重新做一次訓練(注意此時沒有驗證集了,一定要劃分一個出來也行),得到最終訓練參數Wb,此時的模型f(C0,Wb)就是我們要的最終模型,最后在測試集上得到Pb。f(C0,Wb)的準確率既可以用第1步得到的平均準確率Pa,也可以用Pb。
上面兩步如下圖所示:

圖片5.png

注意1:實際中,對于小樣本集而言,在交叉驗證中,可以不用先劃出一個測試集,而是在整個樣本集上做交叉驗證,得到平均準確率Pa,然后在整個樣本集上重新做1次訓練,得到最終模型f(C0,Wb),到此結束。并把交叉驗證得到的平均準確率Pa作為f(C0,Wb)的準確率。

注意2:k折交叉驗證之前,需要把大訓練集分成k個子集,不重復地將每個子集作為驗證集,而把剩余k-1個子集合起來作為訓練集,從而可以得到k個模型(超參數都是C0);分子集時盡量保證每個子集都是均勻的。

2.2 k折交叉驗證特例——留一法(LOOCV)

k=1時,即所有訓練樣本都作為訓練樣本,這種情況相當于沒有交叉驗證,就是前面說的單一驗證;
k=N時(N是訓練集的樣本總數,不包括測試集),即每次只留1個樣本作為驗證集,N-1個作為訓練集,這種方法稱為留一法(LOOCV,Leave-one-out cross-validation);
1<k<N時,k選取實際上是偏差(bias)和方差(variance)的一個折中(trade off)。k越大,每次投入的訓練樣本越多,模型的Bias越小。但k越大,也意味著每次的訓練樣本的相關性越大(或者重疊性越大)(考慮最極端的例子,當k=N,也就是在LOOCV里,每次的訓練樣本幾乎是一樣的),而這種大相關性會導致最終的test error具有更大的Variance;k越小,偏差越大,方差越小。

LOOCV的優點:首先它不受訓訓練集和驗證集劃分(這里指將大訓練集劃分為訓練集和驗證集)的影響,因為每一個樣本都單獨的做過驗證集,同時,其用了N-1個樣本訓練模型,幾乎用到了所有樣本信息。
LOOCV缺點:計算量過大。

根據經驗一般選擇k=5或10
下圖展示了LOOCV和10折交叉驗證的對比結果:
藍色線表示真實的測試誤差曲線;
黑色線表示LOOCV方法測試誤差曲線;
橙色線表示10折交叉驗證測試誤差曲線;
可見LOOCV和10折交叉驗證結果是很相似的,但后者的計算成本卻小了很多

5.jpg

2.3 利用k折交叉驗證確定最優超參數(確定最優模型)

根據前面,交叉驗證的目的是合理評價選擇的模型和超參數的表現,而不是確定最優超參數,更不是得到最終模型。
但我們可以通過選擇多個超參數C0,C1……Cm,分別進行交叉驗證其表現,然后選擇最優準確率Pax對應的超參數Cx,最后利用該超參數訓練得到最終模型f(Cx,Wx),并在測試集上得到準確率px。(同前面一樣,可以將Pax或Px)
如下圖所示:

圖片6.png

總結:

交叉驗證的核心作用是全方位評價“給定模型和超參數C0”的表現(準確率),他會將訓練集分成k份,每份分別作為驗證集,剩下的作為訓練集,并分別針對該模型和超參數訓練得到k個模型,然后在各自的驗證集上得到各自的準確率Pi,最后計算平均準確率Pa作為該模型和給定超參數的評價。到這里,交叉驗證就結束了。交叉驗證得到的平均準確率Pa相比固定劃分情況下只在一個測試或驗證集上得到的準確率要可信得多,因為交叉驗證時所有的樣本都當過訓練樣本和驗證樣本。

至于交叉驗證后,再利用超參數C0重新訓練得到最終的模型f(C0,Wb),或者利用k折交叉驗證后選出最優超參數再訓練最終模型,這些都已經與交叉驗證無關了!

當整個訓練集的樣本數量較少時,可以不用事先留出測試集,而整個樣本集上做交叉驗證,得到模型及給定超參數C0的準確率Pa,然后在整個訓練集上訓練(無驗證集和測試集)得到最終模型f(C0, W0),并將Pa作為他的準確率。

其他交叉驗證方法:多次k折交叉驗證(Repeated k-Fold Cross Validation)、蒙特卡洛交叉驗證(Monte Carlo Cross Validation)

舉例代碼:

導入包:

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as plt

導入數據:

iris_dataset = load_iris()
iris=load_iris()
iris_X=iris.data
iris_y=iris.target
X_train, X_test, y_train, y_test = train_test_split(iris_X, iris_y, train_size=0.3, random_state=5)

采用k鄰近模型,并指定超參數5:

my_neibs = 5 

對于一次驗證:

knn1 = KNeighborsClassifier(n_neighbors = my_neibs)  # 生成一個指定超參數的k鄰近模型
knn1.fit(X_train, y_train) # 在訓練集上一次訓練得到最終模型
print('在訓練集上的一次驗證模型準確率:',knn1.score(X_test,y_test)) #在測試集上測試模型的準確率

輸出:
在訓練集上的一次驗證模型準確率: 0.9428571428571428

對于交叉驗證:

knn2 = KNeighborsClassifier(n_neighbors = my_neibs)  # 生成一個指定超參數的k鄰近模型

# 交叉驗證,確定超參數到底有多好
accuracy= cross_val_score(knn2, X_train, y_train, cv=8, scoring='accuracy')  # 先做8折交叉驗證,看看超參數5的表現
print("交叉驗證后的平均準確率:", accuracy.mean())

輸出:
交叉驗證后的平均準確率: 0.9583333333333334

knn2.fit(iris_X, iris_y) # 在所有數據上訓練得到最終模型,不再需要對該模型進行測試了,其準確率直接用交叉驗證得到的平均準確率
# 可以直接拿這個模型去對其他沒在樣本集中的樣本進行分類了!

說明:“一次驗證”情況下,在訓練集上直接得到的最終模型,在測試集的得分為0.9428571428571428(根據前面分析,這個值直接作為評價該模型(和超參數5)的準確率是不合理的(雖然實際中我們經常這么干),特別對于樣本量較小的情況,換句話說光憑這個結果還沒法相信這個模型(和超參數5),一次驗證的缺陷就在這里;
于是我們在相同的測試集上做交叉驗證得到平均準確率為0.9583333333333334,根據前面分析,這個值的可信度要更高,而且它比前面的值更大,這說明超參數5的效果在一次驗證時被低估了。于是,我們自信滿滿地決定使用超參數5了,為了更多地使用樣本數據,此時直接在全部樣本上訓練得到最終模型,顯然,這個最終模型比一次驗證的模型要更好,因為他用了更多的樣本。此時我們就不再需要測試集了,可以直接用0.9583333333333334作為最終模型的準確率。

交叉驗證確定最優超參數

neighbors_range = range(1,31)  #超參數變化范圍
neighbors_accur = []  # 存放每個超參數對應模型的交叉驗證精度
for i in neighbors_range: #對參數進行控制,選擇參數表現好的,可視化展示
    knn = KNeighborsClassifier(n_neighbors=i) # 根據當前超參數i生成1個KNN模型
    accuracy = cross_val_score(knn,iris_X,iris_y,cv=10,scoring='accuracy') #所有樣本數據上通過交叉驗證來判斷該超參數的表現
    print("第{}個超參數交叉驗證后平均準確率:{}".format(i,accuracy.mean()))
    neighbors_accur.append(accuracy.mean()) #計算均值得分

    #繪圖
plt.figure(figsize=(10,5))
plt.plot(neighbors_range,neighbors_accur)
plt.xlabel("Number of neighbors for KNN")
plt.ylabel("Cross-validates Accuracy")
輸出:
第1個超參數交叉驗證后平均準確率:0.96
第2個超參數交叉驗證后平均準確率:0.9533333333333334
第3個超參數交叉驗證后平均準確率:0.9666666666666666
第4個超參數交叉驗證后平均準確率:0.9666666666666666
第5個超參數交叉驗證后平均準確率:0.9666666666666668
第6個超參數交叉驗證后平均準確率:0.9666666666666668
第7個超參數交叉驗證后平均準確率:0.9666666666666668
第8個超參數交叉驗證后平均準確率:0.9666666666666668
第9個超參數交叉驗證后平均準確率:0.9733333333333334
第10個超參數交叉驗證后平均準確率:0.9666666666666668
第11個超參數交叉驗證后平均準確率:0.9666666666666668
第12個超參數交叉驗證后平均準確率:0.9733333333333334
第13個超參數交叉驗證后平均準確率:0.9800000000000001
第14個超參數交叉驗證后平均準確率:0.9733333333333334
第15個超參數交叉驗證后平均準確率:0.9733333333333334
第16個超參數交叉驗證后平均準確率:0.9733333333333334
第17個超參數交叉驗證后平均準確率:0.9733333333333334
第18個超參數交叉驗證后平均準確率:0.9800000000000001
第19個超參數交叉驗證后平均準確率:0.9733333333333334
第20個超參數交叉驗證后平均準確率:0.9800000000000001
第21個超參數交叉驗證后平均準確率:0.9666666666666666
第22個超參數交叉驗證后平均準確率:0.9666666666666666
第23個超參數交叉驗證后平均準確率:0.9733333333333334
第24個超參數交叉驗證后平均準確率:0.96
第25個超參數交叉驗證后平均準確率:0.9666666666666666
第26個超參數交叉驗證后平均準確率:0.96
第27個超參數交叉驗證后平均準確率:0.9666666666666666
第28個超參數交叉驗證后平均準確率:0.9533333333333334
第29個超參數交叉驗證后平均準確率:0.9533333333333334
第30個超參數交叉驗證后平均準確率:0.9533333333333334
下載.png

可見超參數13、18、20是比較好的,平均準確率達到了0.98。
利用超參數在全部樣本數據中訓練得到最終模型:

# 根據前面結果,可見超參數13、18、20是比較好的。
# 于是選擇其中1個超參數,并在全部樣本上訓練得到最終模型:
knn3 = KNeighborsClassifier(n_neighbors=13)
knn3.fit(iris_X, iris_y) # 在所有數據上訓練得到最終模型,不再需要對該模型進行測試了,其準確率直接用交叉驗證得到的平均準確率
# 可以直接拿這個模型去對其他沒在樣本集中的樣本進行分類了!

參考:

https://zhuanlan.zhihu.com/p/374761602?utm_medium=social&utm_oi=879679121540653056&utm_id=0

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容