模型融合


一、Voting

模型融合其實也沒有想象的那么高大上,從最簡單的Voting說起,這也可以說是一種模型融合。假設對於一個二分類問題,有3個基礎模型,那么就采取投票制的方法,投票多者確定為最終的分類。

二、Averaging

對於回歸問題,一個簡單直接的思路是取平均。稍稍改進的方法是進行加權平均。權值可以用排序的方法確定,舉個例子,比如A、B、C三種基本模型,模型效果進行排名,假設排名分別是1,2,3,那么給這三個模型賦予的權值分別是3/6、2/6、1/6
這兩種方法看似簡單,其實后面的高級算法也可以說是基於此而產生的,Bagging或者Boosting都是一種把許多弱分類器這樣融合成強分類器的思想。

三、Bagging

Bagging就是采用有放回的方式進行抽樣,用抽樣的樣本建立子模型,對子模型進行訓練,這個過程重復多次,最后進行融合。大概分為這樣兩步:

  1. 重復K次
  • 有放回地重復抽樣建模
  • 訓練子模型

        2.模型融合

  • 分類問題:voting
  • 回歸問題:average

Bagging算法不用我們自己實現,隨機森林就是基於Bagging算法的一個典型例子,采用的基分類器是決策樹。R和python都集成好了,直接調用。

四、Boosting

Bagging算法可以並行處理,而Boosting的思想是一種迭代的方法,每一次訓練的時候都更加關心分類錯誤的樣例,給這些分類錯誤的樣例增加更大的權重,下一次迭代的目標就是能夠更容易辨別出上一輪分類錯誤的樣例。最終將這些弱分類器進行加權相加。


同樣地,基於Boosting思想的有AdaBoost、GBDT等,在R和python也都是集成好了直接調用。
PS:理解了這兩點,面試的時候關於Bagging、Boosting的區別就可以說上來一些,問Randomfroest和AdaBoost的區別也可以從這方面入手回答。也算是留一個小問題,隨機森林、Adaboost、GBDT、XGBoost的區別是什么?

五、Stacking

Stacking方法其實弄懂之后應該是比Boosting要簡單的,畢竟小幾十行代碼可以寫出一個Stacking算法。我先從一種“錯誤”但是容易懂的Stacking方法講起。
Stacking模型本質上是一種分層的結構,這里簡單起見,只分析二級Stacking.假設我們有3個基模型M1、M2、M3。

1. 基模型M1,對訓練集train訓練,然后用於預測train和test的標簽列,分別是P1,T1
\begin{pmatrix}\vdots  \\P_1   \\\vdots  \\\vdots  \\\end{pmatrix}\begin{pmatrix}\vdots  \\T_1   \\\vdots  \\\vdots  \\\end{pmatrix}
對於M2和M3,重復相同的工作,這樣也得到P2,T2,P3,T3。

2. 分別把P1,P2,P3以及T1,T2,T3合並,得到一個新的訓練集和測試集train2,test2。

\begin{pmatrix}\vdots  \\P_1   \\\vdots  \\\vdots  \\\end{pmatrix}\begin{pmatrix}\vdots  \\P_2   \\\vdots  \\\vdots  \\\end{pmatrix}\begin{pmatrix}\vdots  \\P_3   \\\vdots  \\\vdots  \\\end{pmatrix}\implies\overbrace{\begin{pmatrix}\vdots &\vdots  &\vdots \\P_1 & P_2 &P_3   \\\vdots  &\vdots &\vdots \\\vdots &\vdots &\vdots  \\\end{pmatrix}}^{train2}

\begin{pmatrix}\vdots  \\T_1   \\\vdots  \\\vdots  \\\end{pmatrix}\begin{pmatrix}\vdots  \\T_2   \\\vdots  \\\vdots  \\\end{pmatrix}\begin{pmatrix}\vdots  \\T_3   \\\vdots  \\\vdots  \\\end{pmatrix}\implies\overbrace{\begin{pmatrix}\vdots &\vdots  &\vdots \\T_1 & T_2 &T_3   \\\vdots  &\vdots &\vdots \\\vdots &\vdots &\vdots  \\\end{pmatrix}}^{test2}


3. 再用第二層的模型M4訓練train2,預測test2,得到最終的標簽列。

