(數據科學學習手札27)sklearn數據集分割方法匯總


一、簡介

  在現實的機器學習任務中,我們往往是利用搜集到的盡可能多的樣本集來輸入算法進行訓練,以盡可能高的精度為目標,但這里便出現一個問題,一是很多情況下我們不能說搜集到的樣本集就能代表真實的全體,其分布也不一定就與真實的全體相同,但是有一點很明確,樣本集數量越大則其接近真實全體的可能性也就越大;二是很多算法容易發生過擬合(overfitting),即其過度學習到訓練集中一些比較特別的情況,使得其誤認為訓練集之外的其他集合也適用於這些規則,這使得我們訓練好的算法在輸入訓練數據進行驗證時結果非常好,但在訓練集之外的新測試樣本上精度則劇烈下降,這樣訓練出的模型可以說沒有使用價值;因此怎樣對數據集進行合理的抽樣-訓練-驗證就至關重要,下面就對機器學習中常見的抽樣技術進行介紹,並通過sklearn進行演示;

 

二、留出法

  留出法(hold-out)在前面的很多篇博客中我都有用到,但當時沒有仔細介紹,其基本思想是將數據集D(即我們獲得的所有樣本數據)划分為兩個互斥的集合,將其中一個作為訓練集S,另一個作為驗證集T,即D=SUT,S∩T=Φ。在S上訓練出模型后,再用T來評估其測試誤差,作為泛化誤差的估計值;

  需要注意的是,訓練集/驗證集的划分要盡可能保持數據分布的一致性,盡量減少因數據划分過程引入額外的偏差而對最終結果產生的影響,例如在分類任務中,要盡量保持ST內的樣本各個類別的比例大抵一致,這可以通過分層抽樣(stratified sampling)來實現;

  因為我們希望實現的是通過這個留出法的過程來評估數據集D的性能,但由於留出法需要划分訓練集與驗證集,這就不可避免的減少了訓練素材,若驗證集樣本數量過於小,導致訓練集與原數據集D接近,而與驗證集差別過大,進而導致無論訓練出的模型效果如何,都無法在驗證集上取得真實的評估結果,從而降低了評估效果的保真性(fidelity),因此訓練集與驗證集間的比例就不能過於隨便,通常情況下我們將2/3到4/5的樣本划分出來用於訓練;

  在sklearn中我們使用sklearn.model_selection中的train_test_split()來分割我們的數據集,其具體參數如下:

X:待分割的樣本集中的自變量部分,通常為二維數組或矩陣的形式;

y:待分割的樣本集中的因變量部分,通常為一維數組;

test_size:用於指定驗證集所占的比例,有以下幾種輸入類型:

  1.float型,0.0~1.0之間,此時傳入的參數即作為驗證集的比例;

  2.int型,此時傳入的參數的絕對值即作為驗證集樣本的數量;

  3.None,這時需要另一個參數train_size有輸入才生效,此時驗證集去為train_size指定的比例或數量的補集;

  4.缺省時為0.25,但要注意只有在train_size和test_size都不輸入值時缺省值才會生效;

train_size:基本同test_size,但缺省值為None,其實test_size和train_size輸入一個即可;

random_state:int型,控制隨機數種子,默認為None,即純隨機(偽隨機);

stratify:控制分類問題中的分層抽樣,默認為None,即不進行分層抽樣,當傳入為數組時,則依據該數組進行分層抽樣(一般傳入因變量所在列);

shuffle:bool型,用來控制是否在分割數據前打亂原數據集的順序,默認為True,分層抽樣時即stratify為None時該參數必須傳入False;

返回值:

依次返回訓練集自變量、測試集自變量、訓練集因變量、測試集因變量,因此使用該函數賦值需在等號右邊采取X_train, X_test, y_train, y_test'的形式;

下面以鳶尾花數據(三個class)為例,分別演示簡單隨機抽樣和分層抽樣時的不同情況:

未分層時:

from sklearn.model_selection import train_test_split
from sklearn import datasets
import pandas as pd

