1. 程式人生 > 其它 >scikit基礎與機器學習入門(11) 欠擬合,過擬合和交叉驗證

scikit基礎與機器學習入門(11) 欠擬合,過擬合和交叉驗證

欠擬合和過擬合的定義

在機器學習問題中,經常會出現模型在訓練資料上的得分很高,但是在新的資料上表現很差的情況,這稱之為過擬合overfitting,又叫高方差high variance

而如果在訓練資料上得分就很低,這稱之為欠擬合underfitting,又叫高偏差high bias

留出法與驗證集

為了解決過擬合問題,常見的方法將資料分為訓練集和測試集,用訓練集去訓練模型的引數,用測試集去測試訓練後模型的表現。有時對於一些具有超引數的模型(例如svm.SVC的引數C和kernel就屬於超引數),還需要從訓練集中劃出一部分資料去驗證超引數的有效性。

交叉驗證法

在資料數量有限時,按留出法將資料分成3部分將會嚴重影響到模型訓練的效果。為了有效利用有限的資料,可以採用交叉驗證cross_validation方法。

交叉驗證的基本思想是:以不同的方式多次將資料集劃分成訓練集和測試集,分別訓練和測試,再綜合最後的測試得分。每個資料在一些劃分情況下屬於訓練集,在另外一些劃分情況下屬於測試集。
常用的交叉驗證方法:K折(KFold),留一交叉驗證(LeaveOneOut,LOO),留P交叉驗證
(LeavePOut,LPO),重複K折交叉驗證(RepeatedKFold),隨機排列交叉驗證(ShuffleSplit)。
此外,為了保證訓練集中每種標籤類別資料的分佈和完整資料集中的分佈一致,可以採用分層交叉驗證方法(StratifiedKFold,StratifiedShuffleSplit)。

當資料集的來源有不同的分組時,獨立同分布假設(independent identical distributed:i.i.d)將被打破,可以使用分組交叉驗證方法來確保測試集合中的的所有樣本來自訓練樣本中沒有表示過的新的分組。(GroupKFold,LeaveOneGroupOut,LeavePGroupsOut,GroupShuffleSplit)

對於時間序列資料,一個非常重要的特點是時間相鄰的觀測之間的相關性(自相關性),因此用過去的資料訓練而用未來的資料測試非常重要。TimeSeriesSplit可以實現這樣的分割。

程式碼實現

注意·,這裡生成的是資料集生成的下標

隨機排列交叉驗證

import numpy as np
from sklearn.model_selection import ShuffleSplit
X = np.arange(5)
ss = ShuffleSplit(n_splits=10, test_size=0.6,
random_state=0)
for train_index, test_index in ss.split(X):
	print("%s %s" % (train_index, test_index))

分層K折交叉驗證

分三折,n_split在這裡指生成資料的組數

# 分層K折交叉驗證
from sklearn.model_selection import StratifiedKFold
X = np.ones(10)
y = [0, 0, 0, 0, 1, 1, 1, 1, 1,1]
skf = StratifiedKFold(n_splits=3,shuffle = False)
for train_index, test_index in skf.split(X, y):
	print("%s %s" % (train_index, test_index))

留P分組交叉驗證

from sklearn.model_selection import LeavePGroupsOut
X = np.arange(6)
y = [1, 1, 1, 2, 2, 2]
groups = [1, 1, 2, 2, 3, 3]
lpgo = LeavePGroupsOut(n_groups=2)
for train_index, test_index in lpgo.split(X, y, groups=groups):
	print("%s %s" % (train_index, test_index))

對於分類問題,可以用這種方法,此時留兩組作為驗證集

實現時間序列的分割

# 時間序列分割
from sklearn.model_selection import TimeSeriesSplit
X = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [1, 2], [3, 4],[2, 2],[4, 6]])
y = np.array([1, 2, 3, 4, 5, 6, 7, 8])
tscv = TimeSeriesSplit(n_splits=3,max_train_size = 3)
for train_index, test_index in tscv.split(X,y):
	print("%s %s" % (train_index, test_index))

我們可以看出,這裡的訓練集和資料集是從前到後一致的

交叉驗證綜合評分

呼叫 cross_val_score 函式可以計算模型在各交叉驗證資料集上的得分。可以指定metrics中的打分函式,也可以指定交叉驗證迭代器。

from sklearn.model_selection import cross_val_score
from sklearn import svm
from sklearn import datasets
iris = datasets.load_iris()
clf = svm.SVC(kernel='linear', C=1)
scores = cross_val_score(clf, iris.data, iris.target, cv=5) #採用5折交叉驗證
print(scores)
#平均得分和 95% 置信區間
print("Accuracy: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))

預設情況下,每個 CV 迭代計算的分數是估計器的 score 方法。可以通過使用 scoring 引數來改變計算方式如下

from sklearn import metrics
scores = cross_val_score(
	clf, iris.data, iris.target, cv=5, scoring='f1_macro')
scores

可以通過傳入一個自己定義的分類器

# 通過傳入一個交叉驗證迭代器來指定其他交叉驗證策略
from sklearn.model_selection import ShuffleSplit
n_samples = iris.data.shape[0]
ss = ShuffleSplit(n_splits=10, test_size=0.3, random_state=0)
cross_val_score(clf, iris.data, iris.target, cv=ss)

cross_validate函式和cross_val_score函式類似,但功能更為強大,它允許指定多個指標進行評估,並且除返回指定的指標外,還會返回一個fit_time和score_time即訓練時間和評分時間。

from sklearn.model_selection import cross_validate
from sklearn.metrics import recall_score
clf = svm.SVC(kernel='linear', C=1, random_state=0)
scores = cross_validate(clf, iris.data, iris.target, scoring=['f1_macro','f1_micro'],
cv=10, return_train_score=False)
print(sorted(scores.keys()))
#scores['test_recall_macro']
print(scores['fit_time']) #訓練集擬合時間
print(scores['score_time'])#測試集評分時間
print('f1_micro:',scores['test_f1_micro'])
print('f1_macro:',scores['test_f1_macro'])

使用cross_val_predict可以返回每條樣本作為CV中的測試集時,對應的模型對該樣本的預測結果。

這就要求使用的CV策略能保證每一條樣本都有機會作為測試資料,否則會報異常。

from sklearn.model_selection import cross_val_predict
predicted = cross_val_predict(clf, iris.data, iris.target, cv=10)
print(predicted)
metrics.accuracy_score(iris.target, predicted)

這裡的方法僅用於評估超引數的優劣,至於用交叉驗證法尋找超引數,下一章會提到

很喜歡聽到一個老師說的“半年理論”,現在做出的努力,一般要在半年的沉澱之後,才能出結果,所以在遇到瓶頸之時,不妨再努力半年