決策樹、裝袋、提升和隨機森林

決策樹是一種簡單、常用的基礎模型。之所以說它簡單,不僅因爲它的思想原理簡單具體、貼近實際,它並不需要像線性迴歸模型一樣用一個數學公式來表徵,而是由規則來抽象。說它基礎,是因爲它是一系列複雜強大的模型的基礎。

決策樹的基本思想是通過將數據不斷劃分,使原來混亂的數據信息逐漸清晰。舉一個簡單的例子:如果你去相親,你可能以外貌爲第一特徵來決定是否繼續往下考慮;如果外貌過關了,你可能還會考慮職位和收入水平;如果收入水平也過關了,再去考慮品質……這種層層篩選的過程就蘊含着決策樹的樸素思想。

決策樹不侷限於數學模型的具體形式,它既可以用來作分類,也可以用來作迴歸,二者的基本思想類似,處理方法上有差別。

分類樹

根據前文的描述,應該有兩個問題:1、如何表徵數據的混亂或清晰的程度?2、如何將數據劃分?

一個分類變量,設想一下極端情況,如果都是True或False,那它取True或False的概率就是0或1,這些都是100%確定的,你無需做任何猜測,這種情況下數據就是最清晰的;反之,如果一個變量各有50%的True或False,你甚至沒辦法預測一個樣本更有可能是True還是False,這種情況下數據就是最混亂的。

有兩個指標可以用來衡量數據的不確定程度:基尼係數(並非經濟學上的概念),定義如下:

熵:


熵的定義


基尼係數:


基尼係數的定義

具體就不推導了,可見當p接近0或1時,這兩個指標都接近於0,表示不確定度最低,信息最爲清晰;當p接近0.5時,不確定度最高,信息最爲混亂。

第一個問題解答了,第二個問題如何來進行數據劃分?分類樹的主要過程如下:

  • 首先計算分類變量在不做任何劃分下的熵或基尼係數
  • 計算每一個特徵在各個水平下的劃分的加權熵或基尼係數
  • 選擇令分類變量熵或基尼係數減少得最多的特徵作爲節點往下劃分
  • 重複以上過程,直至數據被清晰劃分

Carseats的座椅銷量水平高低爲二分類變量,演示構建分類樹的過程。

> library(tree)
> library(ISLR)
> attach(Carseats)
> 
> # 建立二分類變量
> High = ifelse(Sales <= 8 ,"No","Yes")
> Carseats = data.frame(Carseats,High)
> 
> # 劃分訓練集和測試集
> set.seed(1)
> train = sample(1:nrow(Carseats),200)
> Carseats.test = Carseats[-train,]
> High.test = High[-train]
> tree.carseats = tree(High~.-Sales,Carseats,subset=train) # 建立決策樹模型
> dim(Carseats) # 10個特徵
[1] 400  13
> summary(tree.carseats) # 只用了6個特徵,產生了16個節點的決策樹,訓練錯誤率爲6.5%
Classification tree:
tree(formula = High ~ . - Sales, data = Carseats, subset = train)
Variables actually used in tree construction:
[1] "ShelveLoc"   "Price"       "Income"      "CompPrice"   "Advertising" "Population" 
Number of terminal nodes:  16 
Residual mean deviance:  0.3088 = 56.82 / 184 
Misclassification error rate: 0.065 = 13 / 200 
> # 可視化決策樹模型
> plot(tree.carseats)
> text(tree.carseats,pretty = 0)
> tree.pred = predict(tree.carseats,Carseats.test,type="class")
> table(tree.pred,High.test)
         High.test
tree.pred No Yes
      No  93  38
      Yes 21  48
> (93+48)/200 # 測試正確率僅爲70.5%
[1] 0.705

剪枝前的決策樹

這個模型的訓練誤差爲93.5%,測試誤差卻僅爲70.5%,看到這棵樹分支很多,說明什麼?說明模型太複雜,過擬合了。決策樹的過程就是不斷將數據集細分的過程,可是如果細分過頭了,模型的泛化能力就差,在新的測試數據中預測準確率就低。

那麼如何解決決策樹過擬合的問題?剪枝。迭代過程無需細分那麼多步,決策樹無需有那麼多層。

剪枝有2種思路:一種是預剪枝,決策樹每次分裂時,只有分裂後的RSS減小超過某一閾值才分裂,但這種方法只看眼前一步,看不到後面的幾步,因此不免過於短視,容易錯過最優的模型。另一種後剪枝,就是先就生成一棵大樹,然後再剪去那些細枝末節。

> set.seed(2)
> cv.carseats = cv.tree(tree.carseats,FUN = prune.misclass) # 以分類錯誤率爲指標來剪枝
> cv.carseats$size
[1] 16 14 12 10  7  6  2  1
> cv.carseats$dev
[1] 47 46 45 43 46 46 49 78
> plot(cv.carseats$size,cv.carseats$dev,type = "b") # 10個結點的決策樹交叉驗證誤差最低
> prune.carseats = prune.misclass(tree.carseats,best=10)
> plot(prune.carseats)
> text(prune.carseats,pretty = 0)
> tree.pred = predict(prune.carseats,Carseats.test,type = "class")
> table(tree.pred,High.test)
         High.test
tree.pred No Yes
      No  93  37
      Yes 21  49
> (93+49)/200
[1] 0.71

剪枝後的決策樹

可見,剪枝之後的決策樹不僅模型變得更簡單,更易於解釋,測試準確率也提升了(雖然提升得不多)。

迴歸樹

決策樹不僅可以用來分類,也可以用來回歸。迴歸樹與分類樹的差別主要在於兩點:

  • 迴歸樹並不採用熵或基尼係數,而是將連續的特徵採用分割點分割成離散的區間,以左右兩側的RSS最小爲優化目標;
  • 分類樹最後以劃分空間內點的投票作爲分類結果,而分歸樹最後以劃分空間內的平均值作爲迴歸值。

Boston數據集的房價中位數預測爲例,演示構建迴歸樹的過程。

> # 迴歸樹
> 
> library(MASS)
> set.seed(1)
> train = sample(1:nrow(Boston),nrow(Boston)/2)
> tree.boston = tree(medv~.,Boston,subset = train)
> boston.test = Boston[-train,]
> summary(tree.boston)

Regression tree:
tree(formula = medv ~ ., data = Boston, subset = train)
Variables actually used in tree construction:
[1] "lstat" "rm"    "dis"  
Number of terminal nodes:  8 
Residual mean deviance:  12.65 = 3099 / 245 
Distribution of residuals:
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
-14.10000  -2.04200  -0.05357   0.00000   1.96000  12.60000 
> 
> ## 可視化
> plot(tree.boston)
> text(tree.boston,pretty = 0)
> ## 交叉驗證
> tree.pred = predict(tree.boston,boston.test)
> mean((tree.pred-Boston$medv[-train])^2) # MSE
[1] 25.04559
> 
> # 剪枝
> cv.boston = cv.tree(tree.boston)
> plot(cv.boston$size,cv.boston$dev,type = "b")
> cv.boston$size[which.min(cv.boston$dev)] # 最優節點數就是不剪枝時的節點數,因此不必剪枝
[1] 8

迴歸決策樹

節點數的交叉驗證

本例中通過交叉驗證證明不用剪枝,構建出的迴歸值是離散的,通過把新的數據沿着決策樹分枝歸類,然後賦予其一個迴歸值。

決策樹的優缺點

  • 解釋性強,比線性迴歸更強
  • 更貼近人的決策模式,易於理解
  • 易於可視化(高維線性迴歸模型則不能)
  • 可以直接處理分類型變量而不需要創建啞變量
  • 決策樹的準確性不是很高

前文提到決策樹具有容易過擬合、準確性不太高的缺點,可以用裝袋隨機森林提升方法來對組合大量的決策樹,從而提高預測效果。

裝袋

裝袋法(Bagging)又稱自助法聚集(bootstrap aggregation),聯想到之前提到的自助法的思想方法,對於n個同方差σ^2的觀測,其平均值的方差爲σ^2/n,這說明求平均可以降低方差。那麼自然地可以進一步聯想,通過自助法抽取n個樣本,建立n個決策樹模型,然後對n個預測結果求平均,也可以降低方差,提高準確性。那麼裝袋法就可以定義如下:


Bagging

裝袋法通過自助法抽樣B個樣本,建立B棵高方差的決策樹,不必剪枝。對於分類問題,B個分類結果投票選最多的就好;對於迴歸問題,B個迴歸值求平均。B取大一點也不會造成過擬合。裝袋法並不僅適用於決策樹,但對決策樹尤其有用。

隨機森林

隨機森林是裝袋的延伸,不同之處在於:每一次用自助法建立的樣本之後並不用全部特徵去建立決策樹,而是同樣對特徵也進行抽樣,每次抽m個特徵(m一般爲√p,當m=p時,隨機森林就變成裝袋法了)。爲什麼每次不用全部而只有部分特徵?在之前進行多元線性迴歸模型擬合時有一個問題必須注意:特徵之間的相關性。對於裝袋法來說,每次都用所有特徵,如果有一些強特徵,導致每棵樹的分裂方式都類似,這樣不同樹之間的預測變量就高度相關,這樣即使求平均,能減小方差也有限。

隨機森林的思想就是每次只抽一部分特徵來建模,在大量的樹下確保所有的特徵都會被使用,這樣平均之下就會減弱不同樹之間特徵的高度相關性,以減小總體的方差,達到總體的最優。

下面演示裝袋和隨機森林的代碼。

> # 裝袋法做迴歸
> library(randomForest)
> set.seed(1)
> train = sample(1:nrow(Boston),nrow(Boston)/2)
> boston.test = Boston[-train,]
> bag.boston = randomForest(medv~.,data = Boston,subset = train,mtry=13,ntree=500,importance=T) # 每次分裂都考慮全部13個特徵,此時隨機森林變成了裝袋
> bag.boston # 500棵樹解釋了86.57%的方差

Call:
 randomForest(formula = medv ~ ., data = Boston, mtry = 13, ntree = 500,      importance = T, subset = train) 
               Type of random forest: regression
                     Number of trees: 500
No. of variables tried at each split: 13

          Mean of squared residuals: 11.08966
                    % Var explained: 86.57
> 
> ## 測試結果
> yhat.bag = predict(bag.boston,newdata = Boston[-train,])
> plot(yhat.bag,boston.test$medv)
> abline(0,1)
> mean((yhat.bag-boston.test$medv)^2)
[1] 13.33831

與上面最優減枝的單個決策樹相比,裝袋法的測試MSE差不多縮小了一半。

> set.seed(1)
> rf.boston = randomForest(medv~.,data = Boston,subset = train,mtry=6,importance=T)
> yhat.rf = predict(rf.boston,newdata = Boston[-train,])
> mean((yhat.rf-boston.test$medv)^2) 
[1] 11.48022
> importance(rf.boston)
          %IncMSE IncNodePurity
crim    12.547772    1094.65382
zn       1.375489      64.40060
indus    9.304258    1086.09103
chas     2.518766      76.36804
nox     12.835614    1008.73703
rm      31.646147    6705.02638
age      9.970243     575.13702
dis     12.774430    1351.01978
rad      3.911852      93.78200
tax      7.624043     453.19472
ptratio 12.008194     919.06760
black    7.376024     358.96935
lstat   27.666896    6927.98475
> varImpPlot(rf.boston)

可見與裝袋法相比,隨機森林進一步減小了測試MSE。


隨機森林給出的變量重要性

important()函數可以給出隨機森林模型篩選的變量重要性,2列2張圖表示2個測度指標。%IncMSE表示排除此變量時,袋外誤差(自助法並不能抽到全部樣本,每次約能抽到2/3的樣本,剩下的1/3就作爲袋外觀測可用來測試模型)準確性的平均減小值,IncNodePurity表示此變量導致的結點不純度減小的總量。從上圖可看出,最重要的兩個變量是rmlstat

提升

提升(boosting)與裝袋類似,都是集成學習算法,基本思想方法都是把多個弱分類器(但正確率要大於50%否則沒有集成的意義)集成成強分類器。不過與裝袋不同,裝袋的每一步都是獨立抽樣的,提升每一次迭代則是基於前一次的數據進行修正,提高前一次模型中分錯樣本在下次抽中的概率,打個比方就是給一個學生做一張卷子,每做完一次就把他做錯的題抽出來讓他繼續做,直到他所有的題都能做對爲止。

以下是經典的Adaboost算法流程:

爲每個樣本初始化權值w=1/n;開始迭代,在第t輪迭代中:

  1. 使用訓練集訓練分類器Ct,訓練誤差e=所有被分類錯誤樣本的權值之和
  2. 計算分類器的權值爲α=1/2ln((1−e)/e)
  3. 更新樣本當前的權值wt:
    若分類正確,則減少權值,wt+1=wt∗e^−α;
    若分類錯誤,則加大權值,wt+1=wt∗e^α
  4. 將所有樣本的權值歸一化,使其相加爲1。
    用生成的所有分類器預測未知樣本X,最終結果爲所有分類器輸出的加權平均。

以下演示boosting模型的構建過程:

> library(gbm)
> set.seed(1)
> boost.boston = gbm(medv~.,data = Boston[train,],distribution = "gaussian",n.trees=5000,interaction.depth=4)
> summary(boost.boston)
            var    rel.inf
lstat     lstat 45.9627334
rm           rm 31.2238187
dis         dis  6.8087398
crim       crim  4.0743784
nox         nox  2.5605001
ptratio ptratio  2.2748652
black     black  1.7971159
age         age  1.6488532
tax         tax  1.3595005
indus     indus  1.2705924
chas       chas  0.8014323
rad         rad  0.2026619
zn           zn  0.0148083

特徵相對影響圖
> par(mfrow=c(1,2))
> plot(boost.boston,i="rm")
> plot(boost.boston,i="lstat")

兩個變量的偏相關圖
> yhat.boost = predict(boost.boston,newdata = Boston[-train,],n.trees = 5000)
> mean((yhat.boost-boston.test$medv)^2)
[1] 11.84434

可見boosting的效果跟隨機森林差不多。

還可以給提升法加上正則項壓縮參數λ,默認是0.001,這裏增大成0.2。

> boost.boston = gbm(medv~.,data = Boston[train,],distribution = "gaussian",n.trees=5000,interaction.depth=4,shrinkage=0.2,verbose=F)
> yhat.boost = predict(boost.boston,newdata = Boston[-train,],n.trees = 5000)
> mean((yhat.boost-boston.test$medv)^2)
[1] 11.51109

可見效果好一點。

PS:因爲是隨機的,所以,再運行的話結果會有一些不一樣(特別是迴歸樹,分類樹一般一致)。

原文地址:http://www.jianshu.com/p/06f149b7185c


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章