集成學習-Bagging原理與實現 西瓜書

Bagging簡介

Bagging是並行式集成學習的最著名代表,名字是由Bootstrap AGGregatING縮寫而來,看到Bootstrap我們就會聯想到boostrap的隨機模擬法和它對應的樣本獲取方式,它是基於自助採樣法(Boostrap sampleing),Bagging也是同理.給定包含m個樣本的數據集,先隨機抽取一個樣本放入採樣集中,再把該樣本放回,使得下次採樣時該樣本仍有機會被選中,這樣經過m次採樣,我們便從原始是數據集中抽取樣本得到一個數據量同爲m的數據集.說簡單一點就是統計裏的有放回抽樣,且每個樣本被抽取的概率相同,均爲總樣本數分之一.


1)樣本抽取方式

假設一個樣本被抽取的概率是1/m,有:

                                                                        

意味着抽樣次數足夠大時,一個樣本不被抽到的概率爲36.8%,由此可知,初始樣本約有63.2%的樣本出現在採樣集中.這樣我們可以採樣T次,從而得到T個含m個訓練樣本的採樣集,基於每個訓練集樣本訓練一個學習器,最後結合這些學習器的結果,預測總結果,這就是Bagging的大致實現過程,針對最後的預測,Bagging一般採取投票法,若票數相同,則最簡單的方法是是隨機選擇一個,當然也可以進一步考察學習器的投票置信度,或者使用加權投票法等處理最終結果.


2)時間複雜度

這裏基學習器可以選擇DT,LR,NB,SVM,神經網絡等等,假定基學習期的時間複雜度爲O(m),則bagging的複雜度大約爲T(O(m)+O(s)),考慮到採樣與投票的平均過程複雜度O(s)很小,而T通常是一個不太大的常數,所以Bagging的集成與直接使用基學習器算法訓練一個學習器的時間複雜度同階,這說明Bagging是一個很高效的集成學習算法,另外,與AdaBoost只適用於二分類任務不同,Bagging可以用於多分類,迴歸的任務.


3)優缺點

Bagging的個優點是對泛化性能的估計,這得益於自助採樣法,由於訓練只使用了63.2%的樣本,因此剩下約36.8%的樣本可當做測試集來對泛化性能進行‘包外估計’,爲此需記錄每個基學習器所使用的訓練樣本,不放令Dt表示學習器ht實際使用的訓練樣本集,令H_oob(x)表示對有樣本x的包外預測,即測試集不包含數據集中用於訓練的部分:

                                                    

則Bagging的泛化誤差的包外估計爲:

                                                    

利用包外估計的優點,我們可以在基學習器的訓練過程中及時調整訓練模型,例如在使用決策樹爲基學習器時,可以根據泛化性能來輔助剪枝,而當使用神經網絡時,則可利用包外樣本來輔助早停從而減少過擬合的風險.

自助採樣法優點比較明顯,但在數學上也有一定的缺陷,重複有放回採樣的道德樣本集改變了數據原有的分佈,因此在一定程度上引入了偏差,對最終的結果預測會造成一定程度的影響.



Bagging實現

            

這是西瓜書上給出的Bagging僞代碼,我們需要做的很簡單,確定基學習算法,訓練輪數T,利用自助法提取訓練集,最後選擇使誤差最小的y作爲預測樣本的標籤.


導入所需庫

from numpy import *
import matplotlib.pyplot as plt
import random
from sklearn import tree

這裏常用的num庫和mattplot庫就不多贅述了,random庫負責隨機選取樣本實現自助採樣法,sklearn.tree負責使用決策樹作爲基學習器.


讀取數據

def loadDataSet(fileName):#讀取數據
    numFeat = len(open(fileName).readline().split('\t')) 
    dataMat = []; labelMat = []
    fr = open(fileName)
    for line in fr.readlines():
        lineArr =[]
        curLine = line.strip().split('\t')
        for i in range(numFeat-1):#添加數據
            lineArr.append(float(curLine[i]))
        dataMat.append(lineArr)
        labelMat.append(float(curLine[-1]))#添加數據對應標籤
    return dataMat,labelMat

這裏的數據來源《機器學習實戰》第七章的病馬數據,數據包含多匹患有疝氣病的1馬的多項特徵指標,而標籤值對應病馬的死亡率預測,+1對應存活,-1對應死亡,通過決策樹和Bagging,來預測病馬的生存情況.


自助法採樣

def rand_train(dataMat,labelMat):#自助法採樣
    len_train = len(labelMat)#獲取樣本1數
    train_data = [] ; train_label = []
    for i in range(len_train):#抽取樣本數次樣本
        index = random.randint(0,len_train-1)#隨機生成樣本索引
        train_data.append(dataMat[index])#添加對應數據與標籤
        train_label.append(labelMat[index])
    return train_data,train_label#返回訓練集與訓練集標籤

通過random.randint隨機生成樣本數範圍內的索引,構造Bagging數據集.


決策樹基學習器

def bagging_by_tree(dataMat,labelMat,t=10):#默認並行生成十個基學習器
    test_data,test_label = loadDataSet('horseColicTest2.txt')  #獲取測試樣本與標籤   
    predict_list = []
    for i in range(t):#並行生成T個
        train_data,train_label = rand_train(dataMat,labelMat)#自主採樣1得到樣本
        clf = tree.DecisionTreeClassifier()#初始化決策樹模型
        clf.fit(train_data,train_label)#訓練模型
        total = []
        y_predicted = clf.predict(test_data)#預測數據
        total.append(y_predicted)
        predict_list.append(total)#結果添加到預測列表中
    return predict_list,test_label

默認t=10,即產生十個基學習器,這裏基學習器對應決策樹Tree,通過t次採樣,獲得t個樣本,訓練t個決策樹,將每次預測的結果添加到predict_list中,供之後計算準確率.


彙總結果計算準確率

def calc_error(predict_list,test_label):#計算錯誤率
    m,n,k = shape(predict_list)#提取預測集信息
    predict_label = sum(predict_list,axis = 0)
    predict_label = sign(predict_label)
    for i in range(len(predict_label[0])):
        if predict_label[0][i] == 0:#如果票數相同,則隨機生成一個標籤
            tip = random.randint(0,1)
            if tip == 0:
                predict_label[0][i] = 1
            else:
                predict_label[0][i] =-1
    error_count = 0#初始化預測錯誤數
    for i in range(k):
        if predict_label[0][i] != test_label[i]:#判斷預測精度
            error_count += 1
    error_rate = error_count/k
    return error_rate

針對十個基學習器的預測結果,將結果累加,並使用sign函數進行類別預測,對於投票數相同的樣例,按照最簡單的解決方式,隨機選擇一個類別添加,最終統計預測不對的類別,最終計算錯誤率.


主函數

if __name__ == "__main__":
    fileName = 'horseColicTraining2.txt'
    dataMat,labelMat =  loadDataSet(fileName)
    train_data,train_label = rand_train(dataMat,labelMat)
    predict_list , test_label = bagging_by_tree(dataMat,labelMat)
    print("Bagging錯誤率:",calc_error(predict_list,test_label))


運行結果

Bagging錯誤率: 0.26865671641791045
[Finished in 0.9s]


改爲單學習器

def bagging_by_Onetree(dataMat,labelMat,t=10):
    test_data,test_label = loadDataSet('horseColicTest2.txt') 
    train_data,train_label = rand_train(dataMat,labelMat)
    clf = tree.DecisionTreeClassifier()
    clf.fit(train_data,train_label)
    y_predicted = clf.predict(test_data)
    error_count = 0
    for i in range(67):
        if y_predicted[i] != test_label[i]:
            error_count += 1
    return error_count/67

將bagging_by_tree函數稍微修改,取消學習器個數t,得到單一學習器,訓練樣本,計算錯誤率看看如何.

Bagging錯誤率: 0.22388059701492538
單一學習器錯誤率: 0.3582089552238806
[Finished in 0.9s]


總結

將上述過程多次計算,每次結果都會有所不同,這是因爲隨機抽樣造成的樣本擾動所致,而且針對此樣本集,雖然集成後會提升性能,但Bagging最後的泛性能並不是很理想,因此還有更好的分類模型去預測,這裏只是大概實現Bagging的過程,從而加深集成學習的印象.接下來主要介紹隨機森林RF,隨機森林在Bagging樣本擾動的基礎上,還對屬性進行了隨機選取,大大提升了模型泛化能力,是當前集成學習特別常見的模型.









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