拓端tecdat|在Python和R中使用交叉驗(yàn)證方法提高模型性能
原文鏈接:http://tecdat.cn/?p=19518
原文出處:拓端數(shù)據(jù)部落公眾號(hào)
介紹
模型表現(xiàn)差異很大的可能原因是什么?換句話說(shuō),為什么在別人評(píng)估我們的模型時(shí)會(huì)失去穩(wěn)定性?
在本文中,我們將探討可能的原因。我們還將研究交叉驗(yàn)證的概念以及執(zhí)行它的一些常用方法。
目錄
為什么模型會(huì)失去穩(wěn)定性?
什么是交叉驗(yàn)證?
交叉驗(yàn)證的幾種常用方法
驗(yàn)證集方法
留一法交叉驗(yàn)證(LOOCV)
k折交叉驗(yàn)證
分層k折交叉驗(yàn)證
對(duì)抗驗(yàn)證
時(shí)間序列的交叉驗(yàn)證
自定義交叉驗(yàn)證技術(shù)
如何測(cè)量模型的偏差方差?
為什么模型會(huì)失去穩(wěn)定性?
讓我們使用下面的快照來(lái)說(shuō)明各種模型的擬合情況,以了解這一點(diǎn):

在這里,我們?cè)噲D找到數(shù)量和價(jià)格之間的關(guān)系。為此,我們采取了以下步驟:
我們使用線性方程式建立了關(guān)系,并為其顯示曲線圖。從訓(xùn)練數(shù)據(jù)點(diǎn)來(lái)看,第一幅圖有很高的誤差。在這種情況下,我們的模型無(wú)法捕獲數(shù)據(jù)的潛在趨勢(shì)
在第二個(gè)圖中,我們剛剛發(fā)現(xiàn)了價(jià)格和數(shù)量之間的正確關(guān)系,即較低的訓(xùn)練誤差
在第三個(gè)圖中,我們發(fā)現(xiàn)訓(xùn)練誤差幾乎為零的關(guān)系。這是因?yàn)橥ㄟ^(guò)考慮數(shù)據(jù)點(diǎn)中的每個(gè)偏差(包括噪聲)來(lái)建立關(guān)系,即模型過(guò)于敏感并且捕獲僅在當(dāng)前數(shù)據(jù)集中存在的隨機(jī)模式。這是“過(guò)度擬合”的一個(gè)例子。
數(shù)據(jù)科學(xué)競(jìng)賽的一種常見(jiàn)做法是迭代各種模型以找到性能更好的模型。為了找到正確的答案,我們使用驗(yàn)證技術(shù)。
什么是交叉驗(yàn)證?
在給定的建模樣本中,拿出大部分樣本進(jìn)行建模型,留小部分樣本用剛建立的模型進(jìn)行預(yù)測(cè),并求這小部分樣本的預(yù)測(cè)誤差,記錄它們的平方和。
以下是交叉驗(yàn)證中涉及的步驟:
保留?樣本數(shù)據(jù)集
使用數(shù)據(jù)集的其余部分訓(xùn)練模型
使用測(cè)試(驗(yàn)證)集的備用樣本。幫助您評(píng)估模型性能的有效性。
交叉驗(yàn)證的幾種常用方法
有多種方法可用于執(zhí)行交叉驗(yàn)證。我已經(jīng)在本節(jié)中討論了其中一些。
驗(yàn)證集方法
在這種方法中,我們將數(shù)據(jù)集的50%保留用于驗(yàn)證,其余50%用于模型訓(xùn)練。但是,這種方法的主要缺點(diǎn)是,由于我們僅在50%的數(shù)據(jù)集上訓(xùn)練模型,因此很可能會(huì)錯(cuò)過(guò)一些有關(guān)數(shù)據(jù)的信息,導(dǎo)致更高的偏差。
Python代碼:
train, validation = train_test_split(data, test_size=0.50, random_state = 5)
R代碼:
set.seed(101) # 設(shè)置種子,以便將來(lái)可以復(fù)制相同的樣本
#現(xiàn)在從數(shù)據(jù)的總共“ n”行中選擇50%的數(shù)據(jù)作為樣本
sample <- sample.int(n = nrow(data), size = floor(.50*nrow(data)), replace = F)
留一法交叉驗(yàn)證(LOOCV)
在這種方法中,我們僅從可用數(shù)據(jù)集中保留一個(gè)數(shù)據(jù)點(diǎn),并在其余數(shù)據(jù)上訓(xùn)練模型。該過(guò)程針對(duì)每個(gè)數(shù)據(jù)點(diǎn)進(jìn)行迭代。這有其優(yōu)點(diǎn)和缺點(diǎn)。讓我們看看它們:
我們利用所有數(shù)據(jù)點(diǎn),因此偏差會(huì)很低
我們將交叉驗(yàn)證過(guò)程重復(fù)n次(其中n是數(shù)據(jù)點(diǎn)數(shù)),這會(huì)導(dǎo)致執(zhí)行時(shí)間更長(zhǎng)
由于我們針對(duì)一個(gè)數(shù)據(jù)點(diǎn)進(jìn)行測(cè)試,因此這種方法導(dǎo)致測(cè)試模型有效性的較大差異。因此,我們的估計(jì)會(huì)受到數(shù)據(jù)點(diǎn)的影響。如果數(shù)據(jù)點(diǎn)是異常值,則可能導(dǎo)致更大的變化
R代碼:
for(i in 1:nrow(x)){
training = x[-i,]
model = #... 訓(xùn)練模型
score[[i]] = rmse(pred, validation[[label]]) # 得分/誤差
return(unlist(score)) # 返回一個(gè)向量
LOOCV指出了一個(gè)數(shù)據(jù)點(diǎn)。同樣,您可以忽略p個(gè)訓(xùn)練示例,以使每次迭代的驗(yàn)證集大小為p。這稱為L(zhǎng)POCV(留出P交叉驗(yàn)證)
k折交叉驗(yàn)證
通過(guò)以上兩種驗(yàn)證方法,我們了解到:
我們應(yīng)該在很大一部分?jǐn)?shù)據(jù)集上訓(xùn)練模型。否則,我們將無(wú)法讀取和識(shí)別數(shù)據(jù)中的潛在趨勢(shì)。最終將導(dǎo)致更高的偏差
我們還需要一個(gè)良好比例的測(cè)試數(shù)據(jù)點(diǎn)。如上所述,測(cè)試模型的有效性時(shí),較少的數(shù)據(jù)點(diǎn)數(shù)量會(huì)導(dǎo)致誤差
我們應(yīng)該多次重復(fù)訓(xùn)練和測(cè)試過(guò)程。應(yīng)該更改訓(xùn)練并測(cè)試數(shù)據(jù)集分布。這有助于正確驗(yàn)證模型有效性
我們是否有一種方法可以滿足所有這三個(gè)要求?
該方法稱為“?k倍交叉驗(yàn)證”。以下是它的步驟:
隨機(jī)將整個(gè)數(shù)據(jù)集拆分為k個(gè)“部分”
對(duì)于數(shù)據(jù)集中的每k折部分,在數(shù)據(jù)的k – 1折上建立模型。然后,測(cè)試模型以檢查k?折的有效性?
記錄每個(gè)預(yù)測(cè)上看到的誤差
重復(fù)此過(guò)程,直到每個(gè)k折都用作測(cè)試集
您記錄的k個(gè)誤差的平均值稱為交叉驗(yàn)證誤差,它將用作模型的性能指標(biāo)
以下是k = 10時(shí)k倍驗(yàn)證的可視化。

