集成學習中的 stacking 以及python實現


集成學習

  Ensemble learning 中文名叫做集成學習,它並不是一個單獨的機器學習算法,而是將很多的機器學習算法結合在一起,我們把組成集成學習的算法叫做“個體學習器”。在集成學習器當中,個體學習器都相同,那么這些個體學習器可以叫做“基學習器”。

  個體學習器組合在一起形成的集成學習,常常能夠使得泛化性能提高,這對於“弱學習器”的提高尤為明顯。弱學習器指的是比隨機猜想要好一些的學習器。

  在進行集成學習的時候,我們希望我們的基學習器應該是好而不同,這個思想在后面經常體現。 “好”就是說,你的基學習器不能太差,“不同”就是各個學習器盡量有差異。

  集成學習有兩個分類,一個是個體學習器存在強依賴關系、必須串行生成的序列化方法,以Boosting為代表。另外一種是個體學習器不存在強依賴關系、可同時生成的並行化方法,以Bagging和隨機森林(Random Forest)為代表。

 

Stacking 的基本思想

  將個體學習器結合在一起的時候使用的方法叫做結合策略。對於分類問題,我們可以使用投票法來選擇輸出最多的類。對於回歸問題,我們可以將分類器輸出的結果求平均值。

  上面說的投票法和平均法都是很有效的結合策略,還有一種結合策略是使用另外一個機器學習算法來將個體機器學習器的結果結合在一起,這個方法就是Stacking。

  在stacking方法中,我們把個體學習器叫做初級學習器,用於結合的學習器叫做次級學習器或元學習器(meta-learner),次級學習器用於訓練的數據叫做次級訓練集。次級訓練集是在訓練集上用初級學習器得到的。

我們貼一張周志華老師《機器學習》一張圖來說一下stacking學習算法。

                        

過程1-3 是訓練出來個體學習器,也就是初級學習器。

過程5-9是 使用訓練出來的個體學習器來得預測的結果,這個預測的結果當做次級學習器的訓練集。

過程11 是用初級學習器預測的結果訓練出次級學習器,得到我們最后訓練的模型。

如果想要預測一個數據的輸出,只需要把這條數據用初級學習器預測,然后將預測后的結果用次級學習器預測便可。

 

Stacking的實現

  最先想到的方法是這樣的,

  1:用數據集D來訓練h1,h2,h3...,

  2:用這些訓練出來的初級學習器在數據集D上面進行預測得到次級訓練集。

  3:用次級訓練集來訓練次級學習器。

  但是這樣的實現是有很大的缺陷的。在原始數據集D上面訓練的模型,然后用這些模型再D上面再進行預測得到的次級訓練集肯定是非常好的。會出現過擬合的現象。

 

  那么,我們換一種做法,我們用交叉驗證的思想來實現stacking的模型,從這里拿來一張圖

 

  次級訓練集的構成不是直接由模型在訓練集D上面預測得到,而是使用交叉驗證的方法,將訓練集D分為k份,對於每一份,用剩余數據集訓練模型,然后預測出這一份的結果。重復上面步驟,直到每一份都預測出來。這樣就不會出現上面的過擬合這種情況。並且在構造次級訓練集的過程當中,順便把測試集的次級數據也給構造出來了。

  對於我們所有的初級訓練器,都要重復上面的步驟,才構造出來最終的次級訓練集和次級測試集。

構造stacking方法

  我們寫一個stacking方法,下面是它的實現代碼:

import numpy as np
from sklearn.model_selection import KFold
def get_stacking(clf, x_train, y_train, x_test, n_folds=10):
"""
這個函數是stacking的核心,使用交叉驗證的方法得到次級訓練集
x_train, y_train, x_test 的值應該為numpy里面的數組類型 numpy.ndarray .
如果輸入為pandas的DataFrame類型則會把報錯"""
train_num, test_num = x_train.shape[0], x_test.shape[0]
second_level_train_set = np.zeros((train_num,))
second_level_test_set = np.zeros((test_num,))
test_nfolds_sets = np.zeros((test_num, n_folds))
kf = KFold(n_splits=n_folds)

