Stacking是用新的模型(次學(xué)習(xí)器)去學(xué)習(xí)怎么組合那些基學(xué)習(xí)器,它的思想源自于Stacked Generalization(http://www.machine-learning.martinsewell.com/ensembles/stacking/Wolpert1992.pdf)這篇論文。如果把Bagging看作是多個(gè)基分類器的線性組合,那么Stacking就是多個(gè)基分類器的非線性組合。Stacking可以很靈活,它可以將學(xué)習(xí)器一層一層地堆砌起來,形成一個(gè)網(wǎng)狀的結(jié)構(gòu),如下圖:

舉個(gè)更直觀的例子,還是那兩道加法題:

這里A和B可以看作是基學(xué)習(xí)器,C、D、E都是次學(xué)習(xí)器。
[if !supportLists]·?[endif]
Stage1: ?A和B各自寫出了答案。
[if !supportLists]·?[endif]
[if !supportLists]·?[endif]
Stage2: ?C和D偷看了A和B的答案,C認(rèn)為A和B一樣聰明,D認(rèn)為A比B聰明一點(diǎn)。他們各自結(jié)合了A和B的答案后,給出了自己的答案。
[if !supportLists]·?[endif]
[if !supportLists]·?[endif]
Stage3: ?E偷看了C和D的答案,E認(rèn)為D比C聰明,隨后E也給出自己的答案作為最終答案。
[if !supportLists]·?[endif]
在實(shí)現(xiàn)Stacking時(shí),要注意的一點(diǎn)是,避免標(biāo)簽泄漏(Label Leak)。在訓(xùn)練次學(xué)習(xí)器時(shí),需要上一層學(xué)習(xí)器對(duì)Train Data的測(cè)試結(jié)果作為特征。如果我們?cè)赥rain Data上訓(xùn)練,然后在Train Data上預(yù)測(cè),就會(huì)造成Label Leak。為了避免Label Leak,需要對(duì)每個(gè)學(xué)習(xí)器使用K-fold,將K個(gè)模型對(duì)Valid Set的預(yù)測(cè)結(jié)果拼起來,作為下一層學(xué)習(xí)器的輸入。如下圖:

由圖可知,我們還需要對(duì)Test Data做預(yù)測(cè)。這里有兩種選擇,可以將K個(gè)模型對(duì)Test Data的預(yù)測(cè)結(jié)果求平均,也可以用所有的Train Data重新訓(xùn)練一個(gè)新模型來預(yù)測(cè)Test Data。所以在實(shí)現(xiàn)過程中,我們最好把每個(gè)學(xué)習(xí)器對(duì)Train Data和對(duì)Test Data的測(cè)試結(jié)果都保存下來,方便訓(xùn)練和預(yù)測(cè)。
對(duì)于Stacking還要注意一點(diǎn),固定K-fold可以盡量避免Valid Set過擬合,也就是全局共用一份K-fold,如果是團(tuán)隊(duì)合作,組員之間也是共用一份K-fold。如果想具體了解為什么需要固定K-fold,
舉個(gè)例子,假設(shè)訓(xùn)練數(shù)據(jù)一共有x1, x2, x3, x4, x5, x6這6個(gè),并且使用3-fold,在Stage1的時(shí)候使用兩個(gè)Model。
1.使用固定的k-fold的情況如下圖:

先看Stage1:可以看到m1-m6和n1-n2是相同的k-fold預(yù)測(cè)出來的結(jié)果,m1,m2由x3,x4,x5,x6預(yù)測(cè)所得,所以m1,m2包含了x3,x4,x5,x6的信息,以此類推。
Stage2:假設(shè)我要用如圖中所示的Train Set來預(yù)測(cè)Valid Set,那么Train Set包含了x1-x6的信息,而Valid Set包含了x3-x6的信息。
看到這里你會(huì)疑惑,這有什么用呢?別急,再來看看Stage1的兩個(gè)Model各自用不同的k-fold:

請(qǐng)仔細(xì)觀察Model2的k-fold,現(xiàn)在n1,n6包含了x2-x5的信息,以此類推。
關(guān)鍵在于Stage2,現(xiàn)在Train Set也是包含了x1-x6的信息,而Valid Set也包含了x1-x6的信息,這就是不固定kfold與固定kfold的區(qū)別。盡管從固定kfold的圖看來,它也有可能出現(xiàn)一定程度的過擬合,但不固定kfold它對(duì)Valid Set的過擬合情況會(huì)更加嚴(yán)重,所以按照Nutastray說的,通過kfold可以避免人為造成的過擬合。
可能你會(huì)問,那如果我們同一層的Stage固定k-fold,而不同層之間不固定,會(huì)發(fā)生什么?答案是,情況也會(huì)比固定k-fold要糟糕,具體的話可以按照上圖畫一下。所以最終給出的建議是,做Stacking最好還是固定k-fold,如果是團(tuán)隊(duì)合作完成項(xiàng)目,那就組員之間共享一份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("耗時(shí)", stamp / 3600)#print(predictiontest)
大家有感興趣的可以自己拿代碼測(cè)試一下。我的github完整測(cè)試代碼地址如下:https://github.com/crystal-tensor/-360