'''載入數據'''
X,y = datasets.load_iris(return_X_y=True)

'''不采取分層抽樣時的數據集分割'''
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3)

'''打印各個數據集的形狀'''
print(X_train.shape,X_test.shape,y_train.shape,y_test.shape)

'''打印訓練集中因變量的各類別數目情況'''
print(pd.value_counts(y_train))

'''打印驗證集集中因變量的各類別數目情況'''
print(pd.value_counts(y_test))

分層時:

'''采取分層抽樣時的數據集分割'''
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3,stratify=y)

'''打印各個數據集的形狀'''
print(X_train.shape,X_test.shape,y_train.shape,y_test.shape)

'''打印訓練集中因變量的各類別數目情況'''
print(pd.value_counts(y_train))

'''打印驗證集集中因變量的各類別數目情況'''
print(pd.value_counts(y_test))

 

三、交叉驗證法

  交叉驗證法(cross validation)先將數據集D划分為k個大小相似的互斥子集,即D=D1UD2U...UDkDi∩Dj=Φ(i≠j),每個子集Di都盡可能保持數據分布的一致性,即從D中通過分層采樣得到。然后每次用k-1個子集的並集作為訓練集,剩下的那一個子集作為驗證集;這樣就可獲得k組訓練+驗證集,從而可以進行k次訓練與測試,最終返回的是這k個測試結果的均值。顯然,交叉驗證法的穩定性和保真性在很大程度上取決與k的取值,因此交叉驗證法又稱作“k折交叉驗證”(k-fold cross validation),k最常見的取值為10,即“10折交叉驗證”,其他常見的有5,20等;

  假定數據集D中包含m個樣本,若令k=m,則得到了交叉驗證法的一個特例:留一法(Leave-one-out),顯然,留一法不受隨機樣本划分方式的影響,因為m個樣本只有唯一的方式划分m個子集——每個子集包含一個樣本,留一法使用的訓練集與初始數據集相比只少了一個樣本,這就使得在絕大多數情況下,留一法中被實際評估的模型與期望評估的用D訓練出的模型很相似,因此,留一法的評估結果往往被認為比較准確,但其也有一個很大的缺陷:當數據集比較大時,訓練m個模型的計算成本是難以想象的;

在sklearn.model_selection中集成了眾多用於交叉驗證的方法,下面對其中常用的進行介紹:

 

cross_val_score():

  這是一個用於直接計算某個已確定參數的模型其交叉驗證分數的方法,具體參數如下:

estimator:已經初始化的學習器模型;

X:自變量所在的數組;

y:因變量所在的數組;

scoring:str型,控制函數返回的模型評價指標,默認為准確率;

cv:控制交叉驗證中分割樣本集的策略,即k折交叉中的k,默認是3,即3折交叉驗證,有以下多種輸入形式:

  1.int型,則輸入的參數即為k;

  2.None,則使用默認的3折;

  3.一個生成器類型的對象,用來控制交叉驗證,優點是節省內存,下面的演示中會具體介紹;

  *若estimator是一個分類器,則默認使用分層抽樣來產生子集。

n_jobs:int型,用來控制並行運算中使用的核心數,默認為1,即單核;特別的,設置為-1時開啟所有核心;

函數返回值:

對應scoring指定的cv個評價指標;

下面以一個簡單的小例子進行演示:

from sklearn.model_selection import cross_val_score
from sklearn import datasets
from sklearn.neighbors import KNeighborsClassifier


X,y = datasets.load_breast_cancer(return_X_y=True)

clf = KNeighborsClassifier()


'''打印每次交叉驗證的准確率'''
score = cross_val_score(clf,X,y,cv=5,scoring='accuracy')

print('accuracy:'+str(score)+'\n')

'''打印每次交叉驗證的f1得分'''
score = cross_val_score(clf,X,y,cv=5,scoring='f1')

print('f1 score:'+str(score)+'\n')

'''打印正確率的95%置信區間'''
print(str(round(score.mean(),3))+'(+/-'+str(round(2*score.std(),3))+')')

 

