編者按:訓(xùn)練集/測試集劃分和交叉驗證一直是數(shù)據(jù)科學(xué)和數(shù)據(jù)分析中的兩個相當重要的概念,它們也是防止模型過擬合的常用工具。為了更好地掌握它們,在這篇文章中,我們會以統(tǒng)計模型為例,先從理論角度簡要介紹相關(guān)術(shù)語,然后給出一個Python實現(xiàn)的案例。
什么是模型過擬合/欠擬合
在統(tǒng)計學(xué)和機器學(xué)習(xí)中,通常我們會把數(shù)據(jù)分成兩個子集:訓(xùn)練數(shù)據(jù)和測試數(shù)據(jù)(有時也分為訓(xùn)練、驗證、測試三個),然后用訓(xùn)練集訓(xùn)練模型,用測試集檢驗?zāi)P偷膶W(xué)習(xí)效果。但當我們這么做時,模型可能會出現(xiàn)以下兩種情況:一是模型過度擬合數(shù)據(jù),二是模型不能很好地擬合數(shù)據(jù)。常言道過猶不及,這兩種情況都是我們要極力規(guī)避的,因為它們會影響模型的預(yù)測性能——預(yù)測準確率較低,或是泛化性太差,沒法把學(xué)到的經(jīng)驗推廣到其他數(shù)據(jù)上(也就是沒法預(yù)測其他數(shù)據(jù))。
過擬合
模型過擬合意味著我們把模型“訓(xùn)練得太好了”,通過一遍又一遍的訓(xùn)練,它已經(jīng)把訓(xùn)練數(shù)據(jù)的特征都“死記硬背”了下來。這在模型過于復(fù)雜(和觀察樣本數(shù)相比,模型設(shè)置的特征/變量太多)時往往更容易發(fā)生。過擬合的缺點是模型只對訓(xùn)練數(shù)據(jù)非常準確,但在未經(jīng)訓(xùn)練的數(shù)據(jù)或全新數(shù)據(jù)上非常不準確,因為它不是泛化的,沒法推廣結(jié)果,對其他數(shù)據(jù)作出任何推斷。
更確切地說,過擬合的模型學(xué)習(xí)到的只是訓(xùn)練數(shù)據(jù)中的“噪聲”,而不是數(shù)據(jù)中變量之間的實際關(guān)系。顯然,這些“噪聲”是訓(xùn)練數(shù)據(jù)獨有的,這也決定了它不能準確預(yù)測任何新數(shù)據(jù)集。
欠擬合
和過擬合相比,欠擬合是另一個極端,它意味著模型連擬合訓(xùn)練數(shù)據(jù)都做不到,沒能真正把握數(shù)據(jù)的趨勢。毫無疑問,一個欠擬合的模型也是不能被推廣到新數(shù)據(jù)的,它和過擬合恰恰相反,是模型過于簡單(沒有足夠的預(yù)測變量/自變量)的結(jié)果。例如,當我們用線性模型(比如線性回歸)擬合非線性數(shù)據(jù)時,模型就很可能會欠擬合。
欠擬合、恰到好處和過擬合
值得注意的是,在實踐中,欠擬合遠不像過擬合那么普遍。但我們還是要做到在數(shù)據(jù)分析中同時警惕這兩個問題,找到它們的中間地帶。而解決問題的首選方案就是劃分訓(xùn)練/測試數(shù)據(jù)和交叉驗證。
劃分訓(xùn)練/測試數(shù)據(jù)
正如之前提到的,我們使用的數(shù)據(jù)通常會被劃分為訓(xùn)練集和測試集。其中訓(xùn)練集包含輸入的對應(yīng)已知輸出,通過在上面進行訓(xùn)練,模型可以把學(xué)到的特征關(guān)系推廣到其他數(shù)據(jù)上,而測試集就是模型性能的試金石。
那么在Python中,我們能怎么執(zhí)行這個操作呢?這里我們介紹一種用Scikit-Learn庫,特別是traintestsplit的方法。讓我們先從導(dǎo)入庫開始:
import pandas as pd
from sklearn import datasets, linear_model
from sklearn.model_selection import train_test_split
from matplotlib import pyplot as plt
在上述代碼中:
第一行作用是把數(shù)據(jù)導(dǎo)入到pandas數(shù)據(jù)框架中,然后再進行分析;
第二行表示既然已經(jīng)導(dǎo)入了數(shù)據(jù)集模塊,所以我們可以加載一個樣本數(shù)據(jù)集和linear_model來做線性回歸;
第三行表示已經(jīng)導(dǎo)入了traintestsplit,所以可以把數(shù)據(jù)集分成訓(xùn)練集和測試集;
第四行意味著導(dǎo)入pyplot以繪制數(shù)據(jù)圖。
下面我們以糖尿病數(shù)據(jù)集為例,從實踐中看看怎么把它導(dǎo)入數(shù)據(jù)框架,并定義各列的名稱。
數(shù)據(jù)集地址:scikit-learn.org/stable/modules/generated/sklearn.datasets.load_diabetes.html
# Load the Diabetes Housing dataset
columns =“age sex bmi map tc ldl hdl tch ltg glu”.split()# Declare the columns names
diabetes = datasets.load_diabetes()# Call the diabetes dataset from sklearn
df = pd.DataFrame(diabetes.data, columns=columns)# load the dataset as a pandas data frame
y = diabetes.target # define the target variable (dependent variable) as y
現(xiàn)在我們可以用traintestsplit函數(shù)劃分數(shù)據(jù)集。test_size = 0.2表示測試數(shù)據(jù)在數(shù)據(jù)集中的占比,一般情況下,訓(xùn)練集和測試集的比例應(yīng)該是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,)
將模型擬合到訓(xùn)練數(shù)據(jù)上:
# fit a model
lm = linear_model.LinearRegression()
model = lm.fit(X_train, y_train)
predictions = lm.predict(X_test)
把模型放到測試數(shù)據(jù)上:
predictions[0:5]
array([205.68012533, 64.58785513,175.12880278,169.95993301,
128.92035866])
注:預(yù)測后面那個[0:5]表示只顯示前五個預(yù)測值,如果把它刪掉,模型會輸出所有預(yù)測值。
然后是繪制模型結(jié)果:
## The line / model
plt.scatter(y_test, predictions)
plt.xlabel(“TrueValues”)
plt.ylabel(“Predictions”)
最后輸出模型準確度分數(shù):
print“Score:”, model.score(X_test, y_test)
Score:0.485829586737
到這里,劃分訓(xùn)練集/測試集就完成了,如果總結(jié)整個過程,它可以被概括為先加載數(shù)據(jù),將其分成訓(xùn)練集和測試集,用回歸模型擬合訓(xùn)練數(shù)據(jù),基于訓(xùn)練數(shù)據(jù)進行預(yù)測并在測試集上預(yù)測測試數(shù)據(jù)的結(jié)果。一切都好像很完美吧?其實不然,劃分數(shù)據(jù)集也有很多講究——如果我們劃分時沒有做到嚴格意義上的隨機呢?如果數(shù)據(jù)集本身存在明顯偏差,其中大部分數(shù)據(jù)都來自某省、某個收入水平的員工、某個特定性別的員工或只有特定年齡的人,該怎么辦?
即便我們一再避免,模型最后還是會過擬合,而這就是交叉驗證可以發(fā)揮作用的地方。
交叉驗證
為了避免因數(shù)據(jù)集偏差、劃分數(shù)據(jù)集不當引起模型過擬合,我們可以使用交叉驗證,它和劃分訓(xùn)練集/測試集非常相似,但適用于數(shù)量上更多的子集。它的工作原理是先把數(shù)據(jù)分成k個子集,并從中挑選k-1個子集,在每個自己上訓(xùn)練模型,最后再用剩下的最后一個子集進行測試。
劃分訓(xùn)練集/測試集和交叉驗證
交叉驗證的方法有很多,這里我們只討論其中兩個:第一個是k-折交叉驗證,第二個是Leave One Out交叉驗證(LOOCV)。
k-折交叉驗證
在k-折交叉驗證中,我們將數(shù)據(jù)分成k個不同的子集(分成k折),并在k-1個子集上分別訓(xùn)練單獨模型,最后用第k個子集作為測試數(shù)據(jù)。
以下是k-折交叉驗證的Sklearn文檔中的一個簡單示例:
from sklearn.model_selection importKFold# 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)
它的結(jié)果是:
for train_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]))
如你所見,這個函數(shù)將原始數(shù)據(jù)拆分為不同的數(shù)據(jù)子集。
LOOCV
LOOCV(留一驗證)是本文要介紹的第二種交叉驗證方法,它的思路和k-折交叉驗證其實有相似之處,但不同的是,它只從原數(shù)據(jù)集中抽取一個樣本作為測試數(shù)據(jù),剩余的全是訓(xùn)練數(shù)據(jù),整個過程一直持續(xù)到每個樣本都被當做一次測試數(shù)據(jù),最后再用平均值構(gòu)建最終的模型??梢韵胍姡@種方法勢必會得到大量的訓(xùn)練集(等于樣本數(shù)),所以它的計算量會很大,更適合被用于小型數(shù)據(jù)集。
讓讓我們看一下它在Sklearn里的例子:
from sklearn.model_selection importLeaveOneOut
X = np.array([[1,2],[3,4]])
y = np.array([1,2])
loo =LeaveOneOut()
loo.get_n_splits(X)
for train_index, test_index in loo.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]))
那么對于這兩種交叉驗證方法,我們在實踐中該怎么取舍呢?事實上,子集越小、子集數(shù)量越多,模型的準確率就越高,但相應(yīng)的,它的計算量也越多,對內(nèi)存的要求也越大。因此,如果是非常小的數(shù)據(jù)集,建議大家還是用LOOCV;如果數(shù)據(jù)集略大,可以采用k-折,比如k=3是一個比較常用的超參數(shù),當然你也可以視情況選擇5、10等。
split函數(shù)劃分 VS 交叉驗證
之前我們演示了用函數(shù)劃分糖尿病數(shù)據(jù)集的結(jié)果,這里我們嘗試對它做交叉驗證,看看有什么不同。首先,我們可以用cross_val_predict函數(shù)返回測試子集中每個數(shù)據(jù)點的預(yù)測值。
# Necessary imports:
from sklearn.cross_validation import cross_val_score, cross_val_predict
from sklearn import metrics
既然我們已經(jīng)把數(shù)據(jù)集分成了測試集和訓(xùn)練集,這里我們再在原有基礎(chǔ)上進行交叉驗證,看看準確率得分變化:
# 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.461385720.400940840.552207360.439427750.56923406]
得分從0.485提高到了0.569,雖然看起來不是很顯著,但不要心急,我們來繪制交叉驗證后的圖像:
# Make cross validated predictions
predictions = cross_val_predict(model, df, y, cv=6)
plt.scatter(y, predictions)
很明顯,這幅圖里的數(shù)據(jù)點比之前的圖密集多了,因為我們?nèi)v=6。
從本質(zhì)上來看,回歸模型就是用模型擬合數(shù)據(jù),這之中肯定存在誤差,而衡量這個誤差大小的標尺是擬合優(yōu)度。在眾多標準中,R2是度量擬合優(yōu)度的一個常用統(tǒng)計量,這里我們計算一下模型的R2得分:
accuracy = metrics.r2_score(y, predictions)
print“Cross-PredictedAccuracy:”, accuracy
Cross-PredictedAccuracy:0.490806583864
以上就是文本想要介紹的全部內(nèi)容,希望你能從中找到對自己有所幫助的東西!
-
python
+關(guān)注
關(guān)注
56文章
4825瀏覽量
86216 -
數(shù)據(jù)科學(xué)
+關(guān)注
關(guān)注
0文章
168瀏覽量
10407
原文標題:如何在Python中劃分訓(xùn)練/測試集并進行交叉驗證
文章出處:【微信號:jqr_AI,微信公眾號:論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
深度學(xué)習(xí)中開發(fā)集和測試集的定義
建立開發(fā)集和測試集(總結(jié))
Python硬件驗證——摘要
如何在 IIS 中執(zhí)行 Python 腳本
如何避免在數(shù)據(jù)準備過程中的數(shù)據(jù)泄漏

如何測試Python環(huán)境
如何使用Python進行圖像識別的自動學(xué)習(xí)自動訓(xùn)練?
如何理解機器學(xué)習(xí)中的訓(xùn)練集、驗證集和測試集
機器學(xué)習(xí)中的交叉驗證方法
使用Python進行Ping測試

評論