決策樹系列(四)——C4.5
如上一篇文章所述,ID3方法主要有幾個缺點:一是採用資訊增益進行資料分裂,準確性不如資訊增益率;二是不能對連續資料進行處理,只能通過連續資料離散化進行處理;三是沒有采用剪枝的策略,決策樹的結構可能會過於複雜,可能會出現過擬合的情況。
C4.5在ID3的基礎上對上述三個方面進行了相應的改進:
a) C4.5對節點進行分裂時採用資訊增益率作為分裂的依據;
b) 能夠對連續資料進行處理;
c) C4.5採用剪枝的策略,對完全生長的決策樹進行剪枝處理,一定程度上降低過擬合的影響。
1.採用資訊增益率作為分裂的依據
資訊增益率的計算公式為:
其中表示資訊增益,表示分裂子節點資料量的資訊增益,計算公式為:
其中m表示節點的數量,Ni表示第i個節點的資料量,N表示父親節點的資料量,說白了,其實是分裂節點的熵。
資訊增益率越大,說明分裂的效果越好。
以一個實際的例子說明C4.5如何通過資訊增益率選擇分裂的屬性:
表1 原始資料表
當天天氣 |
溫度 |
溼度 |
日期 |
逛街 |
晴 |
25 |
50 |
工作日 |
否 |
晴 |
21 |
48 |
工作日 |
是 |
晴 |
18 |
70 |
週末 |
是 |
晴 |
28 |
41 |
週末 |
是 |
陰 |
8 |
65 |
工作日 |
是 |
陰 |
18 |
43 |
工作日 |
否 |
陰 |
24 |
56 |
週末 |
是 |
陰 |
18 |
76 |
週末 |
否 |
雨 |
31 |
61 |
週末 |
否 |
雨 |
6 |
43 |
週末 |
是 |
雨 |
15 |
55 |
工作日 |
否 |
雨 |
4 |
58 |
工作日 |
否 |
以當天天氣為例:
一共有三個屬性值,晴、陰、雨,一共分裂成三個子節點。
根據上述公式,可以計算資訊增益率如下:
所以使用天氣屬性進行分裂可以得到資訊增益率0.44。
2.對連續型屬性進行處理
C4.5處理離散型屬性的方式與ID3一致,新增對連續型屬性的處理。處理方式是先根據連續型屬性進行排序,然後採用一刀切的方式將資料砍成兩半。
那麼如何選擇切割點呢?很簡單,直接計算每一個切割點切割後的資訊增益,然後選擇使分裂效果最優的切割點。以溫度為例:
從上圖可以看出,理論上來講,N條資料就有N-1個切割點,為了選取最優的切割墊,要計算按每一次切割的資訊增益,計算量是比較大的,那麼有沒有簡化的方法呢?有,注意到,其實有些切割點是很明顯可以排除的。比如說上圖右側的第2條和第3條記錄,兩者的類標籤(逛街)都是“是”,如果從這裡切割的話,就將兩個本來相同的類分開了,肯定不會比將他們歸為一類的切分方法好,因此,可以通過去除前後兩個類標籤相同的切割點以簡化計算的複雜度,如下圖所示:
從圖中可以看出,最終切割點的數目從原來的11個減少到現在的6個,降低了計算的複雜度。
確定了分割點之後,接下來就是選擇最優的分割點了,注意,對連續型屬性是採用資訊增益進行內部擇優的,因為如果使用資訊增益率進行分裂會出現傾向於選擇分割前後兩個節點資料量相差最大的分割點,為了避免這種情況,選擇資訊增益選擇分割點。選擇了最優的分割點之後,再計算資訊增益率跟其他的屬性進行比較,確定最優的分裂屬性。
3. 剪枝
決策樹只已經提到,剪枝是在完全生長的決策樹的基礎上,對生長後分類效果不佳的子樹進行修剪,減小決策樹的複雜度,降低過擬合的影響。
C4.5採用悲觀剪枝方法(PEP)。悲觀剪枝認為如果決策樹的精度在剪枝前後沒有影響的話,則進行剪枝。怎樣才算是沒有影響?如果剪枝後的誤差小於剪枝前經度的上限,則說明剪枝後的效果與更佳,此時需要子樹進行剪枝操作。
進行剪枝必須滿足的條件:
其中:
表示子樹的誤差;
表示葉子節點的誤差;
令子樹誤差的經度滿足二項分佈,根據二項分佈的性質,,,其中,N為子樹的資料量;同樣,葉子節點的誤差。
上述公式中,0.5表示修正因子。由於對父節點進行分裂總會得到比父節點分類結果更好的效果,因此,因此從理論上來說,父節點的誤差總是不小於孩子節點的誤差,因此需要進行修正,給每一個節點都加上0.5的修正因此,在計算誤差的時候,子節點由於加上了修正的因子,就無法保證總誤差總是低於父節點。
算例:
由於,所以應該進行剪枝。
程式設計及原始碼(C#版)
程式的設計過程
(1)資料格式
對原始的資料進行數字化處理,並以二維資料的形式儲存,每一行表示一條記錄,前n-1列表示屬性,最後一列表示分類的標籤。
如表1的資料可以轉化為表2:
表2 初始化後的資料
當天天氣 |
溫度 |
溼度 |
季節 |
明天天氣 |
1 |
25 |
50 |
1 |
1 |
2 |
21 |
48 |
1 |
2 |
2 |
18 |
70 |
1 |
3 |
1 |
28 |
41 |
2 |
1 |
3 |
8 |
65 |
3 |
2 |
1 |
18 |
43 |
2 |
1 |
2 |
24 |
56 |
4 |
1 |
3 |
18 |
76 |
4 |
2 |
3 |
31 |
61 |
2 |
1 |
2 |
6 |
43 |
3 |
3 |
1 |
15 |
55 |
4 |
2 |
3 |
4 |
58 |
3 |
3 |
其中,對於“當天天氣”屬性,數字{1,2,3}分別表示{晴,陰,雨};對於“季節”屬性{1,2,3,4}分別表示{春天、夏天、冬天、秋天};對於類標籤“明天天氣”,數字{1,2,3}分別表示{晴、陰、雨}。
程式碼如下所示:
static double[][] allData; //儲存進行訓練的資料
static List<String>[] featureValues; //離散屬性對應的離散值
featureValues是連結串列陣列,陣列的長度為屬性的個數,陣列的每個元素為該屬性的離散值連結串列。
(2)兩個類:節點類和分裂資訊
a)節點類Node
該類表示一個節點,屬性包括節點選擇的分裂屬性、節點的輸出類、孩子節點、深度等。注意,與ID3中相比,新增了兩個屬性:leafWrong和leafNode_Count分別表示葉子節點的總分類誤差和葉子節點的個數,主要是為了方便剪枝。
View Code(原始碼參見原微博下同)
b)分裂資訊類,該類儲存節點進行分裂的資訊,包括各個子節點的行座標、子節點各個類的數目、該節點分裂的屬性、屬性的型別等。
View Code
主方法findBestSplit(Node node,List<int> nums,int[] isUsed),該方法對節點進行分裂
其中:
node表示即將進行分裂的節點;
nums表示節點資料的行座標列表;
isUsed表示到該節點位置所有屬性的使用情況;
findBestSplit的這個方法主要有以下幾個組成部分:
1)節點分裂停止的判定
節點分裂條件如上文所述,原始碼如下:
View Code
2)尋找最優的分裂屬性
尋找最優的分裂屬性需要計算每一個分裂屬性分裂後的資訊增益率,計算公式上文已給出,其中熵的計算程式碼如下:
View Code
3)進行分裂,同時對子節點進行迭代處理
其實就是遞迴的工程,對每一個子節點執行findBestSplit方法進行分裂。
findBestSplit原始碼:
View Code
(4)剪枝
悲觀剪枝方法(PEP):
View Code
C4.5核心演算法的所有原始碼:
View Code
總結:
要記住,C4.5是分類樹最終要的演算法,演算法的思想其實很簡單,但是分類的準確性高。可以說C4.5是ID3的升級版和強化版,解決了ID3未能解決的問題。要重點記住以下幾個方面:
1.C4.5是採用資訊增益率選擇分裂的屬性,解決了ID3選擇屬性時的偏向性問題;
2.C4.5能夠對連續資料進行處理,採用一刀切的方式將連續型的資料切成兩份,在選擇切割點的時候使用資訊增益作為擇優的條件;
3.C4.5採用悲觀剪枝的策略,一定程度上降低了過擬合的影響。