這節(jié)課我們主要講講:
1。OOB 和 validation 的數(shù)值計算上區(qū)別
2。時間序列模型建立時,如何處理 validation 和 test 與模型精度控制的小技巧
3。去掉時間相關(guān)的元素 —— 消除訓(xùn)練集里面過擬合的特征
---
1。OOB 和 validation set 數(shù)值計算上的差別

這兩個數(shù)值有 2 點不一樣:
a。使用的數(shù)據(jù)不一樣
為了更好的得到模型驗證的結(jié)果,所以我們往往使用一種比較特別的 train/validation split 方式,我們不是直接隨機分類得到,而是根據(jù)時間順序,取時間更為久遠(yuǎn)的作為 train set,取時間較為近的作為 validation set。因此,OOB計算的數(shù)據(jù)其實來自于 train set,是整個train set 里面隨機收取的數(shù)據(jù),其結(jié)果更適用于解釋模型是否過擬合。而 validation set 則完全是時間緯度上更新的數(shù)據(jù),因此其結(jié)果更適合用于說明模型的泛化情況。
b。數(shù)據(jù)量不一樣
在 OOB 中,你的數(shù)據(jù)是 out of bag 數(shù)據(jù),因此每個數(shù)據(jù)被用于計算的情況,是它沒有被 train 選中的情況,這里的數(shù)據(jù)被選中的概率就低于 100%。而在 validation set 里面,每一次每個數(shù)據(jù)被會被計算,因此,計算概率是 100%。因此兩者的數(shù)據(jù)計算均衡性有差別,通常我們認(rèn)為,OOB 會比實際情況更低一點,因為他的 randomness 更低。
2。時間序列模型建立時,如何處理 validation 和 test 與模型精度控制的小技巧
在時間序列模型構(gòu)建時,我們的系統(tǒng)誤差往往來自于通過過去去預(yù)測未來的外推誤差。那在 Kaggle 或者工程上實現(xiàn)時,我們可以考慮這個小技巧:
- 在預(yù)測時,使用時間分區(qū)的方式把數(shù)據(jù)集分成 train 和 validation 集,通過預(yù)測的方式來估算那種模型的精度比較高,并保留這個模型并將 train 和 validation 融合在一起進(jìn)一步訓(xùn)練模型,進(jìn)而用于實際生產(chǎn)或者 kaggle 的 leaderboard。
- 在確定哪種模型比較好的時候,我們可以以時間間隔作為區(qū)分來測試:
? ? - 隨機抽樣(全體)
? ? - 上半個月的數(shù)據(jù)(1日 -? 15日)
? ? - 過去2周(8月1日至15日)
? ? - 下半個月的數(shù)據(jù)(15日至30日)
3。去掉時間相關(guān)的元素 —— 消除訓(xùn)練集里面過擬合的特征
因為 Kaggle 里面追求的是小數(shù)點后幾位的優(yōu)勢,因此一點點提高都是十分必要的。那么我們繼續(xù)來優(yōu)化模型。
從邏輯上來講,時間序列元素中與時間相關(guān)的部分可能隨著時間的變化而變化,因此他對于未來的精度預(yù)測可能帶有副作用,盡管在 train set 和 validation set 上表現(xiàn)較好,但是未必會在真實的表現(xiàn)上比較好。因此我們這里要作的反而是去掉時間序列元素的干擾,去盡量提取跟時間無關(guān)的、更為本質(zhì)的關(guān)系,用于對未來的預(yù)測。
這個理論聽上去很有道理,那么實踐的時候是否真的如此呢?我們來試一試。
df_ext = df_keep.copy()
df_ext['is_valid'] = 1
df_ext.is_valid[:n_trn] = 0
x, y, nas = proc_df(df_ext, 'is_valid')
在這里我們構(gòu)建一個 'is_valid' 特征作為預(yù)測的 target,隨后在 train set 里面把 這個 'is_valid' 特征標(biāo)記為 True,在 validation set 里面把這個特征標(biāo)記為 False。那么我們這樣訓(xùn)練出來的 model 就告訴我們是否所有的參數(shù)都可以被完美預(yù)測。
m = RandomForestClassifier(n_estimators=40, min_samples_leaf=3, max_features=0.5, n_jobs=-1, oob_score=True)
m.fit(x, y);
m.oob_score_
---
0.99998753505765037
ok,這里 score 為 0.999。表明所有的樣本都可以被完美預(yù)測。隨后,我們繼續(xù)輸出 feature importance,通過這個來看看完美預(yù)測的這些特征里面,哪些的重要性過高,這些可能就是造成現(xiàn)階段過擬合的元兇。
fi = rf_feat_importance(m, x); fi[:10]