for i,(train_index, test_index) in enumerate(kf.split(x_train)):
x_tra, y_tra = x_train[train_index], y_train[train_index]
x_tst, y_tst = x_train[test_index], y_train[test_index]

clf.fit(x_tra, y_tra)

second_level_train_set[test_index] = clf.predict(x_tst)
test_nfolds_sets[:,i] = clf.predict(x_test)

second_level_test_set[:] = test_nfolds_sets.mean(axis=1)
return second_level_train_set, second_level_test_set



#我們這里使用5個分類算法,為了體現stacking的思想,就不加參數了
from sklearn.ensemble import (RandomForestClassifier, AdaBoostClassifier,
GradientBoostingClassifier, ExtraTreesClassifier)
from sklearn.svm import SVC

rf_model = RandomForestClassifier()
adb_model = AdaBoostClassifier()
gdbc_model = GradientBoostingClassifier()
et_model = ExtraTreesClassifier()
svc_model = SVC()

#在這里我們使用train_test_split來人為的制造一些數據
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris = load_iris()
train_x, test_x, train_y, test_y = train_test_split(iris.data, iris.target, test_size=0.2)

train_sets = []
test_sets = []
for clf in [rf_model, adb_model, gdbc_model, et_model, svc_model]:
train_set, test_set = get_stacking(clf, train_x, train_y, test_x)
train_sets.append(train_set)
test_sets.append(test_set)

meta_train = np.concatenate([result_set.reshape(-1,1) for result_set in train_sets], axis=1)
meta_test = np.concatenate([y_test_set.reshape(-1,1) for y_test_set in test_sets], axis=1)

#使用決策樹作為我們的次級分類器
from sklearn.tree import DecisionTreeClassifier
dt_model = DecisionTreeClassifier()
dt_model.fit(meta_train, train_y)
df_predict = dt_model.predict(meta_test)

print(df_predict)

輸出結果如下(因為是隨機划分的,所以每次運行結果可能不一樣):

[1 0 1 1 1 2 1 2 2 2 0 0 1 2 2 1 0 2 1 0 0 1 1 0 0 2 0 2 1 2]

 

構造stacking類

事實上還可以構造一個stacking的類,它擁有fit和predict方法

from sklearn.model_selection import KFold
from sklearn.base import BaseEstimator, RegressorMixin, TransformerMixin, clone
import numpy as np
#對於分類問題可以使用 ClassifierMixin


class StackingAveragedModels(BaseEstimator, RegressorMixin, TransformerMixin):
    def __init__(self, base_models, meta_model, n_folds=5):
        self.base_models = base_models
        self.meta_model = meta_model
        self.n_folds = n_folds

    # 我們將原來的模型clone出來,並且進行實現fit功能
    def fit(self, X, y):
        self.base_models_ = [list() for x in self.base_models]
        self.meta_model_ = clone(self.meta_model)
        kfold = KFold(n_splits=self.n_folds, shuffle=True, random_state=156)

        #對於每個模型,使用交叉驗證的方法來訓練初級學習器,並且得到次級訓練集
        out_of_fold_predictions = np.zeros((X.shape[0], len(self.base_models)))
        for i, model in enumerate(self.base_models):
            for train_index, holdout_index in kfold.split(X, y):
                self.base_models_[i].append(instance)
                instance = clone(model)
                instance.fit(X[train_index], y[train_index])
                y_pred = instance.predict(X[holdout_index])
                out_of_fold_predictions[holdout_index, i] = y_pred

        # 使用次級訓練集來訓練次級學習器
        self.meta_model_.fit(out_of_fold_predictions, y)
        return self

    #在上面的fit方法當中,我們已經將我們訓練出來的初級學習器和次級學習器保存下來了
    #predict的時候只需要用這些學習器構造我們的次級預測數據集並且進行預測就可以了
    def predict(self, X):
        meta_features = np.column_stack([
            np.column_stack([model.predict(X) for model in base_models]).mean(axis=1)
            for base_models in self.base_models_ ])
        return self.meta_model_.predict(meta_features)

 

 

參考

  stacking 的基本思想及代碼實現

  Introduction to Ensembling/Stacking in Python

  A Kaggler's Guide to Model Stacking in Practice 

  Stacked Regressions : Top 4% on LeaderBoard


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM