詳解訓練集、測試集以及驗證集

  • 2020 年 11 月 3 日
  • AI

前言

本文首先介紹機器學習的簡單流程,找出評估模型泛化的問題,進而引出將數據集劃分為訓練集和測試集,但是根據測試集調整模型會導致測試集資訊泄露,評估模型泛化能力的結果不具說服力,因此又引入了驗證集用於選擇模型調整參數。通常直接使用訓練集和測試集的方式是將測試集當做了驗證集使用。

a
   模 型 泛 化 能 力

機器學習簡單流程:

  1. 使用大量和任務相關的數據集來訓練模型;
  2. 通過模型在數據集上的誤差不斷迭代訓練模型,得到對數據集擬合合理的模型;
  3. 將訓練好調整好的模型應用到真實的場景中;
我們最終的目的是將訓練好的模型部署到真實的環境中,希望訓練好的模型能夠在真實的數據上得到好的預測效果,換句話說就是希望模型在真實數據上預測的結果誤差越小越好。我們把模型在真實環境中的誤差叫做泛化誤差,最終的目的是希望訓練好的模型泛化誤差越低越好。
我們希望通過某個訊號來了解模型的泛化誤差,這樣就可以指導我們得到泛化能力更強的模型:
  1. 使用泛化誤差本身。這是很自然的想法,我們訓練模型的最終目的就是希望模型的泛化誤差最低,當然可以使用泛化誤差本身來作為檢測訊號。如果泛化誤差小的話還可以接受,但是通常情況下沒有那麼幸運,泛化誤差可能很大,這個時候你肯定會將部署的模型撤回,重新訓練,你可能需要部署和訓練之間往複很多次,這種方式雖然能夠更好的指導我們的模型,但是成本和效率非常的差;
  2. 使用模型在數據集上訓練的擬合程度來作為評估模型的訊號。但是往往我們獲取的數據集並不是完全的乾淨以及有代表性,通常我們獲取到的數據集可能很少、數據的代表性不夠、包含太多的雜訊或者是被一些無關特徵污染,我們獲取到的數據集或多或少都會有這些問題,那麼模型對訓練數據集的擬合程度不能指導泛化誤差,也就是說訓練的時候擬合的好並不代表模型的泛化誤差就小,你甚至可以將模型在數據集上的誤差減小到0,但是因為對模型訓練時候的數據集往往不幹凈,所以這樣的模型並不代表泛化能力就強。

b
   訓 練 集 與 測 試 集

前面說到我們既不能通過直接將泛化誤差作為了解模型泛化能力的訊號,因為在部署環境和訓練模型之間往複,代價很高,也不能使用模型對訓練數據集的擬合程度來作為了解模型泛化能力的訊號,因為我們獲得的數據往往不幹凈。

更好的方式就是將數據分割成兩部分:訓練集和測試集。我們可以使用訓練集的數據來訓練模型,然後用測試集上的誤差作為最終模型在應對現實場景中的泛化誤差。有了測試集,我們想要驗證模型的最終效果,只需將訓練好的模型在測試集上計算誤差,即可認為此誤差即為泛化誤差的近似,我們只需讓我們訓練好的模型在測試集上的誤差最小即可。


這裡有幾點需要注意:

  1. 通常將數據集的80%作為訓練集,20%作為測試集;
  2. 通常需要在開始構建模型之前把數據集進行劃分,防止數據窺探偏誤,也就是說我們避免了解太多關於測試集中的樣本特點,防止我們認為的挑選有助於測試集數據的模型,這樣的結果會過於樂觀,但是實際上並沒有預期的那樣優秀;
  3. 通常我們在構建模型的時候需要將數據進行處理,包括一些數據的清洗,數據的特徵縮放標準化或者歸一化,此時我們只需要在訓練集上進行這些操作,然後將其在訓練集上得到的參數應用到測試集中,也就是說,在工作流程中,你不能使用在測試數據集上計算的得到的任何結果。比如:我們得到的屬性中可能有缺失值,因為在這些操作之前,我們已經把數據集分成了訓練集和測試集,通常的做法是通過計算屬性值的中位數來填充缺失值,注意此時計算屬性值的中位數是通過訓練集上的數據進行計算的,當我們得到一個模型的時候,如果想要測試模型的測試誤差來近似泛化誤差的時候,可能此時的測試集也會有一些缺失值,此時對應屬性的缺失值是通過訓練集計算的中位數來進行填充的;
  4. 由於測試集作為對泛化誤差的近似,所以訓練好模型,最後在測試集上近似估計模型的泛化能力。此時假設有兩個不同的機器學習模型,猶豫不決的時候,可以通過訓練兩個模型,然後對比他們在測試數據上的泛化誤差,選擇泛化能力強的模型。
前面說了這麼多,那如何劃分數據集為訓練集和測試集呢?其實很簡單,可以自己編寫程式,也可以使用sklearn提供的模組。

通過簡單程式碼實現:

通過sklearn實現:

