內容概要¶
- 訓練集/測試集分割用於模型驗證的缺點
- K折交叉驗證是如何克服之前的不足
- 交叉驗證如何用於選擇調節參數、選擇模型、選擇特徵
- 改善交叉驗證
1. 模型驗證回顧¶
進行模型驗證的一個重要目的是要選出一個最合適的模型,對於監督學習而言,我們希望模型對於未知數據的泛化能力強,所以就需要模型驗證這一過程來體現不同的模型對於未知數據的表現效果。
最先我們用訓練準確度(用全部數據進行訓練和測試)來衡量模型的表現,這種方法會導致模型過擬合;爲了解決這一問題,我們將所有數據分成訓練集和測試集兩部分,我們用訓練集進行模型訓練,得到的模型再用測試集來衡量模型的預測表現能力,這種度量方式叫測試準確度,這種方式可以有效避免過擬合。
測試準確度的一個缺點是其樣本準確度是一個高方差估計(high variance estimate),所以該樣本準確度會依賴不同的測試集,其表現效果不盡相同。
高方差估計的例子¶
下面我們使用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 xrange(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)
上面的測試準確率可以看出,不同的訓練集、測試集分割的方法導致其準確率不同,而交叉驗證的基本思想是:將數據集進行一系列分割,生成一組不同的訓練測試集,然後分別訓練模型並計算測試準確率,最後對結果進行平均處理。這樣來有效降低測試準確率的差異。
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, data[0], data[1])
3. 使用交叉驗證的建議¶
- K=10是一個一般的建議
- 如果對於分類問題,應該使用分層抽樣(stratified sampling)來生成數據,保證正負例的比例在訓練集和測試集中的比例相同
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
# use average accuracy as an estimate of out-of-sample accuracy
# 對十次迭代計算平均的測試準確率
print scores.mean()
# 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
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")
上面的例子顯示了偏置-方差的折中,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()
# 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()
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
這裏要注意的是,上面的scores都是負數,爲什麼均方誤差會出現負數的情況呢?因爲這裏的mean_squared_error是一種損失函數,優化的目標的使其最小化,而分類準確率是一種獎勵函數,優化的目標是使其最大化。
# fix the sign of MSE scores
mse_scores = -scores
print mse_scores
# convert from MSE to RMSE
rmse_scores = np.sqrt(mse_scores)
print rmse_scores
# calculate the average RMSE
print rmse_scores.mean()
# 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()
由於不加入Newspaper這一個特徵得到的分數較小(1.68 < 1.69),所以,使用所有特徵得到的模型是一個更好的模型。
參考資料¶
- scikit-learn documentation: Cross-validation, Model evaluation
- scikit-learn issue on GitHub: MSE is negative when returned by cross_val_score
- Scott Fortmann-Roe: Accurately Measuring Model Prediction Error
- Harvard CS109: Cross-Validation: The Right and Wrong Way
- Journal of Cheminformatics: Cross-validation pitfalls when selecting and assessing regression and classification models