关于Stacking模型融合及源码

Stacking是用新的模型(次学习器)去学习怎么组合那些基学习器,它的思想源自于Stacked Generalization(http://www.machine-learning.martinsewell.com/ensembles/stacking/Wolpert1992.pdf)这篇论文。如果把Bagging看作是多个基分类器的线性组合,那么Stacking就是多个基分类器的非线性组合。Stacking可以很灵活,它可以将学习器一层一层地堆砌起来,形成一个网状的结构,如下图:

举个更直观的例子,还是那两道加法题:

这里A和B可以看作是基学习器,C、D、E都是次学习器。

[if !supportLists]· [endif]

Stage1:  A和B各自写出了答案。

[if !supportLists]· [endif]

[if !supportLists]· [endif]

Stage2:  C和D偷看了A和B的答案,C认为A和B一样聪明,D认为A比B聪明一点。他们各自结合了A和B的答案后,给出了自己的答案。

[if !supportLists]· [endif]

[if !supportLists]· [endif]

Stage3:  E偷看了C和D的答案,E认为D比C聪明,随后E也给出自己的答案作为最终答案。

[if !supportLists]· [endif]

在实现Stacking时,要注意的一点是,避免标签泄漏(Label Leak)。在训练次学习器时,需要上一层学习器对Train Data的测试结果作为特征。如果我们在Train Data上训练,然后在Train Data上预测,就会造成Label Leak。为了避免Label Leak,需要对每个学习器使用K-fold,将K个模型对Valid Set的预测结果拼起来,作为下一层学习器的输入。如下图:

由图可知,我们还需要对Test Data做预测。这里有两种选择,可以将K个模型对Test Data的预测结果求平均,也可以用所有的Train Data重新训练一个新模型来预测Test Data。所以在实现过程中,我们最好把每个学习器对Train Data和对Test Data的测试结果都保存下来,方便训练和预测。

对于Stacking还要注意一点,固定K-fold可以尽量避免Valid Set过拟合,也就是全局共用一份K-fold,如果是团队合作,组员之间也是共用一份K-fold。如果想具体了解为什么需要固定K-fold,

举个例子,假设训练数据一共有x1, x2, x3, x4, x5, x6这6个,并且使用3-fold,在Stage1的时候使用两个Model。

1.使用固定的k-fold的情况如下图:

先看Stage1:可以看到m1-m6和n1-n2是相同的k-fold预测出来的结果,m1,m2由x3,x4,x5,x6预测所得,所以m1,m2包含了x3,x4,x5,x6的信息,以此类推。

Stage2:假设我要用如图中所示的Train Set来预测Valid Set,那么Train Set包含了x1-x6的信息,而Valid Set包含了x3-x6的信息。

看到这里你会疑惑,这有什么用呢?别急,再来看看Stage1的两个Model各自用不同的k-fold:

请仔细观察Model2的k-fold,现在n1,n6包含了x2-x5的信息,以此类推。

关键在于Stage2,现在Train Set也是包含了x1-x6的信息,而Valid Set也包含了x1-x6的信息,这就是不固定kfold与固定kfold的区别。尽管从固定kfold的图看来,它也有可能出现一定程度的过拟合,但不固定kfold它对Valid Set的过拟合情况会更加严重,所以按照Nutastray说的,通过kfold可以避免人为造成的过拟合。

可能你会问,那如果我们同一层的Stage固定k-fold,而不同层之间不固定,会发生什么?答案是,情况也会比固定k-fold要糟糕,具体的话可以按照上图画一下。所以最终给出的建议是,做Stacking最好还是固定k-fold,如果是团队合作完成项目,那就组员之间共享一份k-fold。

下面给出我的代码:bb=data_train.iloc[:, 4:6749]#dd=data_train.iloc[:, 3:4]cc = bb.apply(lambda x: x.fillna(x.mean()), axis=0)x_data = preprocessing.minmax_scale(cc.iloc[:, :].values, feature_range=(-1,1))cc['tag'] = data_train.iloc[:, 3:4]test = dfp.iloc[:, 2:6747].apply(lambda x: x.fillna(x.mean()), axis=0)#test_data = preprocessing.minmax_scale(test.iloc[:, :].values, feature_range=(-1,1))Xtest = list(test.columns.values)[4:6745]x_data_output = dfp.iloc[:, 0:1].values#print(data_train.iloc[:, 3:4])predictors = list(cc.columns.values)[4:6745]alg1 = lgb.LGBMClassifier(boosting_type='gbdt', num_leaves=42, max_depth=-1, learning_rate=0.054, n_estimators=490,                               subsample_for_bin=200, objective='binary', class_weight=None, min_split_gain=0.0,                               min_child_weight=1, min_child_samples=21, subsample=0.72, subsample_freq=1,                               colsample_bytree=0.63, reg_alpha=6.18, reg_lambda=2.718, random_state=142857, n_jobs=-1,                               silent=True, importance_type='split')alg2 = XGBClassifier(n_estimators=60,max_depth=9,min_child_weight=2,gamma=0.9,subsample=0.8,learning_rate=0.02,                    colsample_bytree=0.8,objective='binary:logistic',nthread=-1,scale_pos_weight=1)alg3 = GradientBoostingClassifier(learning_rate=0.01, n_estimators=600, max_depth=7, min_samples_leaf=60,                           min_samples_split=1200, max_features=9, subsample=0.7, random_state=10)lr = LogisticRegression()pipe1 = make_pipeline(ColumnSelector(cols=predictors[4:2000]), lr)pipe2 = make_pipeline(ColumnSelector(cols=predictors[2000:4000]), alg2)pipe3 = make_pipeline(ColumnSelector(cols=predictors[4000:5000]), alg3)sclf = StackingClassifier(classifiers=[lr, pipe2, pipe3], meta_classifier=alg1)# Compute the accuracy score for all the cross validation folds.  (much simpler than what we did before!)# kf=cross_validation.KFold(data_train.shape[0],n_folds=10,random_state=1)kf = model_selection.KFold(n_splits=120, shuffle=False, random_state=1)scores = model_selection.cross_val_score(sclf, cc[predictors], cc['tag'], cv=kf)print("scores.mean=", scores.mean())File = open("data/prob_stackingLXG.txt", "w", encoding=u'utf-8', errors='ignore')File.write("id"+"," + "prob" + "\n")classifier = sclf.fit(cc[predictors], cc['tag'])predictiontest = classifier.predict_proba(test[Xtest])[:, 1]for step in range(len(test)):    File.write(str(x_data_output[step][0])+"," + str(predictiontest[step]) + "\n")end = time.time()stamp = end - startprint("耗时", stamp / 3600)#print(predictiontest)

大家有感兴趣的可以自己拿代码测试一下。我的github完整测试代码地址如下:https://github.com/crystal-tensor/-360

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容