1. 程式人生 > 其它 >用交叉驗證改善模型的預測表現(適用於Python和R)

用交叉驗證改善模型的預測表現(適用於Python和R)

原文作者: Sunil Ray

翻譯:王鵬宇

我一直對資料界的程式設計馬拉松(Hackathons)保持關注。通過對比排名榜初期和最終的結果, 我發現了一個有趣的現象:在初期排名較高的參賽者,在最終的驗證環節往往地位不保,有些甚至跌出前 20 名。

猜猜是什麼對引起了排名的劇烈變化?換句話說,為什麼這些參賽者的模型在最終驗證環節無法保證穩定性?讓我們來探討一下可能的原因。

預測模型為何無法保持穩定?

讓我們通過以下幾幅圖來理解這個問題:

此處我們試圖找到尺寸(size)和價格(price)的關係。三個模型各自做了如下工作:

  1. 第一個模型使用了線性等式。對於訓練用的資料點,此模型有很大誤差。這樣的模型在初期排行榜和最終排行榜都會表現不好。這是“擬合不足”(“Under fitting”)的一個例子。此模型不足以發掘資料背後的趨勢。
  2. 第二個模型發現了價格和尺寸的正確關係,此模型誤差低/概括程度高。
  3. 第三個模型對於訓練資料幾乎是零誤差。這是因為此關係模型把每個資料點的偏差(包括噪聲)都納入了考慮範圍,也就是說,這個模型太過敏感,甚至會捕捉到只在當前資料訓練集出現的一些隨機模式。這是“過度擬合”(“Over fitting”)的一個例子。這個關係模型可能在初榜和終榜成績變化很大。

在資料科學競賽中,一個常見的做法是對多個模型進行迭代,從中選擇表現更好的。然而,最終的分數是否會有改善依然未知,因為我們不知道這個模型是更好的發掘潛在關係了,還是過度擬合了。為了解答這個難題,我們應該使用交叉驗證(cross validation)技術。它能幫我們得到更有概括性的關係模型。

注:本文每個希望改善自己在資料科學競賽中提高表現的,雄心勃勃的資料科學家。在文章結尾,我分享了用於交叉驗證的 Python 和 R程式碼。在 R 中,我使用了 iris 資料集進行示範。

什麼是交叉驗證?

交叉驗證意味著需要保留一個樣本資料集,不用來訓練模型。在最終完成模型前,用這個資料集驗證模型。

交叉驗證包含以下步驟:

  1. 保留一個樣本資料集。
  2. 用剩餘部分訓練模型。
  3. 用保留的資料集驗證模型。這樣做有助於瞭解模型的有效性。如果當前的模型在此資料集也表現良好,那就帶著你的模型繼續前進吧!它棒極了!

交叉驗證的常用方法是什麼?

交叉驗證有很多方法。下面介紹其中幾種:

1. “驗證集”法

保留 50% 的資料集用作驗證,剩下 50% 訓練模型。之後用驗證集測試模型表現。不過,這個方法的主要缺陷是,由於只使用了 50% 資料訓練模型,原資料中一些重要的資訊可能被忽略。也就是說,會有較大偏誤。

2. 留一法交叉驗證 ( LOOCV )

這種方法只保留一個數據點用作驗證,用剩餘的資料集訓練模型。然後對每個資料點重複這個過程。這個方法有利有弊:

  • 由於使用了所有資料點,所以偏差較低。
  • 驗證過程重複了 n 次( n 為資料點個數),導致執行時間很長。
  • 由於只使用一個數據點驗證,這個方法導致模型有效性的差異更大。得到的估計結果深受此點的影響。如果這是個離群點,會引起較大偏差。

3. K 層交叉驗證 (K- fold cross validation)

從以上兩個驗證方法中,我們學到了:

  1. 應該使用較大比例的資料集來訓練模型,否則會導致失敗,最終得到偏誤很大的模型。
  2. 驗證用的資料點,其比例應該恰到好處。如果太少,會導致驗證模型有效性時,得到的結果波動較大。
  3. 訓練和驗證過程應該重複多次。訓練集和驗證集不能一成不變。這樣有助於驗證模型有效性。

