在kaggle上打比赛的时候发现,利用ensemble的思路对Base model进行集成是一个对最终模型影响较大的环节,能够有效提升分数,同时还能降低overfitting的风险.
Ensemble思路包括以下几种:
- Bagging: 用不同的训练数据集的随机子集来对指定个数的Base Model进行训练,最后决策由每个Base Model投票决定.典型应用如名头响亮的
随机森林Random Forest
. - Boosting: 相比 bagging,Boosting的思路更加复杂一些.基本的思路在于迭代训练Base Model,每一轮迭代出的新模型都是基于上一代模型所犯错误修正得来,手段在于加重错误样本的权重.典型应用诸如
AdaBoost
和Gradient Boosting
等.通常来说,相比Bagging会有更好的效果,但是也是更加容易overfitting的. - Blending: 使用不相交的数据训练(无放回采样,bootstrap=False)出不同的Base Model,然后将输出结果进行加权平均.
- stacking: 相比其他的Ensemble模型,stacking选择了全新的思路.不再直接聚合所有预测器的结果,而是直接利用Base Model的预测结果作为数据源来训练一个更高层次(我也不知道这么描述对不对 :-)的模型.
Stacking的详解:
根据在kaggle上的经验,Stacking方法相比其他的方法往往能取得更好的一个效果(不知道是不是我以偏概全了 :-).
-
关于Stacking的思路
正如上面所说,stacking的思想在于直接利用Base Model(Layer_1)的预测结果作为更高层次模型(Layer_2,....Layer_n)的训练集.
下图清晰地展示了这一思路.Blending指的是根据Prediction训练出高层模型(通常称为metal learner 或者 blender)后进行的预测操作.
Stacking的简单思路图.png -
Stacking应用技巧
在使用Stacking时,如果直接把所有的数据全都喂给Base Model当然是一种很不正确的操作,这会导致严重的过拟合.因此为了高效使用Stacking,通常的做法是使用交叉验证的思路对数据集进行拆分,然后分别训练Base Model.
在拆分时会留下其中1份(hold-out-set)用来做预测.预测结果是由Base Model预测所得,结果需要保存下来留作第二层的训练使用.在这样一个迭代训练的过程中,每个模型都将被训练n次(n = cv,即交叉验证的折数),做n次预测,将n次预测的结果拼接起来就是一列完整的预测数据.最终我们就可以得到一个训练集行数 * Base Model数量
的矩阵,这个矩阵就会作为第二层Model的训练数据. 第二层Model训练完成后,将每个Base Model对测试数据(不同于hold-out-set)的预测拿来让它进行预测,就可以得到最后的输出.
以上都还只是两层的Stacking,也就是说还能构造三层 . 四层 甚至五层的stacking.
image.png
- 以下是实现代码,并给出了我的注解,方便阅读
class Ensemble(object):
def __init__(self, n_folds, stacker, base_models):
self.n_folds = n_folds
self.stacker = stacker # 将要创建的二层stacker堆叠器
self.base_models = base_models
def fit_predict(self, X, y, T):
X = np.array(X) # 训练集
y = np.array(y)
T = np.array(T) # 测试集
# 创建交叉验证对象fold
folds = list(KFold(len(y), n_folds=self.n_folds, shuffle=True, random_state=2016))
# 创建第二层训练集
S_train = np.zeros((X.shape[0], len(self.base_models)))
# 创建第二层测试集
S_test = np.zeros((T.shape[0], len(self.base_models)))
# 循环生成S_train 和 S_test
for i, clf in enumerate(self.base_models): # 外层遍历每一个base_model
S_test_i = np.zeros((T.shape[0], len(folds))) # 初始化每一个模型的预测集
for j, (train_idx, test_idx) in enumerate(folds): # 内层遍历每一份hold-one-out数据
X_train = X[train_idx] # 交叉验证下的训练集
y_train = y[train_idx]
X_holdout = X[test_idx] # 交叉验证下的hold-one-out数据
# y_holdout = y[test_idx]
clf.fit(X_train, y_train) # 训练拟合
y_pred = clf.predict(X_holdout)[:] # 拿到hold-one-out上的预测结果
S_train[test_idx, i] = y_pred # 放入二层训练集S_train中
S_test_i[:, j] = clf.predict(T)[:] # 拿到二层测试集
# 因为每个模型都会被训练并预测测试集n_fold次,为了保证二层测试集与训练集同规模,需要对其取均值
S_test[:, i] = S_test_i.mean(1)
self.stacker.fit(S_train, y) # 二层stacker堆叠器训练
y_pred = self.stacker.predict(S_test)[:] # 拿到最终的预测结果
return y_pred
参考自 https://dnc1994.com/2016/04/rank-10-percent-in-first-kaggle-competition/
网友评论