決策樹的構建演算法 -- ID3 與 C4.5 演算法
1. 概述
上一篇日誌中,我們介紹了最簡單的分類迴歸演算法 – K 近鄰演算法。
k 近鄰演算法
本篇日誌我們來介紹構建專家系統和資料探勘最常用的演算法 – 決策樹。
2. 決策樹
在系統流程圖中,我們常常會構建決策樹,例如上面的例子是一個簡單的用於動物分類的專家系統,是一個典型的樹狀結構。
決策樹通常用來處理數值型或標稱型資料,它用來預測物件屬性與物件值之間的關係。
2.1. 決策樹的構成
決策樹由結點(node)和有向邊(directed edge)組成。
節點分為:
- 內部節點(internal node) – 儲存用於決策的特徵或屬性
- 葉子節點(leaf node) – 儲存判斷結果類
3. 決策樹演算法的優缺點
3.1. 優點
- 計算複雜度不高
- 輸出結果易於理解
- 中間值缺失不敏感
- 可以處理不相關特徵的資料
3.2. 缺點
決策樹演算法最大的缺點是可能存在過度匹配的問題。
4. 如何構造決策樹 – ID3 演算法
ID3 演算法是構建決策樹最常用的演算法之一。
ID3 演算法即“Iterative Dichotomiser III”。
他是基於“奧卡姆剃刀原理”指導思想的演算法,也就是用盡量少的資訊做更多的事。
他認為越是小型的決策樹越優於大的決策樹,所以,ID3 演算法是以啟發式的方式構建儘量小的決策樹。
那麼,如何定義決策樹的大小呢?
5. 資訊熵
“熵”最早起源於物理學,用來度量一個熱力學系統的無序程度,1948年,夏農引入了資訊熵,將其定義為離散隨機事件出現的概率。
一個系統越是有序,資訊熵就越低,反之一個系統越是混亂,它的資訊熵就越高。
假如一個隨機變數 X 的取值為 X={x1, x2, x3 … xn},他們出現的概率分別是 {p1, p2, p3 … pn},那麼 X 的熵定義為:
也就是說,一個變數的變化情況可能越多,那麼它攜帶的資訊量就越大,系統也就越混亂。
5.1. 資訊熵的計算
下面是一個實際的例子,根據天氣情況決定是否打高爾夫:
是否要打高爾夫?
Day | Temperatrue | Outlook | Humidity | Windy | PlayGolf? |
---|---|---|---|---|---|
07-05 | hot | sunny | high | false | no |
07-06 | hot | sunny | high | true | no |
07-07 | hot | overcast | high | false | yes |
07-09 | cool | rain | normal | false | yes |
07-10 | cool | overcast | normal | true | yes |
07-12 | mild | sunny | high | false | no |
07-14 | cool | sunny | normal | false | yes |
07-15 | mild | rain | normal | false | yes |
07-20 | mild | sunny | normal | true | yes |
07-21 | mild | overcast | high | true | yes |
07-22 | hot | overcast | normal | false | yes |
07-23 | mild | sunny | high | true | no |
07-26 | cool | sunny | normal | true | no |
07-30 | mild | sunny | high | false | yes |
一共 14 個樣本,包括 9 個 yes 和 5 個 no。
- 系統的熵
- 選定指標後計算熵
如果我們用 outlook 指標來分類,那麼可以分成如圖所示的三部分:
各個分支的資訊熵:
- 選定指標後系統的條件熵
選定指標以後,各個分支的熵的加權和就是整個系統的條件熵:
6. 資訊增益
資訊增益指的是從分支劃分前到分支劃分後,系統熵的差異:
- S – 全部樣本集合
- value(T) – 屬性 T 所有取值的集合
- v – T 其中的一個屬性
- Sv – S 中屬性 T 值為 v 的樣例集合
- |Sv| – Sv 中的樣本數
因此,上面例子中選定指標後資訊增益為:
7. ID3 演算法的實現
基於上面的計算,我們有了用來衡量系統複雜度的指標 – 資訊熵,以及用指標劃分系統後的熵差 – 資訊增益。
根據 ID3 演算法的核心思想,只要在每次決策樹非葉子節點劃分之前,計算出每一個屬性所帶來的資訊增益,選擇最大資訊增益的屬性來劃分,就可以讓本次劃分更優,因此整個 ID3 實際上是一個貪心演算法。
7.1. 程式碼實現
下面是 ID3 的 python 程式碼實現:
# -*- coding: UTF-8 -*-
from math import log
def calcShannonEnt(dataSet):
"""
計算給定資料集的資訊熵(夏農熵)
"""
numEntires = len(dataSet)
""" 儲存每個標籤出現的次數 """
labelCounts = {}
""" 每個所有特徵向量 """
for featVec in dataSet:
currentLabel = featVec[-1] # 提取標籤資訊
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1
# 資訊熵
shannonEnt = 0.0
for key in labelCounts:
# 選擇該標籤的概率
prob = float(labelCounts[key]) / numEntires
# 計算熵
shannonEnt -= prob * log(prob, 2)
return shannonEnt
def createDataSet():
"""
建立測試資料集
"""
dataSet = [[0, 0, 0, 0, 'no'], # 資料集
[0, 0, 0, 1, 'no'],
[0, 1, 0, 1, 'yes'],
[0, 1, 1, 0, 'yes'],
[0, 0, 0, 0, 'no'],
[1, 0, 0, 0, 'no'],
[1, 0, 0, 1, 'no'],
[1, 1, 1, 1, 'yes'],
[1, 0, 1, 2, 'yes'],
[1, 0, 1, 2, 'yes'],
[2, 0, 1, 2, 'yes'],
[2, 0, 1, 1, 'yes'],
[2, 1, 0, 1, 'yes'],
[2, 1, 0, 2, 'yes'],
[2, 0, 0, 0, 'no']]
labels = ['overlook', 'temperature', 'humidity', 'windy'] # 分類屬性
return dataSet, labels
def splitDataSet(dataSet, axis, value):
"""
從 dataSet 中取出 axis 特徵
"""
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
""" 去掉axis特徵 """
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis + 1:])
retDataSet.append(reducedFeatVec)
return retDataSet
def chooseBestFeatureToSplit(dataSet):
"""
選擇最優特徵
"""
numFeatures = len(dataSet[0]) - 1
""" 計算資料集的資訊熵 """
baseEntropy = calcShannonEnt(dataSet)
# 資訊增益
bestInfoGain = 0.0
# 最優特徵的索引值
bestFeature = -1
for i in range(numFeatures):
# 獲取dataSet的第i個所有特徵
featList = [example[i] for example in dataSet]
uniqueVals = set(featList) # 去重
# 條件熵
newEntropy = 0.0
for value in uniqueVals:
""" 計算資訊增益 """
# subDataSet劃分後的子集
subDataSet = splitDataSet(dataSet, i, value)
# 計運算元集的概率
prob = len(subDataSet) / float(len(dataSet))
""" 計算經驗條件熵 """
newEntropy += prob * calcShannonEnt(subDataSet)
""" 計算資訊增益 """
infoGain = baseEntropy - newEntropy
print("第%d個特徵的增益為%.3f" % (i, infoGain))
""" 獲取最大資訊增益 """
if (infoGain > bestInfoGain):
bestInfoGain = infoGain
bestFeature = i
return bestFeature
if __name__ == '__main__':
dataSet, features = createDataSet()
print("最優劃分特徵為: " + features[chooseBestFeatureToSplit(dataSet)])
7.2. 執行結果
8. C4.5 演算法
C4.5 演算法是 ID3 演算法的擴充套件,C4.5生成的決策樹可以用於分類,因此,C4.5通常被稱為統計分類器。
C4.5 對 ID3 演算法最大的改進就是在獲取最優分類特徵的時候,將 ID3 所使用的資訊增益換成了資訊增益比。
我們在上面的例子中,沒有使用表中的 Day 屬性作為特徵參與計算,如果我們把這個特徵引入進來參與計算會怎麼樣呢?
顯然,H(D|Day)=0,g(D,Day)=0.9403,Day 屬性是最好的分類屬性,可事實上,這是沒有任何意義的,因為每個結果都有一個唯一的 Day 屬性,如果用 Day 屬性為 root 構造決策樹,決策樹將形成一顆葉子數為 14,深度只有兩層的樹。
9. 資訊增益比
造成這樣的問題原因是什麼呢?因為 Day 屬性的可選值過多,而資訊增益偏向於選擇取值較多的特徵。
解決辦法也很簡單,就是對樹分支過多的情況進行懲罰。
- 資訊增益比 = 懲罰引數 * 資訊增益
根據熵的公式可知,特徵越多,熵越大,所以,懲罰引數取熵的倒數,也就是用資訊增益除以特徵 A 的熵,從而抵消了特徵變數的複雜程度,避免了過擬合:
10. C4.5 的其他改進
- 處理連續和離散屬性 – 為了處理連續屬性,C4.5建立一個閾值,然後將列表拆分為屬性值高於閾值的列表以及小於或等於閾值的列表
- 處理缺少屬性值的訓練資料 – C4.5允許將屬性值標記為?為了失蹤。丟失的屬性值根本不用於增益和熵計算。
- 處理具有不同成本的屬性
- 建立後修剪樹 – C4.5一旦建立就會返回樹中,並嘗試通過用葉節點替換它們來刪除無效的分支。
歡迎關注微信公眾號
參考資料
Peter Harrington 《機器學習實戰》。
李航 《統計學習方法》。
https://en.wikipedia.org/wiki/ID3_algorithm。
https://en.wikipedia.org/wiki/C4.5_algorithm。
https://blog.csdn.net/c406495762/article/details/75663451?utm_source=blogxgwz0。
https://blog.csdn.net/fly_time2012/article/details/70210725。
http://www.cnblogs.com/ooon/p/5643494.html。
https://blog.csdn.net/olenet/article/details/46433297。
https://www.jianshu.com/p/268c4095dbdc。
https://www.cnblogs.com/muzixi/p/6566803.html。