1. 程式人生 > >機器學習基礎:(Python)訓練集測試集分割與交叉驗證

機器學習基礎:(Python)訓練集測試集分割與交叉驗證

在上一篇關於Python中的線性迴歸的文章之後,我想再寫一篇關於訓練測試分割和交叉驗證的文章。在資料科學和資料分析領域中,這兩個概念經常被用作防止或最小化過度擬合的工具。我會解釋當使用統計模型時,通常將模型擬合在訓練集上,以便對未被訓練的資料進行預測。

在統計學和機器學習領域中,我們通常把資料分成兩個子集:訓練資料和測試資料,並且把模型擬合到訓練資料上,以便對測試資料進行預測。當做到這一點時,可能會發生兩種情況:模型的過度擬合或欠擬合。我們不希望出現這兩種情況,因為這會影響模型的可預測性。我們有可能會使用具有較低準確性或不常用的模型(這意味著你不能泛化對其它資料的預測)。

什麼是模型的過度擬合(Overfitting)和欠擬合(Underfitting)?

過度擬合

過度擬合意味著模型訓練得“太好”了,並且與訓練資料集過於接近了。這通常發生在模型過於複雜的情況下,模型在訓練資料上非常的準確,但對於未訓練資料或者新資料可能會很不準確。因為這種模型不是泛化的,意味著你可以泛化結果,並且不能對其它資料進行任何推斷,這大概就是你要做的。基本上,當發生這種情況時,模型學習或描述訓練資料中的“噪聲”,而不是資料中變數之間的實際關係。這種噪聲顯然不是任何新資料集的一部分,不能應用於它。

欠擬合

與過度擬合相反,當模型欠擬合的時候,它意味著模型不適合訓練資料,因此會錯過資料中的趨勢特點。這也意味著該模型不能被泛化到新的資料上。你可能猜到了,這通常是模型非常簡單的結果。例如,當我們將線性模型(如線性迴歸)擬合到非線性的資料時,也會發生這種情況。不言而喻,該模型對訓練資料的預測能力差,並且還不能推廣到其它的資料上。

3ea59ded087fa409c47078201361d9c784440f28 

例項

值得注意的是,欠擬合不像過度擬合那樣普遍。然而,我們希望避免資料分析中的這兩個問題。你可能會說,我們正在試圖找到模型的欠擬合與過度擬合的中間點。像你所看到的,訓練測試分割和交叉驗證有助於避免過度擬合超過欠擬合。

訓練測試分割

正如我之前所說的,我們使用的資料通常被分成訓練資料和測試資料。訓練集包含已知的輸出,並且模型在該資料上學習,以便以後將其泛化到其它資料上。我們有測試資料集(或子集),為了測試模型在這個子集上的預測。

f5919c651129f2342435fa981482a2d4bd0b79c3 

我們將使用Scikit-Learn library,特別是其中的訓練測試分割方法。我們將從匯入庫開始:

 

 

 

快速地看一下匯入的庫:

· Pandas —將資料檔案作為Pandas資料幀載入,並對資料進行分析;

· 在Sklearn中,我匯入了資料集模組,因此可以載入一個樣本資料集和linear_model,因此可以執行線性迴歸;

· 在Sklearn的子庫model_selection中,我匯入了train_test_split,因此可以把它分成訓練集和測試集;

· 在Matplotlib中,我匯入了pyplot來繪製資料圖表;

好了,一切都準備就緒,讓我們輸入糖尿病資料集,將其轉換成資料幀並定義列的名稱:

 

 

 

現在我們可以使用train_test_split函式來進行分割。函式內的test_size=0.2表明了要測試的資料的百分比,通常是80/20或70/30左右。

 

# create training and testing vars
X_train, X_test, y_train, y_test = train_test_split(df, y, test_size=0.2) print X_train.shape, y_train.shape print X_test.shape, y_test.shape (353, 10) (353,) (89, 10) (89,)

 

現在我們將在訓練資料上擬合模型:

 

# fit a model
lm = linear_model.LinearRegression() model = lm.fit(X_train, y_train) predictions = lm.predict(X_test)

 

正如所看到的那樣,我們在訓練資料上擬合模型並嘗試預測測試資料。讓我們看一看都預測了什麼:

 

predictions[0:5] array([ 205.68012533, 64.58785513, 175.12880278, 169.95993301, 128.92035866])

 

注:因為我在預測之後使用了[0:5],它只顯示了前五個預測值。去掉[0:5]的限制就會使它輸出我們模型建立的所有預測值。

讓我們來繪製模型:

 

## The line / model
plt.scatter(y_test, predictions) plt.xlabel(“True Values”) plt.ylabel(“Predictions”)

 

844f111fe44495e6f1276ad7c40c26d10dfce651 

列印準確度得分:

 

print “Score:”, model.score(X_test, y_test) Score: 0.485829586737

 

總結:將資料分割成訓練集和測試集,將回歸模型擬合到訓練資料,基於該資料做出預測,並在測試資料上測試預測結果。但是訓練和測試的分離確實有其危險性,如果我們所做的分割不是隨機的呢?如果我們資料的一個子集只包含來自某個州的人,或者具有一定收入水平但不包含其它收入水平的員工,或者只有婦女,或者只有某個年齡段的人,那該怎麼辦呢?這將導致過度擬合,即使我們試圖避免,這就是交叉驗證要派上用場了。

