在機器學習中,交叉驗證是模型選擇的常用方法。如果給定的樣本數據充足,進行模型選擇的一種簡單方法是隨機的降數據集分成:訓練集(training set)、驗證集(validation set)、測試集(test set)。訓練集用來訓練模型,驗證集用來選擇模型,測試集用於最終對學習方法的評估。在學習到的不同復雜度的模型中,選擇對驗證集有最小預測誤差的模型。由於驗證集有足夠多的數據,用它對模型進行選擇也是有效的。
但是在實際應用中數據時不充足的。為了選擇好的模型,可以采用交叉驗證方法。交叉驗證的基本思想:重復的使用數據;把給定的數據進行切分,將切分的數據集組合為訓練集與測試集,在此基礎上反復的進行訓練、測試及模型選擇。
1 交叉驗證的原理
簡單交叉驗證
方法:首先隨機的將已給數據分為兩部分,一部分作為訓練集,另一部分作為測試集(例如70%作為訓練,30%作為測試),然后用訓練集在各種條件下(不同的參數個數)訓練模型,從而得到不同的模型;在訓練集上評價各個模型的測試誤差,選出測試誤差最小的模型。
S折交叉驗證
方法:首先隨機的將已給數據切分為S個互不相交的大小相同的自己;然后利用S-1個自己的數據訓練模型,利用余下的子集測試模型;將這一過程對可能的S種選擇重復進行;最后選出S次評測中平均測試誤差最小的模型。
留一交叉驗證
\(S\)折交叉驗證的特殊情形是\(S=N\),稱為留一交叉驗證(leave-one-out cross validation),往往在數據缺乏的情況下使用。這里,\(N\)是給定數據集的容量。
2 交叉驗證代碼實現
2.1 簡單交叉驗證
簡單交叉驗證就是對訓練集進行train_test_split,划分成train 和test 兩部分,其中train用來訓練模型,test用來評估模型,模型通過fit方法從t訓練數據集中學習,然后調用score方法在測試集上進行評估。
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(cancer.data,cancer.target,random_state=0)
logreg = LogisticRegression().fit(X_train,y_train)
print("Test set score:{:.2f}".format(logreg.score(X_test,y_test))) #Test set score:0.96
這種方式數據集只拆分一次,默認比例是3:1,即75%的樣本數據被作為訓練集,25%的樣本被作為測試集,這種默認帶有偶然性,訓練的模型不穩定。
2.2 S折交叉驗證
交叉驗證就是對數據集進行多次train_test_split划分,每次划分,在不同數據集上進行訓練、測試和評估。如5折交叉驗證,就是對原數據集進行5次划分,然后每次划分進行一次訓練、評估,得到5次划分的評估結果,最后對5次評估結果取平均。
from sklearn.model_selection import cross_val_score
logreg = LogisticRegression()
scores = cross_val_score(logreg,cancer.data,cancer.target) #默認是3折交叉驗證
print("Cross validation scores:{}".format(scores)) #Cross validation scores:[ 0.93684211 0.96842105 0.94179894]
print("Mean cross validation score:{:2f}".format(scores.mean())) #Mean cross validation score:0.949021
交叉驗證每次拆分是對數據進行均分,如果數據集是按類別存放的,那么划分時會出現每一折全是同一類數據,這樣訓練得到的模型泛化能力極差。
2.3 分層交叉驗證
為了解決S折交叉驗證存在的問題,分層交叉驗證的思想是在每一折中都保持着原始數據中各個類別的比例關系,比如說:原始數據有3類,比例為1:2:1,采用3折分層交叉驗證,那么划分的3折中,每一折中的數據類別保持着1:2:1的比例,這樣的驗證結果更加可信。
from sklearn.datasets import load_iris
from sklearn.model_selection import StratifiedKFold,cross_val_score
from sklearn.linear_model import LogisticRegression
iris = load_iris()
#print('Iris labels:\n{}'.format(iris.target))
logreg = LogisticRegression()
strKFold = StratifiedKFold(n_splits=3,shuffle=False,random_state=0)
scores = cross_val_score(logreg,iris.data,iris.target,cv=strKFold)
print("straitified cross validation scores:{}".format(scores)) #straitified cross validation scores:[ 0.96078431 0.92156863 0.95833333]
print("Mean score of straitified cross validation:{:.2f}".format(scores.mean())) #Mean score of straitified cross validation:0.95
2.4 留一法
留一法是一種特殊的交叉驗證方式。如果樣本容量為n,則k=n,進行n折交叉驗證,每次留下一個樣本進行驗證。主要針對小樣本數據。
from sklearn.datasets import load_iris
from sklearn.model_selection import LeaveOneOut,cross_val_score
from sklearn.linear_model import LogisticRegression
iris = load_iris()
#print('Iris labels:\n{}'.format(iris.target))
logreg = LogisticRegression()
loout = LeaveOneOut()
scores = cross_val_score(logreg,iris.data,iris.target,cv=loout)
#print("leave-one-out cross validation scores:{}".format(scores))
print("Mean score of leave-one-out cross validation:{:.2f}".format(scores.mean())) #Mean score of leave-one-out cross validation:0.95
參考:統計學習方法 幾種交叉驗證比較