決策樹和隨機森林

一、前述

決策樹是一種非線性有監督分類模型,隨機森林是一種非線性有監督分類模型。線性分類模型比如說邏輯迴歸,可能會存在不可分問題,但是非線性分類就不存在。
二、具體原理

ID3算法

1、相關術語

根節點:最頂層的分類條件
葉節點:代表每一個類別號
中間節點:中間分類條件
分枝:代表每一個條件的輸出
二叉樹:每一個節點上有兩個分枝
多叉樹:每一個節點上至少有兩個分枝

 2、決策樹的生成:

數據不斷分裂的遞歸過程,每一次分裂,儘可能讓類別一樣的數據在樹的一邊,當樹的葉子節點的數據都是一類的時候,則停止分類。(if else 語句)

3、如何衡量純粹度

舉例:

箱子1:100個紅球
箱子2:50個紅球  50個黑球
箱子3:10個紅球  30個藍球 60綠球
箱子4:各個顏色均10個球

憑人的直覺感受,箱子1是最純粹的,箱子4是最混亂的,如何把人的直覺感受進行量化呢?
將這種純粹度用數據進行量化,計算機才能讀懂

舉例:

度量信息混亂程度指標:

熵的介紹:

熵公式舉例:

熵代表不確定性,不確定性越大,熵越大。代表內部的混亂程度。

比如兩個集合  A有5個類別 每個類別2個值 則每個概率是0.2

比如B有兩個類別,每個類別5個 ,則每個概率是0.5

顯然0.5大於0.2所以熵大,混亂程度比較大。


信息熵H(X):信息熵是香農在1948年提出來量化信息的信息量的。熵的定義如下

n代表種類,每一個類別中,p1代表某個種類的概率*log當前種類的概率,然後將各個類別計算結果累加。

以上例子車禍的信息熵是-(4/9log4/9+5/9log5/9)

條件熵:H(X,Y)類似於條件概率,在知道X的情況下,Y的不確定性

以上例子在知道溫度的情況下,求車禍的概率。

hot 是3個,其中兩個沒有車禍,一個車禍,則3/9(2/3log2/3+1/3log1/3)。

mild是2個,其中一個車禍,一個沒有車禍,則2/9(1/2log1/2+1/2log1/2)

cool是4個,其中三個車禍,一個沒有車禍,則4/9(3/4log3/4+1/4log1/4)。

以上相加即爲已知溫度的情況下,車禍的條件熵。

信息增益:代表的熵的變化程度
                 特徵Y對訓練集D的信息增益g(D,Y)= H(X) - H(X,Y)
以上車禍的信息熵-已知溫度的條件熵就是信息增益。

信息增益即是表示特徵X使得類Y的不確定性減少的程度。
(分類後的專一性,希望分類後的結果是同類在一起)

信息增益越大,熵的變化程度越大,分的越乾脆,越徹底。不確定性越小。

在構建決策樹的時候就是選擇信息增益最大的屬性作爲分裂條件(ID3),使得在每個非葉子節點上進行測試時,都能獲得最大的類別分類增益,使分類後數據集的熵最小,這樣的處理方法使得樹的平均深度較小,從而有效提高了分類效率。

 

C4.5算法:有時候給個特徵,它分的特別多,但是完全分對了,比如訓練集裏面的編號
信息增益特別大,都甚至等於根節點了,那肯定是不合適的
問題在於行編號的分類數目太多了,分類太多意味着這個特徵本身的熵大,大到都快是整個H(X)了
爲了避免這種現象的發生,我們不是用信息增益本身,而是用信息增益除以這個特徵本身的熵值,看除之後的值有多大!這就是信息增益率,如果用信息增益率就是C4.5

 

CART算法:

CART使用的是GINI係數相比ID3和C4.5,CART應用要多一些,既可以用於分類也可以用於迴歸。

CART假設決策樹是二叉樹,內部結點特徵的取值爲“是”和“否”,左分支是取值爲“是”的分支,右分支是取值爲“否”的分支。這樣的決策樹等價於遞歸地二分每個特徵,將輸入空間即特徵空間劃分爲有限個單元,並在這些單元上確定預測的概率分佈,也就是在輸入給定的條件下輸出的條件概率分佈。

CART算法由以下兩步組成:

  1. 決策樹生成:基於訓練數據集生成決策樹,生成的決策樹要儘量大;
  2. 決策樹剪枝:用驗證數據集對已生成的樹進行剪枝並選擇最優子樹,這時損失函數最小作爲剪枝的標準。

CART決策樹的生成就是遞歸地構建二叉決策樹的過程。CART決策樹既可以用於分類也可以用於迴歸。本文我們僅討論用於分類的CART。對分類樹而言,CART用Gini係數最小化準則來進行特徵選擇,生成二叉樹。GINI係數其實是用直線段代替曲線段的近似,GINI係數就是熵的一階近似。

公式如下:

比如兩個集合  A有5個類別 每個類別2個值 則每個概率是0.2

比如B有兩個類別,每個類別5個 ,則每個概率是0.5

假設C有一個類別,則基尼係數是0 ,類別越多,基尼係數越接近於1,所以

我們希望基尼係數越小越好

CART生成算法如下:

輸入:訓練數據集停止計算的條件: 
輸出:CART決策樹。

過程:

損失函數:

以上構建好分類樹之後,評價函數如下:

(希望它越小越好,類似損失函數了)

三、解決過擬合問題方法

 1、背景

葉子節點的個數作爲加權葉子節點的熵乘以加權的加和就是評價函數這就是損失函數,這個損失函數肯定是越小越好了

如何評價呢?
用訓練數據來計算損失函數,決策樹不斷生長的時候,看看測試數據損失函數是不是變得越低了,
這就是交互式的做調參的工作,因爲我們可能需要做一些決策樹葉子節點的剪枝,因爲並不是樹越高越好,因爲樹如果非常高的話,可能過擬合了。

2、解決過擬合兩種方法

剪枝

隨機森林

3、解決過擬合方法之剪枝

爲什麼要剪枝:決策樹過擬合風險很大,理論上可以完全分得開數據(想象一下,如果樹足夠龐大,每個葉子節點不就一個數據了嘛)
剪枝策略:預剪枝,後剪枝
預剪枝:邊建立決策樹邊進行剪枝的操作(更實用)
後剪枝:當建立完決策樹後來進行剪枝操作

預剪枝(用的多)
邊生成樹的時候邊剪枝,限制深度,葉子節點個數,葉子節點樣本數,信息增益量等樹的高度,每個葉節點包含的樣本最小個數,每個葉節點分裂的時候包含樣本最小的個數,每個葉節點最小的熵值等max_depth min_sample_split min_sample_leaf min_weight_fraction_leaf,max_leaf_nodes max_features,增加min_超參 減小max_超參
後剪枝

葉子節點個數越多,損失越大
還是生成一顆樹後再去剪枝,alpha值給定就行

後剪枝舉例:

4、解決過擬合方法之隨機森林

思想Bagging的策略:

從樣本集中重採樣(有可能存在重複)選出n個樣本在所有屬性上,對這n個樣本建立分類器(ID3、C4.5、CART、SVM、Logistic迴歸等)
重複上面兩步m次,產生m個分類器將待預測數據放到這m個分類器上,最後根據這m個分類器的投票結果,決定待預測數據屬於那一類(即少數服從多數的策略)

在Bagging策略的基礎上進行修改後的一種算法
從樣本集中用Bootstrap採樣選出n個樣本;
從所有屬性中隨機選擇K個屬性,選擇出最佳分割屬性作爲節點創建決策樹;
重複以上兩步m次,即建立m棵CART決策樹;
這m個CART形成隨機森林(樣本隨機,屬性隨機),通過投票表決結果決定數據屬於那一類。

當數據集很大的時候,我們隨機選取數據集的一部分,生成一棵樹,重複上述過程,我們可以生成一堆形態各異的樹,這些樹放在一起就叫森林。

隨機森林之所以隨機是因爲兩方面:樣本隨機+屬性隨機

選取過程:

取某些特徵的所有行作爲每一個樹的輸入數據。

然後把測試數據帶入到每一個數中計算結果,少數服從多數,即可求出最終分類。

隨機森林的思考:

在隨機森林的構建過程中,由於各棵樹之間是沒有關係的,相對獨立的;在構建
的過程中,構建第m棵子樹的時候,不會考慮前面的m-1棵樹。因此引出提升的算法,對分錯的樣本加權。

提升是一種機器學習技術,可以用於迴歸和分類的問題,它每一步產生弱預測模型(如決策樹),並加權累加到總模型中;如果每一步的弱預測模型的生成都是依
據損失函數的梯度方式的,那麼就稱爲梯度提升(Gradient boosting)提升技術的意義:如果一個問題存在弱預測模型,那麼可以通過提升技術的辦法得到一個強預測模型。

四、代碼

決策樹:

決策樹的訓練集必須離散化,因爲如果不離散化的話,分類節點很多。

package com.bjsxt.rf
 
import org.apache.spark.mllib.tree.DecisionTree
import org.apache.spark.mllib.util.MLUtils
import org.apache.spark.{SparkContext, SparkConf}
 
object ClassificationDecisionTree {
  val conf = new SparkConf()
  conf.setAppName("analysItem")
  conf.setMaster("local[3]")
  val sc = new SparkContext(conf)
 
  def main(args: Array[String]): Unit = {
    val data = MLUtils.loadLibSVMFile(sc, "汽車數據樣本.txt")
    // Split the data into training and test sets (30% held out for testing)
    val splits = data.randomSplit(Array(0.7, 0.3))
    val (trainingData, testData) = (splits(0), splits(1))
    //指明類別
    val numClasses=2
    //指定離散變量,未指明的都當作連續變量處理
    //1,2,3,4維度進來就變成了0,1,2,3
    //這裏天氣維度有3類,但是要指明4,這裏是個坑,後面以此類推
    val categoricalFeaturesInfo=Map[Int,Int](0->4,1->4,2->3,3->3)
    //設定評判標準
    val impurity="entropy"
    //樹的最大深度,太深運算量大也沒有必要  剪枝   防止模型的過擬合!!!
    val maxDepth=3
    //設置離散化程度,連續數據需要離散化,分成32個區間,默認其實就是32,分割的區間保證數量差不多 這裏可以實現把數據分到0-31這些數中去  這個參數也可以進行剪枝
    val maxBins=32
    //生成模型
    val model =DecisionTree.trainClassifier(trainingData,numClasses,categoricalFeaturesInfo,impurity,maxDepth,maxBins)
   //測試
   val labelAndPreds = testData.map { point =>
     val prediction = model.predict(point.features)
     (point.label, prediction)
   }
    val testErr = labelAndPreds.filter(r => r._1 != r._2).count().toDouble / testData.count()//錯誤率的統計
    println("Test Error = " + testErr)
    println("Learned classification tree model:\n" + model.toDebugString)
 
     }
}

樣本數據:

將第5列數據離散化。

結果:

深度爲3一共15個節點。

隨機森林:

package com.bjsxt.rf
 
import org.apache.spark.{SparkContext, SparkConf}
import org.apache.spark.mllib.util.MLUtils
import org.apache.spark.mllib.tree.RandomForest
 
object ClassificationRandomForest {
  val conf = new SparkConf()
  conf.setAppName("analysItem")
  conf.setMaster("local[3]")
  val sc = new SparkContext(conf)
  def main(args: Array[String]): Unit = {
    //讀取數據
    val data =  MLUtils.loadLibSVMFile(sc,"汽車數據樣本.txt")
    //將樣本按7:3的比例分成
    val splits = data.randomSplit(Array(0.7, 0.3))
    val (trainingData, testData) = (splits(0), splits(1))
    //分類數
    val numClasses = 2
    // categoricalFeaturesInfo 爲空,意味着所有的特徵爲連續型變量
    val categoricalFeaturesInfo =Map[Int, Int](0->4,1->4,2->3,3->3)
    //樹的個數
    val numTrees = 3
    //特徵子集採樣策略,auto 表示算法自主選取
    //"auto"根據特徵數量在4箇中進行選擇
    // 1,all 全部特徵 2,sqrt 把特徵數量開根號後隨機選擇的 3,log2 取對數個 4,onethird 三分之一
    val featureSubsetStrategy = "auto"
    //純度計算
    val impurity = "entropy"
    //樹的最大層次
    val maxDepth = 3
    //特徵最大裝箱數,即連續數據離散化的區間
    val maxBins = 32
    //訓練隨機森林分類器,trainClassifier 返回的是 RandomForestModel 對象
    val model = RandomForest.trainClassifier(trainingData, numClasses, categoricalFeaturesInfo,
      numTrees, featureSubsetStrategy, impurity, maxDepth, maxBins)
    //打印模型
    println(model.toDebugString)
    //保存模型
   //model.save(sc,"汽車保險")
    //在測試集上進行測試
    val count = testData.map { point =>
    val prediction = model.predict(point.features)
//    Math.abs(prediction-point.label)
    (prediction,point.label)
     }.filter(r => r._1 != r._2).count()
    println("Test Error = " + count.toDouble/testData.count().toDouble)
  }
}

結果:

根據DEBUGTREE畫圖舉例:

原貼:https://www.cnblogs.com/LHWorldBlog/p/8656608.html

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