天池大賽:街景字符編碼識別——Part5:模型集成

街景字符編碼識別

更新流程↓
Task01:賽題理解
Task02:數據讀取與數據擴增
Task03:字符識別模型
Task04:模型訓練與驗證
Task05:模型集成
老夜店鳥
炸…炸遼
給朋友看要破殼的雞蛋
比賽鏈接


Part5:模型集成





0. 簡介


 本章講解的知識點包括:

  • 分類器集成 / 集成學習
  • 深度學習中的集成學習
  • 結果後處理思路



1. 分類器集成(集成學習)

  集成學習,即分類器集成,通過構建並結合多個學習器來完成學習任務。一般結構是:先產生一組“個體學習器”,再用某種策略將它們結合起來。結合策略主要有平均法、投票法和學習法等。
  集成學習(ensemble learning)通過構建並結合多個學習器來完成學習任務,有時也被稱爲多分類器系統(multi-classifier system)、基於委員會的學習(committee-based learning)。

  集成學習是這樣一個過程,按照某種算法生成多個模型,如分類器或者稱爲專家,再將這些模型按照某種方法組合在一起來解決某個智能計算問題。集成學習主要用來提高模型(分類,預測,函數估計等)的性能,或者用來降低模型選擇不當的可能性。集成算法本身是一種監督學習算法,因爲它可以被訓練然後進行預測,組合的多個模型作爲整體代表一個假設(hypothesis)。

  集成方法是將幾種機器學習技術組合成一個預測模型的元算法,以達到減小方差(bagging)偏差(boosting) 或改進預測(stacking) 的效果。

 1.1. 分類器(Classifier)

  分類器是數據挖掘中對樣本進行分類的方法的統稱,包含決策樹邏輯迴歸樸素貝葉斯神經網絡等算法。分類是數據挖掘的一種非常重要的方法。分類的概念是在已有數據的基礎上學會一個分類函數或構造出一個分類模型(即分類器)。該函數或模型能夠把數據庫中的數據紀錄映射到給定類別中的某一個,從而可以應用於數據預測。

 分類器的構造和實施大體會經過以下幾個步驟:

  • 選定樣本(包含正樣本和負樣本),將所有樣本分成訓練樣本和測試樣本兩部分。
  • 在訓練樣本上執行分類器算法,生成分類模型
  • 在測試樣本上執行分類模型,生成預測結果
  • 根據預測結果,計算必要的評估指標,評估分類模型的性能

  更多分類器種類可以參考分類器


  1.1.1. 決策樹分類器

    限於篇幅,可以點擊鏈接瞭解具體——原理實現

  1.1.2. 樸素貝葉斯分類器

    素貝葉斯分類器是假設數據樣本特徵完全獨立,以貝葉斯定理爲基礎的簡單概率分類器。限於篇幅,可以點擊鏈接瞭解具體原理實現

  1.1.3. AdaBoost算法

    AdaBoost算法的自適應在於前一個分類器產生的錯誤分類樣本會被用來訓練下一個分類器,從而提升分類準確率,但是AdaBoost算法對於噪聲樣本和異常樣本比較敏感。限於篇幅,可以點擊鏈接瞭解具體原理與實現

  1.1.4. 支持向量機

    支持向量機是用過構建一個或者多個高維的超平面來將樣本數據進行劃分,超平面即爲樣本之間的分類邊界。限於篇幅,可以點擊鏈接瞭解具體原理與實現

  1.1.5. K近鄰算法

    基於k近鄰的K個樣本作爲分析從而簡化計算提升效率,K近鄰算法的分類器是一種基於距離計算的分類器。限於篇幅,可以點擊鏈接瞭解具體原理與實現



 1.2. 集成學習方法

  集成學習有許多集成模型,例如自助法、自助聚合(Bagging)、隨機森林、提升法(Boosting)堆疊法(stacking) 以及許多其它的基礎集成學習模型。
  集成方法的思想是通過將這些個體學習器(個體學習器稱爲“基學習器”,基學習器也被稱爲弱學習器。
)的偏置和/或方差結合起來,從而創建一個 強學習器(或 集成模型),從而獲得更好的性能。
我們可以用三種主要的旨在組合弱學習器的 元算法

  • 自助聚合(Bagging),該方法通常考慮的是同質弱學習器,相互獨立地並行學習這些弱學習器,並按照某種確定性的平均過程將它們組合起來。
  • 提升法(Boosting),該方法通常考慮的也是同質弱學習器。它以一種高度自適應的方法順序地學習這些弱學習器(每個基礎模型都依賴於前面的模型),並按照某種確定性的策略將它們組合起來。
  • 堆疊法(Stacking),該方法通常考慮的是異質弱學習器,並行地學習它們,並通過訓練一個 元模型 將它們組合起來,根據不同弱模型的預測結果輸出一個最終的預測結果。

  非常粗略地說,我們可以說Bagging的重點在於獲得一個方差比其組成部分更小的集成模型,而Boosting和Stacking則將主要生成偏置比其組成部分更低的強模型(即使方差也可以被減小)


  1.2.1. 自助聚合(Bagging)

  在 並行化的方法 中,我們單獨擬合不同的學習器,因此可以同時訓練它們。最著名的方法是自助聚合(Bagging),它的目標是生成比單個模型更棒的集成模型。Bagging的方法實現

  自助法:這種統計技術先隨機抽取出作爲替代的 B 個觀測值,然後根據一個規模爲 N 的初始數據集生成大小爲 B 的樣本(稱爲自助樣本)。
在這裏插入圖片描述

自助抽樣過程示意圖

  在某些假設條件下,這些樣本具有非常好的統計特性:在一級近似中,它們可以被視爲是直接從真實的底層(並且往往是未知的)數據分佈中抽取出來的,並且彼此之間相互獨立。因此,它們被認爲是真實數據分佈的代表性和獨立樣本(幾乎是獨立同分布的樣本)。

  爲了使這種近似成立,必須驗證兩個方面的假設。

  1. 初始數據集的大小N應該足夠大,以捕獲底層分佈的大部分複雜性。這樣,從數據集中抽樣就是從真實分佈中抽樣的良好近似(代表性)。
  2. 與自助樣本的大小B相比,數據集的規模N應該足夠大,這樣樣本之間就不會有太大的相關性(獨立性)。注意,接下來我可能還會提到自助樣本的這些特性(代表性和獨立性),但讀者應該始終牢記:這只是一種近似

  舉例而言,自助樣本通常用於評估統計估計量的方差或置信區間。根據定義,統計估計量是某些觀測值的函數。因此,隨機變量的方差是根據這些觀測值計算得到的。爲了評估這種估計量的方差,我們需要對從感興趣分佈中抽取出來的幾個獨立樣本進行估計。在大多數情況下,相較於實際可用的數據量來說,考慮真正獨立的樣本所需要的數據量可能太大了。然而,我們可以使用自助法生成一些自助樣本,它們可被視爲最具代表性以及最具獨立性(幾乎是獨立同分布的樣本)的樣本。這些自助樣本使我們可以通過估計每個樣本的值,近似得到估計量的方差。
在這裏插入圖片描述

自助法經常被用於評估某些統計估計量的方差或置信區間





  1.2.2. 提升法(Boosting)

  在順序化的方法中,組合起來的不同弱模型之間不再相互獨立地擬合。其思想是迭代地擬合模型,使模型在給定步驟上的訓練依賴於之前的步驟上擬合的模型。提升法(Boosting) 是這些方法中最著名的一種,它生成的集成模型通常比組成該模型的弱學習器偏置更小。

  Boosting和Bagging的工作思路是一樣的:我們構建一系列模型,將它們聚合起來得到一個性能更好的強學習器。然而,與重點在於減小方差的Bagging不同,Boosting着眼於以一種適應性很強的方式順序擬合多個弱學習器:序列中每個模型在擬合的過程中,會更加重視那些序列中之前的模型處理地很糟糕的觀測數據。
  直觀地說,每個模型都把注意力集中在目前最難擬合的觀測數據上。這樣一來,在這個過程的最後,我們就獲得了一個具有較低偏置的強學習器(我們會注意到,Boosting也有減小方差的效果)。和Bagging 一樣,Boosting也可以用於迴歸和分類問題。由於其重點在於減小偏置,用於Boosting 的基礎模型通常是那些低方差高偏置的模型。例如,如果想要使用樹作爲基礎模型,我們將主要選擇只有少許幾層的較淺決策樹。而選擇低方差高偏置模型作爲Boosting 弱學習器的另一個重要原因是:這些模型擬合的計算開銷較低(參數化時自由度較低)。實際上,由於擬合不同模型的計算無法並行處理(與Bagging 不同),順序地擬合若干複雜模型會導致計算開銷變得非常高。

  一旦選定了弱學習器,我們仍需要定義它們的擬合方式聚合方式。介紹兩個重要的Boosting算法:自適應提升(adaboost)梯度提升(gradient boosting)

  簡而言之,這兩種元算法在順序化的過程中創建和聚合弱學習器的方式存在差異。自適應提升算法會更新附加給每個訓練數據集中觀測數據的權重,而梯度提升算法則會更新這些觀測數據的值。這裏產生差異的主要原因是:兩種算法解決優化問題(尋找最佳模型——弱學習器的加權和)的方式不同。
在這裏插入圖片描述

Boosting會迭代地擬合一個弱學習器, 將其聚合到集成模型中,並「更新」訓練數據集,從而在擬合下一個基礎模型時更好地考 慮當前集成模型的優缺點。

  1.2.2.1. 自適應adaboost

  在自適應adaboost中,我們將集成模型定義爲LL個弱學習器的加權和:sL(.)=l=1Lcl×wl(.)          clwls_{L}(.)=\sum_{l=1}^{L}c_{l}\times w_{l}(.)\; \; \; \; \; 其中c_{l}爲係數,w_{l}爲弱學習器  尋找這種最佳集成模型是一個困難的優化問題。因此,我們並沒打算一次性地解決該問題(找到給出最佳整體加法模型的所有係數和弱學習器),而是使用了一種更易於處理的迭代優化過程(即使它有可能導致我們得到次優解)。

  另外,我們將弱學習器逐個添加到當前的集成模型中,在每次迭代中尋找可能的最佳組合(係數、弱學習器)。換句話說,我們循環地將 sls_{l} 定義如下: sl(.)=sl1(.)+cl×wl(.)s_{l}(.)=s_{l-1}(.)+c_{l}\times w_{l}(.)  其中,clc_{l}wlw_{l}被挑選出來,使得sls_{l}是最適合訓練數據的模型,因此這是對sl1s_{l-1} 的最佳可能改進。我們可以進一步將其表示爲:(cl,wl(.))=argc,w(.)minE(sl1(.)+c×w(.))=argc,w(.)minn=1Ne(yn,sl1(xn)+c×w(xn))(c_{l},w_{l}(.))=arg_{c,w(.)}minE(s_{l-1}(.)+c\times w(.))=arg_{c,w(.)}min\sum_{n=1}^{N}e(y_{n},s_{l-1}(x_{n})+c\times w(x_{n}))  其中,E(.)E(.)是給定模型的擬合誤差,e(.,.)e(.,.)是損失/誤差函數。因此,我們並沒有在求和過程中對所有LL個模型進行「全局優化」,而是通過「局部」優化來近似最優解並將弱學習器逐個添加到強模型中。

  更特別的是,在考慮二分類問題時,我們可以將 adaboost 算法重新寫入以下過程:首先,它將更新數據集中觀測數據的權重,訓練一個新的弱學習器,該學習器重點關注當前集成模型誤分類的觀測數據。其次,它會根據一個表示該弱模型性能的更新系數,將弱學習器添加到加權和中:弱學習器的性能越好,它對強學習器的貢獻就越大。

  因此,假設我們面對的是一個二分類問題:數據集中有N個觀測數據,我們想在給定一組弱模型的情況下使用adaboost算法。在算法的起始階段(序列中的第一個模型),所有的觀測數據都擁有相同的權重1/N。然後,我們將下面的步驟重複LL次(作用於序列中的LL個學習器):

  • 用當前觀測數據的權重擬合可能的最佳弱模型
  • 計算更新系數的值,更新系數是弱學習器的某種標量化評估指標,它表示相對集成模型來說,該弱學習器的分量如何
  • 通過添加新的弱學習器與其更新系數的乘積來更新強學習器計算新觀測數據的權重,該權重表示我們想在下一輪迭代中關注哪些觀測數據(聚和模型預測錯誤的觀測數據的權重增加,而正確預測的觀測數據的權重減小)

  重複這些步驟,我們順序地構建出LL個模型,並將它們聚合成一個簡單的線性組合,然後由表示每個學習器性能的係數加權。注意,初始adaboost算法有一些變體,比如LogitBoost(分類)或L2Boost(迴歸),它們的差異主要取決於損失函數的選擇。
在這裏插入圖片描述

Adaboost在每次迭代中更新觀測數據的權重。正確分類的觀測數據的權重相對於錯誤分類的觀測數據的權重有所下降。在最終的集成模型中,性能更好的模型具有更高的權重。




  1.2.3. 堆疊法(Stacking)

  堆疊法Stacking與Bagging和Boosting主要存在兩方面的差異。首先,堆疊法通常考慮的是異質弱學習器(不同的學習算法被組合在一起),而Bagging和Boosting主要考慮的是同質弱學習器。其次,stacking堆疊法學習用元模型組合基礎模型,而Bagging和Boosting則根據確定性算法組合弱學習器。

  正如上文已經提到的,堆疊法的概念是學習幾個不同的弱學習器,並通過訓練一個元模型來組合它們,然後基於這些弱模型返回的多個預測結果輸出最終的預測結果。

  因此,爲了構建Stacking模型,我們需要定義兩個東西:想要擬合的LL個學習器以及組合它們的元模型。例如,對於分類問題來說,我們可以選擇KNN分類器logistic迴歸SVM作爲弱學習器,並決定學習神經網絡作爲元模型。然後,神經網絡將會把三個弱學習器的輸出作爲輸入,並返回基於該輸入的最終預測。所以,假設我們想要擬合由LL個弱學習器組成的Stacking集成模型。我們必須遵循以下步驟:

  • 將訓練數據分爲兩組
  • 選擇 L 個弱學習器,用它們擬合第一組數據
  • 使 L 個學習器中的每個學習器對第二組數據中的觀測數據進行預測
  • 在第二組數據上擬合元模型,使用弱學習器做出的預測作爲輸入

  在前面的步驟中,我們將數據集一分爲二,因爲對用於訓練弱學習器的數據的預測與元模型的訓練不相關。因此,將數據集分成兩部分的一個明顯缺點是,我們只有一半的數據用於訓練基礎模型,另一半數據用於訓練元模型。
  爲了克服這種限制,我們可以使用某種k-折交叉訓練方法(類似於 k-折交叉驗證中的做法)。這樣所有的觀測數據都可以用來訓練元模型:對於任意的觀測數據,弱學習器的預測都是通過在k-1折數據(不包含已考慮的觀測數據)上訓練這些弱學習器的實例來完成的。換句話說,它會在k-1折數據上進行訓練,從而對剩下的一折數據進行預測。迭代地重複這個過程,就可以得到對任何一折觀測數據的預測結果。這樣一來,我們就可以爲數據集中的每個觀測數據生成相關的預測,然後使用所有這些預測結果訓練元模型。

在這裏插入圖片描述

Stacking方法會訓練一個元模型, 該模型根據較低層的弱學習器返回的輸出結果生成最後的輸出





 1.3.十折交叉驗證

  由於深度學習模型一般需要較長的訓練週期,如果硬件設備不允許建議選取留出法,如果需要追求精度可以使用交叉驗證的方法。

  十折交叉驗證用來測試算法準確性。將數據集分成十份,輪流將其中九份作爲訓練數據,一份作爲測試數據,進行試驗。每次試驗都會得出相應的正確率(或差錯率)。十次的結果的正確率(或差錯率)的平均值作爲對算法精度的估計,一般還需要進行多次十折交叉驗證(例如十次十折交叉驗證),再求其均值,作爲對算法準確性的估計。

  下面假設構建了十折交叉驗證,訓練得到十個CNN模型。
