The Practical Guide to the Random Forests - Random Forests Model Part II
(Using Kaggle’s Titanic Data with R code)

Photo source: https://datafloq.com

Previously in this series:

Random Forests

在建立Tree-based models的流程中使用Bagging演算法的Bagging trees模型, 導入了隨機元件(random component)的架構, 大幅改善了模型建立的變異,因而提升了預測績效。然而, Bagging trees模型中每一個tree model彼此之間並非是完全獨立的(independent),因為, 每一個tree model在進行每一次split, 所有資料的欄位變數(original predictors)都會被分析考慮。假設今天訓練資料集, 其欄位變數和response之間的關係(relationship)可以用tree model所建立, 則以此資料數據所產生的每一個bootstrap samples產生之tree model結構將會非常類似(尤其位於tree modeltop nodes); 我們稱此特性為”Tree Correlation”



Tree Correlation會讓Bagging演算法無法更進一步優化預測變異(variance) – 因為每一個bootstrap samplestree model可能都使用相似的變數進行split, 並因此產生類似的預測數值。以Titanic 數據為例, 性別(gender)這個變數是一個強效的特徵(因為女性存活率明顯高於男性, 因此以此做為分割條件可以最有效的進行分類), 在這情況下, Bagging產生之大多數tree model都會以性別變數做為第一個split條件。顯然,降低tree models之間的相關性(correlation), 我們稱之為 de-correlating trees”, 是我們進一步優化預測績效的下一個目標。

以統計的觀點,要降低變數之間的關聯性,可以透過在tree model建構過程中, 增加隨機性(randomness)達到。依照此概念, Breiman (2001)創造一個稱之為random forests的演算法, 此演算法的流程架構如Algorithm 1(b)所示:

Bagging 演算法的預測邏輯一樣, ensemble中的每一個模型都會產生各自的預測數據; 依照問題型態的不同, 分別使用兩種方式進行預測:
ü   For regression: m個預測數值平均

ü   For classification: 使用投票(votes)的比率決定預測類別

Bagging相較, Random forests更進一步增加模型的隨機性(randomness) - 在每次split隨機選取變數,因此可以有效地減低tree correlation
Tree Model在進行split時需隨機選取多少數量的變數, Random forests演算法的調校參數(tuning parameter), 一般用代號k表示。Breiman (2001)依照預測類型的不同, 分別建議兩個算式如下:
ü   For regression: 資料數據變數數量的1/3

ü   For classification: 資料數據變數數量平方根

若想對此參數k進行tuning, 我們建議從不直接使用上列算式取得之數字開始, 並以5為間距等量增加至所有的資料數據變數數量。例如資料數據共有210個變數, For regression, 參數k的調校數值為{70,75,80…,210}, 而後依照各k值所產生的模型預測績效, 決定參數k的最佳化數值。

回到Titanic 數據, 此資料集共有10個變數, 若不對參數k進行tuning, 依照classification的算式, 我們設定k=3(10的平方根大約為3.16)做為參數值。因此, Tree Model在每次進行split, 僅會選取10個變數中的任意3個參數, 決定split; 在這個情況下, 性別變數就不會總是出現在每一個Tree Model的第一次split, 甚至有些Tree Model可能要到較深的node才會看到性別變數。

實務上,我們還需設定Random forests需包含建立多少數量的Tree Model; Breiman (2001)已經驗證Random forests並不會有over-fitting問題, 因此, 模型預測的績效不會因為過多的Tree Models而扭曲下降; 實際需要考量的是運算效能, 越多的Tree Model, 系統需要越多的運算時間去訓練以建構模型。一般建議以1000Tree Models開始進行評估, cross-validation績效數值在1000Tree Models仍有顯著的改善, 則可以加入更多的Tree Models直到績效改善效率平穩為止。

Random forests藉由選取 strong, complex learners” – 也就是low bias的模型組件, 來達到降低variance的目的。這種合併多個獨立且stronglearners可以改善模型的錯誤率。也由於每一個訓練模型組件(learners)是各自獨立所選取的,每一個元件選擇不會受前次的選擇結果(selected independently of all previous learners)所影響, Random forests演算法具有robust to a noisy response特性優勢。

