scikit-learn中交叉驗證及其用於引數選擇、模型選擇、特徵選擇的例子
內容概要
- 訓練集/測試集分割用於模型驗證的缺點
- K折交叉驗證是如何克服之前的不足
- 交叉驗證如何用於選擇調節引數、選擇模型、選擇特徵
- 改善交叉驗證
1. 模型驗證回顧
進行模型驗證的一個重要目的是要選出一個最合適的模型,對於監督學習而言,我們希望模型對於未知資料的泛化能力強,所以就需要模型驗證這一過程來體現不同的模型對於未知資料的表現效果。
最先我們用訓練準確度(用全部資料進行訓練和測試)來衡量模型的表現,這種方法會導致模型過擬合;為了解決這一問題,我們將所有資料分成訓練集和測試集兩部分,我們用訓練集進行模型訓練,得到的模型再用測試集來衡量模型的預測表現能力,這種度量方式叫測試準確度,這種方式可以有效避免過擬合。
測試準確度的一個缺點
高方差估計的例子
下面我們使用iris資料來說明利用測試準確度來衡量模型表現的方差很高。
from sklearn.datasets import load_iris
from sklearn.cross_validation import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn import metrics
# read in the iris data
iris = load_iris()
X = iris.data
y = iris.target
for i in range(1,5):
print ("random_state is ", i,", and accuracy score is:")
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=i)
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)
y_pred = knn.predict(X_test)
print (metrics.accuracy_score(y_test, y_pred))
random_state is 1 , and accuracy score is:
1.0
random_state is 2 , and accuracy score is:
1.0
random_state is 3 , and accuracy score is:
0.947368421053
random_state is 4 , and accuracy score is:
0.973684210526
上面的測試準確率可以看出,不同的訓練集、測試集分割的方法導致其準確率不同,而交叉驗證的基本思想是:將資料集進行一系列分割,生成一組不同的訓練測試集,然後分別訓練模型並計算測試準確率,最後對結果進行平均處理。這樣來有效降低測試準確率的差異。
2. K折交叉驗證
- 將資料集平均分割成K個等份
- 使用1份資料作為測試資料,其餘作為訓練資料
- 計算測試準確率
- 使用不同的測試集,重複2、3步驟
- 對測試準確率做平均,作為對未知資料預測準確率的估計
# 下面程式碼演示了K-fold交叉驗證是如何進行資料分割的
# simulate splitting a dataset of 25 observations into 5 folds
from sklearn.cross_validation import KFold
kf = KFold(25, n_folds=5, shuffle=False)
# print the contents of each training and testing set
print ('{} {:^61} {}'.format('Iteration', 'Training set observations', 'Testing set observations'))
for iteration, data in enumerate(kf, start=1):
print ('{:^9} {} {:^25}'.format(iteration, str(data[0]), str(data[1])))
Iteration Training set observations Testing set observations
1 [ 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24] [0 1 2 3 4]
2 [ 0 1 2 3 4 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24] [5 6 7 8 9]
3 [ 0 1 2 3 4 5 6 7 8 9 15 16 17 18 19 20 21 22 23 24] [10 11 12 13 14]
4 [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 20 21 22 23 24] [15 16 17 18 19]
5 [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19] [20 21 22 23 24]
3. 使用交叉驗證的建議
- K=10是一個一般的建議
- 如果對於分類問題,應該使用分層抽樣(stratified sampling)來生成資料,保證正負例的比例在訓練集和測試集中的比例相同
4. 交叉驗證的例子
4.1 用於調節引數
交叉驗證的方法可以幫助我們進行調參,最終得到一組最佳的模型引數。下面的例子我們依然使用iris資料和KNN模型,通過調節引數,得到一組最佳的引數使得測試資料的準確率和泛化能力最佳。
from sklearn.cross_validation import cross_val_score
knn = KNeighborsClassifier(n_neighbors=5)
# 這裡的cross_val_score將交叉驗證的整個過程連線起來,不用再進行手動的分割資料
# cv引數用於規定將原始資料分成多少份
scores = cross_val_score(knn, X, y, cv=10, scoring='accuracy')
print (scores)
[ 1. 0.93333333 1. 1. 0.86666667 0.93333333
0.93333333 1. 1. 1. ]
# use average accuracy as an estimate of out-of-sample accuracy
# 對十次迭代計算平均的測試準確率
print (scores.mean())
0.966666666667
# search for an optimal value of K for KNN model
k_range = range(1,31)
k_scores = []
for k in k_range:
knn = KNeighborsClassifier(n_neighbors=k)
scores = cross_val_score(knn, X, y, cv=10, scoring='accuracy')
k_scores.append(scores.mean())
print (k_scores)
[0.95999999999999996, 0.95333333333333337, 0.96666666666666656, 0.96666666666666656, 0.96666666666666679, 0.96666666666666679, 0.96666666666666679, 0.96666666666666679, 0.97333333333333338, 0.96666666666666679, 0.96666666666666679, 0.97333333333333338, 0.98000000000000009, 0.97333333333333338, 0.97333333333333338, 0.97333333333333338, 0.97333333333333338, 0.98000000000000009, 0.97333333333333338, 0.98000000000000009, 0.96666666666666656, 0.96666666666666656, 0.97333333333333338, 0.95999999999999996, 0.96666666666666656, 0.95999999999999996, 0.96666666666666656, 0.95333333333333337, 0.95333333333333337, 0.95333333333333337]
import matplotlib.pyplot as plt
#matplotlib inline
plt.plot(k_range, k_scores)
plt.xlabel("Value of K for KNN")
plt.ylabel("Cross validated accuracy")
plt.show()
上面的例子顯示了偏置-方差的折中,K較小的情況時偏置較低,方差較高;K較高的情況時,偏置較高,方差較低;最佳的模型引數取在中間位置,該情況下,使得偏置和方差得以平衡,模型針對於非樣本資料的泛化能力是最佳的。
4.2 用於模型選擇
交叉驗證也可以幫助我們進行模型選擇,以下是一組例子,分別使用iris資料,KNN和logistic迴歸模型進行模型的比較和選擇。
# 10-fold cross-validation with the best KNN model
knn = KNeighborsClassifier(n_neighbors=20)
print (cross_val_score(knn, X, y, cv=10, scoring='accuracy').mean())
0.98
# 10-fold cross-validation with logistic regression
from sklearn.linear_model import LogisticRegression
logreg = LogisticRegression()
print (cross_val_score(logreg, X, y, cv=10, scoring='accuracy').mean())
0.953333333333
4.3 用於特徵選擇
下面我們使用advertising資料,通過交叉驗證來進行特徵的選擇,對比不同的特徵組合對於模型的預測效果。
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
# read in the advertising dataset
data = pd.read_csv('http://www-bcf.usc.edu/~gareth/ISL/Advertising.csv', index_col=0)
# create a Python list of three feature names
feature_cols = ['TV', 'radio', 'newspaper']
# use the list to select a subset of the DataFrame (X)
X = data[feature_cols]
# select the Sales column as the response (y)
y = data.sales
# 10-fold cv with all features
lm = LinearRegression()
scores = cross_val_score(lm, X, y, cv=10, scoring='mean_squared_error')
print (scores)
[-3.56038438 -3.29767522 -2.08943356 -2.82474283 -1.3027754 -1.74163618
-8.17338214 -2.11409746 -3.04273109 -2.45281793]
這裡要注意的是,上面的scores都是負數,為什麼均方誤差會出現負數的情況呢?因為這裡的mean_squared_error是一種損失函式,優化的目標的使其最小化,而分類準確率是一種獎勵函式,優化的目標是使其最大化。
# fix the sign of MSE scores
mse_scores = -scores
print (mse_scores)
[ 3.56038438 3.29767522 2.08943356 2.82474283 1.3027754 1.74163618
8.17338214 2.11409746 3.04273109 2.45281793]
# convert from MSE to RMSE
rmse_scores = np.sqrt(mse_scores)
print (rmse_scores)
[ 1.88689808 1.81595022 1.44548731 1.68069713 1.14139187 1.31971064
2.85891276 1.45399362 1.7443426 1.56614748]
# calculate the average RMSE
print (rmse_scores.mean())
1.69135317081
# 10-fold cross-validation with two features (excluding Newspaper)
feature_cols = ['TV', 'Radio']
X = data[feature_cols]
print np.sqrt(-cross_val_score(lm, X, y, cv=10, scoring='mean_squared_error')).mean()
1.67967484191
由於不加入Newspaper這一個特徵得到的分數較小(1.68 < 1.69),所以,使用所有特徵得到的模型是一個更好的模型。