關於決策樹,想必大部分人都已經耳熟能詳了,這是一種用來預測行為的樹狀分叉結構。本文主要想總結一下最常用的決策樹生成算法
構造的原則
熟悉決策樹的你一定記得,決策樹每個非葉子結點對應的其實是一個屬性。比方說,想判斷一個男生是不是 gay,我們首先需要判斷他的性別是不是男的,是的話繼續判斷他的性取向,之后繼續判斷他的其他行為……這里的「性別」,「性取向」就是屬性,而決策樹的生成其實是依次挑選這些屬性組成自己的節點,到最終可以明確得出結論的時候(也就是葉子結點),整棵樹便生成了。所以,我們的目標就是按照某種方法依次挑選出這些屬性。
我們挑選的原則是:每次選出這個屬性后,可以最大限度地減小分類的可能性。回到 gay 這個問題,如果擺在我們眼前的屬性有:「性取向」,「是否喜歡日漫」,「是否長發披肩」,那么,選擇「性取向」這個屬性,對我們之后的判斷,幫助無疑是最大的。因為得知「性取向」之后,基本也就得到結論了。所以,對這個例子而言,「性取向」是我們優先挑選的屬性。
那么,我們如何衡量這種幫助的大小呢?請往下看👇。
ID3 算法
ID3 算法歸根到底就是提出一種合理的選擇屬性的方法。
(注意,決策樹是一種知識學習算法,只有從眾多樣本中才能得出哪個屬性最好,所以,構造決策樹的前提是有大量的樣本可供學習)
下面,為了方便講解,我們需要引入信息學中「熵」的概念🙈。
熵(entropy)
第一次接觸熵的概念是在學高中化學的時候,課本告訴我們:一堆整齊有序的分子,最終都會演變成一個混亂復雜的群體,也就是,這個系統的熵值會逐漸變大。因此,簡單整齊的系統,熵越小,越混亂的系統,熵越大。接下來,讓我們回顧一下分子的布朗運動……
開個玩笑啦🤗。
同化學里的熵一樣,信息學的熵也有類似的作用。在信息學中,如果熵越大,證明掌握的信息越少,事情越不確定。看到這里,你有沒有覺得,熵的定義和我們前面提出的挑選屬性的原則有點類似。是的,ID3 的精髓也就是在這,它通過計算屬性的熵,來得出一個屬性對事情的確定性能產生多大的影響,從而選出最好的屬性。
那么熵該如何度量呢?
著名的信息論創始人「香農」提出一個度量熵的方法:假設有一堆樣本 D,那么 D 的熵可以這樣計算:
其中,pipi 表示第 i 個類別在整個樣本中出現的概率。 舉個例子吧。假設我們投擲 10 枚硬幣,其中,5 枚正面朝上,5 枚正面朝下,那么我們總共得到 10 個樣本,這堆樣本的熵為:
H(D)=−(510log2510+510log2510)=1H(D)=−(510log2510+510log2510)=1
反之,如果只有 1 枚硬幣正面朝上,9 枚硬幣正面朝下,那么熵為:
H(D)=−(110log2110+910log2910)=0.469H(D)=−(110log2110+910log2910)=0.469
如果全部硬幣正面朝上,你應該可以算出來,熵為 0。 舉這個例子是想說明:當熵的值越大的時候,事情會更加難以確定,如果你知道 10 次實驗中,正面朝上的為 5 次,朝下的也為 5 次,那么下一次哪一面朝上,你是不是很難確定。相反,如果熵的值越小,事情就越明朗。當熵為 0,也就是 10 次都正面朝上的時候,下一次你會不會覺得正面朝上的概率會大很多(請忘掉你的傳統思維,我沒說這是一枚正常的硬幣)。
選擇屬性
好了,有了熵的概念以及度量方法,下面我們可以正式地走一遍 ID3 的流程了。 同樣的,假設我們有一堆數據 D,我們先計算出這堆樣本的熵H(D)H(D),接下來,我們要對每個屬性對信息的價值進行評估。假設我們挑選出 A 屬性,那么,根據 A 屬性的類別,我們可以把這堆樣本分成幾個子樣本,每個子樣本都對應 A 屬性中的一類。我們繼續按照熵的定義計算這幾個子樣本的熵,由於它們的熵是挑選出 A 后剩下的,因此我們定義為:
這個公式其實和之前的是一個道理,我們通過 A 將 D 分成幾個子集 DjDj,這個時候,我們仍然需要計算這堆樣本的熵,因此,先分別計算出每個 DjDj 的熵,然后乘以這個 DjDj 樣本占所有數據集的比重,最后將全部子集的熵加起來即可。前面說了,這個熵是挑選 A 后剩下的,那么很自然的,我們想知道 A 到底幫助消減了多少熵,於是,我們定義最后一個公式,即信息增益:
之前對熵的說明告訴我們,熵越大,信息越少,反之,信息越多。Gain(A)Gain(A)其實就是 A 對信息的確定作用,它是我們選出 A 屬性后,信息的混亂程度減少的量。 好了,到這里,ID3 的關鍵部分已經講完了。其實,每次挑選屬性的時候,我們都是計算出所有屬性的信息增益,選擇最大的作為分裂的屬性,將樣本分成幾個子集,然后,對每個子集,繼續選出最好的屬性,繼續分裂,直到所有樣本都屬於同一類為止(都是 gay 或者都是正面朝上)
舉個例子
下面用的這個例子摘自文末的參考博客算法雜貨鋪——分類算法之決策樹(Decision tree)。 假設我們有以下這堆 SNS 社區的資料,我們想確定一個賬號是否是真實。
其中,s 、m 和 l 分別表示小、中和大。 我們先計算出這堆樣本的熵:
H(D)=−(0.7∗log20.7+0.3∗log20.3)=0.879H(D)=−(0.7∗log20.7+0.3∗log20.3)=0.879
然后,我們計算每個屬性的信息增益:
同樣的道理:
經過比較,我們發現 F 的增益最高,於是選出 F 作為節點,構造出如下決策樹:
注意,F 屬性有三個類別,對應三個分支,其中,l 和 m 兩個分支的數據都是同一類(賬號真實性要么都是 no 要么都是 yes),因此這兩個分支沒法再分了,而 s 屬性的分支,剩下一個四個樣本的子集,我們之后的任務,是對這個子集繼續分割,直到沒法再分為止。 接下來要考慮 L 和 H 屬性,同樣的道理,我們繼續計算增益,只不過這一次我們是在這個子集上計算。
這一次,我們選擇 L 屬性進行分裂:
剩下的只有 H 屬性,因此最后加上 H 節點。由於剩下的樣本中只有 H=no 的數據,因此 yes 節點的數據沒法判斷(這種情況在數據量很大的時候一般不會遇到,因為數據量越大,涵蓋的情況會更多),而剩下的兩個樣本存在 yes 和 no 兩種情況,因此 no 節點往下也只能隨機選擇一種類別進行判斷(這種情況一般是根據進行「多數表決」,即選擇出現次數最多的類別作為最終類別,在數據量很大的情況下,出現次數一樣多的情況幾乎不會發生)。
屬性為連續值的情況
上面給出的例子中,樣本的特征都是離散值(e.g. s,m,l),而 ID3 算法確實也只對離散值起作用。如果遇到特征為連續值的情況,一般需要先將其離散化,例如:可以選定幾個閾值a1a1,a2a2,a3a3(a1a1<a2a2<a3a3),根據這些閾值,將樣本特征分為四類:f<a1f<a1,a1<f<a2a1<f<a2,a2<f<a3a2<f<a3,f>a3f>a3。然后,便可以按照一般的思路構建決策樹了。
C4.5算法
C4.5 算法主要對 ID3 進行了改進,用「增益率」來衡量屬性的信息增益效率。算法中定義了「分裂信息」:
SplitInfo(A)=−∑vj=1|Dj||D|log2|Dj||D|SplitInfo(A)=−∑j=1v|Dj||D|log2|Dj||D|
然后,通過該信息,定義增益率公式為:
GainRatio(A)=Gain(A)SplitInfo(A)GainRatio(A)=Gain(A)SplitInfo(A)。
C4.5選擇具有最大「增益率」的屬性作為分裂屬性,而其余步驟,和 ID3 完全一致。
CART
CART 指的是分類回歸樹(Classification And Regression Tree)。顧名思義,這是一棵既可以用於分類,也可以用於回歸的樹。不同於上面的兩種樹,CART 每一個非葉子節點只有有兩個分支,所以 CART 是一棵二叉樹。下面我們按照分類和回歸兩個用途分別介紹 CART 的構建。
分類樹的生成
CART 在選擇分裂節點的時候,用「基尼指數(Gini)」來挑選最合適的特征進行分裂。所謂「基尼指數」,其實和 ID3 中熵的作用類似。假設我們有一個數據集 D,其中包含 N 個類別,那么「基尼指數」為:
其中,P_jP_j表示每個類別出現的概率。 同熵一樣,「基尼系數」的值越小,樣本越純,分類越容易。 我們根據 Gini 選擇一個最合適的特征作為 CART 的分裂節點。注意,與 ID3 不同的是,如果特征的類別超過兩類,CART 不會根據特征的所有類別分出子樹,而是選擇其中的一個類別,根據是否屬於這個類別分成兩棵子樹。假設 A 特征中有三種類別(s、m、l),我們需要分別按照「是否屬於 s 」、「是否屬於 m 」、「是否屬於 l 」將樣本分為兩類,根據 Gini 值最小的情況挑選出分裂的特征類別(比如:按照「是否屬於 s 」將樣本分為兩類)。對於分裂后的樣本的 Gini 值,我們按照如下公式計算:
其中,k 表示分裂的子集數目。事實上,在 CART 中,k 的取值為 2。 然后,選擇 Gini(D,A)Gini(D,A) 最小的特征 A 作為分裂的特征即可。 這里還需要注意一點,在 ID3 中,已經選擇過的特征是沒法在之后的節點分裂中被選上的,即每個特征只能被挑選一次。但 CART 沒有這種限制,每次都是將所有特征放在一起,通過「基尼系數」選出最好的,哪怕這個特征已經在之前被挑選過了。有學者認為,ID3 這種只挑選一次的做法容易導致 overfitting 問題,所以相信 CART 的這種做法能使模型的泛化能力更強。 CART 按照這樣的方式,不斷挑選特征分裂子數,直到剩下的子樣本都屬於同一類別,或者特征沒法再分為止,這個停止條件可以參考 ID3 ,這里就不再舉例說明了。
回歸樹的生成
回歸樹相對來說比較難理解,我自己也花了較長時間咀嚼,其中還有一些不明白的地方,日后有了新的想法會繼續補充修正。 為了更好地說明回歸樹的構建流程,我們假設有以下訓練數據:
| XX | YY |
|---|---|
| (x11x11,x12x12,x13x13) | y1y1 |
| (x21x21,x22x22,x23x23) | y2y2 |
| (x31x31,x32x32,x33x33) | y3y3 |
上面的表中一共有三個樣本,每個樣本有三個特征,為了解說方便,我們分別命名為特征 1、特征 2、特征 3(比如:x11x11,x21x21,x31x31 就屬於特征 1)。 <br\>與分類樹類似,回歸樹每次也需要從樣本 XX選取特征,並根據特征值將數據集切分為兩份。在分類樹中,我們是用「熵」或者「基尼系數」來確定分裂特征,但回歸樹中的 XX 和 YY 都是連續值,因此需要采用新的特征挑選方式。 <br\>首先,我們先簡單明確一下回歸樹的分裂原則:每次分裂后,每個樣本集合內的數據要盡可能相似。這一點與分類樹是不謀而合的,盡可能相似就是說,類別上要盡量統一。而為了做到這一點,最常用的方法便是「最小二乘法」。下面,我們定義「最小二乘法」的代價函數(可以簡單認為就是回歸樹的信息熵):
其中,
- jj 表示樣本的特征,上面例子中的 x11x11,x21x21 就屬於同一個特征。
- ss 表示特征的分裂值,如果 s=x11s=x11,就表示所有樣本以特征 1 為基准,按照 >=x11>=x11 和 <x11<x11分為兩類。
- R1R1 表示分裂后的第一個樣本集,R2R2 表示分裂后的第二個樣本集。
- c1c1、c2c2 分別表示 R1R1、R2R2 的固定輸出值。簡單點說,它們是最能代表 R1R1,R2R2 內所有樣本的值。
如果我們進一步對 ∑xi ∈ R1 (j,s)(yi−c1)2∑xi ∈ R1 (j,s)(yi−c1)2 求導的話,就會發現,要使這個式子最小,cc 必須取 yiyi的平均值( yi∈Ryi∈R )。因此,我們對原公式進行簡化:
其中,c1c1、c2c2 分別是 R1R1、R2R2 兩個集合中 yy 的平均值。
希望上面對符號的說明能減少讀者對公式的畏懼🤒。
這個公式的做法其實很簡單,就是枚舉所有特征以及特征值,挑選出最好的特征以及特征值作為分裂點,將樣本分為兩部分,其中,每一部分內的樣本值 yy 的平方差最小。平方差最小,意味着這個樣本內的數據是最相近的,可以認為屬於同一類。
至此,回歸樹的精髓部分就介紹完了。下面順藤摸瓜講一下回歸樹的構建過程。
最小二乘回歸樹生成算法:
- 依次遍歷每個特征 j,根據所有樣本中特征 j 的取值 s,我們按照上面的公式計算代價函數,這樣便可以得到每對 ( jj,ss ) 的代價函數,我們選擇函數值最小的作為切分點;
- 使用上一步的切分點將數據分為兩份;
- 重復第 1、2 步,直到樣本的平方差小於閾值或樣本數目小於閾值為止。此時,葉子節點的數據就是該樣本空間 RmRm 的平均值 cmcm;
- 根據第 3 步構造的各個樣本空間 RmRm,生成回歸樹。
