決策樹是機器學習中非常基礎的演算法,也是我研究生生涯學習到的第一個有監督模型,其中最基礎的ID3是1986年被發表出來的,一經發表,之後出現了眾多決策樹演算法,不過最常見的還是C4.5和cart樹。在我的研究中,用不到決策樹,在天池或者Kaggle也很少用到單個決策樹,這種競賽一般用的整合演算法較多,畢竟是20世紀的產物,經過這麼多年的發展,已經有了很多可供選擇的模型。其實很多基礎模型都是如此,在研究中根本用不到,但是你得了解這些模型,下面我就根據我的瞭解來談談決策樹。本文全部的程式碼都放在我的github。
引言
決策樹的分類過程類似於小時候玩的那種性格測試題,就是不斷地做選擇題,然後跳到下一題繼續做,直到最後出來結果。從中也可以看出它就是一個if-else決策過程,我做選擇提供資訊,它根據資訊減少範圍,更確切地就是消除不確定性,我提供的選擇越多,提供的資訊也就越多,那麼不確定性就越小,它就越能測出我是哪種性格。根據數學之美里面的說法就是資訊量就是不確定性的多少,最終我提供了大量資訊,那麼它也消除了不確定性,給出了結果。
小時候我做這種性格測試題的時候,思考過為什麼這些題目就能測出?它測的難道真的就是對的嗎?這些題目都是哪來的?現在想來,這些都是模型建立的問題,也就是用決策樹來測資料之前,我們如何建立這個決策樹。那這些題目的依據從何而來呢?我猜想是心理學家通過大量調查得到的,具體就是心理學家調查了很多人,這些資訊多到可以總結出一個規律出來,這個規律就是人類性格的規律,如果這些題目給外星人做,那肯定就不行了,心理學家還需要先對大量外星人做問卷調查,然後得到大量資訊做出適合外星人的題目。那我們建立關於某類資料的決策樹的時候,也需要先得到大量的相關資訊,這種相關資訊在機器學習中被稱為訓練資料。但是我那時就想到如果心理學家調查那些人的時候,有些人為了搗亂專門回答相反的呢?這些人的回答肯定不能用,有些人可能為了保護隱私不能回答一些比較私人的問題呢?,並且每個人都有特別之處,心理學家不能把所有人都包括進去,心理學家需要找出的是大部分人的特徵即可。那麼在我們使用的訓練資料中也有這樣的情況,這是因為現實資料往往很複雜,也有可能缺失值,也有個別特殊情況,我們也不需要決策樹模型將這些資料全部學習,但是決策樹模型是死的,它並不會像心理學家一樣挑選資料,它只會全部學習完,這種情況在機器學習中就叫做過擬合,這種問題在大型網路模型中特別常見,需要做的減少特殊情況,適用一般情況,專業話就是提高泛化能力。那麼心理學家如何才能得到大部分的人心理答案呢?最簡單的就是減少設定的題目,這樣問的就不是那麼細了,只問籠統的問題,這樣出來的結果就會好一點。到決策樹中就是剪枝策略,減去一些沒用的屬性,只保留對模型有用的屬性分支,這也符合了奧卡姆剃刀原理,越精簡越好。
這就是所有模型所都要考慮的:
- 如何建立模型?
- 如何處理資料?
- 如何精簡模型?
熵
決策樹就是根據資訊減少不確定性,那麼正如上面所說,心理學家會減少設定的題目,那麼如何知道每一道題目都提供了多少資訊量呢,那我們需要的就是根據得到的資料度量資訊,我們可以從生活中來看資訊量等於什麼,當我們對一件事很瞭解的時候,那麼我們需要的資訊就少,我們對一件事不瞭解,那麼需要的資訊就多,那麼我們對一件事完全瞭解所需要的資訊量就是不確定性的多少,將得到的資訊填滿這個不確定,就變成了確定。那麼如何量化這種度量方式呢,也就是如何用公式表示出來?數學之美這本書給出了一個非常好的例子,就是我錯過看世界盃,但是我想知道冠軍是誰,就問一位觀眾,但是這個人不肯告訴我,只是說讓我猜,他收錢之後告訴我,我猜的對不對,那麼我要花多少錢才能在32中球隊中猜中呢?我也是個程式設計師了,自然知道二分查詢,分別給球隊編上號,問他,冠軍球隊是否在1-16中,他說對的,我接著說,冠軍球隊是否在1-8中,他說錯了,那麼我也知道冠軍球隊在9-16中,這樣猜的話,只需要五次就能猜出,那麼誰是冠軍這個訊息只值五塊錢,我們知道\(log32 = 5\),正好二者對應起來,他就是個對數函式。
上面的猜測是基於概率相等的情況,但是球迷都知道有強隊,有弱隊,冠軍很有可能就是在強隊中出現的,因而每個隊都有自身的奪冠概率,這樣的話就不能直接用\(log32 = 5\)來計算,需要用概率來計算,那麼可以將\(log32 = 5\)直接進行拆分就可以,將之變成\(log32 = -(\frac{1}{32}log\frac{1}{32} + \frac{1}{32}log\frac{1}{32} + \dots + \frac{1}{32}log\frac{1}{32})\),那麼當大家概率都不同的時候,它的準確資訊量就是\(H = -(p_1logp_1 + p_2logp_2 + \dots + p_{32}logp_{32})\),夏農稱之為資訊熵(Entropy),衡量了系統的不確定性,如果要消除不確定性,所要付出的最小努力大小就是這個資訊熵\(-\sum\limits_{i=1}^{32}p_klog{p_k}\)。而上面概率都相等的情況,也被稱為最大熵,也被用於最大熵模型中。現在有了每隻球隊的奪冠真實概率,但是如果我是個偽球迷,我憑感覺猜每個球隊的奪冠概率分別是\(q_k\),然後將之用到\(\sum\limits_{i=1}^{32}p_klog\frac{1}{q_k}\),這個就是交叉熵,給定真實分佈的情況下,使用非真實分佈指定的策略消除不確定性所作出的努力,交叉熵一般用作損失函式,交叉熵越低,越好。最後我發現我猜的都是錯的,我想要衡量一下我們之間猜的差異,也就是衡量倆個分佈之間的差異,用的就是相對熵,也就是推導SVM中的KL散度,它的公式就是交叉熵減去資訊熵\(KL(p|q) = \sum\limits_{i=1}^{32}p_klog\frac{1}{q_k} - \sum\limits_{i=1}^{32}p_klog\frac{1}{p_k}\)。這些熵之後都會使用到。藉此機會正好全部分析好。
ID3
理論分析
如果目前已經有了大量的訓練資料D,那麼我們只要從這些資料中依次挑選出資料特徵(資料屬性)來作為決策樹的結點,那麼哪一個特徵具有最強的分類能力呢?從上面的熵可知,最好的特徵會使得分類的不確定性下降到最低,計算的也就是由於特徵A使得對訓練資料分類的不確定性減少的程度,專業話就是資訊增益,那麼我們需要計算的就是本身的不確定性和在特徵A下分類的不確定性,使用\(本身的不確定性 - 在特徵A下分類的不確定性 = 不確定性減少的程度\),機器學習中將之稱為資訊增益,這個公式專業化就是\(資訊熵 - 條件熵 = 資訊增益\)
條件熵中的\(v\)就是特徵A中的離散值,離散值就是不能無限細分的,比如性別,只有男和女倆個值,對於連續值的特徵的選擇,之後我們會講。那麼我們就可以利用上面得到的資訊增益來進行特徵選擇,選擇資訊增益最大的作為該節點,該特徵的離散值作為該節點的分支,之後在每個分支下,僅僅使用該節點下的資料集來計算資訊熵和剩下的每個特徵的條件熵,繼續進行特徵選擇,直到停止條件:1. 當前節點所包含的樣本都是同一類的,無需劃分;2. 已經選完了全部的屬性了,無法劃分了;3. 當前節點包含的資料集為空,沒有資料了,不能劃分。空談誤國,實業興邦,下面以李航博士的統計學習方法一書中的一個列子,依託上面的過程來做一下,真正的計算一下才能加深理解。
ID | 年齡 | 有工作 | 有自己房子 | 信貸情況 | 類別 |
---|---|---|---|---|---|
1 | 青年 | 否 | 否 | 一般 | 否 |
2 | 青年 | 否 | 否 | 好 | 否 |
3 | 青年 | 是 | 否 | 好 | 是 |
4 | 青年 | 是 | 是 | 一般 | 是 |
5 | 青年 | 否 | 否 | 一般 | 否 |
6 | 中年 | 否 | 否 | 一般 | 否 |
7 | 中年 | 否 | 否 | 好 | 否 |
8 | 中年 | 是 | 是 | 好 | 是 |
9 | 中年 | 否 | 是 | 非常好 | 是 |
10 | 中年 | 否 | 是 | 非常好 | 是 |
11 | 老年 | 否 | 是 | 非常好 | 是 |
12 | 老年 | 否 | 是 | 好 | 是 |
13 | 老年 | 是 | 否 | 好 | 是 |
14 | 老年 | 是 | 否 | 非常好 | 是 |
15 | 老年 | 否 | 否 | 一般 | 否 |
先計算整體的資訊熵,直接看類別一列,發現類別為否的概率是\(\frac{6}{15}\),類別為是的概率是\(\frac{9}{15}\),利用公式計算整體資訊熵
之後再計算每個特徵下的條件熵,要想計算每個特徵的條件熵,先計算這個特徵中每個離散值的資訊熵。
用每個離散值的資訊熵乘以個數比例就是條件熵。
之後每個都像上面那個計算,計算有工作的特徵的資訊增益
計算有自己房子的資訊增益
計算信貸情況的資訊增益
找出資訊增益最大的那個作為決策樹的第一個節點,也就是特徵(有自己的房子)作為第一個節點,是和否作為節點左右分支,刪除資料集D中有自己房子這一特徵,並將是和否倆個分支的資料集分別劃分出來,否的資料如下所示
ID | 年齡 | 有工作 | 信貸情況 | 類別 |
---|---|---|---|---|
1 | 青年 | 否 | 一般 | 否 |
2 | 青年 | 否 | 好 | 否 |
3 | 青年 | 是 | 好 | 是 |
4 | 青年 | 否 | 一般 | 否 |
5 | 中年 | 否 | 一般 | 否 |
6 | 中年 | 否 | 好 | 否 |
7 | 老年 | 是 | 好 | 是 |
8 | 老年 | 是 | 非常好 | 是 |
9 | 老年 | 否 | 一般 | 否 |
從這些資料中再進行資訊增益的計算,先計算整體的資訊熵
計算年齡的資訊增益
計算有工作的資訊增益
計算信貸情況
直接選出特徵(有工作),並且它的倆邊資料集都是屬於同一類別,無需再劃分了。
特徵(有自己房子 = 是)資料如下所示
ID | 年齡 | 有工作 | 有自己房子 | 信貸情況 | 類別 |
---|---|---|---|---|---|
1 | 青年 | 是 | 是 | 一般 | 是 |
2 | 中年 | 是 | 是 | 好 | 是 |
3 | 中年 | 否 | 是 | 非常好 | 是 |
4 | 中年 | 否 | 是 | 非常好 | 是 |
5 | 老年 | 否 | 是 | 非常好 | 是 |
6 | 老年 | 否 | 是 | 好 | 是 |
發現都是屬於同一類別的,那麼就不用計算了,根據上面的過程ID3就生成了
程式碼實現
這節我們根據上面的過程寫程式碼,首先看樹如何儲存,這次的樹非常簡單,因此我們使用字典來儲存即可,對於決策樹的建立,根據之前的資料結構中內容,我們使用遞迴建樹,先看遞迴結束條件,之後開始進行特徵選擇,然後從資料中刪除該特徵,然後開始建立決策樹。
#遞迴建樹
def buildTree(dataSet,labelSet):
decision = [example[-1] for example in dataSet]
decisionType = set(decision)
#如果熵值為0
if len(decisionType) == 1:
return decisionType
#這裡的樹採用了字典,二叉樹的話,之後再實現
#特徵選擇
key = chioceBest(dataSet)
keyLabel = labelSet[key]
#構造決策樹
myTree = {keyLabel:{}}
#使用完了就刪除
del labelSet[key]
bestCol = [example[key] for example in dataSet]
valueCol = set(bestCol)
for value in valueCol:
subLabel = labelSet[:]
myTree[keyLabel][value] = buildTree(splitValue(dataSet,key,value),subLabel)
return myTree
其中特徵選擇的程式碼也是和之前過程差不多,就是計算每個特徵的資訊增益,這裡的話,其中不用計算整體熵值,因為整體熵值是一樣的,我們只需要計算條件熵,看誰的條件熵最小即可。
#選擇出最好的屬性
def chioceBest(dataSet,entro = entropy):
#計算出整個的熵值
score_all = entropy(dataSet)
print('整體資訊熵', score_all)
#最好的特徵
bestAttri = -1
#最好特徵的增益因子
bestAttriGain = 0.0
#屬性的個數
attributes = len(dataSet[0]) - 1
#計算每個屬性的條件熵,並選出最好的那個
for col in range(attributes):
#得到這個列的所有的值
data_col = [example[col] for example in dataSet]
#得出這一屬性的值
values_col = set(data_col)
col_entropy = 0.0
#屬性值的條件熵
for value in values_col:
#得出相關屬性值的所有行數
value_dataSet = splitValue(dataSet,col,value)
newEntropy = entropy(value_dataSet)
print('資訊熵', newEntropy)
ratio = len(value_dataSet)/len(dataSet)
col_entropy += ratio*newEntropy
#得到增益因子
attributeGain = score_all - col_entropy
print('資訊增益:', attributeGain)
if attributeGain > bestAttriGain:
bestAttriGain = attributeGain
bestAttri = col
return bestAttri
之後將資訊熵進行實現
#求一個屬性值的資訊熵
def entropy(rows):
gainFactor = 0
length = len(rows);
#求出一個屬性值的每個決策的數目
decisionChoice = entropyDecision(rows)
#對這個屬性值求出熵值
for value in decisionChoice.keys():
ratio = float(decisionChoice[value])/length
gainFactor -= ratio*(math.log(ratio)/math.log(2))
return gainFactor
C4.5
ID3的問題
問題1:上述處理的都是離散值,無法處理連續值。
問題2:資訊增益在選擇特徵上會選擇取值比較多的特徵,這也是很容易理解,取值比較多的話,每個分支都是一個特徵值,那麼它更容易使資料變得更純。也可以從公式來看,每個分支都是一個值,那麼它的條件熵就是0,比如上面的特徵ID。
問題3:對有缺失值的資料沒有處理。
問題4:容易過擬合,沒有考慮過擬合。
解決
問題1的解決,因為很多資料的特徵都是連續值,因而必須要考慮到。這裡我們可以使用二分法對連續屬性離散化,具體做法就是先將全部特徵值進行從小到大排序,之後倆點之間的中位點\(\frac{A^i + A^{i+1}}{2}\)作為候選劃分點,然後像前面那樣計算所有候選劃分點的資訊增益,選出資訊增益最大的那個候選劃分點,這個劃分點就不再是特徵值中的一員了,而是一個具體的數字。下面我們來算一個連續特徵的資訊增益看看,例子選自周志華的西瓜書。
假設西瓜資料集中有一個特徵是密度,一共有17個西瓜,密度值分別
編號 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
密度 | 0.697 | 0.774 | 0.634 | 0.608 | 0.556 | 0.403 | 0.481 | 0.437 | 0.666 | 0.243 | 0.245 | 0.343 | 0.639 | 0.657 | 0.360 | 0.593 | 0.719 |
好瓜 | 是 | 是 | 是 | 是 | 是 | 是 | 是 | 是 | 否 | 否 | 否 | 否 | 否 | 否 | 否 | 否 | 否 |
- 先將密度值進行從小到大排序,變成{0.243,0.245,0.343,0.360,0.403,0.437,0.481,0.556,0.593,0.608,0.634,0.639,0.657,0.666,0.697,0.719,0.774}。
- 之後求倆倆中位數作為候選劃分點,\(\frac{0.243 + 0.245}{2} = 0.244\),如此一直計算,最終得到所有的候選劃分點{0.244,0.294,0.351,0.381,0.420,0.459,0.518,0.574,0.600,0.621,0.636,0.648,0.661,0.681,0.708,0.746}。
- 依次選取候選劃分點,並且計算資訊增益,比如,在此之前先計算整體
第一次選取0.244,那麼小於0.244的只有0.243,大於0.244的有16個,分別計算。
然後選取0.294,再進行計算,一直計算下去。
4. 從上面的候選劃分點中選取一個資訊增益最大的,在這個例子中選取的是0.381,它的資訊增益是0.262。
#連續數值候選的集合
def continuousCandidate(continuousDataSet):
splitSet = []
for i in range(len(continuousDataSet) - 1):
temp = (continuousDataSet[i] + continuousDataSet[i+1])/2
splitSet.append(temp)
return splitSet
#連續變數的劃分點的選擇
def chooesContinueDivision(dataSet,col):
#得到這一列中所有的值
data_col = [example[col] for example in dataSet]
data_col.sort()
#得出切分點的候選集
splitSet = continuousCandidate(data_col)
#最好的切分點
best_split = 0.0
best_gain = 1000.0
best_sets = None
#選取資訊增益最大的那個切分點
for split in splitSet:
leftPoint = []
rightPoint = []
for i in range(len(data_col)):
if split >= dataSet[i][col]:
leftPoint.append(dataSet[i])
else:
rightPoint.append(dataSet[i])
left_gain = entropy(leftPoint)
right_gain = entropy(rightPoint)
gain = left_gain + right_gain
if gain < best_gain:
best_gain = gain
best_split = split
best_sets = [leftPoint,rightPoint]
return best_gain,best_split,best_sets
問題2的解決,就是將資訊增益變成資訊增益率,C4.5並不是直接選擇資訊增益率,而是先用資訊增益選出高於平均水平的屬性,再從中選出資訊增益率最高的。先來看看資訊增益率,特徵的特徵值越多,資訊增益就越大,那麼可以想到,如果資訊增益除以多特徵值的相關表示式不就限制住了,這就是資訊增益率的想法,因而可以定義為
其中
這個\(IV(A)\)式子中\(V\)取值越多的話,\(IV(A)\)的值就會越大。
問題3的解決,缺失值資料的處理,資料缺失在現實資料中很常用,但是我沒有處理過這樣的資料,我處理的都是天文光譜資料,這些資料都是經過天文臺的處理,很少有缺失的資料,因而我對這部分只瞭解了大概。這方面一共有倆個問題
- 如果訓練資料有缺失值,那麼如何進行特徵選擇?
- 如果給定劃分特徵,資料有缺失值,如何進行樣本的劃分(這裡不僅僅是測試資料,外加建立決策樹選好特徵,有缺失值的資料該劃分到特徵的哪個特徵值中去)
這倆個問題都可以使用權重法,先給每個資料一個權重為\(\omega_i = 1\),之後進行特徵選擇的時候,選出無該特徵缺失值的資料\(D^w\)計算資訊增益,計算出來的資訊增益乘以權重\(\rho\),這裡的權重\(\rho\)就是無該特徵缺失值的資料的總權重除以整個資料的總權重。
依舊是選出資訊增益最大的特徵作為節點,之後將該節點缺失值的資料以不同的權重劃分到全部分支中,這裡的權重就是該分支無缺失值除以全部分支無缺失值,下圖就是一個例子,假如資料8和10有缺失值,那麼就以不同的權重進入三個分支中,其他的資料權重都是1,
這樣就會影響分支中的資訊增益權重\(\rho\)。
問題4的解決,引入正則化係數進行初步剪枝,一般都不會這樣做,在CART樹中有著更完善的剪枝策略。
CART
C4.5的問題
問題1:ID3和C4.5使用了資訊熵,其中有了大量耗時的對數運算,如果連續值的話,還存在了排序,能夠將之改進?
問題2:決策樹演算法非常容易過擬合,並且C4.5的剪枝演算法也是需要優化的。思想就是倆種,預剪枝和後剪枝。
問題3:上面倆種都只能用於分類,能不能擴充到迴歸中?
CART樹
CART樹在一般決策樹演算法中,算是比較優的演算法了,sklearn裡面決策樹演算法也是使用的CART樹的原理,我在研究中要使用這些基礎演算法的時候,一般都會使用sklearn裡面的呼叫方法,直接呼叫。需要注意的就是ID3和C4.5可以是多叉樹,但是CART樹是一個二叉樹。
對於問題1,CART樹使用的是基尼係數,經濟學指數也有個基尼係數,但是好像不一樣。這裡的基尼係數代表的是模型的不純度,基尼係數越小,不純度越低,特徵越好,這個倒是和資訊增益相反。公式如下
假設資料有K類
我們現在算一個特徵的基尼係數看看,資料就用上面的
ID | 年齡 | 有工作 | 信貸情況 | 類別 |
---|---|---|---|---|
1 | 青年 | 否 | 一般 | 否 |
2 | 青年 | 否 | 好 | 否 |
3 | 青年 | 是 | 好 | 是 |
4 | 青年 | 否 | 一般 | 否 |
5 | 中年 | 否 | 一般 | 否 |
6 | 中年 | 否 | 好 | 否 |
7 | 老年 | 是 | 好 | 是 |
8 | 老年 | 是 | 非常好 | 是 |
9 | 老年 | 否 | 一般 | 否 |
計算特徵年齡的基尼係數
對於問題2中的剪枝問題,一共有倆種常用的思想,一種是預剪枝,就是在決策樹生成的時候,對每個節點在生成前先進行估計,如果當前節點的劃分並不能給決策樹帶來泛化效能的提升,那麼停止劃分,並將當前節點標記為葉子節點,該葉子節點的類別就是訓練資料最多的類別,第二種就是後剪枝,就是已經生成了一個完整的決策樹,然後自底向上的對非葉節點進行考察,如果該節點對應的子樹替換成葉子節點能夠帶來效能的提升,那麼替換成葉子節點。先要考慮如何判斷效能的提升,這裡使用的就是留出法,將資料劃分成訓練集和驗證集,驗證集就是用來驗證模型的泛化效能的。下面我們用一個例子來看看整個過程,
ID | 色澤 | 根蒂 | 敲聲 | 紋理 | 臍部 | 觸感 | 好瓜 |
---|---|---|---|---|---|---|---|
1 | 青綠 | 蜷縮 | 濁響 | 清晰 | 凹陷 | 硬滑 | 是 |
2 | 烏黑 | 蜷縮 | 沉悶 | 清晰 | 凹陷 | 硬滑 | 是 |
3 | 烏黑 | 蜷縮 | 濁響 | 清晰 | 凹陷 | 硬滑 | 是 |
4 | 青綠 | 稍蜷 | 濁響 | 清晰 | 稍凹 | 軟粘 | 是 |
5 | 烏黑 | 稍蜷 | 濁響 | 稍糊 | 稍凹 | 軟粘 | 是 |
6 | 青綠 | 硬挺 | 清脆 | 清晰 | 平坦 | 軟粘 | 否 |
7 | 淺白 | 稍蜷 | 沉悶 | 稍糊 | 凹陷 | 硬滑 | 否 |
8 | 烏黑 | 稍蜷 | 濁響 | 清晰 | 稍凹 | 軟粘 | 否 |
9 | 淺白 | 蜷縮 | 濁響 | 模糊 | 平坦 | 硬滑 | 否 |
10 | 青綠 | 蜷縮 | 沉悶 | 稍糊 | 稍凹 | 硬滑 | 否 |
ID | 色澤 | 根蒂 | 敲聲 | 紋理 | 臍部 | 觸感 | 好瓜 |
---|---|---|---|---|---|---|---|
1 | 青綠 | 蜷縮 | 沉悶 | 清晰 | 凹陷 | 硬滑 | 是 |
2 | 淺白 | 蜷縮 | 濁響 | 清晰 | 凹陷 | 硬滑 | 是 |
3 | 烏黑 | 稍蜷 | 濁響 | 清晰 | 稍凹 | 硬滑 | 是 |
4 | 烏黑 | 稍蜷 | 沉悶 | 稍糊 | 稍凹 | 硬滑 | 否 |
5 | 淺白 | 硬挺 | 清脆 | 模糊 | 平坦 | 硬滑 | 否 |
6 | 淺白 | 蜷縮 | 濁響 | 模糊 | 平坦 | 軟粘 | 否 |
7 | 青綠 | 稍蜷 | 濁響 | 稍糊 | 凹陷 | 硬滑 | 否 |
上面10條資料就是測試集,下面7條資料就是驗證集,我們先來看預剪枝。
預剪枝,計算每個特徵的資訊增益,這裡我們就不計算了,第一個節點應該會選取臍部來進行劃分,但是要先進行估計,如果不劃分的話,所有的資料都在根節點中,那麼選取資料最多的類別,是和否都是一樣多,我們隨便選擇是,放入測試資料中,那麼所有的測試資料都被分成是類別,最終的準確率就是\(\frac{3}{7} = 0.429\),如果使用臍部進行劃分的話,一共分為三個分支,如圖
這裡凹陷分支包含的資料是{1,2,3,7},其中最多資料的類別就是類別是,那麼類別就是好瓜,稍凹分支包含的資料是{4,5,8,10},其中類別是和否都一樣多,選擇是,那麼類別就是好瓜,平坦分支包含的資料是{6,9},類別否最多,選擇否,那麼類別就是壞瓜,因而形成了三個分支。我們將驗證集放進去,其中{1,2,3,5,6}分對了,準確率就是\(\frac{5}{7} = 0.714 > 0.429\),那麼可以進行分支。之後的結點都不會對準確率有提升,因此都會被剪枝掉。
從這個例子可以看出預剪枝使得很多分支都沒有展開,確實降低了過擬合風險,減少了訓練時間開銷,但是這種貪心剪枝會給決策樹帶來欠擬合的風險,就像上圖的決策樹一樣,只存在了一個特徵,這就忽略了那些後續能夠提升效能的劃分,因而個人感覺後剪枝更好一點。
後剪枝,先從訓練集中得到一顆完整的決策樹,如下圖所示
我們先從最下面的紋理特徵開始,將之標記為葉子節點,節點中的資料是{5,8},找出資料最多的類別,將這個葉子節點標記為好瓜,準確率得到了提升,成為了0.57,所以將之剪枝,之後看紋理上面的色澤,將之變成葉子節點,發現準確率並沒有下降,因而可以不剪枝,之後再看上面的根蒂,發現也沒有提升,之後再看根蒂左邊的色澤,發現準確率提升了0.714,因此剪枝,因此最後的決策樹應該如下圖所示
可以看出後剪枝比預剪枝保留了更多的分支,後剪枝的欠擬合風險更小,泛化效能往往優於預剪枝,但是後剪枝是自底向上對每一個節點進行考察的,開銷也更大。
決策樹與條件概率分佈
條件概率就是事件A在事件B已經發生條件下的發生概率,條件概率可以用決策樹進行計算。決策樹就是表示在給定特徵條件下類發生的條件概率分佈,決策樹的一條路徑就是特徵空間中劃分的一個單元,具體可以看下圖
決策樹的各個分支就是將特徵空間劃分成幾個互不相交的單元或者區域,決策樹的整個的條件概率分佈就是各個單元給定條件下的類的條件概率分佈組成的。
但是我們也可以從上圖中可以看到它的劃分都是與座標軸平行的,這樣雖然有可解釋性,但是碰到複雜的分類邊界的時候,必須要使用很多段劃分才能得到近似結果,因此有了後來的多變數決策樹,也稱為斜決策樹,就是可以實現更加複雜的劃分邊界,主要的演算法就是OC1.
總結
決策樹還是很好理解的,雖然如今,深度學習橫行霸道,但是決策樹依舊有著自己的優勢,最重要的就是可解釋性,這種優勢讓它可以用在根因分析中。並且它也不需要預處理,簡單直觀,更多的就是應用在小資料集中。決策樹最重要的就是劃分準則,上面的就是資訊增益和基尼係數,雖然後來又有了很多劃分準則,但是這些準則對效能影響較小,現在的很多決策樹更多是CART樹,比如sklearn中的決策樹。決策樹也與增量學習結合,就是接收到新的樣本的時候,只需要對決策樹進行調整即可,不需要重新學習,但是同時決策樹對資料的波動很敏感,我覺得這主要還是決策樹的擬合程度太高了。本文的程式碼都在我的github。