機器學習實戰之路 —— 4 Boosting算法

 2023-12-25 阅读 70 评论 0

摘要:機器學習實戰之路 —— 4 Boosting算法1. Boosting算法概述2. 主要算法實現2.1 AdaBoost2.2 GBDT2.3 XGBoost3. 實戰 - 鳶尾花數據集分類3.1 AdaBoost3.2 GBDT & XGBoost4.參考學習的書目及論文 1. Boosting算法概述 一般集成學習的結構可如下圖所示:通過樣本訓練

機器學習實戰之路 —— 4 Boosting算法

  • 1. Boosting算法概述
  • 2. 主要算法實現
    • 2.1 AdaBoost
    • 2.2 GBDT
    • 2.3 XGBoost
  • 3. 實戰 - 鳶尾花數據集分類
    • 3.1 AdaBoost
    • 3.2 GBDT & XGBoost
  • 4.參考學習的書目及論文

1. Boosting算法概述

一般集成學習的結構可如下圖所示:通過樣本訓練集產生一組個體學習器,每個個體學習器完成訓練后對于每個測試樣本都會產生各自的預測結果。經由一種組合策略,從個體學習器的預測結果中選擇一個結果或者給出一個平均值作為最終的輸出。對于分類任務而言,個體學習器通常由現有的分類算法從訓練數據中產生。但是每種分類器都會有各自的優點或者缺點,不可能適用所有的分類場景。因此,在具體的分類任務中,需要采用適用的分類算法作為集成學習的個體學習器。 一般而言,個體學習器存在一定差異的集成學習算法擁有更好的分類性能。
在這里插入圖片描述

根據個體學習器的不同生成方式,目前集成學習算法按照學習的分類方式可以大致分為兩類:并行序列化生成和串行生成。并行化生成個體學習器的算法有 Bagging和Random Forest<請參考博主前面寫的隨機森林算法章節博客>,它們通過采樣方法,訓練出多樣性的基學習器,降低方差,之后 Random Forest 又引入了隨機屬性,使得多樣性進一步提高,于是獲得了更好的效果。
串行化生成個體學習器的代表算法則是Boosting (也稱作提升法),通過對錯判訓練樣本重新賦權來重復訓練,通過提高基學習器準確性來降低偏差。

在這里插入圖片描述

上圖即展示了一個Boosting的實現過程,算法主要關注分錯樣本的數據處理。如果在當前建模的分類器中,存在分類錯誤的樣本數據,則將會在下一次建模的分類器中,給上次分錯的樣本增大權重,如圖2(m=2)之后的紅點和藍點被標識放大。隨著訓練次數增加,被分錯的樣本權重不斷增大,被錯分的樣本將不斷被重視及加大權重,直至模型所有樣本基本實現分類正確。
Boosting的主要算法有Ada Boost、GBDT、XGboost,以下將對這三種算法分別展開論述。

2. 主要算法實現

2.1 AdaBoost

AdaBoost(Adaptive Boosting)是最著名的 Boosting 算法,屬于提升類的自適應算法。其基本工作過程是:第一輪訓練數據的權重都相同,訓練得出第一個基分類器,之后,下一輪的基分類器的權重根據上一輪的基分類器的錯分樣本進行調整,即錯分樣本具有更高的權重值;之后,每一輪中具有不同權重值的樣本對本輪基分類器進行訓練,即在考慮樣本不同權重的情況下得到本輪錯誤率最低的基分類器。重復以上步驟直到完成設定的輪數,每一輪訓練得到一個基分類器,最后根據采取合適的結合策略得到次級分類器的預測結果。AdaBoost算法流程如下圖所示。
在這里插入圖片描述

