1、介紹
決策樹(decision tree)是一種有監督的機器學習算法,是一個分類算法。在給定訓練集的條件下,生成一個自頂而下的決策樹,樹的根為起點,樹的葉子為樣本的分類,從根到葉子的路徑就是一個樣本進行分類的過程。
下圖為一個決策樹的例子,見http://zh.wikipedia.org/wiki/%E5%86%B3%E7%AD%96%E6%A0%91
可見,決策樹上的判斷節點是對某一個屬性進行判斷,生成的路徑數量為該屬性可能的取值,最終到葉子節點時,就完成一個分類(或預測)。決策樹具有直觀、易於解釋的特性。
2、決策樹生成算法
本文主要討論如何由一個給定的訓練集生成一個決策樹。如果都一個數據集合$D$,其特征集合為$A$,那么以何種順序對A中的特征進行判斷就成為決策樹生成過程中的關鍵。首先給出一個決策樹生成算法-ID3算法(參考《統計學習方法》李航著)
--------------------我是算法開始分割線-------------------------------------------
ID3算法:
輸入:訓練數據集D,特征集A,閾值e
輸出:決策樹T
(1)若D中所有樣本屬於同一類Ck,則T為單節點樹,並將類Ck作為該節點的類標記,返回T;
(2)A為空集,T為單節點樹,將D中實例數最大的類Ck作為該節點的類標記,返回T;
(3)否則,計算A中各特征對D的信息增益,選擇信息增益最大的特征值Ag;
(4)如果Ag<e,則置T為單節點樹,將D中實例數最大的類Ck作為該節點的類標記,返回T;
(5)否則,對Ag的每一個可能的取值ai,依Ag=ai將D分割為若干非空子集Di,將Di中實例數最大的類作為標記,構建子節點,由節點及其子節點構成樹T,返回T;
(6)對第i個子節點,以Di為訓練集,以 A-{Ag}為特征集,遞歸調用(1)~(5)步,得到子樹Ti,返回Ti。
--------------------我是算法結束分割線-------------------------------------------
算法第(3)步中,信息增益是評估每一個特征值對D的划分效果,划分的原則為將無序的數據變得盡量有序。評價隨機變量不確定性的一個概念是熵,熵越大,不確定性越大。如果確定一個特征Ag,在確定該特征前后,D的熵的變化值就是特征Ag的信息增益。
3、熵及信息增益
熵:
設X是一個取有限個值(n)的離散隨機變量,其概率分布為
\[P(X=x_{i})=P_{i}, i=1,2,...,n\]
則隨機變量X的熵定義為
\[H(x) = - \sum\limits_{i = 1}^n {{P_i}\log {P_i}} \]
信息增益:
訓練集為\(D\),\(|D|\)為樣本容量,設有k個類\({C_k}\),k=1,...k, \({|C_k|}\)為類\({C_k}\)的樣本個數,且有\(\sum\limits_{i = 1}^k {|{C_k}|} = |D|\)
設特征A有n個不同取值\(\{ {a_{1,}}{a_2}, \cdots ,{a_n}\} \) ,根據A的值,將D划分為n個子集\({D_1},{D_2}, \cdots ,{D_n}\), \({|D_i|}\)為\({D_i}\) 的樣本數,\(\sum\limits_{i = 1}^n {|{D_i}|} = |D|\)。
記子集\({D_i}\)中屬於類\({C_k}\)的樣本集合為\({D_{ik}}\),即\({D_{ik}} = {D_i} \cap {C_k}\)。
\({|D_{ik}|}\)為\({D_{ik}}\)的樣本個數。
(1)數據集D的經驗熵H(D)
\[H(D) = - \sum\limits_{k = 1}^K {\frac{{|{C_k}|}}{{|D|}}{{\log }_2}} \frac{{|{C_k}|}}{{|D|}}\]
(2)特征A對數據集D的經驗條件熵H(D|A)
\[H(D|A) = \sum\limits_{i = 1}^n {\frac{{|{D_i}|}}{{|D|}}H({D_i}) = - } \sum\limits_{i = 1}^n {\frac{{|{D_i}|}}{{|D|}}\sum\limits_{k = 1}^K {\frac{{|{D_{ik}}|}}{{|{D_i}|}}} } {\log _2}\frac{{|{D_{ik}}|}}{{|{D_i}|}}\]
(3)計算信息增益
\[g(D,A) = H(D) - H(D|A)\]
信息增益越大,表示A對D趨於有序的貢獻越大。
-------------------------------分割線------------------------------------------------
決策樹的R語言實現如下:
library(plyr)
# 測試數據集 http://archive.ics.uci.edu/ml/datasets/Car+Evaluation
##計算訓練集合D的熵H(D)
##輸入:trainData 訓練集,類型為數據框
## nClass 指明訓練集中第nClass列為分類結果
##輸出:訓練集的熵
cal_HD <- function(trainData, nClass){
if ( !(is.data.frame(trainData) & is.numeric(nClass)) )
"input error"
if (length(trainData) < nClass)
"nClass is larger than the length of trainData"
rownum <- nrow(trainData)
#對第nClass列的值統計頻數
calss.freq <- count(trainData,nClass)
#計算每個取值的 概率*log2(概率)
calss.freq <- mutate(calss.freq, freq2 = (freq / rownum)*log2(freq / rownum))
-sum(calss.freq[,"freq2"])
#使用arrange代替order,方便的按照多列對數據框進行排序
#mtcars.new2 <- arrange(mtcars, cyl, vs, gear)
}
#cal_HD(mtcars,11)
##計算訓練集合D對特征值A的條件熵H(D|A)
##輸入:trainData 訓練集,類型為數據框
## nClass 指明訓練集中第nClass列為分類結果
## nA 指明trainData中條件A的列號
##輸出:訓練集trainData對特征A的條件熵
cal_HDA <- function(trainData, nClass, nA){
rownum <- nrow(trainData)
#對第nA列的特征A計算頻數
nA.freq <- count(trainData,nA)
i <- 1
sub.hd <- c()
for (nA.value in nA.freq[,1]){
#取特征值A取值為na.value的子集
sub.trainData <- trainData[which(trainData[,nA] == nA.value),]
sub.hd[i] <- cal_HD(sub.trainData,nClass)
i <- i+1
}
nA.freq <- mutate(nA.freq, freq2 = (freq / rownum)*sub.hd)
sum(nA.freq[,"freq2"])
}
##計算訓練集合D對特征值A的信息增益g(D,A)
##輸入:trainData 訓練集,類型為數據框
## nClass 指明訓練集中第nClass列為分類結果
## nA 指明trainData中特征A的列號
##輸出:訓練集trainData對特征A的信息增益
g_DA <- function(trainData, nClass, nA){
cal_HD(trainData, nClass) - cal_HDA(trainData, nClass, nA)
}
##根據訓練集合生成決策樹
##輸入:trainData 訓練集,類型為數據框
## strRoot 指明根節點的屬性名稱
## strRootAttri 指明根節點的屬性取值
## nClass 指明訓練集中第nClass列為分類結果
## cAttri 向量,表示當前可用的特征集合,用列號表示
## e 如果特征的最大信息增益小於e,則剩余作為一個分類,類頻數最高的最為分類結果
##輸出:決策樹T
gen_decision_tree <- function(trainData, strRoot, strRootAttri, nClass, cAttri, e){
# 樹的描述,(上級節點名稱、上級節點屬性值、自己節點名稱,自己節點的取值)
decision_tree <- data.frame()
nClass.freq <- count(trainData,nClass) ##類別出現的頻數
nClass.freq <- arrange(nClass.freq, desc(freq)) ##按頻數從低到高排列
col.name <- names(trainData) ##trainData的列名
##1、如果D中所有屬於同一類Ck,則T為單節點樹
if nrow(nClass.freq) == 1{
rbind(decision_tree, c(strRoot, strRootAttri, nClass.freq[1,1], ''))
return decision_tree
}
##2、如果屬性cAttri為空,將D中頻數最高的類別返回
if length(cAttri) == 0{
rbind(decision_tree, c(strRoot, strRootAttri, nClass.freq[1,1], ''))
return decision_tree
}
##3、計算cAttri中各特征值對D的信息增益,選擇信息增益最大的特征值Ag及其信息增益
maxDA <- 0 #記錄最大的信息增益
maxAttriName <- '' #記錄最大信息增益對應的屬性名稱
maxAttriIndex <- '' #記錄最大信息增益對應的屬性列號
for(i in cAttri){
curDA <- g_DA(trainData,nClass,i)
if (maxDA <= curDA){
maxDA <- curDA
maxAttriName <- col.name[i]
}
}
##4、如果最大信息增益小於閾值e,將D中頻數最高的類別返回
if (maxDA < e){
rbind(decision_tree, c(strRoot, strRootAttri, nClass.freq[1,1], ''))
return decision_tree
}
##5、否則,對Ag的每一可能值ai,依Ag=ai將D分割為若干非空子集Di
## 將Di中實例數最大的類作為標記,構建子節點
## 由節點及其子節點構成樹T,返回T
for (oneValue in unique(trainData[,maxAttriName])){
sub.train <- trainData[which(trainData[,maxAttriName] == oneValue),] #Di
#sub.trian.freq <- count(sub.train,nClass) ##類別出現的頻數
#sub.trian.freq <- arrange(sub.trian.freq, desc(freq)) ##按頻數從低到高排列
rbind(decision_tree, c(strRoot, strRootAttri, maxAttriName , oneValue))
##6、遞歸構建下一步
# 剔除已經使用的屬性
next.cAttri <- cAttri[which(cAttri !=maxAttriIndex)]
# 遞歸調用
next.dt <-gen_decision_tree(sub.train, maxAttriName,
oneValue, nClass, next.cAttri, e)
rbind(decision_tree, next.dt)
}
names(decision_tree) <- c('preName','preValue','curName','curValue')
decision_tree
}
---------------決策樹總結-------------------
1、R中有實現決策樹算法的包rpart,和畫出決策樹的包rpart.plot,本例自己實現決策樹算法是為了更好的理解。
2、由於決策樹只能處理離散屬性,因此連續屬性應首先進行離散化。
3、決策樹易於理解,對業務的解釋性較強。
4、ID3算法容易引起過擬合,需考慮樹的剪枝。
