決策樹是機器學習中非常基礎的算法,也是我研究生生涯學習到的第一個有監督模型,其中最基礎的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。