1. 內建學習概述
1.1. 定義
內建學習(Ensemble learning)就是将若幹個弱分類器通過一定的政策組合之後産生一個強分類器。 弱分類器(Weak Classifier)指的就是那些分類準确率隻比随機猜測略好一點的分類器,而強分類器( Strong Classifier)的分類準确率會高很多。這裡的"強"&"弱"是相對的。某些書中也會把弱分類器稱 為“基分類器”。
image-20230217113940835
目前內建學習算法的流派主要有兩種:
- bagging
- boosting
1.2. bagging
裝袋(bagging)又稱自主聚集(bootstrap aggregating),是一種根據均勻機率分布從資料集中重複 抽樣(有放回的)的技術。每個新資料集和原始資料集的大小相等。由于新資料集中的每個樣本都是從 原始資料集中有放回的随機抽樣出來的,是以新資料集中可能有重複的值,而原始資料集中的某些樣本 可能根本就沒出現在新資料集中。
bagging方法的流程,如下圖所示:
bagging
- 有放回的随機抽樣:
自主采樣法(Bootstap sampling),也就是說對于m個樣本的原始資料集,每次 随機選取一個樣本放入采樣集,然後把這個樣本重新放回原資料集中,然後再進行下一個樣本的随機抽 樣,直到一個采樣集中的數量達到m,這樣一個采樣集就建構好了,然後我們可以重複這個過程,生成 n個這樣的采樣集。也就是說,最後形成的采樣集,每個采樣集中的樣本可能是重複的,也可能原資料 集中的某些樣本根本就沒抽到,并且每個采樣集中的樣本分布可能都不一樣。
根據有放回的随機抽樣構造的n個采樣集,我們就可以對它們分别進行訓練,得到n個弱分類器,然後根 據每個弱分類器傳回的結果,我們可以采用一定的組合政策得到我們最後需要的強分類器。
bagging方法的代表算法是随機森林,準确的來說,随機森林是bagging的一個特化進階版,所謂的特 化是因為随機森林的弱學習器都是決策樹。所謂的進階是随機森林在bagging的樣本随機采樣基礎上, 又加上了特征的随機選擇,其基本思想沒有脫離bagging的範疇。
1.3. boosting
boosting是一個疊代的過程,用來自适應地改變訓練樣本的分布,使得弱分類器聚焦到那些很難分類的 樣本上。它的做法是給每一個訓練樣本賦予一個權重,在每一輪訓練結束時自動地調整權重。
boosting方法的流程,如下圖所示:
boosting
boosting方法的代表算法有Adaboost、GBDT、XGBoost算法
1.4. 結合政策
1.4.1. 平均法
對于數值類的回歸預測問題,通常使用的結合政策是平均法,也就是說,對于若幹個弱學習器的輸出進 行平均得到最終的預測輸出。
假設我們最終得到的n個弱分類器為
最簡單的平均是算術平均,也就是說最終預測是
如果每個弱分類器有一個權重w,則最終預測是
1.4.2. 投票法
對于分類問題的預測,我們通常使用的是投票法。假設我們的預測類别是
對于任意一個預測樣本x,我們的n個弱學習器的預測結果分别是
最簡單的投票法是相對多數投票法,也就是我們常說的少數服從多數,也就是n個弱學習器的對樣本x的 預測結果中,數量最多的類别 為最終的分類類别。如果不止一個類别獲得最高票,則随機選擇一個做 最終類别。
稍微複雜的投票法是絕對多數投票法,也就是我們常說的要票過半數。在相對多數投票法的基礎上,不 光要求獲得最高票,還要求票過半數。否則會拒絕預測。
更加複雜的是權重投票法,和權重平均法一樣,每個弱學習器的分類票數要乘以一個權重,最終将各個 類别的權重票數求和,最大的值對應的類别為最終類别。
1.4.3. 學習法
前兩種方法都是對弱學習器的結果做平均或者投票,相對比較簡單,但是可能學習誤差較大,于是就有 了學習法這種方法,對于學習法,代表方法是stacking,當使用stacking的結合政策時, 我們不是對弱 學習器的結果做簡單的邏輯處理,而是再加上一層學習器,也就是說,我們将訓練集弱學習器的學習結 果作為輸入,将訓練集的輸出作為輸出,重新訓練一個學習器來得到最終結果。
在這種情況下,我們将弱學習器稱為初級學習器,将用于結合的學習器稱為次級學習器。對于測試集, 我們首先用初級學習器預測一次,得到次級學習器的輸入樣本,再用次級學習器預測一次,得到最終的 預測結果。
2. AdaBoost
Adaboost是adaptive boosting(自适應boosting)的縮寫。算法步驟如下:
2.1. 計算樣本權重
賦予訓練集中每個樣本一個權重,構成權重向量D,将權重向量D初始化相等值。設定我們有m個樣本,每個樣本的權重都相等,則權重為
2.2. 計算錯誤率
在訓練集上訓練出一個弱分類器,并計算分類器的錯誤率:
2.3. 計算弱分離器權重
為目前分類器賦予權重值alpha,則alpha計算公式為:
2.4. 調整權重值
根據上一次訓練結果,調整權重值(上一次分對的權重降低,分錯的權重增加
如果第i個樣本被正确分類,則該樣本權重更改為:
如果第i個樣本被分錯,則該樣本權重更改為:
把上面兩個公式彙整成一個:
之後,在同一資料集上再一次訓練弱分類器,然後循環上述過程,直到訓練錯誤率為0,或者弱分類器 的數目達到指定值。
2.5. 結果
循環結束後,我們可以得到我們的強分類器的預測結果:
3. 基于單層決策樹建構弱分類器
單層決策樹(decision stump)也稱決策樹樁,是一種簡單的決策樹。我們已經講過決策樹的相 關原理了,接下來我們一起來建構一個單層決策樹,它僅僅基于單個特征來做決策。由于這棵樹隻有一 次分裂過程,是以它實際上就是一個樹樁。
3.1. 建構資料集
- 我們先建構一個簡單資料集來確定我們寫出的函數能夠正常運作。
import pandas as pd
import numpy as np
# 獲得特征矩陣和标簽矩陣
def get_Mat(path):
dataSet = pd.read_table(path,header = None)
xMat = np.mat(dataSet.iloc[:,:-1].values)
yMat = np.mat(dataSet.iloc[:,-1].values).T
return xMat,yMat
xMat,yMat = get_Mat('simpdata.txt')
- 建構資料可視化函數,并運作檢視資料分布
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['simhei']
%matplotlib inline
# 資料集可視化函數
def showPlot(xMat,yMat):
x=np.array(xMat[:,0])
y=np.array(xMat[:,1])
label = np.array(yMat)
plt.scatter(x,y,c=label)
plt.title('單層決策樹測試資料')
plt.show()
showPlot(xMat,yMat)
3.2. 建構單層決策樹
我們會建立兩個函數來實作我們的單層決策樹:
第一個函數用來測試是否有某個值小于或者大于我們正在測試的門檻值。
"""
函數功能:單層決策樹分類函數
參數說明:
xMat: 特征矩陣
i: 第i列,也就是第幾個特征
Q: 門檻值
S: 标志
傳回:
re: 分類結果
"""
def Classify0(xMat,i,Q,S):
re = np.ones((xMat.shape[0],1)) # 初始化re為1
if S == 'lt':
re[xMat[:,i] <= Q] = -1 # 如果小于門檻值,則指派為-1
else:
re[xMat[:,i] > Q] = -1 # 如果大于門檻值,則指派為-1
return re
第二個函數稍微複雜一些,會在一個權重資料集中循環,并找到具有最低錯誤率的單層決策樹
"""
函數功能:找到資料集上最佳的單層決策樹
參數說明:
xMat:特征矩陣
yMat:标簽矩陣
D:樣本權重
傳回:
bestStump:最佳單層決策樹資訊
minE:最小誤差
bestClas:最佳的分類結果
"""
def get_Stump(xMat,yMat,D):
m,n = xMat.shape # m為樣本個數,n為特征數
Steps = 10 # 初始化一個步數
bestStump = {} # 用字典形式來儲存樹樁資訊
bestClas = np.mat(np.zeros((m,1))) # 初始化分類結果為1
minE = np.inf # 最小誤差初始化為正無窮大
for i in range(n): # 周遊所有特征
Min = xMat[:,i].min() # 找到特征中最小值
Max = xMat[:,i].max() # 找到特征中最大值
stepSize = (Max - Min) / Steps # 計算步長
for j in range(-1, int(Steps)+1):
for S in ['lt', 'gt']: # 大于和小于的情況,均周遊。lt:less than,gt:greater than
Q = (Min + j * stepSize) # 計算門檻值
re = Classify0(xMat, i, Q, S) # 計算分類結果
err = np.mat(np.ones((m,1))) # 初始化誤差矩陣
err[re == yMat] = 0 # 分類正确的,指派為0
eca = D.T * err # 計算誤差
# print(f'切分特征: {i}, 門檻值:{np.round(Q,2)}, 标志:{S}, 權重誤差:{np.round(eca,3)}')
if eca < minE: # 找到誤差最小的分類方式
minE = eca
bestClas = re.copy()
bestStump['特征列'] = i
bestStump['門檻值'] = Q
bestStump['标志'] = S
return bestStump,minE,bestClas
測試函數并運作檢視結果:
m = xMat.shape[0]
D = np.mat(np.ones((m, 1)) / m) # 初始化樣本權重(每個樣本權重相等)
bestStump,minE,bestClas= get_Stump(xMat,yMat,D)
4. AdaBoost代碼
用python代碼來實作完整版AdaBoost算法
"""
函數功能:基于單層決策樹的AdaBoost訓練過程
參數說明:
xMat:特征矩陣
yMat:标簽矩陣
maxC:最大疊代次數
傳回:
weakClass:弱分類器資訊
aggClass:類别估計值(其實就是更改了标簽的估計值)
"""
def Ada_train(xMat, yMat, maxC = 40):
weakClass = []
m = xMat.shape[0]
D = np.mat(np.ones((m, 1)) / m) # 初始化權重
aggClass = np.mat(np.zeros((m,1)))
for i in range(maxC):
Stump, error, bestClas = get_Stump(xMat, yMat,D) # 建構單層決策樹
# print(f"D:{D.T}")
alpha=float(0.5 * np.log((1 - error) / max(error, 1e-16))) # 計算弱分類器權重alpha
Stump['alpha'] = np.round(alpha,2) # 存儲弱學習算法權重,保留兩位小數
weakClass.append(Stump) # 存儲單層決策樹
# print("bestClas: ", bestClas.T)
expon = np.multiply(-1 * alpha *yMat, bestClas) # 計算e的指數項
D = np.multiply(D, np.exp(expon))
D = D / D.sum() # 根據樣本權重公式,更新樣本權重
aggClass += alpha * bestClas #更新累計類别估計值
# print(f"aggClass: {aggClass.T}" )
aggErr = np.multiply(np.sign(aggClass) != yMat, np.ones((m,1)))# 計算誤差
errRate = aggErr.sum() / m
# print(f"分類錯誤率: {errRate}")
if errRate == 0: break # 誤差為0,退出循環
return weakClass, aggClass
- 運作函數,檢視結果:
weakClass, aggClass =Ada_train(xMat, yMat, maxC = 40)
weakClass
aggClass
5. 基于AdaBoost的分類
這裡我們使用弱分類器的權重求和來計算最後的結果。
"""
函數功能:AdaBoost分類函數
參數說明:
data: 待分類樣例
classifys:訓練好的分類器
傳回:
分類結果
"""
def AdaClassify(data,weakClass):
dataMat = np.mat(data)
m = dataMat.shape[0]
aggClass = np.mat(np.zeros((m,1)))
for i in range(len(weakClass)): # 周遊所有分類器,進行分類
classEst = Classify0(dataMat,
weakClass[i]['特征列'],
weakClass[i]['門檻值'],
weakClass[i]['标志'])
aggClass += weakClass[i]['alpha'] * classEst
# print(aggClass)
return np.sign(aggClass)
- 結果
AdaClassify([0,0],weakClass)