現(xiàn)在,最常見(jiàn)的問(wèn)題之一是:“如何選擇正確的k值?”。
k的?值越低,?偏差越大。另一方面,較高的K值偏差較小,但可能會(huì)出現(xiàn)較大的可變性。
準(zhǔn)確地說(shuō),LOOCV等效于n倍交叉驗(yàn)證,其中n是訓(xùn)練的數(shù)量。
Python代碼:
kf = RepeatedKFold(n_splits=5, n_repeats=10, random_state=None)
R代碼:
# 定義訓(xùn)練集進(jìn)行k折交叉驗(yàn)證
trainControl(method="cv", number=10)
# 擬合樸素貝葉斯模型
train(Species~., data=iris, trControl=train_control, method="nb")
# 總結(jié)結(jié)果
4.分層k折交叉驗(yàn)證
分層是重新排列數(shù)據(jù)的過(guò)程,以確保每個(gè)折都能很好地代表整體。例如,在二進(jìn)制分類問(wèn)題中,每個(gè)類別包含50%的數(shù)據(jù),最好安排數(shù)據(jù),在每一折中每個(gè)類別包含大約一半的實(shí)例。
?
當(dāng)同時(shí)處理偏差和方差時(shí),這通常是更好的方法。
用于分層k折交叉驗(yàn)證的Python代碼段:
# X是特征集,y是因變量
for train_index, test_index in skf.split(X,y):
print("Train:", train_index, "Validation:", val_index)
R代碼:
#折是根據(jù)因變量創(chuàng)建的
folds <- createFolds(factor(data$target), k = 10, list = FALSE)
話雖如此,如果訓(xùn)練集不能充分代表整個(gè)數(shù)據(jù),那么使用分層k折可能不是最好的方法。在這種情況下,應(yīng)使用帶有重復(fù)的簡(jiǎn)單?k倍交叉驗(yàn)證。
在重復(fù)的交叉驗(yàn)證中,交叉驗(yàn)證過(guò)程將重復(fù)?n?次,從而產(chǎn)生??原始樣本的n個(gè)隨機(jī)分區(qū)。將?n個(gè)?結(jié)果再次平均(或以其他方式組合)以產(chǎn)生單個(gè)估計(jì)。
用于重復(fù)k折交叉驗(yàn)證的Python代碼:
# X是特征集,y是因變量
print("Train:", train_index, "Validation:", val_index)
X_train, X_test = X[train_index], X[val_index]
y_train, y_test = y[train_index], y[val_index]
5.對(duì)抗性驗(yàn)證
在處理真實(shí)數(shù)據(jù)集時(shí),通常會(huì)遇到測(cè)試集和訓(xùn)練集非常不同的情況。結(jié)果,內(nèi)部交叉驗(yàn)證技術(shù)可能給出的分?jǐn)?shù)甚至不及測(cè)試分?jǐn)?shù)。在這種情況下,對(duì)抗性驗(yàn)證提供了一種解決方案。
總體思路是根據(jù)特征分布檢查訓(xùn)練和測(cè)試之間的相似程度。如果情況并非如此,我們可以懷疑它們是完全不同的。可以通過(guò)組合訓(xùn)練和測(cè)試集,分配0/1標(biāo)簽(0-訓(xùn)練,1-test)并評(píng)估二進(jìn)制分類任務(wù)來(lái)量化這種判斷。
讓我們了解一下,如何通過(guò)以下步驟完成此操作:
從訓(xùn)練集中刪除因變量
train.drop(['target'], axis = 1, inplace = True)
創(chuàng)建一個(gè)新的因變量,該變量對(duì)于訓(xùn)練集中的每一行是1,對(duì)于測(cè)試集中的每一行是0
train['is_train'] = 1
test['is_train'] = 0
結(jié)合訓(xùn)練和測(cè)試數(shù)據(jù)集
pd.concat([train, test], axis = 0)
使用上面新創(chuàng)建的因變量,擬合分類模型并預(yù)測(cè)要進(jìn)入測(cè)試集中的每一行的概率
# Xgboost 參數(shù)
clf = xgb.XGBClassifier(**xgb_params, seed = 10)
使用步驟4中計(jì)算出的概率對(duì)訓(xùn)練集進(jìn)行排序,并選擇前n%個(gè)樣本/行作為驗(yàn)證組(n%是要保留在驗(yàn)證組中的訓(xùn)練集的分?jǐn)?shù))
new_df = new_df.sort_values(by = 'probs', ascending=False)?# 30% 驗(yàn)證集
val_set_ids?將從訓(xùn)練集中獲取ID,這些ID將構(gòu)成最類似于測(cè)試集的驗(yàn)證集。對(duì)于訓(xùn)練和測(cè)試集非常不同的情況,這可以使驗(yàn)證策略更可靠。
但是,使用這種類型的驗(yàn)證技術(shù)時(shí)必須小心。一旦測(cè)試集的分布發(fā)生變化,驗(yàn)證集可能就不再是評(píng)估模型的良好子集。
6.時(shí)間序列的交叉驗(yàn)證
隨機(jī)分割時(shí)間序列數(shù)據(jù)集不起作用,因?yàn)閿?shù)據(jù)的時(shí)間部分將被弄亂。對(duì)于時(shí)間序列預(yù)測(cè)問(wèn)題,我們以以下方式執(zhí)行交叉驗(yàn)證。
時(shí)間序列交叉驗(yàn)證的折疊以正向連接方式創(chuàng)建
假設(shè)我們有一個(gè)時(shí)間序列,用于在n?年內(nèi)消費(fèi)者對(duì)產(chǎn)品的年度需求?。驗(yàn)證被創(chuàng)建為:
fold 1: training [1], test [2]
fold 2: training [1 2], test [3]
fold 3: training [1 2 3], test [4]
fold 4: training [1 2 3 4], test [5]
fold 5: training [1 2 3 4 5], test [6]
.
.
.
fold n: training [1 2 3 ….. n-1], test [n]