cross_validate():

  這個方法與cross_val_score()很相似,但有幾處新特性:

  1.cross_validate()可以返回多個評價指標,這在需要一次性產生多個不同種類評分時很方便;

  2.cross_validate()不僅返回模型評價指標,還會返回訓練花費時長、

 其具體參數如下:

estimator:已經初始化的分類器模型;

X:自變量;

y:因變量;

scoring:字符型或列表形式的多個字符型,控制產出的評價指標,可以通過在列表中寫入多個評分類型來實現多指標輸出;

cv:控制交叉驗證的子集個數;

n_jobs:控制並行運算利用的核心數,同cross_val_score();

return_train_score:bool型,控制是否在得分中計算訓練集回帶進模型的結果;

函數輸出項:字典形式的訓練時間、計算得分時間、及各得分情況;

下面以一個簡單的小例子進行說明:

from sklearn.model_selection import cross_validate
from sklearn import datasets
from sklearn.neighbors import KNeighborsClassifier


X,y = datasets.load_breast_cancer(return_X_y=True)

clf = KNeighborsClassifier()

'''定義需要輸出的評價指標'''
scoring = ['accuracy','f1']

'''打印每次交叉驗證的准確率'''
score = cross_validate(clf,X,y,scoring=scoring,cv=5,return_train_score=True)

score

 

 

四、基於生成器的采樣方法

  sklearn中除了上述的直接完成整套交叉驗證的方法外,還存在着一些基於生成器的方法,這些方法的好處是利用Python中生成器(generator)的方式,以非常節省內存的方式完成每一次的交叉驗證,下面一一羅列:

 

KFold():

  以生成器的方式產出每一次交叉驗證所需的訓練集與驗證集,其主要參數如下:

n_splits:int型,控制k折交叉中的k,默認是3;

shuffle:bool型,控制是否在采樣前打亂原數據順序;

random_state:設置隨機數種子,默認為None,即不固定隨機水平;

下面以一個簡單的小例子進行演示:

from sklearn.model_selection import KFold
import numpy as np


X = np.random.randint(1,10,20)

kf = KFold(n_splits=5)

for train,test in kf.split(X):
    print(train,'\n',test)

 

 LeaveOneOut():

  對應先前所介紹的留出法中的特例,留一法,因為其性質很固定,所以無參數需要調節,下面以一個簡單的小例子進行演示:

from sklearn.model_selection import LeaveOneOut
import numpy as np


X = np.random.randint(1,10,5)

kf = LeaveOneOut()

for train,test in kf.split(X):
    print(train,'\n',test)

 

 LeavePOut():

  LeaveOneOut()的一個變種,唯一的不同就是每次留出p個而不是1個樣本作為驗證集,唯一的參數是p,下面是一個簡單的小例子:

from sklearn.model_selection import LeavePOut
import numpy as np


X = np.random.randint(1,10,5)

kf = LeavePOut(p=2)

for train,test in kf.split(X):
    print(train,'\n',test)

 

TimeSeriesSplit():

  在機器學習中還存在着一種叫做時間序列的數據類型,這種數據的特點是高度的自相關性,前后相鄰時段的數據關聯程度非常高,因此在對這種數據進行分割時不可以像其他機器學習任務那樣簡單隨機抽樣的方式采樣,對時間序列數據的采樣不能破壞其時段的連續型,在sklearn.model_selection中我們使用TimeSeriesSplit()來分割時序數據,其主要參數如下:

n_splits:int型,控制產生(訓練集+驗證集)的數量;

max_train_size:控制最大的時序數據長度;

下面是一個簡單的小例子:

from sklearn.model_selection import TimeSeriesSplit
import numpy as np


X = np.random.randint(1,10,20)

kf = TimeSeriesSplit(n_splits=4)

for train,test in kf.split(X):
    print(train,'\n',test)

 

  以上就是sklearn中關於樣本抽樣的常見功能,如有筆誤,望指出。

 

我的博客即將搬運同步至騰訊雲+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=1dq5sf6aad1ud


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM