【回到首頁】


本篇內容,會繼續介紹一些常用的資料探勘模型:

這裡拿網路上一個公開資料(鐵達尼號的乘客資料)來進行分析,資料載點如下


決策樹(Decision Tree)

無論在分類或預測上,決策樹的演算法都有很好的效果。

但它最強大的地方,莫過於樹狀分支的結構:可以明顯呈現分類的規則!與一些機器學習的方法(NN, SVM…)相比,相當容易進行解釋,以及分析規則之間的關係。

這裡就簡單用CART決策樹來練習,對應的套件是rpart,一樣使用上次鐵達尼號的資料:

# 記得要給定資料所在的路徑(path),例如:我把下載的資料放在C槽下:
load("C:/titanic.raw.rdata")  #匯入.rdata檔
require(rpart)

# 先把資料區分成 train=0.8, test=0.2 
set.seed(22)
train.index <- sample(x=1:nrow(titanic.raw), size=ceiling(0.8*nrow(titanic.raw) ))
train <- titanic.raw[train.index, ]
test <- titanic.raw[-train.index, ]

# CART的模型:把存活與否的變數(Survived)當作Y,剩下的變數當作X
cart.model<- rpart(Survived ~. , 
                    data=train)

# 輸出各節點的細部資訊(呈現在console視窗)
cart.model
## n= 1761 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
##  1) root 1761 558 No (0.68313458 0.31686542)  
##    2) Sex=Male 1398 288 No (0.79399142 0.20600858)  
##      4) Age=Adult 1348 264 No (0.80415430 0.19584570) *
##      5) Age=Child 50  24 No (0.52000000 0.48000000)  
##       10) Class=3rd 37  11 No (0.70270270 0.29729730) *
##       11) Class=1st,2nd 13   0 Yes (0.00000000 1.00000000) *
##    3) Sex=Female 363  93 Yes (0.25619835 0.74380165)  
##      6) Class=3rd 155  73 No (0.52903226 0.47096774) *
##      7) Class=1st,2nd,Crew 208  11 Yes (0.05288462 0.94711538) *

要畫出決策樹(視覺化),雖然用平常的plot()就可以達成

但rpart有專屬的繪圖套件rpart.plot,函式是prp()

說真的,用prp()畫出來的決策樹,比較好看一些:

require(rpart.plot) 
prp(cart.model,         # 模型
    faclen=0,           # 呈現的變數不要縮寫
    fallen.leaves=TRUE, # 讓樹枝以垂直方式呈現
    shadow.col="gray",  # 最下面的節點塗上陰影
    # number of correct classifications / number of observations in that node
    extra=2)  

(最下面節點的數字,代表:number of correct classifications / number of observations in that node)

根據以上決策樹,可以發現是男生或女生其實很重要(因為是第一個分支規則),其次是在船上的艙位等級。

因此,我們可以這樣解釋:

即使是女性,可是擁有的艙位若是最低下的(3rd),則大概有一半的死亡機率(82/155=53%);   
但當妳的艙位高人一等時,則有相當高的存活機率(197/208=95%)。  

又或者是:

當你是男性成人時,大概有八成機率會死(1084/1348=77%)  

以及

若是男性小孩,就和艙位等級有關:高級艙位的小孩全都獲救(13/13),可是低艙位的小孩有七成機率(26/37=70%)會死。  

(男生好可憐)

●也可用另一個繪圖套件partykit,函式是as.party()plot()

require(partykit)   
rparty.tree <- as.party(cart.model) # 轉換cart決策樹
rparty.tree # 輸出各節點的細部資訊
## 
## Model formula:
## Survived ~ Class + Sex + Age
## 
## Fitted party:
## [1] root
## |   [2] Sex in Male
## |   |   [3] Age in Adult: No (n = 1348, err = 19.6%)
## |   |   [4] Age in Child
## |   |   |   [5] Class in 3rd: No (n = 37, err = 29.7%)
## |   |   |   [6] Class in 1st, 2nd: Yes (n = 13, err = 0.0%)
## |   [7] Sex in Female
## |   |   [8] Class in 3rd: No (n = 155, err = 47.1%)
## |   |   [9] Class in 1st, 2nd, Crew: Yes (n = 208, err = 5.3%)
## 
## Number of inner nodes:    4
## Number of terminal nodes: 5
plot(rparty.tree) 

