機器學習之樹迴歸——CART算法(Python代碼)

一:背景

        線性迴歸包含了強大的方法,但這些方法創建的模型需要擬合所有的樣本(局部加權線性迴歸除外)。當數據擁有衆多特徵並且特徵之間的關係十分複雜時,構建全局模型的想法就顯得太難了。而且現實生活中很多數據都是非線性的,不可能使用全局線性模型來擬合這些數據。

        一種可行的方法時將數據集切分成很多分容易建模的數據,然後可利用線性迴歸技術來建模。如果切分後的仍然難以擬合線性模型就繼續切分。在這個切分方式背景下。樹結構和迴歸方法就十分有用。

      CART(Classification And Regression Tree,即分類迴歸樹),該算法既可以用於分類也可以用於迴歸。下面將介紹複雜數據的局部性建模過程,並分析其中的缺陷,然後對比引出CART算法建樹過程。


二:複雜數據的局部性建模

      在利用決策樹進行分類時,決策樹不斷將數據切分成小數據集。知道所有目標變量完全相同,或者數據不能再切分爲止。實際上,這是一種貪心算法,它要在給定時間內做出最佳選擇,而並沒有關心能否達到全局最優。

樹迴歸

優點 可以對複雜和非線性的數據建模
缺點 結果不容易理解
適用數據類型 數值型和標稱型數據

 

 

 

 

在使用ID3算法構建決策樹模型時,ID3的做法是每次選取當前最佳的特徵類分割數據,並按照該特徵的所有可能進行切分。也就是說特徵有多少種取值,那麼數據將被切分成多少份。一旦按某個特徵切分後,該特徵在之後的算法的執行過程中將不會在起作用。因此,這種切分方法過於迅速。所以又有一種二元切分方法,即每次把數據切分成兩份。如果數據的某個特徵值等於切分所要求的值,那麼這些數據就進入數的左子樹,否則進入樹的右子樹。

     除了切分過於迅速之外,ID3算法還存在另外一種問題,它不能直接處理連續型特徵,只有預先將連續型特徵轉換成離散型特徵,纔可以使用ID3算法。但是這種轉換過程往往會破壞連續型數據特徵內在的性質。而使用二元切分方法則容易對樹構建過程進行調整以便處理連續型特徵。具體的處理方法是:如果特徵值大於給定的閾值就劃分到左子數,佛則劃分到右子樹。


三:CART構建過程

      下面將實現CART算法和迴歸樹。迴歸樹和分類樹的思路類似,但葉節點的數據類型不是離散型而是連續型。

樹迴歸的一般步驟:

  1. 收集數據採用任意方法收集
  2. 準備數據:需要署執行的數據,標稱型數據應該映射成二值型數據
  3. 分析數據:繪出數據的二維可視化顯示結果,以字典方式生成樹
  4. 訓練算法:大部分時間都話費在葉節點樹模型的構建上
  5. 測試算法:使用冊數數據類分析模型的效果
  6. 使用算法:使用訓練處的樹做預測,預測結果還可以用來做很多事情

 

下面將用Python 來實現CART算法用於實現迴歸過程的基礎代碼:

from numpy import *

def loadDataSet(fileName):      #general function to parse tab -delimited floats
    dataMat = []                #assume last column is target value
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split('\t')
        fltLine = map(float,curLine) #map all elements to float()
        dataMat.append(fltLine)
    return dataMat

def binSplitDataSet(dataSet, feature, value):
    mat0 = dataSet[nonzero(dataSet[:,feature] > value)[0],:][0]
    mat1 = dataSet[nonzero(dataSet[:,feature] <= value)[0],:][0]
    return mat0,mat1


