淺談 Tree model 的 feature importance

本文想回答的問題:

Hyades Lai
11 min readJul 31, 2022
  1. 不同套件中,使用了哪些 feature importance 與其原理
  2. 各種 feature importance 有什麼優缺點與限制
  • 關鍵字: feature importance, lightGBM, random forest, gradient boosting decision tree
  • 先備知識: decision tree 的基本概念 、 entropy 、 gini-index

0. 前言:

在處理傳統表格式的資料時, tree-based model 仍舊是目前 Machine learning 演算法的主流…吧?而 feature importance 則是訓練完一定會拿來看的重要資訊。所以我在當面試官時,常常會問這樣的問題:「tree model 的 feature importance 如何計算?如何解讀?」一問就知道面試者對 tree model 的理論理解程度,以及是否有真的拿資料 fit 過的實務經驗。

但 feature importance 到底怎麼算的 ? 在 scikit-learn, lightGBM, XGBoost 三個常用套件中,各自有不同的作法,今天就來一次講清楚,當一個有溫度的套件俠^ ^。

1. 不同套件支援的 feature importance:

直接上表格給大家參考

各種套件的 feature importance

2. Scikit-learn 支援的 feature importance:

scikit-learn 的 random forest 與 GBDT 有兩種 feature importance 可以使用,分別為 impurity-based feature importance 與 permutation importance。

a. impurity-based feature importance

一句話解釋的話,就是計算每個特徵對於 loss 下降的貢獻。但貢獻度要如何計算?

tree model 都是透過特徵將資料分組 (切分枝),達到降低 entropy (或 gini index) 的目的。因此,以單顆 decision tree 來說,我們可以去找樹上每一個分枝,看他用的是哪個特徵,以及切了之後的 entropy 變化,就可以當作該特徵在這個節點上對模型的貢獻。

某特徵在某節點的貢獻 = 切之前節點的_entropy — 切之後的兩個節點的_entropy_加總

接著,找出所有節點,把不同特徵的貢獻個別累加起來,就可以算出一棵樹中,每個特徵的貢獻度,即是 feature importance。不過,每個節點資料的數量都不相同,直接拿每個節點上 entropy 相減的值來比其實不太公平。所以 scikit learn 的做法其實是這樣的:

importance_data[node.feature] += (
node.weighted_n_node_samples * node.impurity —
left.weighted_n_node_samples * left.impurity —
right.weighted_n_node_samples * right.impurity
)

(source: 程式碼)

簡單來說,就是乘上節點上的資料數量,一個權重的概念。

這邊隨便舉個例子。參考下圖,第二層左邊的藍色節點,用 sepal_length 作爲切點後將資料切為兩堆,各有 1 個與 36 個樣本,且可以完美分類。該節點上 sepal_length 的 feature importance 為:

(0.053 * 37) - (0 * 1) - (0 * 36) = 1.961
decision tree 範例(source: https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0258658)

算出每顆樹的 feature importance 後,因為我們是 random forest 嘛,接著就把每顆樹上的 feature importance 取平均,就可以得到最終的 feature importance 值。

附帶一提, scikit-learn 在計算 feature importance 過程中,共經過兩次的標準化 (normalize),第一次是在 decision tree 時,讓每顆樹的所有特徵 feature importance 的加總是一,第二次則是在 random forest 中,將每個特徵在每顆樹中 importance 取平均,使得最後輸出的 feature importance 加總為一。

impurity-based feature importance 的限制

其實官方的範例都講得很清楚了,可以直接看。總結一下, impurity-based 算出來的 feature importance 只能反應訓練資料上的重要性,因而易受 overfitting 影響。另外,它會過度放大 high cardinality 特徵的重要性 (一個二元特徵切過之後,其下游分枝就不能再切了,但連續型特徵卻可以一直切一直爽)。

b. permutation importance

要理解其概念,可以試想下面情境:我們有了一個訓練好的模型後,接著就會去評估(evaluate)模型在訓練或測試集上的準確度。假設今天有個特徵很重要,我們卻耍白目把它在資料集上的數值打亂(random shuffle),那理論上模型的預測結果應該會因此大幅失準。

permutation importance 的概念大概就是這樣,針對每個特徵,我們依序隨機打散其中的數值,並計算模型準確度,而準確度下降的幅度即代表特徵的重要性。如果一個特徵被打亂後,準確度根本不受影響,那合理判斷這個特徵根本可有可無。

雖然上面都說模型準確度,但 permutation importance 其實可以吃不同的指標(recall, F1 score 等等),也能自定義函數作為指標。另外也可以在不同的資料集上計算 permutation_importance ,也就是說我們可以在訓練集上,也可以在測試集上做。另外, scikit-learn 實作的 permutation importance 也不限定於 tree-model 上,只要是分類器都可以使用!

那什麼時候可考慮 permutation importance 呢? 首先,他可以在 validation set 上做,減少了 biased 的問題,也沒有 impurity-based 方法會偏好 high cardinality features 的問題。