同時我們進(jìn)一步比較頭三個元素,在 trian set 和 validation set 里面的 describe 會發(fā)現(xiàn),兩者完全不一樣。這些參數(shù)可能對于線性模型很重要,但是對于隨機森林就可能是拖后腿的特征了。那么讓我們 drop 掉這 3 個,來再作一次隨機森林訓(xùn)練。
x.drop(feats, axis=1, inplace=True)
m = RandomForestClassifier(n_estimators=40, min_samples_leaf=3, max_features=0.5, n_jobs=-1, oob_score=True)
m.fit(x, y);
m.oob_score_
---
0.9789018385789966
OK,這次也 score 挺高的,表示解釋度特別高。我們再來看看影響比較高的 feature 是哪些。

把兩次排名前 3 的分別拿出來,進(jìn)行影響分析,就是去掉之后,計算對最終 score 的影響。
feats=['SalesID', 'saleElapsed', 'MachineID', 'age', 'YearMade', 'saleDayofyear']
for f in feats:
????df_subs = df_keep.drop(f, axis=1)
????X_train, X_valid = split_vals(df_subs, n_trn)
????m = RandomForestRegressor(n_estimators=40, min_samples_leaf=3, max_features=0.5, n_jobs=-1, oob_score=True)
????m.fit(X_train, y_train)
????print(f)
????print_score(m)
---
SalesID[0.20918653475938534, 0.2459966629213187, 0.9053273181678706, 0.89192968797265737, 0.89245205174299469]
saleElapsed[0.2194124612957369, 0.2546442621643524, 0.90358104739129086, 0.8841980790762114, 0.88681881032219145]
MachineID[0.206612984511148, 0.24446409479358033, 0.90312476862123559, 0.89327205732490311, 0.89501553584754967]
age[0.21317740718919814, 0.2471719147150774, 0.90260198977488226, 0.89089460707372525, 0.89185129799503315]
YearMade[0.21305398932040326, 0.2534570148977216, 0.90555219348567462, 0.88527538596974953, 0.89158854973045432]
saleDayofyear[0.21320711524847227, 0.24629839782893828, 0.90881970943169987, 0.89166441133215968, 0.89272793857941679]
從數(shù)據(jù)上來看,我們可以去掉其中的 3 個影響不大的,來提升其他特征的有效性。因為在樹葉數(shù)量相等的情況下,在對最終結(jié)果不有效的特征上浪費時間,會造成很大的浪費。
df_subs = df_keep.drop(['SalesID', 'MachineID', 'saleDayofyear'], axis=1)
X_train, X_valid = split_vals(df_subs, n_trn)
m = RandomForestRegressor(n_estimators=40, min_samples_leaf=3, max_features=0.5, n_jobs=-1, oob_score=True)
m.fit(X_train, y_train)
print_score(m)
---
[0.1418970082803121, 0.21779153679471935, 0.96040441863389681, 0.91529091848161925, 0.90918594039522138]
OK,分?jǐn)?shù)達(dá)到 0.9 拉!我很滿意。那么,最終模型我們就加大樹的棵樹(比如,n_estimators=160),來作一個最終模型吧。
記得把有效的 features 保存下來。
np.save('tmp/subs_cols.npy', np.array(df_subs.columns))
筆記:我們往往進(jìn)行小范圍的測試之后,再對整體數(shù)據(jù)進(jìn)行學(xué)習(xí)和訓(xùn)練。因此,往往是白天測試優(yōu)化模型,晚上訓(xùn)練模型。