实用随机森林 - 针对时间序列的 kaggle 技巧

这节课我们主要讲讲:

1。OOB 和 validation 的数值计算上区别

2。时间序列模型建立时,如何处理 validation 和 test 与模型精度控制的小技巧

3。去掉时间相关的元素 —— 消除训练集里面过拟合的特征

---

1。OOB 和 validation set 数值计算上的差别

这两个数值有 2 点不一样:

a。使用的数据不一样

为了更好的得到模型验证的结果,所以我们往往使用一种比较特别的 train/validation split 方式,我们不是直接随机分类得到,而是根据时间顺序,取时间更为久远的作为 train set,取时间较为近的作为 validation set。因此,OOB计算的数据其实来自于 train set,是整个train set 里面随机收取的数据,其结果更适用于解释模型是否过拟合。而 validation set 则完全是时间纬度上更新的数据,因此其结果更适合用于说明模型的泛化情况。

b。数据量不一样

在 OOB 中,你的数据是 out of bag 数据,因此每个数据被用于计算的情况,是它没有被 train 选中的情况,这里的数据被选中的概率就低于 100%。而在 validation set 里面,每一次每个数据被会被计算,因此,计算概率是 100%。因此两者的数据计算均衡性有差别,通常我们认为,OOB 会比实际情况更低一点,因为他的 randomness 更低。

2。时间序列模型建立时,如何处理 validation 和 test 与模型精度控制的小技巧

在时间序列模型构建时,我们的系统误差往往来自于通过过去去预测未来的外推误差。那在 Kaggle 或者工程上实现时,我们可以考虑这个小技巧:

- 在预测时,使用时间分区的方式把数据集分成 train 和 validation 集,通过预测的方式来估算那种模型的精度比较高,并保留这个模型并将 train 和 validation 融合在一起进一步训练模型,进而用于实际生产或者 kaggle 的 leaderboard。

- 在确定哪种模型比较好的时候,我们可以以时间间隔作为区分来测试:

    - 随机抽样(全体)

    - 上半个月的数据(1日 -  15日)

    - 过去2周(8月1日至15日)

    - 下半个月的数据(15日至30日)

3。去掉时间相关的元素 —— 消除训练集里面过拟合的特征

因为 Kaggle 里面追求的是小数点后几位的优势,因此一点点提高都是十分必要的。那么我们继续来优化模型。

从逻辑上来讲,时间序列元素中与时间相关的部分可能随着时间的变化而变化,因此他对于未来的精度预测可能带有副作用,尽管在 train set 和 validation set 上表现较好,但是未必会在真实的表现上比较好。因此我们这里要作的反而是去掉时间序列元素的干扰,去尽量提取跟时间无关的、更为本质的关系,用于对未来的预测。

这个理论听上去很有道理,那么实践的时候是否真的如此呢?我们来试一试。

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')

在这里我们构建一个 'is_valid' 特征作为预测的 target,随后在 train set 里面把 这个 'is_valid' 特征标记为 True,在 validation set 里面把这个特征标记为 False。那么我们这样训练出来的 model 就告诉我们是否所有的参数都可以被完美预测。

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。表明所有的样本都可以被完美预测。随后,我们继续输出 feature importance,通过这个来看看完美预测的这些特征里面,哪些的重要性过高,这些可能就是造成现阶段过拟合的元凶。

fi = rf_feat_importance(m, x); fi[:10]


同时我们进一步比较头三个元素,在 trian set 和 validation set 里面的 describe 会发现,两者完全不一样。这些参数可能对于线性模型很重要,但是对于随机森林就可能是拖后腿的特征了。那么让我们 drop 掉这 3 个,来再作一次随机森林训练。

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 的分别拿出来,进行影响分析,就是去掉之后,计算对最终 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]

从数据上来看,我们可以去掉其中的 3 个影响不大的,来提升其他特征的有效性。因为在树叶数量相等的情况下,在对最终结果不有效的特征上浪费时间,会造成很大的浪费。

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,分数达到 0.9 拉!我很满意。那么,最终模型我们就加大树的棵树(比如,n_estimators=160),来作一个最终模型吧。

记得把有效的 features 保存下来。

np.save('tmp/subs_cols.npy', np.array(df_subs.columns))

笔记:我们往往进行小范围的测试之后,再对整体数据进行学习和训练。因此,往往是白天测试优化模型,晚上训练模型。

你可能感兴趣的:(实用随机森林 - 针对时间序列的 kaggle 技巧)