Random forests透過使用兩項隨機元件, 其所組合完成之模型, 將包含完全各自獨特的tree models, 然後每一個tree models皆會產出完全不一樣的預測結果。由於Random foreststree model並不會進行prune, 因此每一個tree model都可能會over-fitting, 但是每個over-fitting的結構又都是不一樣的。所有的這些”mistakes”都會在最後進行彙整預測時被平均掉

Bagging演算法相較, random forests在運算效能上表現更為優秀; 這是因為在Tree model建置過程中, 僅有部分比率的數據變數在split時進行分析。將此特性加上平行處理, random forests的運算效能遠高於另外一個有名ensemble演算法 - Boosting

由於模型合併(ensemble)的架構, 使得運用random forests模型時, 我們無法獲得/解釋predictorsresponse之間的關聯性, 但由於tree model仍是此Ensemble模型的基礎元件, 我們仍然可以量化各個predictor對於預測的影響程度數據。量化的方法為,classification預測為例, 評量每一個Nodepredictor, 其使用前後purity的變化(使用Gini index)彙整整個random forests的量測數據後, 就可以獲得所有的predictors重要性數值。

Predictors重要性數據仍會有下列2個侷限:
1.      那些與重要的predictor具有高度關聯性的無用/無意義變數, 也會同樣產生錯誤的高重要性數值。在某些情況下, 這些無用/無意義變數的重要性數值甚至會高於次重要的predictor

2.      稀釋關鍵predictor的重要性。假設有一關鍵predictor其重要性為X, 若有另一個predictor和此一關鍵predictor高度相關, 則關鍵predictor之重要性將會影響降成X/2; 若有兩個predictor同樣具有和此關鍵predictor高度關聯性, 則重要性數值會進一步稀釋成X/3

Building Random Forests model on Titanic sample

R環境下的Random Forest演算法有一些限制, 必須在模型建置前先行處理。最主要的一項是, RRandom Forest模型訓練資料集中, 不可以有missing values (也就是NA)。在上一篇文章中, 我們提到R有許多package支援我們處理資料集中的NA數值, 這裡參照Trevor Stephens的做法, 使用我們學過的rpart這個package, 來處理資料集中的missing valuerpart模型訓練中遇到NA數值時, rpart運用surrogate variables方式填補missing value資料, RRandom Forest package並沒有預設或內建NA數值的處理模式, 因此必須先行處理資料集中的missing value

首先, 我們先check上一個小節所產生的combi data set,有哪些欄位有NA value:
summary(combi)
可以發現:Age, Fare,Embarked這三個欄位有missing values

我們使用上一個post所學會的Decision Tree model, 來填補Age欄位的NA value;由於這次的預測對象是continuous variable, 所以改變參數method="anova",接著使用is.na()這個方式, 選取出資料集中有NA value的資料行, 將預測值與以取代NA數值
Agefit <- rpart(Age ~ Pclass + Sex + SibSp + Parch + Fare + Embarked + Title + FamilySize,
                data=combi[!is.na(combi$Age),], 
  method="anova")

combi$Age[is.na(combi$Age)] <- predict(Agefit, combi[is.na(combi$Age),])
summary(combi$Age)

再次檢視Age欄位, NA’s已經沒有了







Embarked欄位和Age不太一樣, 首先,它是blank value, 再者,僅有2筆資料是blank value. 由於資料數很小不至於影響到model的建立, 因此我們直接將此兩筆資料用最多的Embarked – S取代
combi$Embarked[which(combi$Embarked == '')] <- "S"
combi$Embarked <- factor(combi$Embarked)

最後, Fare欄位僅有一筆資料為NA value, 我們使用median數値取代
which(is.na(combi$Fare))
[1] 1044
combi$Fare[1044] <- are="" code="" combi="" median="" na.rm="TRUE)">
RRandom Forest演算法第2個限制是: 模型訓練資料集中的factor欄位, 最多只能到32levels。檢視combi data set, FamilyID這個變數共有61levels

我們有兩個方式來處理這一個問題:
1.      使用unclass() function , 轉換factor數值變成為其所代表之整數數值, 讓演算法將此欄位視為連續變數(continuous variables)進行模型訓練

