1. 程式人生 > 其它 >Python例項介紹正則化貪心森林演算法(附程式碼)

Python例項介紹正則化貪心森林演算法(附程式碼)

作者:Ankit Chaoudhary

翻譯:笪潔瓊

校對:樑傅淇

通過本文與大家討論一個被稱為正則化的貪心森林演算法。

引言

作為一名參與多個機器學習競賽資料科學家,我總是在尋找“尚未流行”的演算法。我是這樣定義這些演算法的:它們本身最終不會成為競賽裡的贏家,但是它們會給的預測帶來不同。

為什麼我對這些演算法感興趣?

關鍵在於“它們本身”。這些演算法能夠用在整合模型之中,來獲取超過大多數流行的梯度提升演算法(XGBoost,LightGBM等等)的額外優勢。

這篇文章討論的是一種被稱為正則化的貪心森林(RGF)的演算法。它在大量的資料集裡的表現都和Boosting演算法相當(如果沒有優於它們的話)。它們產生更少的預測,並且在與其他樹提升模型整合時表現更好。

目錄

1. 正則化貪心森林演算法vs. 梯度提升

  • 權重優化
  • 樹的大小
  • 模型大小

2. 使用Python實現正則化貪心演算法

正則化貪心森林演算法(RGF) vs. 梯度提升

在Boosting演算法之中,每個分類器/迴歸因子都使用資料進行訓練,同時也會將之前分類器和迴歸因子的成功經驗納入考量。每一次訓練之後,都會重新分配權重。被錯誤分類的資料所佔的權重將會上升,用以突出最困難的案例。這樣,後續的學習器將會專注於這些資料。

但是,Boosting方法只是把決策樹基學習器視為一個黑盒子,並沒有利用樹結構本身。從某種意義上說,在每次迭代中,Boosting都會對模型進行部分糾正。

相比之下,正則化貪心森林演算法(RGF)執行兩個步驟:

找出對目前的森林可進行的結構上的一步改造,以使得新森林損失(例如,最小二乘或對數損失)最小化。

調整整個森林的葉子重量,使損失函式最小化。

尋找最優結構變化:

1.為了計算效率,在搜尋新的森林時只執行兩種操作 :

  • 要分割一個現有的葉節點,
  • 開始生長新樹(即:在森林裡增加上一個新的樹樁)。

2.通過不斷地評估所有可能的結構變化下最大的損失減少量,直到所有現有的葉子權重都確定後,搜尋結束。

3.搜尋整個森林是很昂貴的(通常情況下,這是實際應用的情況)。

因此,搜尋被限制在最近建立的“t”棵樹中,預設選項是t=1。

讓我用一個例子來解釋這個問題。

圖3顯示在與圖2相同的階段,我們可以考慮將一個葉子節點分裂成兩個標記為X的節點,或者生出一棵新樹T4。


權重優化

引數可以用作指定損失函式和權重優化的間隔。在每增加100個新葉子節點時校正一次權重的效果最好,因此k=100通常被用作正則化貪心森林模型訓練時的預設引數。

如果k很大的話,那麼跟最後才做一次權重更新是一樣的;如果k很小的話(例如k=1的情況下),訓練將會非常緩慢。

正則化

對於這個演算法來說,對損失函式明確的正則化非常重要,因為它很快就會過擬合。

在森林生長過程和權重優化過程中,可能有不同的L2正則化引數。

正則化有三種方法:

一種是對僅包含葉子的模型的L2正則化,在這種模型中,正則化罰項G(F)是:

另外兩種被稱為最小懲罰,它們對每棵樹的正則化罰項都是這樣的形式:

γ > 1對更深的節點(對應於更復雜的函式)進行了更嚴厲的懲罰。可以通過λ 或γ來對正則化程度進行調整。在森林生長的過程和權重調整的過程中,有可能會使用不同的L2正則化引數。

樹的大小

正則化貪心森林演算法需要樹的大小引數(例如,樹的數量,最大深度),在梯度提升決策樹中需要。

對於正則化貪心森林演算法而言,樹的大小是由正則化損失最小化的結果所決定的。我們需要定義的是森林中的最大葉子數和正則化引數(L1和L2)。

模型大小

由於正則化貪心森林演算法在模型和森林上執行充分的優化措施,因此和需要低學習率/收縮率以及更多子模型的Boosting演算法相比,它可以使用更簡單的模型獲得良好的結果。

使用Python實現正則化貪心森林演算法

最初正則化貪心森林演算法來進行二分類和迴歸是在C++中實現的,由初始研究論文作者Rie Johnson和Tong Zhang完成;而對該演算法最廣為流行的、支援多分類的封裝是在MLWave工作的基礎上,由fukatani所完成的。

超引數

我們來談談影響模型準確性、訓練速度或者同時影響兩者的最重要的引數:

max_leaf:當森林中的葉節點數達到此值時,訓練將終止。它應該足夠大,以便在訓練點可以獲得一個好的模型,而較小的值可以縮短訓練時間。

  • loss:損失函式
  • LS:平方損失(p-y)^ 2/2,
  • Expo:指數損失exp(py)
  • Log:對數損失 log(1 + exp(py))

演算法:

RGF:在僅包含樹葉的模型上進行L2正則化的正則化貪心森林演算法

RGF opt:最小懲罰的正則化貪心森林演算法

RGF Sib:最小懲罰、同層級零和約束的正則化貪心森林演算法

reg_depth:必須小於1。用於演算法為RGF opt和RGF Sib的情況。更大的值意味著對更深的節點執行更嚴厲的懲罰