用這個套件畫出來的圖也是蠻容易一目了然的呢! 有不一樣的感覺~


有決策樹之後,就要進行預測!

還記得在線性迴歸使用過的predict()嗎?這時就會派上用場囉(在這裡,會同時計算預測準確率):

pred <- predict(cart.model, newdata=test, type="class")

# 用table看預測的情況
table(real=test$Survived, predict=pred)
##      predict
## real   No Yes
##   No  278   9
##   Yes  93  60
# 計算預測準確率 = 對角線的數量/總數量
confus.matrix <- table(real=test$Survived, predict=pred)
sum(diag(confus.matrix))/sum(confus.matrix) # 對角線的數量/總數量
## [1] 0.7681818

結果顯示,模型在測試集中的預測能力大約77%,但模型的預測準確率還有提升的可能嗎?我們繼續對模型進行修樹~

printcp(cart.model) # 先觀察未修剪的樹,CP欄位代表樹的成本複雜度參數
## 
## Classification tree:
## rpart(formula = Survived ~ ., data = train)
## 
## Variables actually used in tree construction:
## [1] Age   Class Sex  
## 
## Root node error: 558/1761 = 0.31687
## 
## n= 1761 
## 
##         CP nsplit rel error  xerror     xstd
## 1 0.317204      0   1.00000 1.00000 0.034989
## 2 0.016129      1   0.68280 0.68280 0.030966
## 3 0.011649      2   0.66667 0.68817 0.031054
## 4 0.010000      4   0.64337 0.66487 0.030668
plotcp(cart.model) # 畫圖觀察未修剪的樹

prunetree_cart.model <- prune(cart.model, cp = cart.model$cptable[which.min(cart.model$cptable[,"xerror"]),"CP"]) # 利用能使決策樹具有最小誤差的CP來修剪樹

修剪完決策樹之後,讓我們重新建構一次預測模型

prunetree_pred <- predict(prunetree_cart.model, newdata=test, type="class")

# 用table看預測的情況
table(real=test$Survived, predict=prunetree_pred)
##      predict
## real   No Yes
##   No  278   9
##   Yes  93  60
prunetree_confus.matrix <- table(real=test$Survived, predict=prunetree_pred)
sum(diag(prunetree_confus.matrix))/sum(prunetree_confus.matrix) # 對角線的數量/總數量
## [1] 0.7681818

很顯然,模型的預測準確率並沒有提升,一樣是大約77%,這是因為我們在修剪時所挑選到滿足條件的CP值為0.01,而函式rpart()預設的CP值就是0.01,故前後模型的結果一致。


再來,我們爲了避免模型過度擬合(overfitting),故要利用K-fold Cross-Validation的方法進行交叉驗證,我們使用caret這個套件,而K先設定為10次~

require(caret)
require(e1071)
train_control <- trainControl(method="cv", number=10)
train_control.model <- train(Survived~., data=train, method="rpart", trControl=train_control)
train_control.model
## CART 
## 
## 1761 samples
##    3 predictor
##    2 classes: 'No', 'Yes' 
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold) 
## Summary of sample sizes: 1585, 1584, 1584, 1585, 1586, 1585, ... 
## Resampling results across tuning parameters:
## 
##   cp          Accuracy   Kappa    
##   0.01164875  0.7796983  0.4045009
##   0.01612903  0.7774223  0.4055445
##   0.31720430  0.7097437  0.1417312
## 
## Accuracy was used to select the optimal model using  the largest value.
## The final value used for the model was cp = 0.01164875.

然而,我們一開始修剪樹之後所得到的決策樹模型,最佳的預測準確率大約為77%,而現在再透過交叉驗證所Tune得的參數,使得模型的最佳預測準確率大約提升為78%。


總結

在資料探勘領域中,決策樹(Decision Tree)是相當常見的方法,例如在醫學研究上,對某種特定的疾病(糖尿病,代謝症候群等)找出可以前期篩檢分類,或是預測的因子時,就常以決策樹的方法來進行,而決策樹較爲不同之處在於可用圖像化來呈現結果,即使不了解背後理論,仍可解讀並判斷。

進行決策樹分析要注意的是,當樣本存在類別不不衡的問題時,決策樹對於小類的樣本根本無能為力,模型的效能會大打折扣。