1. 程式人生 > >sklearn中的交叉驗證和資料劃分

sklearn中的交叉驗證和資料劃分

給定一個訓練資料集合,尋找一個模型去fit這個訓練資料,如果在全部的訓練資料上訓練獲得模型並且在全部的訓練資料上測試模型,則測試結果會很好;
但是對於未知的資料泛化效果會很不好,即過擬合。所以需要在不同的資料集上訓練和測試。


import numpy as np
from sklearn.model_selection import train_test_split
from sklearn import datasets
from sklearn import svm
iris = datasets.load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.4, random_state=0)


#train_test_split將資料劃分成訓練集合和測試集合,random_state引數設定使得能夠固定隨機劃分。test_size即為測試集佔比


#先前基礎上加入以下程式碼
clf = svm.SVC(kernel='linear', C=1).fit(X_train, y_train)
print clf.score(X_test, y_test) #輸出0.966666666667


針對SVM模型而言,如果只將原有的訓練資料劃分為訓練集和測試集,必須人工設定引數C,模型在測試集合上依然存在過擬合的風險。
因為這個引數會不斷優化使得其在測試集上取得最佳的效能,這時候測試集的內容就會被洩露到模型中,由測試集得到的評價指標不再能夠反映出泛化的效能。
為了解決這個問題,可以將原有的訓練資料劃分為訓練集、驗證集和測試集三個部分,在訓練集上訓練,再在驗證集上評估,最後在測試集上進行評價。
這樣洩露的只是驗證集上的資料,而測試集的資料不會洩漏,能夠得到無干擾的泛化效能指標。
但是針對資料量較小的訓練資料這樣劃分資料集會使得訓練集的量不夠,無法得到有效的訓練,同時隨機劃分這三個集合也會造成最終結果一定的隨機性。


可以用交叉驗證的方式解決這個問題。基本的方法稱為k折交叉驗證,下面介紹sklearn中的交叉驗證
#先前基礎上加入以下程式碼
from sklearn.model_selection import cross_val_score
clf = svm.SVC(kernel='linear', C=1)
scores = cross_val_score(clf, iris.data, iris.target, cv=5)  #線性SVM連續5次採用不同方法劃分資料擬合併評分
print scores  #輸出[ 0.96666667  1.          0.96666667  0.96666667  1.        ]
print scores.mean()  #輸出平均分數0.98




預設地,每次 CV 迭代獲得的分數由估計器的 score 函式計算得到。可以通過分數引數來改變計算方式,如下
#先前基礎上加入以下程式碼
from sklearn import metrics
scores = cross_val_score(clf, iris.data, iris.target, cv=5, scoring='f1_macro')
print scores   #輸出[ 0.96658312  1.          0.96658312  0.96658312  1.        ]                                           
#此處的scoring引數可以設定為accuracy,f1等值,用於分類、聚類或迴歸的結果評價。
#具體結果scoring設定可參考網頁:http://sklearn.lzjqsdd.com/modules/model_evaluation.html#scoring-parameter


當 cv 引數是一個整數時,cross_val_score 預設地使用 KFold 或者 StratifiedKFold 策略


對於同一個模型的驗證,測試集上的資料標準化應該與訓練集一致
#先前基礎上加入以下程式碼
from sklearn import preprocessing
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.4, random_state=0)
scaler = preprocessing.StandardScaler().fit(X_train)
X_train_transformed = scaler.transform(X_train)
clf = svm.SVC(C=1).fit(X_train_transformed, y_train)
X_test_transformed = scaler.transform(X_test)
print clf.score(X_test_transformed, y_test) #輸出0.933333333333


可以使用pipeline將資料標準化和交叉驗證結合起來
#先前基礎上加入以下程式碼
from sklearn.pipeline import make_pipeline
clf = make_pipeline(preprocessing.StandardScaler(), svm.SVC(C=1))
print cross_val_score(clf, iris.data, iris.target, cv=5) #輸出[ 0.96666667  0.96666667  0.96666667  0.93333333  1.        ]


介紹一下函式 cross_val_predict,除了返回結果不同,函式 cross_val_predict 具有和 cross_val_score 相同的介面
#這個計算的結果和 cross_val_score 有輕微的差別,因為兩者用了不同的方式組織元素
from sklearn.model_selection import cross_val_predict
predicted = cross_val_predict(clf, iris.data, iris.target, cv=10)
print metrics.accuracy_score(iris.target, predicted) #輸出0.973333333333


介紹一下函式cross_validate,它與cross_val_score有兩點不同:
1.允許設定多個度量評價
2.It returns a dict containing training scores, fit-times and score-times in addition to the test score.
對於單個度量評價,返回的dict的key值為['test_score', 'fit_time', 'score_time']
對於多個度量評價,返回的dict的key值為:
['test_<scorer1_name>', 'test_<scorer2_name>', 'test_<scorer...>', 'fit_time', 'score_time']
引數return_train_score預設設定為True,會輸出訓練集的train score,設定為False則不輸出。


#新增如下程式碼
from sklearn.model_selection import cross_validate
from sklearn.metrics import recall_score
# scoring = ['precision_macro', 'recall_macro']
#輸出{'test_precision_macro': array([ 0.96969697,  1.        ,  0.96969697,  0.96969697,  1.        ]), 'train_recall_macro': array([ 0.975     ,  0.975     ,  0.99166667,  0.98333333,  0.98333333]), 'score_time': array([ 0.00099993,  0.00099993,  0.00099993,  0.00099993,  0.00099993]), 'fit_time': array([ 0.00099993,  0.        ,  0.00099993,  0.        ,  0.        ]), 'test_recall_macro': array([ 0.96666667,  1.        ,  0.96666667,  0.96666667,  1.        ]), 'train_precision_macro': array([ 0.97674419,  0.97674419,  0.99186992,  0.98412698,  0.98333333])}
scoring = 'accuracy'
clf = svm.SVC(kernel='linear', C=1, random_state=0)
scores = cross_validate(clf, iris.data, iris.target, scoring=scoring,cv=5, return_train_score=True)
print scores
#輸出{'score_time': array([ 0.        ,  0.        ,  0.00099993,  0.        ,  0.00100017]), 'test_score': array([ 0.96666667,  1.        ,  0.96666667,  0.96666667,  1.        ]), 'train_score': array([ 0.975     ,  0.975     ,  0.99166667,  0.98333333,  0.98333333]), 'fit_time': array([ 0.        ,  0.00099993,  0.        ,  0.00099993,  0.        ])}





以下介紹為資料集的劃分方法
列出了一些用於生成索引標號,用於在不同的交叉驗證策略中生成資料劃分的工具。
#在 4 個樣例的資料集上使用 2-fold 交叉驗證的例子
X = ["a", "b", "c", "d"]
kf = KFold(n_splits=2)
print kf
for train, test in kf.split(X):
    print train,test
#輸出 
#[2 3] [0 1]
#[0 1] [2 3]
每個摺疊由兩個陣列組成,第一個作為 training set,另一個作為 test set;由此,可以通過使用 numpy 的索引建立訓練/測試集合
X = np.array([[0., 0.], [1., 1.], [-1., -1.], [2., 2.]])
y = np.array([0, 1, 0, 1])
X_train, X_test, y_train, y_test = X[train], X[test], y[train], y[test]
#輸出
#[[ 0.  0.]
# [ 1.  1.]] [[-1. -1.]
# [ 2.  2.]] [0 1] [0 1]


StratifiedKFold是k-fold 的變種,會返回分層的摺疊:每個小集合中,各個類別的樣例比例大致和完整資料集中相同。
#在有10個樣例的,有兩個略不均衡類別的資料集上進行分層 3-fold 交叉驗證的例子
from sklearn.model_selection import StratifiedKFold
X = np.ones(10)
print X
y = [0, 0, 0, 0, 1, 1, 1, 1, 1, 1]
skf = StratifiedKFold(n_splits=3)
for train, test in skf.split(X, y):
    print train, test
#輸出:
#[2 3 6 7 8 9] [0 1 4 5]
#[0 1 3 4 5 8 9] [2 6 7]
#[0 1 2 4 5 6 7] [3 8 9]


Leave One Out (LOO)
from sklearn.model_selection import LeaveOneOut
X = [1, 2, 3, 4]
loo = LeaveOneOut()
for train, test in loo.split(X):
print("%s %s" % (train, test))
#[1 2 3] [0]
#[0 2 3] [1]
#[0 1 3] [2]
#[0 1 2] [3]
經驗證據表明5-或者10-的摺疊交叉驗證要好於LOO


Random permutations cross-validation a.k.a. Shuffle & Split
ShuffleSplit 迭代器 將會生成一個使用者給定數量的獨立的訓練/測試資料劃分。樣例首先被打散然後劃分為一對訓練測試集合。
可以通過設定明確的 random_state,使得偽隨機生成器的結果可以重複。
from sklearn.model_selection import ShuffleSplit
X = np.arange(5)
ss = ShuffleSplit(n_splits=3, test_size=0.25,random_state=0)
for train_index, test_index in ss.split(X):
    print("%s %s" % (train_index, test_index))
#輸出:
#[1 3 4] [2 0]
#[1 4 3] [0 2]
#[4 0 2] [1 3]


Repeated K-Fold
Leave-P-Out - LPO
…………………………等等方法
可以參考:
http://scikit-learn.org/stable/modules/cross_validation.html
http://sklearn.lzjqsdd.com/modules/cross_validation.html#leave-p-out-lpo
https://www.cnblogs.com/nolonely/p/7007432.html
http://blog.csdn.net/kancy110/article/details/74910185
https://www.cnblogs.com/hellcat/p/7045585.html