前面介紹的兩種分割數據集的方式都是採用純隨機的取樣方式,這種方式對於大量數據集以及對於目標值分布均勻的情況是可行的。比如對於分類任務,我們訓練一個二值分類器,可能數據中包含大量的正例樣本,僅僅包含10%的反例樣本,此時的標籤分布很不均勻,如果我們通過隨機取樣的方式,極端情況下可能將正例樣本都劃分到訓練集上,而反例樣本恰好都分到測試集,這樣訓練出來的模型,效果一定不會太好,所以我們需要採用分層取樣的方式進行劃分數據集,也就是說保證訓練集中既包含一定比例的正例樣本又要包含一定比例的負例樣本。


幸運的是sklearn提供了我們分層抽樣的函數,在這之前先看看官方提供的例子:

通過上面的例子我們可以了解使用分層進行劃分數據集的大概流程,以及各個參數的含義:
  • n_splits:分割迭代的次數,如果我們要劃分訓練集和測試集的話,將其設置為1即可;
  • test_size:分割測試集的比例;
  • random_state:設置隨機種子;

下面通過兩種方式對原始的mnist數據集進行劃分,首先要準備數據集:

  • 使用隨機取樣的方式分割數據集:

  • 使用分層取樣的方式分割數據集:

如果想要知道抽取的各個樣本的比例,你可以將數據轉換成DataFrame對象(當然在處理數據的開始你也可以將數據轉換為DataFrame方便操作):

下面各圖分別是:原始數據10個類別所佔的比例、隨機取樣訓練集中10個類別所佔比例以及分層取樣訓練集中10個類別所佔的比例(當然也可以對測試集進行統計)。

▲統計對比

通過上面的分析可以看出,分層取樣出的10個類別所佔的比例和原數據中的10個類別所佔的比例很接近。

c
   驗 證 集

前面說到我們將數據集劃分為訓練集和測試集,我們讓模型在訓練集上進行訓練,然後在測試集上來近似模型的泛化能力。我們如果想要挑選不同的模型的話,可以讓兩個模型分別在訓練集上訓練,然後將兩個訓練好的模型分別在測試集上進行測試,由於我們把測試集上的誤差近似近似為泛化誤差,所以我們自然可以選擇在測試集上誤差小的模型作為最終我們要選擇的泛化能力強的模型。

但是我們要做的不僅是不同的模型與模型之間的對比,很多時候我們需要對模型本身進行選擇,假如我們有兩個模型,線性模型和神經網路模型,我們知道神經網路的泛化能力要比線性模型要強,我們選擇了神經網路模型,但是神經網路中還有很多的需要人工進行選擇的參數,比如神經網路的層數和每層神經網路的神經元個數以及正則化的一些參數等等,我們將這些參數稱為超參數。這些參數不同選擇對模型最終的效果也很重要,我們在開發模型的時候總是需要調節這些超參數。


現在我們需要調節這些超參數來使得模型泛化能力最強。我們使用測試集來作為泛化誤差估計,而我們最終的目的就是選擇泛化能力強的模型,那麼我們可以直接通過模型在測試集上的誤差來調節這些參數不就可以了。可能模型在測試集上的誤差為0,但是你拿著這樣的模型去部署到真實場景中去使用的話,效果可能會非常差。



這一現象叫做資訊泄露。我們使用測試集作為泛化誤差的近似,所以不到最後是不能將測試集的資訊泄露出去的,就好比考試一樣,我們平時做的題相當於訓練集,測試集相當於最終的考試,我們通過最終的考試來檢驗我們最終的學習能力,將測試集資訊泄露出去,相當於學生提前知道了考試題目,那最後再考這些提前知道的考試題目,當然代表不了什麼,你在最後的考試中得再高的分數,也不能代表你學習能力強。而如果通過測試集來調節模型,相當於不僅知道了考試的題目,學生還都學會怎麼做這些題了因為我們肯定會人為的讓模型在測試集上的誤差最小,因為這是你調整超參數的目的,那再拿這些題考試的話,人人都有可能考滿分,但是並沒有起到檢測學生學習能力的作用。原來我們通過測試集來近似泛化誤差,也就是通過考試來檢驗學生的學習能力,但是由於資訊泄露,此時的測試集即考試無任何意義,現實中可能學生的能力很差。所以,我們在學習的時候,老師會準備一些小測試來幫助我們查缺補漏,這些小測試也就是要說的驗證集。我們通過驗證集來作為調整模型的依據,這樣不至於將測試集中的資訊泄露。

也就是說我們將數據劃分訓練集、驗證集和測試集。在訓練集上訓練模型,在驗證集上評估模型,一旦找到的最佳的參數,就在測試集上最後測試一次,測試集上的誤差作為泛化誤差的近似。關於驗證集的劃分可以參考測試集的劃分,其實都是一樣的,這裡不再贅述。

吳恩達老師的影片中,如果當數據量不是很大的時候萬級別以及以下的時候將訓練集、驗證集以及測試集劃分為6:2:2;若是數據很大,可以將訓練集、驗證集、測試集比例調整為98:1:1;但是當可用的數據很少的情況下也可以使用一些高級的方法,比如留出方,K折交叉驗證等。

首發:

觸摸壹縷陽光~//zhuanlan.zhihu.com/p/48976706

參考:
1.《Hands-On Machine Learning with Scikit-Learn and TensorFlow》