週二、週三參加了QCon上海2017|全球軟件開發大會,聽了幾場機器學習相關的 Session,多次提及 GBDT(Gradient Boost Decision Tree),並且在模型演化歷史中,都有很重要或者最重要的地位。
如《Pinterest如何利用機器學習實現兩億月活躍用戶》提到的模型發展歷史,GBDT帶來過巨大的效果提升。
《唯品金融機器學習實踐》中也提到因爲GBDT+LR良好的表達能力和可解釋性成爲他們的最重要模型之一。筆者的工作實踐中,餓了麼的Rank系統中,GBDT+FTRL,也是AUC最高的模型(不是之一,超過深度學習嘗試)。
因爲最近負責使用 GBDT模型做競價排名廣告的點擊率預估(CTR),所以就從頭學習了一下原理並涉獵了一些相關的應用場景,寫一篇文章總結一下,希望可以表述清楚,讀者會有所收穫。
概述
DT-Decision Tree決策樹,GB是Gradient Boosting,是一種學習策略,GBDT的含義就是用Gradient Boosting的策略訓練出來的DT模型。模型的結果是一組迴歸分類樹組合(CART Tree Ensemble): 。其中 學習的是之前 棵樹預測結果的殘差,這種思想就像準備考試前的複習,先做一遍習題冊,然後把做錯的題目挑出來,在做一次,然後把做錯的題目挑出來在做一次,經過反覆多輪訓練,取得最好的成績。
而模型最後的輸出,是一個樣本在各個樹中輸出的結果的和:
假設我們要預測一個人是否會喜歡電腦遊戲,特徵包括年齡,性別是否爲男,是否每天使用電腦。標記(label)爲是否喜歡電腦遊戲,假設訓練出如下模型
該模型又兩棵樹組成, 使用 age < 15 和 is male 作爲內節點,葉子節點是輸出的分數。 使用是否每日使用電腦作爲根節點。假設測試樣本如下:
樣本在兩棵樹中所在的葉節點如下:
最後對某樣本累加它所在的葉子節點的輸出值,例如:
GBDT + LR
單獨的使用GBDT模型,容易出現過擬合,在實際應用中往往使用 GBDT+LR的方式做模型訓練,算法更多細節可以參考 [Practical Lessons from Predicting Clicks on Ads at Facebook]。本文只介紹結論性的做法。
首先根據樣本訓練出GBDT樹,對於每個葉子節點,回溯到根節點都可以得到一組組合特徵,所以用葉子節點的標號可以代表一個新的組合特徵。結合上面的圖,用一個樣本爲例,直觀的表達如下:
其中 0號 組合特徵的含義是:ageLessThan15AndIsMale,該樣本取值 0
其中 1號 組合特徵的含義是:ageLessThan15AndIsNotMale,該樣本取值 1
其中 2號 組合特徵的含義是:ageLargerOrEqualThan15,該樣本取值 0
其中 3號 組合特徵的含義是:useComputerDaily,該樣本取值 0
其中 3號 組合特徵的含義是:notUseComputerDaily,該樣本取值 1
這部分特徵是GBDT生成的組合特徵,再結合LR固有的稀疏特徵,就組成了 GBDT + LR 模型。生成樣本向量階段,樣本首先過GBDT模型,生成組合特徵部分的輸入向量,再結合固有的稀疏特徵向量,組成新的特徵向量,示例如下:
在該例子中,第一行綠顏色是通過 GBDT 模型生成的特徵向量,每個值都代表一個葉子節點的輸出(樣本在某棵樹只在一個葉子節點有輸出),第二行表示 LR 模型的稀疏特徵向量,第三行表示把兩部分特徵向量拼接在一起,組成一個最終的特徵向量,並使用該向量訓練LR模型。
實踐
XGBoost是GBDT最廣爲人知的一個實現。通過使用一定程度的近似,使得求解變得更高效。同時支持分佈式和 GPU 優化,有着廣泛的使用。在實踐中,算法工程師使用 Spark 或者Python 的 XGBoost 庫訓練模型,並保存成文件,線上根據不同的語言採用相應的依賴包,將模型導入,執行決策。Java 中使用 xgboost4j 導入模型,完成特徵變換後,調用 方法,就可以得到當前樣本的預測值。
需要注意的是,xgboost4j 需要鏈接到本地庫,需要自己編譯並打包。首先在本地編譯 xgboost4j,生成平臺相關的本地庫文件,例如 linux 下生的 libxgboost4j.so。然後把這個文件連同xgboost4j 的源代碼一起,發佈成一個新的工程,供線上依賴。
hopeztm7500/xgboost4jL 這個項目是我在實踐中自己編譯 xgboost4j 生成了一個平臺相關xgboost4j 包。不過這個項目僅供參考,因爲平臺相關,所以不一定能被其他人使用。
XGBoost算法原理
這部分內容比較便理論,對於沒有興趣的讀者可以忽略掉。
還記得前文提到的 GBDT 可以用如下公式表示:
優化目標如下:
樣本數量, 表示樣本真實 Label, 是模型輸出,所以前半部分代表模型的損失函數。
表示樹的個數, 表示第 棵樹,形式化的來說它是一個樣本到葉子節點值的映射( ), 是模型複雜度函數。模型越複雜,越容易出現過擬合,所以採用這樣的目標函數,爲了使得最終的模型既有很好的準確度也有不錯的泛化能力。
那麼如何找到一組樹,使得 最小呢。
追加法訓練(Additive Training、Boosting)
核心的思想是,已經訓練好的樹 不再調整。根據目標函數最小原則,新增樹 ,表示如下:
算法初始化
訓練第 棵樹
訓練第二棵樹 ,第 棵不再調整
訓練第 棵樹 ,前面 棵不再調整
假設此時對第 棵樹訓練,則目標函數表示爲
代表 ,因爲前面 棵樹結構不再變化,所以他們的複雜度爲常數。
回顧高等數學中的泰勒展開,它使用一個函數的高階導數,用多項式的形式逼近原始函數。當展開到二階導數的時候公式如下:
利用泰勒展開公式和上文推導的 , 式中, 對應泰勒公式中的 ,而 是一棵待增加的新樹,對應泰勒公式中的 , 對應泰勒公式中的 , 對應泰勒公式中的 。則對 做二階泰勒展開後,得:
其中
即對應泰勒公式中的一階導數 和二階導數 。因爲我們求解的目標是使得 最小的 。當前面 棵樹都已經確定時, 是一個常量,可以省略, 常量也可以省略,簡化得到新的目標函數:
複雜度函數 的引入
假設待訓練的第 棵樹有 個葉子節點,葉子節點的輸出用向量表述爲。那麼 可以表示爲如下形式:
可以理解爲 是樹結構,把樣本 帶到某個葉子節點 ,而這個節點的輸出值是 。例如:
前文提到爲了提高泛化能力,引入複雜度函數,定義複雜度函數的方式也可以有很多選擇,XGBoost中定義如下:
如前述 表示葉節點個數, 是葉子節點 的輸出。例如:
則目標函數可以推導爲如下:
前半部分, , 選取是最直觀的樣本累加視角,我們可以把這個視角換一下。既然每個樣本都會落到一個葉子節點,最外層的累加唯獨可以爲葉節點。定義 ,含義是被映射到第 個葉子節點的樣本,該葉子節點的輸出爲 ,那麼
可以推導出
定義 , ,則上式
假設新樹的結構不變,上式是一個由 個相互獨立的二次函數的累加,所以目標函數最小,等價於求對每個 最小。
回顧對於 當 ,當 對稱軸位置的時候,函數取得最小值,此時最小值爲 。
對 分別取極小值時, 取值如下
舉個例子,假設要求解第一棵 樹的結構如下,各個樣本按照該樹的結構,會被映射到相應的位置,得到每個樣本 對應的 ,就可以求得到對應
, 前文定義爲損失函數 對 的一階導數和二階導數。如果把損失函數定義爲平方損失(Square Loss),則可以得到如下等式:
所以根對於待訓練的新樹而言, 可以根據訓練好的樹預先計算好,枚舉所有可能的新樹的結構,選擇目標函數最小的那棵樹,同時使用 求得該樹每個葉子節點的輸出值,不斷求解下去,就可以訓練出一組最優樹。但是,枚舉所有的樹是不現實的,因爲樣本特徵值可能是連續的,樹的結構可能是無窮的。
貪心法求解樹
在XGBoost使用貪心法求解樹結構,算法描述如下:
- 初始化樹深度 0(即只有一個葉子節點,所有樣本都落在該節點)
- 對於每個葉子節點,嘗試分裂該節點,在分裂後得到的增益定義如下:
該公式定義了,分裂後左右子樹的新增得分減去不分裂時候節點得分,再減去因爲新增一個節點,增加的複雜度。
那麼,如何定義最佳分裂。
- 對於每個葉子節點,枚舉所有的特徵
- 對每個特徵,把映射到該葉節點的樣本按照該特徵值排序
- 使用線性掃描來決定最佳分裂點
- 在所有枚舉的特徵中,選擇最優的分裂
示例如下,假如當前枚舉的特徵是年齡,樣本排序和分裂點如下所示:
在該分裂點計算分裂後的增益:
如果用 表示新增樹的深度,那麼該算法複雜度是 , 代表特徵數, 是對樣本排序複雜度。如果緩存對樣本在各個特徵下對排序結果,算法還可以進一步在時間上優化。
總結算法流程如下:
- 迭代生成樹
- 迭代初始,對每個樣本計算 ,該計算只依賴於訓練好的樹
- 使用 爲目標函數,採用貪心法求得樹
- 新增樹後,模型表示爲 ,實踐中使用
替代上面的迭代公式, 被稱爲步長(steps-size)或收縮率(shrinkage),通常設置爲 0.1 附近的值。設置它的目的在於,不期望模型每一次迭代學習到所有殘差,防止過擬合。
從XGBoost原理部分可以看出,XGBoost的實現中,採用了二階泰勒展開公式展開來近似損失函數,同時在求解樹的過程中,使用了貪心算法,並使用枚舉特徵,樣本按照特徵排序後,採用線性掃描找到最優分裂點。這種實現方法,是對GDBT思想的逼近,但是在工程上確非常有效。
總結
本文首先從應用出發,概括了GBDT的應用場景,介紹瞭如何使用GBDT和GBDT+LR。然後對訓練和上線的工程化給予簡單闡述。然後用最長的篇幅,介紹了 XGBoost原理。希望讀者有所收穫。
傳送門:https://zhuanlan.zhihu.com/p/30339807