是否有一種方法可以兼顧這三個方面?

答案是肯定的!這種方法就是“ K 層交叉驗證”這種方法簡單易行。簡要步驟如下:

  1. 把整個資料集隨機分成 K“層”
  2. 用其中 K-1 層訓練模型,然後用第K層驗證。
  3. 記錄從每個預測結果獲得的誤差。
  4. 重複這個過程,直到每“層”資料都作過驗證集。
  5. 記錄下的 k 個誤差的平均值,被稱為交叉驗證誤差(cross-validation error)。可以被用做衡量模型表現的標準。

當 k=10 時,k 層交叉驗證示意圖如下:

這裡一個常見的問題是:“如何確定合適的k值?”

記住,K 值越小,偏誤越大,所以越不推薦。另一方面,K 值太大,所得結果會變化多端。K 值小,則會變得像“驗證集法”;K 值大,則會變得像“留一法”(LOOCV)。所以通常建議的值是 k=10 。

如何衡量模型的偏誤/變化程度?

K 層交叉檢驗之後,我們得到 K 個不同的模型誤差估算值(e1, e2 …..ek)。理想的情況是,這些誤差值相加得 0 。要計算模型的偏誤,我們把所有這些誤差值相加。平均值越低,模型越優秀。

模型表現變化程度的計算與之類似。取所有誤差值的標準差,標準差越小說明模型隨訓練資料的變化越小。

我們應該試圖在偏誤和變化程度間找到一種平衡。降低變化程度、控制偏誤可以達到這個目的。這樣會得到更好的預測模型。進行這個取捨,通常會得出複雜程度較低的預測模型。

Python Code

from sklearn import cross_validation
model = RandomForestClassifier(n_estimators=100)
#簡單K層交叉驗證,10層。
cv = cross_validation.KFold(len(train), n_folds=10, indices=False)
results = []
# "Error_function" 可由你的分析所需的error function替代
for traincv, testcv in cv:
        probas = model.fit(train[traincv], target[traincv]).predict_proba(train[testcv])
        results.append( Error_function )
print "Results: " + str( np.array(results).mean() )

R Code

setwd('C:/Users/manish/desktop/RData')
library(plyr)
library(dplyr)
library(randomForest)
data <- iris
glimpse(data)
#交叉驗證,使用rf預測sepal.length
k = 5
data$id <- sample(1:k, nrow(data), replace = TRUE)
list <- 1:k
# 每次迭代的預測用資料框,測試用資料框
# the folds
prediction <- data.frame()
testsetCopy <- data.frame()
# 寫一個進度條,用來了解CV的進度
progress.bar <- create_progress_bar("text")
progress.bar$init(k)
#k層的函式
for(i in 1:k){
# 刪除id為i的行,建立訓練集
# 選id為i的行,建立訓練集
trainingset <- subset(data, id %in% list[-i])
testset <- subset(data, id %in% c(i))
#執行一個隨機森林模型
mymodel <- randomForest(trainingset$Sepal.Length ~ ., data = trainingset, ntree = 100)
#去掉迴應列1, Sepal.Length
temp <- as.data.frame(predict(mymodel, testset[,-1]))
# 將迭代出的預測結果新增到預測資料框的末尾
prediction <- rbind(prediction, temp)
# 將迭代出的測試集結果新增到測試集資料框的末尾
# 只保留Sepal Length一列
testsetCopy <- rbind(testsetCopy, as.data.frame(testset[,1]))
progress.bar$step()
}
# 將預測和實際值放在一起
result <- cbind(prediction, testsetCopy[, 1])
names(result) <- c("Predicted", "Actual")
result$Difference <- abs(result$Actual - result$Predicted)
# 用誤差的絕對平均值作為評估 
summary(result$Difference)