2.      手動降低levels數量
這裡, 我們採用第2個方式。我們找出FamilySize <= 3的數據, 然後將FamilyID改指定為“Small”
1.      Copy the FamilyID column to create a new variable FamilyID2
2.      Convert FamilyID2 from a factor back into a character string with as.character().

3.      After replace with ‘Small’, convert FamilyID2 back to a factor.


combi$FamilyID2 <- combi$FamilyID
combi$FamilyID2 <- as.character(combi$FamilyID2)
combi$FamilyID2[combi$FamilySize <= 3] <- 'Small'
combi$FamilyID2 <- factor(combi$FamilyID2)

快速說明一下程式碼。首先複製FamilyID欄位成為FamilyID2; 第二步, 移除factor, 將欄位轉換回文字型態; 最後, 找出需給定‘Small’數值的資料, 取代完成後, 再轉換回factor檢視資料, levels成功降低至22

OK, 現在我們可以開始建立Random Forest模型了
install.packages('randomForest')
library(randomForest)

train <- combi[1:891,]
test <- combi[892:1309,]

set.seed(415)
rffit <- randomForest(as.factor(Survived) ~ Pclass + Sex + Age + SibSp + Parch + Fare + Embarked + Title + FamilySize +FamilyID2, 
  data=train, 
  importance=TRUE, 
  ntree=2000)


由於建立之模型用以進行分類預測, 我們使用as.factor() function, 將預測之目標變數轉換成2levelsfactor; randomForest package提供許多參數供我們進行模型調校, 這裡整理幾個重要的參數進行說明:
n   ntree: Random Forest模型中Tree Models的數量
n   mtry: Tree Models splits時變數分析的數量。預設值為所有變數數量的平方根
n   samplesize: 每個bootstrap的樣本數量
n   nodesize: Tree ModelsNode, 最少(minimum)的數據資料數量。此數值越大, 所建構的Tree Model深度(depth)越小, 因此可以加快模型的建置速度, 並限制Random Forest模型的複雜度。

n   importance:將此參數設定為TRUE, 模型訓練完成之後才能取得; 數值越高代表該參數越重要
varImpPlot(rffit)



varImpPlot() function產生2importance measures:
1.      正確率(Accuracy): 若模型沒有此變數, 其預測準確會下降多少

2.      Gini Index: 若模型無此變數情況下, Tree models最末端nodepurity會損失多少
我們使用predict()方法產生test data set的預測數值。呼叫predict方法時,加上參數type=prob, 可以取得類別機率(class probabilities)數值, 而不是預測結果; 有了類別機率數值, 我們可以另外自行決定, 當類別機率高於多少時, 才選定該類別; 例如class probabilities > 70%, 才認定該人員Survived
Prediction <- predict(rffit, test)
submit <- data.frame(PassengerId = test$PassengerId, Survived = Prediction)
write.csv(submit, file = "randomforest.csv", row.names = FALSE)
n   The result works out exactly the same as Kaggle’s Python random forest tutorial.

上述之Random Forest演算法使用CART模型做為base learner, 我們另外嘗試以conditional inference trees模型做為base learner, 來進行Titanic Data Set的預測Conditional inference trees模型的基本架構與一般Tree-based模型類似, 唯一一個差異在於模型的決策衡量, 並非以purity做為目標, 而是使用statistical test進行評估。R implementpackageparty, package並沒有randomForest packagefactor 32 level限制; 另外, partymtry預設參數為5, 由於Titanic Data Set的變數量不多, 因此調整為3
install.packages('party')
library(party)

set.seed(415)
cffit <- cforest(as.factor(Survived) ~ Pclass + Sex + Age + SibSp + Parch + Fare + Embarked + Title + FamilySize + FamilyID,
               data = train, controls=cforest_unbiased(ntree=2000, mtry=3))

Prediction <- predict(cffit, test, OOB=TRUE, type = "response")
write.csv(submit, file = "conditionforest.csv", row.names = FALSE)


Random Forests模型系列到此告一段落, 希望這6篇文章有幫助各位了解Random Forests演算法, 並能運用R進行實作分類預測


by J.D. 

0 意見: