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

摘要: 本文講述瞭如何用Python對訓練集測試集進行分割與交叉驗證。

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

在統計學和機器學習領域中,咱們一般把數據分紅兩個子集:訓練數據和測試數據,而且把模型擬合到訓練數據上,以便對測試數據進行預測。當作到這一點時,可能會發生兩種狀況:模型的過分擬合或欠擬合。咱們不但願出現這兩種狀況,由於這會影響模型的可預測性。咱們有可能會使用具備較低準確性或不經常使用的模型(這意味着你不能泛化對其它數據的預測)。python

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

過分擬合

過分擬合意味着模型訓練得「太好」了,而且與訓練數據集過於接近了。這一般發生在模型過於複雜的狀況下,模型在訓練數據上很是的準確,但對於未訓練數據或者新數據可能會很不許確。由於這種模型不是泛化的,意味着你能夠泛化結果,而且不能對其它數據進行任何推斷,這大概就是你要作的。基本上,當發生這種狀況時,模型學習或描述訓練數據中的「噪聲」,而不是數據中變量之間的實際關係。這種噪聲顯然不是任何新數據集的一部分,不能應用於它。dom

欠擬合

與過分擬合相反,當模型欠擬合的時候,它意味着模型不適合訓練數據,所以會錯過數據中的趨勢特色。這也意味着該模型不能被泛化到新的數據上。你可能猜到了,這一般是模型很是簡單的結果。例如,當咱們將線性模型(如線性迴歸)擬合到非線性的數據時,也會發生這種狀況。不言而喻,該模型對訓練數據的預測能力差,而且還不能推廣到其它的數據上。機器學習

實例

值得注意的是,欠擬合不像過分擬合那樣廣泛。然而,咱們但願避免數據分析中的這兩個問題。你可能會說,咱們正在試圖找到模型的欠擬合與過分擬合的中間點。像你所看到的,訓練測試分割和交叉驗證有助於避免過分擬合超過欠擬合。函數

訓練測試分割

正如我以前所說的,咱們使用的數據一般被分紅訓練數據和測試數據。訓練集包含已知的輸出,而且模型在該數據上學習,以便之後將其泛化到其它數據上。咱們有測試數據集(或子集),爲了測試模型在這個子集上的預測。工具

咱們將使用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」)

打印準確度得分:

print 「Score:」, model.score(X_test, y_test)
Score: 0.485829586737

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

交叉驗證

在前一段中,我提到了訓練測試分割方法中的注意事項。爲了不這種狀況,咱們能夠執行交叉驗證。它很是相似於訓練測試分割,可是被應用於更多的子集。意思是,咱們將數據分割成k個子集,並訓練第k-1個子集。咱們要作的是,爲測試保留最後一個子集。

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

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

K-Folds 交叉驗證

在K-Folds交叉驗證中,咱們將數據分割成k個不一樣的子集。咱們使用第k-1個子集來訓練數據,並留下最後一個子集做爲測試數據。而後,咱們對每一個子集模型計算平均值,接下來結束模型。以後,咱們對測試集進行測試。

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)

這是另外一種交叉驗證的方法,棄一法交叉驗證。能夠在Sklearn website上查看。在這種交叉驗證中,子集的數量等於咱們在數據集中觀察到的數量。而後,咱們計算全部子集的平均數,並利用平均值創建模型。而後,對最後一個子集測試模型。由於咱們會獲得大量的訓練集(等於樣本的數量),所以這種方法的計算成本也至關高,應該在小數據集上使用。若是數據集很大,最好使用其它的方法,好比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)

你能夠看到這和原來的圖有很大的不一樣,是原來圖的點數的六倍,由於我用的cv=6。

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

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



本文做者:【方向】

閱讀原文

本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索