def createTree(dataSet, leafType=regLeaf, errType=regErr, ops=(1,4)):#assume dataSet is NumPy Mat so we can array filtering
    feat, val = chooseBestSplit(dataSet, leafType, errType, ops)#choose the best split
    if feat == None: return val #if the splitting hit a stop condition return val
    retTree = {}
    retTree['spInd'] = feat
    retTree['spVal'] = val
    lSet, rSet = binSplitDataSet(dataSet, feat, val)
    retTree['left'] = createTree(lSet, leafType, errType, ops)
    retTree['right'] = createTree(rSet, leafType, errType, ops)
    return retTree  

      第一個函數loadDataSet是用於導入數據集; 

      第二個函數binSplitDataSet,該函數有3個參數:數據集合,待切分的特徵和該特徵的某個值。在給定特徵和特徵值的情況下,該函數通過數組過濾方式將上述數據集合切分得到兩個子集並返回。

      第三個函數createTree是一個遞歸函數,該函數首先嚐試將數據集分層兩個部分。即大於特徵值的左子樹,和小於特徵值的右子樹。


四:將CART算法用於迴歸

     當我們決定由樹結構來進行數據切分時,很自然聯想到一個問題:如何實現數據的切分?如何知道是否已經充分切分?

     這些問題的答案取決於葉節點的建模方式,迴歸樹假設葉節點是常數值,這種策略認爲數據中的複雜關係可以用樹結構來概括。

      爲成功構建以分段常數爲葉節點的樹,需要度兩處數據的一致性。在學習利用決策樹進行分類時,會在給定節點時,利用信息熵計算數據的混亂度。而在計算連續型數據時,則利用均值計算混亂度,即:首先計算所有數據的均值,然後計算每條數據的值到均值的差值,爲了對正負差值同等看待,一般是由絕對值或者平方值來代替差值。注意:這裏需要進一步計算平方誤差的總值(總方差),總方差等於均方差乘以數據集中的樣本個數來得到。

 

def regLeaf(dataSet):#returns the value used for each leaf
    return mean(dataSet[:,-1])

def regErr(dataSet):
    return var(dataSet[:,-1]) * shape(dataSet)[0]


def chooseBestSplit(dataSet, leafType=regLeaf, errType=regErr, ops=(1,4)):
    tolS = ops[0]; tolN = ops[1]
    #if all the target variables are the same value: quit and return value
    if len(set(dataSet[:,-1].T.tolist()[0])) == 1: #exit cond 1
        return None, leafType(dataSet)         #如果所有值相等則退出
    m,n = shape(dataSet)
    #the choice of the best feature is driven by Reduction in RSS error from mean
    S = errType(dataSet)
    bestS = inf; bestIndex = 0; bestValue = 0
    for featIndex in range(n-1):
        for splitVal in set(dataSet[:,featIndex]):
            mat0, mat1 = binSplitDataSet(dataSet, featIndex, splitVal)
            if (shape(mat0)[0] < tolN) or (shape(mat1)[0] < tolN): continue
            newS = errType(mat0) + errType(mat1)
            if newS < bestS: 
                bestIndex = featIndex
                bestValue = splitVal
                bestS = newS
    #if the decrease (S-bestS) is less than a threshold don't do the split
    if (S - bestS) < tolS: 
        return None, leafType(dataSet)   #如果過誤差減少不大則退出
    mat0, mat1 = binSplitDataSet(dataSet, bestIndex, bestValue)
    if (shape(mat0)[0] < tolN) or (shape(mat1)[0] < tolN):  #exit cond 3
        return None, leafType(dataSet)
    return bestIndex,bestValue  #如果切分出的數據集很小則退出
                              

      上述代碼實現的任務如下:

1 對每個特徵:

      2  對每個特徵值:

            3.1 將數據集切分成兩份

            3.2 計算切分的誤差

            3.3 如果當前誤差小於當前最小誤差,那麼將當前切分設定爲最佳切分並更新最小誤差,返回最佳切分的特徵和閾值


 五:總結

      CART是基於但特徵的,沒有考慮特徵之間的關係,因此不需要在初始時進行特徵歸一化處理。

      CART假設決策樹是二叉樹,內部節點特徵取值只有"是" 或者“否”,左分支是“是”,右分支是“否”

      CART ---> Boosting Decision Tree  ---> adaboost  ---> GBDT ---> XGBoost ,它們的基函數都是決策樹。

 

參考文獻:

《機器學習》--周志華

《機器學習實戰》--Peter Harrington

 

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