其中,加權誤差率 e 的定義為:
e=P(Gm(xi)≠yi)=∑i=1NωmiI(Gm(xi)≠yi)e=P(G_m(x_i)≠y_i)=\sum_{i=1}^Nω_{mi}I(G_m(x_i)≠y_i) e=P(Gm?(xi?)?=yi?)=i=1N?ωmi?I(Gm?(xi?)?=yi?)
其中Gm(xi)G_m(x_i)Gm?(xi?)為基本分類器。其系數為:
αm=12ln1?emem\alpha_m=\frac{1}{2}ln\frac{1-e_m}{e_m} αm?=21?lnem?1?em??
樣本的權值分布為:
ωm+1,i=ωmiZme(?αmyiGm(xi))ω_{m+1,i}=\frac{ω_{mi}}{Z_m}e^{(-\alpha_my_iG_m(x_i))} ωm+1,i?=Zm?ωmi??e(?αm?yi?Gm?(xi?))
其中yiy_iyi?為樣本標記值,對于二分類,yi∈{?1,1}y_i∈\{-1,1\}yi?{?1,1}ZmZ_mZm?為規范化因子,使得ωm+1,iω_{m+1,i}ωm+1,i?相加為1。
Zm=∑i=1Nωmie(?αmyiGm(xi))Z_m=\sum_{i=1}^Nω_{mi}e^{(-\alpha_my_iG_m(x_i))} Zm?=i=1N?ωmi?e(?αm?yi?Gm?(xi?))
最終分類器為:
G(x)=sign(f(x))=sign(∑m=1MαmGm(x))G(x)=sign(f(x))=sign\left({\sum_{m=1}^M\alpha_mG_m(x)}\right) G(x)=sign(f(x))=sign(m=1M?αm?Gm?(x))
ωm+1,iω_{m+1,i}ωm+1,i?可知被基本分類器Gm(x)G_m(x)Gm?(x)誤分類樣本的權值得以擴大,而被正確分類樣本的權值得以縮小。因此,誤分類樣本將在下一輪學中起更大的作用。訓練數據沒有改變,而不斷改變訓練數據權值的分布,使得訓練數據在基本分類器的學習中起不同的作用。

αm\alpha_mαm?可知其隨著eme_mem?的減小而增大,所以分類誤差率越小的基本分類器在最終分類器中的作用越大。

實現代碼如下:

"""
單層決策樹分類
Args:dataMatrix - 數據矩陣dimen - 特征列threshVal - 閾值threshIneq - 標志位
Returns: retArray - 分類結果
"""
def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):retArray = np.ones((np.shape(dataMatrix)[0],1))         # 初始化retArray為1if threshIneq == 'lt':retArray[dataMatrix[:,dimen] <= threshVal] = -1.0   # 若小于等于閾值,賦值為-1else:retArray[dataMatrix[:,dimen] > threshVal] = -1.0    # 若大于閾值,賦值為-1return retArray"""
基于權重向量生成單層決策樹  
Args:dataArr - 數據矩陣classLabels - 類別標簽D - 權重向量
Returns:bestStump - 最佳單層決策樹信息minError - 最小誤差bestClassEst - 最優分類結果
"""
def buildStump(dataArr, classLabels, D):dataMatrix = np.mat(dataArr); labelMat = np.mat(classLabels).T  # 轉換數據及數據標簽為numpy矩陣m, n = np.shape(dataMatrix)                                     # 數據集的行數和列數numSteps = 10.0                                                 # 遍歷的步數bestStump = {}                                                  # 最佳單層決策樹信息bestClassEst = np.mat(np.zeros((m, 1)))                         # 初始化最佳預測值minError = np.inf                                               # 初始化最小誤差為正無窮大for i in range(n):                                              # 遍歷特征rangeMin = dataMatrix[:, i].min()                           # 特征中的最小值rangeMax = dataMatrix[:, i].max()                           # 特征中的最大值stepSize = (rangeMax - rangeMin) / numSteps                 # 計算步長for j in range(-1, int(numSteps) + 1):                      # 遍歷步長for inequal in ['lt', 'gt']:                            # 遍歷大于、小于threshVal = (rangeMin + float(j) * stepSize)        # 計算閾值predictedVals = stumpClassify(dataMatrix, i, threshVal, inequal)  # 計算分類結果errArr = np.mat(np.ones((m, 1)))                    # 初始化誤差矩陣errArr[predictedVals == labelMat] = 0               # 分類正確的賦值為0weightedError = D.T * errArr                        # 計算加權錯誤率if weightedError < minError:                        # 找到誤差最小的分類方式,并賦值minError = weightedErrorbestClassEst = predictedVals.copy()bestStump['dim'] = ibestStump['thresh'] = threshValbestStump['ineq'] = inequalreturn bestStump, minError, bestClassEst"""
基于單層決策樹的AdaBoost訓練    
Args:dataArr - 數據矩陣classLabels - 類別標簽numIt - 迭代次數
Returns:weakClassArr - 分類器數組aggClassEst - 類別估計累計值
"""
def adaBoostTrainDS(dataArr,classLabels,numIt=40):weakClassArr = []                                                   # 分類器數組m = np.shape(dataArr)[0]                                            # 數據集的特征值個數D = np.mat(np.ones((m,1))/m)                                        # 初始化權重向量aggClassEst = np.mat(np.zeros((m,1)))                               # 初始化類別估計累計值for i in range(numIt):                                              # 根據迭代次數遍歷bestStump,error,classEst = buildStump(dataArr,classLabels,D)    # 基于權重向量生成單層決策樹alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))  # 計算權重alpha,防止發生除零溢出bestStump['alpha'] = alpha                                      # 記錄權重值weakClassArr.append(bestStump)                                  # 存儲最好的單層決策樹expon = np.multiply(-1 * alpha * np.mat(classLabels).T, classEst) # 計算e的指數項D = np.multiply(D, np.exp(expon))                               # 計算新的權重向量D = D / D.sum()                                                 # 權重向量歸一化aggClassEst += alpha * classEst                                 # 計算類別估計累計值aggErrors = np.multiply(np.sign(aggClassEst) != np.mat(classLabels).T, np.ones((m, 1))) # 計算誤差errorRate = aggErrors.sum() / mprint("total error: ", errorRate)if errorRate == 0.0: break                                      # 誤差為0則退出循環return weakClassArr, aggClassEst"""
AdaBoost分類函數
Args:datToClass - 待分類樣例classifierArr - 訓練好的弱分類器
Returns: 分類結果
"""
def adaClassify(datToClass,classifierArr):dataMatrix = np.mat(datToClass)             # 轉換待分類樣例為numpy矩陣m = np.shape(dataMatrix)[0]                 # 待分類樣例個數aggClassEst = np.mat(np.zeros((m,1)))       # 初始化類別估計累計值for i in range(len(classifierArr)):         # 遍歷所有弱分類器classEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],classifierArr[i]['thresh'],classifierArr[i]['ineq'])  # 計算類別估計值aggClassEst += classifierArr[i]['alpha']*classEst   # 根據權重累加得到類別估計累計值print(aggClassEst)return np.sign(aggClassEst)if __name__ == '__main__':dataArr,classLabels = loadSimpData()weakClassArr, aggClassEst = adaBoostTrainDS(dataArr, classLabels)print(adaClassify([[0,0],[5,5]], weakClassArr))

使用sklearn.ensemble 實現 AdaBoost 代碼如下:

from sklearn.ensemble import AdaBoostClassifier# 決策樹
base_estimator = DecisionTreeClassifier(criterion='gini', max_depth=3, min_samples_split=4)
# AdaBoost學習器,10棵樹,學習率為0.1
model = AdaBoostClassifier(base_estimator=base_estimator, n_estimators=10, learning_rate=0.1)
# 訓練AdaBoost學習器
model.fit(x_train, y_train)
# AdaBoost學習器預測訓練集
y_train_pred = model.predict(x_train)

2.2 GBDT

Gradient Boosting與之前的Boosting的不同點在于,在每一次進行訓練的目的是為了減少上一次的殘差,為了不斷的降低殘差,我們需要在減少殘差的梯度方向訓練一個新的模型,所以可以理解,Gradient Boosting算法訓練每一個新的模型都是為讓之前的模型的殘差在梯度方向上降低。將Gradient Boosting用于決策樹模型上之后,就是GBDT(Gradient Boosting Decision Tree,梯度提升樹),其損失函數的負梯度在當前模型的值:
?[?L(y,f(xi)))?f(xi)]f(x)=fm?1(x)-\bigg[\frac{\partial L(y, f(x_i)))}{\partial f(x_i)}\bigg]_{f(x) = f_{m-1} (x)} ?[?f(xi?)?L(y,f(xi?)))?]f(x)=fm?1?(x)?
作為回歸問題提升樹算法中的殘差的近似值,擬合一個回歸樹。

實現代碼如下:
使用sklearn.ensemble 實現 GBDT 代碼如下:

from sklearn.ensemble import GradientBoostingClassifier# GBDT,100棵樹,學習率0.1,最大深度2
gb = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1, max_depth=2)
# GBDT訓練
gb.fit(x_train, y_train.ravel())
# GBDT預測訓練集
y_train_pred = gb.predict(x_train)

2.3 XGBoost

GBDT在優化時僅用一階導數信息,XGBoost則對損失函數進行了二階泰勒展開,同時用到了一階導數和二階導數,加入該正則化項對模型的復雜程度施加懲罰,以提高模型的泛化能力,防止過擬合,也具備較高的計算效率。

實現代碼如下:

import xgboost as xgb# 設置參數
# max_depth - 樹的深度為3
# eta - 學習率,為1時是原始模型,過小的學習率會造成計算次數增多,可防止過擬合,通過減少每一步的權重,可以提高模型的魯棒性。典型值0.01-0.2
# silent-是否輸出中間結果
# objective-定義需要被最小化的損失函數(分類問題的邏輯回歸)
param = {'max_depth': 3, 'eta': 1, 'silent': 1, 'objective': 'binary:logistic'}
watchlist = [(data_test, 'eval'), (data_train, 'train')]    # 輸出過程中的錯誤率
n_round = 7 # 7顆決策樹
# 訓練XGBoost
bst = xgb.train(param, data_train, num_boost_round=n_round, evals=watchlist)
# XGBoost預測數據集
y_hat = bst.predict(data_test)

3. 實戰 - 鳶尾花數據集分類

本小節所用實戰數據同樣為前面章節博客用過的經典鶯尾花數據集,具體的數據描述可參見前面博客。以下分別給出AdaBoost和GBDT & XGBoost的算法實現。

3.1 AdaBoost

與前面的隨機森林章節一樣,對鳶尾花數據選取兩特征組合進行分類,使用Adaboost算法實現代碼如下:

import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_splitif __name__ == "__main__":mpl.rcParams['font.sans-serif'] = ['SimHei']mpl.rcParams['axes.unicode_minus'] = False# 讀取數據path = 'iris.data'  # 數據文件路徑data = pd.read_csv(path, header=None)# 特征定義iris_feature = '花萼長度', '花萼寬度', '花瓣長度', '花瓣寬度'# 數據處理:如果是字符型特征,則轉換成數值型x_prime = data[list(range(4))]              # 數值型數據,無需額外轉換操作y = pd.Categorical(data[4]).codes           # 字符型數據,需轉換成數值型# 拆分數據為訓練集(70%)和測試集(30%)x_prime_train, x_prime_test, y_train, y_test = train_test_split(x_prime, y, train_size=0.7, random_state=0)# 特征組合feature_pairs = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]plt.figure(figsize=(11, 8), facecolor='#FFFFFF')for i, pair in enumerate(feature_pairs):# 準備數據x_train = x_prime_train[pair]x_test = x_prime_test[pair]# 決策樹base_estimator = DecisionTreeClassifier(criterion='gini', max_depth=3, min_samples_split=4)# AdaBoost學習器,10棵樹,學習率為0.1model = AdaBoostClassifier(base_estimator=base_estimator, n_estimators=10, learning_rate=0.1)model.fit(x_train, y_train)# 畫圖N, M = 500, 500  # 橫縱各采樣多少個值x1_min, x2_min = x_train.min()x1_max, x2_max = x_train.max()t1 = np.linspace(x1_min, x1_max, N)t2 = np.linspace(x2_min, x2_max, M)x1, x2 = np.meshgrid(t1, t2)  # 生成網格采樣點x_show = np.stack((x1.flat, x2.flat), axis=1)  # 測試點# 訓練集上的預測結果y_train_pred = model.predict(x_train)acc_train = accuracy_score(y_train, y_train_pred)y_test_pred = model.predict(x_test)acc_test = accuracy_score(y_test, y_test_pred)print('特征:', iris_feature[pair[0]], ' + ', iris_feature[pair[1]])print('\t訓練集準確率: %.4f%%' % (100*acc_train))print('\t測試集準確率: %.4f%%\n' % (100*acc_test))cm_light = mpl.colors.ListedColormap(['#A0FFA0', '#FFA0A0', '#A0A0FF'])cm_dark = mpl.colors.ListedColormap(['g', 'r', 'b'])y_hat = model.predict(x_show)y_hat = y_hat.reshape(x1.shape)plt.subplot(2, 3, i+1)plt.contour(x1, x2, y_hat, colors='k', levels=[0, 1], antialiased=True, linestyles='--', linewidths=1.5)plt.pcolormesh(x1, x2, y_hat, cmap=cm_light)  # 預測值plt.scatter(x_train[pair[0]], x_train[pair[1]], c=y_train, s=20, edgecolors='k', cmap=cm_dark)plt.scatter(x_test[pair[0]], x_test[pair[1]], c=y_test, s=100, marker='*', edgecolors='k', cmap=cm_dark)plt.xlabel(iris_feature[pair[0]], fontsize=14)plt.ylabel(iris_feature[pair[1]], fontsize=14)plt.xlim(x1_min, x1_max)plt.ylim(x2_min, x2_max)plt.grid(b=True)plt.suptitle('Adaboost對鳶尾花數據兩特征組合的分類結果', fontsize=18)plt.tight_layout(1, rect=(0, 0, 1, 0.95))    # (left, bottom, right, top)plt.show()

輸出結果如下:

特征: 花萼長度  +  花萼寬度訓練集準確率: 87.6190%測試集準確率: 75.5556%特征: 花萼長度  +  花瓣長度訓練集準確率: 99.0476%測試集準確率: 88.8889%特征: 花萼長度  +  花瓣寬度訓練集準確率: 98.0952%測試集準確率: 88.8889%特征: 花萼寬度  +  花瓣長度訓練集準確率: 100.0000%測試集準確率: 95.5556%特征: 花萼寬度  +  花瓣寬度訓練集準確率: 98.0952%測試集準確率: 93.3333%特征: 花瓣長度  +  花瓣寬度訓練集準確率: 99.0476%測試集準確率: 97.7778%

輸出的分類結果圖像如下:

在這里插入圖片描述

3.2 GBDT & XGBoost

分別使用XGBoost、GBDT、隨機森林、邏輯回歸對鳶尾花數據進行分類,實現代碼如下:

import numpy as np
import pandas as pd
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegressionCV
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.metrics import accuracy_scoreif __name__ == "__main__":# 讀取數據path = 'iris.data'    				   data = pd.read_csv(path, header=None)   # 數據處理:如果是字符型特征,則轉換成數值型x = data[list(range(4))]              # 數值型數據,無需額外轉換操作y = pd.Categorical(data[4]).codes           # 字符型數據,需轉換成數值型# 拆分數據為訓練集(50%)和測試集(50%)x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=1, test_size=50)# 轉換為xgboost中的矩陣格式data_train = xgb.DMatrix(x_train, label=y_train)data_test = xgb.DMatrix(x_test, label=y_test)watch_list = [(data_test, 'eval'), (data_train, 'train')]   # 輸出過程中的錯誤率# 3分類的softmax訓練模型param = {'max_depth': 8, 'eta': 0.8, 'silent': 1, 'objective': 'multi:softmax', 'num_class': 3}# 訓練XGBoostbst = xgb.train(param, data_train, num_boost_round=6, evals=watch_list)# XGBoost預測數據集y_hat = bst.predict(data_test)result = y_test == y_hatprint('測試集正確率:\t', float(np.sum(result)) / len(y_hat))print('END.....\n')# 分別用GBDT、隨機森林和邏輯回歸訓練models = [# GBDT 30棵決策樹,學習率為0.1,樹的最大深度為5('GBDT', GradientBoostingClassifier(n_estimators=30, learning_rate=0.1, max_depth=5)),# 隨機森林 30棵決策樹,特征選擇用gini系數,樹的最大深度為5('RandomForest', RandomForestClassifier(n_estimators=30, criterion='gini', max_depth=5)),('LogisticRegression', LogisticRegressionCV(Cs=np.logspace(1, 100, 100), cv=3))]for name, model in models:model.fit(x_train, y_train)print(name, '訓練集正確率:', accuracy_score(y_train, model.predict(x_train)))print(name, '測試集正確率:', accuracy_score(y_test, model.predict(x_test)))

輸出結果如下:

[0]	eval-merror:0.02	train-merror:0.02
[1]	eval-merror:0.02	train-merror:0.02
[2]	eval-merror:0.02	train-merror:0.02
[3]	eval-merror:0.04	train-merror:0.01
[4]	eval-merror:0.04	train-merror:0.01
[5]	eval-merror:0.04	train-merror:0.01
測試集正確率:	 0.96
END.....GBDT 訓練集正確率: 1.0
GBDT 測試集正確率: 0.96
RandomForest 訓練集正確率: 1.0
RandomForest 測試集正確率: 0.96
LogisticRegression 訓練集正確率: 0.97
LogisticRegression 測試集正確率: 0.92

4.參考學習的書目及論文

  1. 機器學習算法視頻 - 鄒博
  2. 《機器學習實戰》第7章 利用AdaBoost元算法提高分類性能
  3. 《機器學習之路》第2章 2.6 模型融合
  4. 《機器學習 - 周志華》第8章 集成學習
  5. 《統計學習方法》第8章 提升方法
  6. 《基于Boosting的集成樹算法研究與分析》 連克強 2019 碩士論文 第3章 Boosting 集成方法
  7. 《基于AdaBoost算法的車載CAN總線報文異常檢測》王成龍 2019 碩士論文 第4章 基于AdaBoost算法的CAN總線報文異常檢測

=文檔信息=
本學習筆記由博主整理編輯,僅供非商用學習交流使用
由于水平有限,錯誤和紕漏之處在所難免,歡迎大家交流指正
如本文涉及侵權,請隨時留言博主,必妥善處置
版權聲明:非商用自由轉載-保持署名-注明出處
署名(BY) :zhudj
文章出處:https://zhudj.blog.csdn.net/

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/196852.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息