\overbrace{\begin{pmatrix}\vdots &\vdots  &\vdots \\P_1 & P_2 &P_3   \\\vdots  &\vdots &\vdots \\\vdots &\vdots &\vdots  \\\end{pmatrix}}^{train2}\overbrace{\implies}^{train}\overbrace{\begin{pmatrix}\vdots &\vdots  &\vdots \\T_1 & T_2 &T_3   \\\vdots  &\vdots &\vdots \\\vdots &\vdots &\vdots  \\\end{pmatrix}}^{test2}\overbrace{\implies}^{predict}\begin{pmatrix}\vdots  \\pred   \\\vdots  \\\vdots  \\\end{pmatrix}

Stacking本質上就是這么直接的思路,但是這樣肯定是不行的,問題在於P1的得到是有問題的,用整個訓練集訓練的模型反過來去預測訓練集的標簽,毫無疑問過擬合是非常非常嚴重的,因此現在的問題變成了如何在解決過擬合的前提下得到P1、P2、P3,這就變成了熟悉的節奏——K折交叉驗證。我們以2折交叉驗證得到P1為例,假設訓練集為4行3列

\begin{pmatrix}a_{11} & a_{12} &a_{13}  \\a_{21} & a_{22} &a_{23}  \\a_{31} & a_{32} &a_{33}  \\a_{41} & a_{42} &a_{43}  \\\end{pmatrix}

將其划分為2部分

\overbrace{\begin{pmatrix}a_{11} & a_{12} &a_{13}  \\a_{21} & a_{22} &a_{23}  \\\end{pmatrix}}^{traina}

\overbrace{\begin{pmatrix}a_{31} & a_{32} &a_{33}  \\a_{41} & a_{42} &a_{43}  \\\end{pmatrix}}^{trainb}

用traina訓練模型M1,然后在trainb上進行預測得到preb3和pred4
\overbrace{\begin{pmatrix}a_{11} & a_{12} &a_{13}  \\a_{21} & a_{22} &a_{23}  \\\end{pmatrix}}^{traina}\overbrace{\implies}^{train}\overbrace{\begin{pmatrix}a_{31} & a_{32} &a_{33}  \\a_{41} & a_{42} &a_{43}  \\\end{pmatrix}}^{trainb}\overbrace{\implies}^{predict}\begin{pmatrix}pred3  \\pred4   \\\end{pmatrix}
在trainb上訓練模型M1,然后在traina上進行預測得到pred1和pred2
\overbrace{\begin{pmatrix}a_{31} & a_{32} &a_{33}  \\a_{41} & a_{42} &a_{43}  \\\end{pmatrix}}^{trainb}\overbrace{\implies}^{train}\overbrace{\begin{pmatrix}a_{11} & a_{12} &a_{13}  \\a_{21} & a_{22} &a_{23}  \\\end{pmatrix}}^{traina}\overbrace{\implies}^{predict}\begin{pmatrix}pred1  \\pred2   \\\end{pmatrix}
然后把兩個預測集進行拼接
\begin{pmatrix}pred1  \\pred2   \\\end{pmatrix}+\begin{pmatrix}pred3  \\pred4   \\\end{pmatrix}=\begin{pmatrix}pred1  \\pred2   \\pred3  \\pred4   \\\end{pmatrix}=\begin{pmatrix}\vdots  \\P_1   \\\vdots  \\\vdots  \\\end{pmatrix}
對於測試集T1的得到,有兩種方法。注意到剛剛是2折交叉驗證,M1相當於訓練了2次,所以一種方法是每一次訓練M1,可以直接對整個test進行預測,這樣2折交叉驗證后測試集相當於預測了2次,然后對這兩列求平均得到T1。
或者直接對測試集只用M1預測一次直接得到T1。
P1、T1得到之后,P2、T2、P3、T3也就是同樣的方法。理解了2折交叉驗證,對於K折的情況也就理解也就非常順利了。所以最終的代碼是兩層循環,第一層循環控制基模型的數目,每一個基模型要這樣去得到P1,T1,第二層循環控制的是交叉驗證的次數K,對每一個基模型,會訓練K次最后拼接得到P1,取平均得到T1。

 

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

    # We again fit the data on clones of the original models
    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)

        # 使用K-fold的方法來進行交叉驗證,將每次驗證的結果作為新的特征來進行處理
        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):
                instance = clone(model)
                self.base_models_[i].append(instance)
                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

    # 從得到的新的特征  采用新的模型進行預測  並輸出結果
    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)

stacked_averaged_models = StackingAveragedModels(base_models=(ENet, GBoost, KRR), meta_model=lasso)

 


免責聲明!

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



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