缺點呢,他要跑很久!因為要對每個 feature shuffle n 次,所以若有 k 個 feature,就要做 n * k 次的預測。另外,若兩個特徵相關性很高,也會使兩個特徵的 importance 都不高,類似迴歸的共線性問題。

此外,自己覺得 permutation importance 的數值滿難解釋的。它的數值呈現的是每個特徵被打亂後,對應的指標下降數值,以準確度來說,一個掉 5% ,一個掉 2% ,怎樣算多?看起來實在沒什麼感覺。而且,如果用不同參數訓練出兩個模型,在不同準確度下,其數值也很難拿來比較。另外,要用這個東西,還要試著跟業務端解釋其概念,他們會買單嗎?

相較之下 impurity-based 的概念很好理解,又可以換算成比例,可說是非常親切。而且大家都愛用,業務端大概也有相關經驗,就不用多費唇舌解釋一遍。

特徵 A 可以解釋 60% 的模型預測力!業務端:哦哦哦哦哦!!

3. LightGBM 支援的 feature importance

lightGBM 是我個人最常使用的 boosting tree 演算法,它實作了 gain 與 split-count 兩種方法:

a. gain:

基本上與 impurity-based feature importances 概念相同,只是 lightGBM 與 XGBoost 都能吃客製化的 objective function,所以實際上要看 objective function 長怎樣,但基本上就是看 objective function 減少的幅度。另外,注意 lightGBM 沒有做標準化,所以 feature importance 加總不為 1。

b. split count:

計算特徵被選為切點的次數,簡單直白。是預設的 feature importance 算法。一樣沒有做標準化,所以 feature importance 加總不為 1。根據自己做的實驗, split 比 gain 更容易受噪音影響(參考最下面的附錄)。

c. scikit-learn permutation importance:

What? 眾所皆知,lightGBM 有實作 scikit-learn API,所以可以用這種方式直接去接 scikit-learn 寫好的功能。

4. XGBoost 支援的 feature importance

xgboost 有五種給你選,直接上表格。

XGBoost 的 feature importance

gain 與 weight 基本上與 lightGBM 相同,但是多了一個 cover:

a. cover:

cover 代表特徵作為切點時該節點的資料量。 decision tree 在越下游的分枝,其資料量會越來越少,直覺來想當然是越上游的切點越重要,所以一個特徵越常在上游被當切點,其 cover 就會越大。要注意的是,同一棵樹中特徵可能被重複拿來當切點,可能因此造成該特徵的平均 cover 變小(weight 很大,但 cover 很小)。

除了平均 ,xgboost 還很友善的提供加總的 gain 與 cover 供大家參考(不過其實 total_gain 就是 weight * gain 啦)。

XGBoost 給你五種選擇,不過這些 feature importance 算出來通常不太相同,有時反而讓人眼花撩亂。

下面用 scikit-learn 的 wine dataset 來比較各種 feature importance , 因為 feature importance 通常是拿來做特徵篩選,這邊就直接比排名 (rank),並計算 rank_avg ,代表五種 feature importance 的 rank 平均。 lgb_rank 則是我額外 train 了一個 lightGBM 模型,它的 gain importance (rank)。

可以發現各種 importance 變動不小,建議大家可以多加比較再做判斷,另外算出來的 importance 與 lightGBM 也差滿多的。

比較 XGBoost 的 feature importance

5. 結語

這次簡介了不同套件實作的 feature importance 。 feature importance 除了用來解釋模型,也很常用於特徵篩選(過濾掉不需要的特徵),因此,我也針對噪音對 feature importance 跑了一些實驗,就留到下次(哪次?)分享囉^^。

附錄:比較噪音對 gain 與 split 的影響:

這邊我針對兩種方法,做了一個簡單的實驗:在 wine dataset 中加入不同數量的噪音變數(random normal),看看兩種方法受噪音影響的程度。

參考下表, index 代表加入的噪音數量(原始有 13 個特徵, index 13 代表加入 13 個噪音變數,所以總共有 26 個變數建模),對應數值則為建模後這些噪音變數的 feature importance 加總。這邊都經過 normalize ,所以假設數值為 0.2 ,即代表噪音變數分走了 20% 的 feature importance,以 gain 來解釋的話,代表 20% 的 loss 下降都是透過噪音變數亂切的結果。

可以發現, 使用 LightGBM 時, split 明顯比 gain 易受噪音影響。而比較 random forest 與 boosting tree,又可以發現 random forest 較易受噪音影響。這個結果也告訴我們,如果你的模型中存在很多噪音或不重要的變數,的確是會影響模型準確度,必須要小心的進行特徵篩選,或設定相關的超參數(例如樹的深度、隨機抽取特徵的比例等)。

--

--

Hyades Lai
Hyades Lai

Written by Hyades Lai

為了紀錄知識與生活,開始寫 medium 啦!文筆不好請見諒qq