在這裏插入圖片描述
 那麼在十個CNN模型可以使用如下方式進行集成:

  • 對預測的結果的概率值進行平均,然後解碼爲具體字符
  • 對預測的字符進行投票,得到最終字符

  實現方法可以通過十折交叉驗證深入理解瞭解。




2. 深度學習中的集成學習


 此外在深度學習中本身還有一些集成學習思路的做法,值得借鑑學習:
  • 丟棄法Dropout
  • 測試集數據擴增TTA

 2.1. 丟棄法Dropout

  具體原理可以參考天池大賽:街景字符編碼識別——Part4:模型訓練與驗證

  加入丟棄法後的網絡結構如下:

# 定義模型
class SVHN_Model1(nn.Module):
    def __init__(self):
        super(SVHN_Model1, self).__init__()
        # CNN提取特徵模塊
        self.cnn = nn.Sequential(
            nn.Conv2d(3, 16, kernel_size=(3, 3), stride=(2, 2)),
            nn.ReLU(),
            nn.Dropout(0.25),
            nn.MaxPool2d(2),
            nn.Conv2d(16, 32, kernel_size=(3, 3), stride=(2, 2)),
            nn.ReLU(), 
            nn.Dropout(0.25),
            nn.MaxPool2d(2),
        )
        # 
        self.fc1 = nn.Linear(32*3*7, 11)
        self.fc2 = nn.Linear(32*3*7, 11)
        self.fc3 = nn.Linear(32*3*7, 11)
        self.fc4 = nn.Linear(32*3*7, 11)
        self.fc5 = nn.Linear(32*3*7, 11)
        self.fc6 = nn.Linear(32*3*7, 11)
    
    def forward(self, img):        
        feat = self.cnn(img)
        feat = feat.view(feat.shape[0], -1)
        c1 = self.fc1(feat)
        c2 = self.fc2(feat)
        c3 = self.fc3(feat)
        c4 = self.fc4(feat)
        c5 = self.fc5(feat)
        c6 = self.fc6(feat)
        return c1, c2, c3, c4, c5, c6



 2.2. 測試集數據擴增TTA

  具體原理可以參考天池大賽:街景字符編碼識別——Part4:模型訓練與驗證,實現方法參考TTA(Test-Time Augmentation) 之Pytorch

def predict(test_loader, model, tta=10):
   model.eval()
   test_pred_tta = None
   # TTA 次數
   for _ in range(tta):
       test_pred = []
   
       with torch.no_grad():
           for i, (input, target) in enumerate(test_loader):
               c0, c1, c2, c3, c4, c5 = model(data[0])
               output = np.concatenate([c0.data.numpy(), c1.data.numpy(),
                  c2.data.numpy(), c3.data.numpy(),
                  c4.data.numpy(), c5.data.numpy()], axis=1)
               test_pred.append(output)
       
       test_pred = np.vstack(test_pred)
       if test_pred_tta is None:
           test_pred_tta = test_pred
       else:
           test_pred_tta += test_pred
   
   return test_pred_tta



 2.3. Snapshot

  本章的開頭已經提到,假設我們訓練了10個CNN則可以將多個模型的預測結果進行平均。但是加入只訓練了一個CNN模型,如何做模型集成呢?

  在論文Snapshot Ensembles中(論文解讀1論文解讀2),作者提出使用cyclical learning rate進行訓練模型,並保存精度比較好的一些checkopint,最後將多個checkpoint進行模型集成。
在這裏插入圖片描述
  由於在cyclical learning rate中學習率的變化有周期性變大和減少的行爲,因此CNN模型很有可能在跳出局部最優進入另一個局部最優。在Snapshot論文中作者通過使用表明,此種方法可以在一定程度上提高模型精度,但需要更長的訓練時間。
在這裏插入圖片描述



3. 結果後處理

  在不同的任務中可能會有不同的解決方案,不同思路的模型不僅可以互相借鑑,同時也可以修正最終的預測結果。

  在本次賽題中,可以從以下幾個思路對預測結果進行後處理:

  • 統計圖片中每個位置字符出現的頻率,使用規則修正結果
  • 單獨訓練一個字符長度預測模型,用來預測圖片中字符個數,並修正結果。


4. 參考鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章