建議先閱讀以下文章
- 知乎:Kaggle機器學習之模型融合(stacking)心得
- Blog:Stacking Models for Improved Predictions
- Blog:KAGGLE ENSEMBLING GUIDE(注腳)
- Blog:如何在 Kaggle 首戰中進入前 10%
- Github:ikki407/stacking
- Paper:M. Paz Sesmero, Agapito I. Ledezma, Araceli Sanchis, “Generating ensembles of heterogeneous classifiers using Stacked Generalization,” WIREs Data Mining and Knowledge Discovery 5: 21-34 (2015) paper下載地址 密碼: c7rf
- 神作:Stacked Generalization (Stacking)
回歸問題構建stacking模型
墻裂推薦閱讀第一篇文章,讀完之后還不懂,那就繼續讀,讀懂為止,這是最入門的教程了,寫的不錯的,Titanic數據是回歸的問題,最后的predict方法得到是幸存的概率,所以最后作者的代碼可用
這里寫圖片描述
這里需要再次強調的是,對于回歸問題的stacking learning的處理方案,是在一級模型進行out-of-fold預測產生的預測精度,這個當做第二級分類器的訓練集。但是在分類問題時,就會產生問題,分類之后產生的類別的判斷,所以代碼最后進行取均值是不恰當的,難道會出現3.2這個類么?所以,這種方法是不適合用于分類的stacking
分類問題構建stacking模型
解決方案是,使用predict_proba方法,產出對各類別的判斷概率,對于sklearn模型來說,實際上最后輸出的判斷類即如果使用predict方法,等效于numpy.argmax(predict_proba),也就是說,在分類器內部,它取了把握最大的概率所在的類作為輸出,打個比方,svm對iris數據的三個類進行判斷時候,predict_proba輸出的為1類概率0.5,2類概率0.3,三類概率0.2,而predict方法則直接輸出上述中最大概率所在的類,即1類作為結果。ok,這樣的話,現在新的特征大小被擴充成 : 數據集所含類別數 X一級分類器個數,比如iris數據做分類,設定有四個一級分類器,而需要做的數據是有3類,則新的特征維度 為 3*4 = 12,每個分類器都貢獻了3個維度.
code
# -*- coding:utf-8 -*-
# Author:哈士奇說喵
# 二級stacking learning
from sklearn.model_selection import KFold
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_digits
import numpy as np
from sklearn.svm import SVC
from sklearn import metrics
from sklearn.ensemble import RandomForestClassifier
from sklearn import preprocessing
import pandas as pd
# 導入數據集切割訓練與測試數據
data = load_digits()
data_D = preprocessing.StandardScaler().fit_transform(data.data)
data_L = data.target
data_train, data_test, label_train, label_test = train_test_split(data_D,data_L,random_state=1,test_size=0.7)
def SelectModel(modelname):
if modelname == "SVM":
from sklearn.svm import SVC
model = SVC(kernel='rbf', C=16, gamma=0.125,probability=True)
elif modelname == "GBDT":
from sklearn.ensemble import GradientBoostingClassifier
model = GradientBoostingClassifier()
elif modelname == "RF":
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier()
elif modelname == "XGBOOST":
import xgboost as xgb
model = xgb()
elif modelname == "KNN":
from sklearn.neighbors import KNeighborsClassifier as knn
model = knn()
else:
pass
return model
def get_oof(clf,n_folds,X_train,y_train,X_test):
ntrain = X_train.shape[0]
ntest = X_test.shape[0]
classnum = len(np.unique(y_train))
kf = KFold(n_splits=n_folds,random_state=1)
oof_train = np.zeros((ntrain,classnum))
oof_test = np.zeros((ntest,classnum))
for i,(train_index, test_index) in enumerate(kf.split(X_train)):
kf_X_train = X_train[train_index] # 數據
kf_y_train = y_train[train_index] # 標簽
kf_X_test = X_train[test_index] # k-fold的驗證集
clf.fit(kf_X_train, kf_y_train)
oof_train[test_index] = clf.predict_proba(kf_X_test)
oof_test += clf.predict_proba(X_test)
oof_test = oof_test/float(n_folds)
return oof_train, oof_test
# 單純使用一個分類器的時候
clf_second = RandomForestClassifier()
clf_second.fit(data_train, label_train)
pred = clf_second.predict(data_test)
accuracy = metrics.accuracy_score(label_test, pred)*100
print accuracy
# 91.0969793323
# 使用stacking方法的時候
# 第一級,重構特征當做第二級的訓練集
modelist = ['SVM','GBDT','RF','KNN']
newfeature_list = []
newtestdata_list = []
for modelname in modelist:
clf_first = SelectModel(modelname)
oof_train_ ,oof_test_= get_oof(clf=clf_first,n_folds=10,X_train=data_train,y_train=label_train,X_test=data_test)
newfeature_list.append(oof_train_)
newtestdata_list.append(oof_test_)
# 特征組合
newfeature = reduce(lambda x,y:np.concatenate((x,y),axis=1),newfeature_list)
newtestdata = reduce(lambda x,y:np.concatenate((x,y),axis=1),newtestdata_list)
# 第二級,使用上一級輸出的當做訓練集
clf_second1 = RandomForestClassifier()
clf_second1.fit(newfeature, label_train)
pred = clf_second1.predict(newtestdata)
accuracy = metrics.accuracy_score(label_test, pred)*100
print accuracy
# 96.4228934817
Pay Attention
- 這里只是使用了兩層的stacking,完成了一個基本的stacking操作,也可以同理構建三層,四層等等
- 對于第二級的輸入來說,特征進行了變化(有一級分類器構成的判決作為新特征),所以相應的測試集也需要進行同樣的轉換,畢竟分類器學習的訓練集已經不一樣了,學習的內容肯定是無法適用于舊的測試集的,要清楚的是,當初我們是對整個Data集合隨機分測試集和訓練集的!
- 適用k-fold的方法,實質上使用了cv的思想,所以數據并沒有泄露(沒有用到測試集,用的是訓練集中的hold-set),所以這個方法也叫做out-of-folds
Further
- 可以將之前的原始特征和之后新的特征進行融合,相當于特征擴充,然后注意標準化和歸一化進行處理,畢竟一級的特征量綱和產出的后驗概率組成的特征量綱不一樣,自己做測試吧~