l2:用於控制l2正則化的程度。對演算法的表現至關重要,而恰當的取值依賴於資料。1、0.1或0.01通常會產生良好表現,體現在指數損失、對數損失上,一些資料需要更小的值,比如1e-10或1e-20。

sl2:在森林生長過程中,覆蓋L2規則化引數λ。也就是說,如果設定了具體的引數,那麼權重優化的過程就會使用λ,而森林生長的過程則會使用λg;如果省略該引數的話,整個過程中都會使用λ而不會覆蓋它。在某些資料上,λ/100表現良好。

test_interval:正則化貪心森林演算法在指定的間隔(interval)和訓練結束時會對所有樹的所有葉子的權重進行優化。

由於這個原因,如果我們儲存了250棵樹的模型,那麼這250棵樹只能用於測試250棵樹的附加模型。如果我們在獲得“k”樹的時候停止訓練,分配給這“k”棵樹的權重將與500棵樹的模型前“k”棵樹的權重完全不同。

如果test_interval=500,那麼每500個節點被新增到森林的時候,都會模擬一次訓練結束,這個模型會被測試或儲存以備後續測試。

normalize:如果(開啟這個引數),訓練目標就會被標準化以使得平均數為零。

使用Python裝飾器進行訓練和評估

讓我們嘗試使用正則化貪心森林演算法來解決Big Mart銷售預測問題。資料集可以從此連結下載。在這篇文章中,我已經引入了某些預處理步驟。

import pandas as pd
import numpy as np
#Read files:
train =pd.read_csv("Train_UWu5bXk.csv")
test =pd.read_csv("Test_u94Q5KV.csv")
train['source']='train'
test['source']='test'
data = pd.concat([train,test],ignore_index=True)
#Filter categorical variables
categorical_columns = [x for x indata.dtypes.index if data.dtypes[x]=='object']
#Exclude ID cols and source:
categorical_columns = [x for x in categorical_columnsif x not in ['Item_Identifier','Outlet_Identifier','source']]
#Get the first two characters of ID:
data['Item_Type_Combined'] =data['Item_Identifier'].apply(lambda x: x[0:2])
#Rename them to more intuitive categories:
data['Item_Type_Combined'] =data['Item_Type_Combined'].map({'FD':'Food',
 'NC':'Non-Consumable',
 'DR':'Drinks'})
#Years
data['Outlet_Years'] = 2013 -data['Outlet_Establishment_Year']
#Change categories of low fat:
data['Item_Fat_Content'] = data['Item_Fat_Content'].replace({'LF':'LowFat',
 'reg':'Regular',
 'lowfat':'Low Fat'})
#Mark non-consumables as separate categoryin low_fat:
data.loc[data['Item_Type_Combined']=="Non-Consumable",'Item_Fat_Content']= "Non-Edible"
#Fill missing values by a very largenegative val
data.fillna(-9999,inplace = True)
#Import library:
from sklearn.preprocessing importLabelEncoder
le = LabelEncoder()
#New variable for outlet
data['Outlet'] =le.fit_transform(data['Outlet_Identifier'])
var_mod = ['Item_Fat_Content','Outlet_Location_Type','Outlet_Size','Item_Type_Combined','Outlet_Type','Outlet']
le = LabelEncoder()
for i in var_mod:
 data[i] =le.fit_transform(data[i].astype(str))
train_new =train.drop(['Item_Identifier','Outlet_Identifier','Item_Outlet_Sales'],axis=1)
test_new =test.drop(['Item_Identifier','Outlet_Identifier'],axis=1)
y_all = train['Item_Outlet_Sales']

Once we have the pre-processed the storeddata, we can then import RGF using the following command:

一旦我們已經預處理儲存的資料,我們就可以使用以下程式碼匯入正則化貪心森林演算法。

from rgf.sklearn import RGFRegressor
from sklearn.model_selection importGridSearchCV

需要設定的最重要的兩個引數時葉子最大數和L2正則化。我們可以使用網格搜尋來找到具有經交叉驗證的最優均方誤差的引數。

parameters ={'max_leaf':[1000,1200,1300,1400,1500,1600,1700,1800,1900,2000],
              'l2':[0.1,0.2,0.3],
              'min_samples_leaf':[5,10]}
clf = GridSearchCV(estimator=rgf,
                   param_grid=parameters,
                  scoring='neg_mean_squared_error',
                   n_jobs = -1,
                   cv = 3)

看起來我們使用太多的葉子構造出一個複雜的模型了,高懲罰項需要匹配更小的max_Leaf,讓我們使用更小的max_leaf來做網格搜尋。

parameters ={'max_leaf':[100,200,300,400,500,800,900,1000],
 'algorithm':("RGF_Sib","RGF"),
 'l2':[0.1,0.2,0.3],
 'min_samples_leaf':[5,10]}

看起來這些引數是最合適的。在公共排行榜上,這些引數的均方根誤差得分是1146。

後記

正則化貪心森林演算法只是和梯度提升演算法類似的另一種樹整合技術,能夠有效地應用於非線性關係建模。這個庫的相關文件可以在這裡連結中找到。Alpha版本包含多核支援的快速正則化貪心演算法。這個演算法還有其他幾個引數可以除錯。歡迎在評論中告訴我這個演算法是如何為你解決問題的。

原文連結:https://www.analyticsvidhya.com/blog/2018/02/introductory-guide-regularized-greedy-forests-rgf-python/