當前位置: 首頁>>技術教程>>正文


梯度提升入門介紹(GBM)

梯度提升入門

簡化複雜的算法

動機

雖然大部分Kaggle競賽獲勝者使用各種模型的堆疊/集成,但是作為大部分集成的一部分的一個特定模型是梯度提升(GBM)算法的一些變體。以最新的Kaggle比賽獲勝者為例:邁克爾Jahrer安全司機預測他的解決方案是6個模型的混合。 1個LightGBM(GBM的變體)和5個神經網絡。雖然他的成功歸因於他將半監督學習用於結構化數據,但梯度提升模型也起到了重要作用。

盡管GBM被廣泛使用,但許多從業人員仍將其視為複雜的黑盒(black-box)算法,隻需使用預編譯的(pre-built)庫運行模型即可。這篇文章的目的是為了簡化所謂的複雜算法,並幫助讀者直觀地理解算法。解釋梯度提升算法的簡介版本,並將在最後共享其不同的變體鏈接。我已經采用了基本的DecisionTree代碼 Library (fastai /courses /ml1 /lesson3-rf_foundations.ipynb),最重要的是,我已經構建了自己的簡單版本的基本梯度提升模型。

關於Ensemble,Bagging和Boosting的簡要描述

當我們試圖用任何機器學習技術來預測目標變量時,造成實際值和預測值之間差異的主要原因是噪音(noise),方差(variance)和偏差(bias)。集成(Ensemble)有助於減少這些因素(除了噪音,這是不可避免的錯誤)

Ensemble(集成)隻是匯集了一些預測變量(例如所有預測的均值)以給出最終預測。我們使用集成的原因是,許多不同的預測因素試圖預測相同的目標變量,這將比任何單獨的預測變量表現更好。集成技術進​​一步分為Bagging和Boosting。

  • Bagging是一個簡單的集成技術,我們在其中建立了許多獨立預測因子/模型/學習器,並使用一些模型平均技術將它們結合起來。 (例如加權平均數,多數票或正常平均數)

我們通常為每個模型采用隨機下采樣/bootstrap數據,以便所有模型都彼此略有不同。每個觀察結果出現在所有模型中的概率相同。因為這種技術需要很多不相關的學習器來製作最終模型,所以它通過減少方差(variance)來減少偏差(bias)。Bagging集成的例子是隨機森林模型。

  • Boosting是一種集成技術,其中預測指標不是獨立製定的,而是依次進行的。

這種技術采用了後麵的預測變量從以前的預測變量中學習的邏輯。因此,觀測值在後續模型中出現的概率是不相等的,而最高誤差值出現最多。預測因子可以從一係列模型中選擇,如決策樹,回歸因子,分類器等。因為新的預測因子是從以前的預測因子所犯的錯誤中學習的,所以它需要更少的時間/迭代來接近實際的預測。但是,我們必須謹慎選擇停止標準,否則可能導致過度訓練數據。梯度提升是增強算法的一個例子。

圖。1。Ensembling

圖2。Bagging(獨立型號)&Boosting(提升(順序模型))。參考:

梯度提升算法

梯度提升是一種用於回歸和分類問題的機器學習技術,該技術以弱預測模型(通常為決策樹)的集合的形式產生預測模型。(維基百科定義)

任何監督學習算法的目標是定義一個損失函數並將其最小化。讓我們看看梯度提升算法的數學運算。假設我們將均方誤差(MSE)定義為:

我們希望我們的預測,使我們的損失函數(MSE)最小。通過使用梯度下降並根據學習速率更新我們的預測,我們可以找到MSE最小的值。

因此,我們更新預測使得我們的殘差總和接近於0(或最小值),並且預測值足夠接近實際值。

梯度提升的直觀理解

梯度提升背後的邏輯很簡單,(可以直觀地理解,不使用數學符號)。我希望閱讀這篇文章的人可能會很熟悉simple linear regression模型。

線性回歸的一個基本假設是其殘差之和為0,即殘差應該在零附近隨機擴散。

圖3。隨機正態分布殘差的均值在0附近

現在將這些殘差視為我們的預測模型犯下的錯誤。雖然,樹模型(考慮決策樹作為我們梯度提升的基礎模型)不是基於這樣的假設,但如果我們從邏輯上(而不是統計上)考慮這個假設,我們可能會爭辯說,如果我們能夠看到0左右的殘差模式,我們可以利用該模式來擬合模型。

所以,背後的直覺梯度提升(gradient boosting)算法是重複利用殘差中的模式,並加強一個弱預測模型並使其更好。一旦我們達到殘差沒有任何可模擬模式的階段,我們可以停止建模殘差(否則可能導致過擬合)。在算法上,持續最小化損失函數,使得測試損失達到最小值。

綜上所述,
•我們首先用簡單的模型對數據進行建模並分析數據中的錯誤。
•這些錯誤表示難以用簡單模型擬合的數據點。
•然後對於以後的模型,我們特別關注那些難以擬合的數據點,以使他們正確。
•最後,我們通過給每個預測變量賦予一些權重來組合所有預測變量。

更多背後邏輯介紹參考可能近似正確:在複雜世界中學習和繁榮的自然算法

“這個想法是多次使用弱學習方法來獲得連續的假設,每個假設重新聚焦在之前(很難並且分類錯誤)的例子上。

適合梯度提升模型的步驟

讓我們考慮模擬數據,如下麵的散點圖所示,帶有1個輸入(x)和1個輸出(y)變量。

圖4。模擬數據(x:輸入,y:輸出)

上麵顯示的圖的數據是使用下麵的python代碼生成的:


x = np.arange(0,50)
x = pd.DataFrame({'x':x})

# just random uniform distributions in differnt range

y1 = np.random.uniform(10,15,10)
y2 = np.random.uniform(20,25,10)
y3 = np.random.uniform(0,5,10)
y4 = np.random.uniform(30,32,10)
y5 = np.random.uniform(13,17,10)

y = np.concatenate((y1,y2,y3,y4,y5))
y = y[:,None]

代碼塊1.數據模擬

1.在數據上擬合一個簡單的線性回歸器或決策樹(我在我的代碼中選擇了決策樹) [將x作為輸入,將y作為輸出]


xi = x # initialization of input
yi = y # initialization of target
# x,y --> use where no need to change original y
ei = 0 # initialization of error
n = len(yi)  # number of rows
predf = 0 # initial prediction 0

for i in range(30): # loop will make 30 trees (n_estimators). 
    tree = DecisionTree(xi,yi) # DecisionTree scratch code can be found in shared github/kaggle link. 
                               # It just create a single decision tree with provided min. sample leaf
    tree.find_better_split(0)  # For selected input variable, this splits (n) data so that std. deviation of 
                               # target variable in both splits is minimum as compared to all other splits
    
    r = np.where(xi == tree.split)[0][0]   #  finds index where this best split occurs
    
    left_idx = np.where(xi <= tree.split)[0] # index lhs of split
    right_idx = np.where(xi > tree.split)[0] # index rhs of split

代碼塊2.(步驟1)使用決策樹找到最佳分割(我們的樹的深度為1)

2.計算錯誤殘差。實際目標值,減去預測目標值[e1 = y – y_predicted1]

3.將誤差殘差的新模型擬合為具有相同輸入變量的目標變量[稱之為e1_predicted]

4.將預測殘差添加到先前的預測中
[y_predicted2 = y_predicted1 + e1_predicted]

5.擬合剩餘的殘差模型。即[e2 = y – y_predicted2]並重複步驟2至5,直至開始過擬合或殘差總和恒定。通過持續檢查驗證數據的準確性可以控製過度擬合。


 # predictions by ith decisision tree
    
    predi = np.zeros(n)
    np.put(predi, left_idx, np.repeat(np.mean(yi[left_idx]), r))  # replace left side mean y
    np.put(predi, right_idx, np.repeat(np.mean(yi[right_idx]), n-r))  # right side mean y
    
    predi = predi[:,None]  # make long vector (nx1) in compatible with y
    predf = predf + predi  # final prediction will be previous prediction value + new prediction of residual
    
    ei = y - predf  # needed originl y here as residual always from original y    
    yi = ei # update yi as residual to reloop

代碼塊3.(步驟2到5)計算殘差並更新新的目標變量和新的預測

為了幫助理解底層概念,下麵是從零開始完整實現簡單梯度提升模型的鏈接。[鏈接:漸變提升]

共享代碼是梯度增強的未優化基礎實現。庫中大多數梯度提升模型都經過了優化,並且有許多超參數。

梯度提升樹的可視化

藍點(左)圖是輸入(x)對輸出(y)•紅線(左)顯示由決策樹預測的值•綠點(右)顯示第i次迭代的殘差與輸入(x)•迭代表示順序擬合梯度提升樹的順序

圖5.梯度提升預測的可視化(前4次迭代)

圖6.梯度提升預測的可視化(第18次至第20次迭代)

我們觀察到,在第20次迭代之後,殘差在0附近是隨機分布的(我不是說隨機正態值),我們的預測值非常接近真值。 (在sklearn實現中迭代叫做n_estimators)。這應該是一個很好的停止點或我們的模型開始過度擬合的點。

讓我們看看我們的模型是如何進行第50次迭代的。

圖7.梯度提升預測的可視化(第50次迭代)

我們可以看到,即使在第50次迭代之後,殘差對x的曲線看起來與我們在第20次迭代中看到的相似。但是模型變得越來越複雜,預測對訓練數據過度擬合,並試圖學習每個訓練數據。所以,在第20次迭代停止會更好。

用於繪製所有上述數字的Python代碼片段。


    # plotting after prediction
    xa = np.array(x.x) # column name of x is x 
    order = np.argsort(xa)
    xs = np.array(xa)[order]
    ys = np.array(predf)[order]
    
    #epreds = np.array(epred[:,None])[order]

    f, (ax1, ax2) = plt.subplots(1, 2, sharey=True, figsize = (13,2.5))

    ax1.plot(x,y, 'o')
    ax1.plot(xs, ys, 'r')
    ax1.set_title(f'Prediction (Iteration {i+1})')
    ax1.set_xlabel('x')
    ax1.set_ylabel('y / y_pred')

    ax2.plot(x, ei, 'go')
    ax2.set_title(f'Residuals vs. x (Iteration {i+1})')
    ax2.set_xlabel('x')
    ax2.set_ylabel('Residuals')

代碼塊4.繪製預測和殘差(以第一代碼塊的循環供給)

參考資料

本文由《純淨天空》出品。文章地址: https://vimsky.com/zh-tw/article/3877.html,未經允許,請勿轉載。