我們逐步選擇新的訓(xùn)練和測(cè)試集。我們從一個(gè)訓(xùn)練集開(kāi)始,該訓(xùn)練集具有最小擬合模型所需的觀測(cè)值。逐步地,我們每次折疊都會(huì)更改訓(xùn)練和測(cè)試集。在大多數(shù)情況下,第一步預(yù)測(cè)可能并不十分重要。在這種情況下,可以將預(yù)測(cè)原點(diǎn)移動(dòng)來(lái)使用多步誤差。例如,在回歸問(wèn)題中,以下代碼可用于執(zhí)行交叉驗(yàn)證。
Python代碼:
X_train, X_test = X[train_index], X[val_index]
y_train, y_test = y[train_index], y[val_index]
TRAIN: [0] TEST: [1]
TRAIN: [0 1] TEST: [2]
TRAIN: [0 1 2] TEST: [3]
R代碼:
tsCV(ts, Arima(x, order=c(2,0,0), h=1) # ?arima模型交叉驗(yàn)證
sqrt(mean(e^2, na.rm=TRUE)) # RMSE
h = 1表示我們僅對(duì)提前1步的預(yù)測(cè)采用誤差。
(h = 4)下圖描述了4步提前誤差。如果要評(píng)估模型來(lái)進(jìn)行多步預(yù)測(cè),可以使用此方法。

7.自定義交叉驗(yàn)證技術(shù)
如果沒(méi)有一種方法可以最有效地解決各種問(wèn)題。則可以創(chuàng)建基于函數(shù)或函數(shù)組合的自定義交叉驗(yàn)證技術(shù)。
如何測(cè)量模型的偏差方差?
經(jīng)過(guò)k倍交叉驗(yàn)證后,我們將獲得?k個(gè)?不同的模型估計(jì)誤差(e1,e2…..ek)。在理想情況下,這些誤差值應(yīng)總計(jì)為零。為了得到模型的偏差,我們獲取所有誤差的平均值。降低平均值,使模型更好。
同樣,為了計(jì)算模型方差,我們將所有誤差作為標(biāo)準(zhǔn)差。標(biāo)準(zhǔn)偏差值低表明我們的模型在不同的訓(xùn)練數(shù)據(jù)子集下變化不大。
我們應(yīng)該集中精力在偏差和方差之間取得平衡??梢酝ㄟ^(guò)減小方差并在一定程度上控制偏差來(lái)實(shí)現(xiàn)。這將獲得更好的預(yù)測(cè)模型。這種權(quán)衡通常也會(huì)導(dǎo)致建立不太復(fù)雜的預(yù)測(cè)模型。
尾注
在本文中,我們討論了過(guò)度擬合和諸如交叉驗(yàn)證之類的方法,來(lái)避免過(guò)度擬合。我們還研究了不同的交叉驗(yàn)證方法,例如驗(yàn)證集方法,LOOCV,k折交叉驗(yàn)證,分層k折等,然后介紹了每種方法在Python中的實(shí)現(xiàn)以及在Iris數(shù)據(jù)集上執(zhí)行的R實(shí)現(xiàn)。
最受歡迎的見(jiàn)解
1.Matlab馬爾可夫鏈蒙特卡羅法(MCMC)估計(jì)隨機(jī)波動(dòng)率(SV,Stochastic Volatility) 模型
2.基于R語(yǔ)言的疾病制圖中自適應(yīng)核密度估計(jì)的閾值選擇方法
3.WinBUGS對(duì)多元隨機(jī)波動(dòng)率模型:貝葉斯估計(jì)與模型比較
4.R語(yǔ)言回歸中的hosmer-lemeshow擬合優(yōu)度檢驗(yàn)
5.matlab實(shí)現(xiàn)MCMC的馬爾可夫切換ARMA – GARCH模型估計(jì)
6.R語(yǔ)言區(qū)間數(shù)據(jù)回歸分析
7.R語(yǔ)言WALD檢驗(yàn) VS 似然比檢驗(yàn)
8.python用線性回歸預(yù)測(cè)股票價(jià)格
9.R語(yǔ)言如何在生存分析與Cox回歸中計(jì)算IDI,NRI指標(biāo)