交叉驗證

在前一段中,我提到了訓練測試分割方法中的注意事項。為了避免這種情況,我們可以執行交叉驗證。它非常類似於訓練測試分割,但是被應用於更多的子集。意思是,我們將資料分割成k個子集,並訓練第k-1個子集。我們要做的是,為測試保留最後一個子集。

2602432ee053d821137b52d3d368be18f4b00865 

訓練測試分割和交叉驗證的視覺化表示

有一組交叉驗證方法,我來介紹其中的兩個:第一個是K-Folds Cross Validation,第二個是Leave One Out Cross Validation (LOOCV)。

K-Folds 交叉驗證

在K-Folds交叉驗證中,我們將資料分割成k個不同的子集。我們使用第k-1個子集來訓練資料,並留下最後一個子集作為測試資料。然後,我們對每個子集模型計算平均值,接下來結束模型。之後,我們對測試集進行測試。

85adc4e7d027f07fce527ea7827c63943c8bb9d3 

K-Folds的視覺化表示

這裡有一個在Sklearn documentation上非常簡單的K-Folds例子:

 

fromsklearn.model_selection import KFold # import KFold X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]]) # create an array y = np.array([1, 2, 3, 4]) # Create another array kf = KFold(n_splits=2) # Define the split - into 2 folds kf.get_n_splits(X) # returns the number of splitting iterations in the cross-validator print(kf) KFold(n_splits=2, random_state=None, shuffle=False)

 

讓我們看看結果:

 

fortrain_index, test_index in kf.split(X): 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:', array([2, 3]), 'TEST:', array([0, 1])) ('TRAIN:', array([0, 1]), 'TEST:', array([2, 3]))

 

正如看到的,函式將原始資料拆分成不同的資料子集。這是個非常簡單的例子,但我認為它把概念解釋的相當好。

棄一法交叉驗證(Leave One Out Cross Validation,LOOCV)

這是另一種交叉驗證的方法,棄一法交叉驗證。在這種交叉驗證中,子集的數量等於我們在資料集中觀察到的數量。然後,我們計算所有子集的平均數,並利用平均值建立模型。然後,對最後一個子集測試模型。因為我們會得到大量的訓練集(等於樣本的數量),因此這種方法的計算成本也相當高,應該在小資料集上使用。如果資料集很大,最好使用其它的方法,比如kfold。

讓我們看看Sklearn上的另一個例子:

 

fromsklearn.model_selectionimportLeaveOneOut
X = np.array([[1, 2], [3, 4]]) y = np.array([1, 2]) loo = LeaveOneOut() loo.get_n_splits(X) fortrain_index, test_indexinloo.split(X): 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] print(X_train, X_test, y_train, y_test)

 

以下是輸出:

 

('TRAIN:', array([1]), 'TEST:', array([0])) (array([[3, 4]]), array([[1, 2]]), array([2]), array([1])) ('TRAIN:', array([0]), 'TEST:', array([1])) (array([[1, 2]]), array([[3, 4]]), array([1]), array([2]))

 

那麼,我們應該使用什麼方法呢?使用多少子集呢?擁有的子集越多,我們將會由於偏差而減少誤差,但會由於方差而增加誤差;計算成本也會上升,顯然,擁有的子集越多,計算所需的時間就越長,也將需要更多的記憶體。如果利用數量較少的子集,我們減少了由於方差而產生的誤差,但是由於偏差引起的誤差會更大。它的計算成本也更低。因此,在大資料集中,通常建議k=3。在更小的資料集中,正如我之前提到的,最好使用棄一法交叉驗證。

讓我們看看以前用過的一個例子,這次使用的是交叉驗證。我將使用cross_val_predict函式來給每個在測試切片中的資料點返回預測值。

 

# Necessary imports: 
from sklearn.cross_validation import cross_val_score, cross_val_predict from sklearn import metrics

 

之前,我給糖尿病資料集建立了訓練測試分割,並擬合了一個模型。讓我們看看在交叉驗證之後的得分是多少:

 

# Perform 6-fold cross validation
scores = cross_val_score(model, df, y, cv=6) print “Cross-validated scores:”, scores Cross-validated scores: [ 0.4554861 0.46138572 0.40094084 0.55220736 0.43942775 0.56923406]

 

正如你所看到的,最後一個子集將原始模型的得分從0.485提高到0.569。這並不是一個驚人的結果,但我們得到了想要的。

現在,在進行交叉驗證之後,讓我們繪製新的預測圖:

 

# Make cross validated predictions
predictions = cross_val_predict(model, df, y, cv=6) plt.scatter(y, predictions)

 

8fe8a1abff68706b54e517f34ce9d8b89296c1a8 

你可以看到這和原來的圖有很大的不同,是原來圖的點數的六倍,因為我用的cv=6。

最後,讓我們檢查模型的R²得分(R²是一個“表示與自變數分離的可預測的因變數中方差的比例的數量”)。可以看一下我們的模型有多準確:

 

accuracy = metrics.r2_score(y, predictions) print “Cross-Predicted Accuracy:”, accuracy Cross-Predicted Accuracy: 0.490806583864

 

原文連結
本文為雲棲社群原創內容,未經允許不得轉載。