cart迴歸樹:練手+sklearn

背景

cart樹作爲決策樹的一種,在非常多的地方被使用。既可以用於分類問題,也可以用於迴歸問題。分類問題則非常容易理解,利用gini係數較大的特徵進行樣本分裂,從而構建一顆分類樹。 今天我們要探討的是迴歸樹。

迴歸樹cart簡介

迴歸樹,則目標函數是平方差,也就是說,分完之後形成left和right子樹,
每個子樹對label,也就是y,進行平方差的計算。最後左右子樹的平方差之和則是評估標準。 我們的目標則是選擇平方差之和比較小的特徵來進行劃分。 停止條件則是,沒有可劃分的,或者誤差之和非常小。

練手實現

如果明白了上面的定義,其實就兩個點:選特徵,目標平方差最小;
分裂; 然後繼續直到結束。

def loadDataSet():
    dataSet = []
    f = open('regData.txt')
    fr = f.readlines()
    for line in fr:
        line = line.strip().split('\t')
        linef = [float(li) for li in line]
        dataSet.append(linef)
    dataSetMat = mat(dataSet)
    return dataSetMat

def calcErr(dataSetMat):
    '''
    dataSetMat[line,col]
    :param dataSetMat:
    :return:
    '''
    label = dataSetMat[:,-1]
    #print(type(label),len(label))
    sqrtvar = var(label)* len(label)
    #print(sqrtvar)
    return sqrtvar

def chooseBestFeatVal2Split(dataSetMat):
    t = dataSetMat[:, -1].T.tolist()[0]
    if(len(set(t))==1):
        return None,None
    # print(t)
    # print(type(t))
    bestFeature = 0
    bestValue = 0
    lowestErr = 100000
    totalErr = calcErr(dataSetMat)
    for feature in range(shape(dataSetMat)[1] - 1): #2個屬性
        allValues = [d[feature] for d in dataSetMat.tolist()] #每個屬性下面的所有值
        values = set(allValues) #理論上排個序比較好
        for value in values:
            leftChild, rightChild = splitByFeatVal(feature, value, dataSetMat)
            if (shape(leftChild)[0] == 0 or shape(rightChild)[0] == 0): continue
            curErr = calcErr(leftChild) + calcErr(rightChild)
            if (curErr < lowestErr):
                bestFeature = feature
                bestValue = value
                lowestErr = curErr
    # 如果誤差減少很小,停止
    if (totalErr - lowestErr < 1): return None, None

    return bestFeature, bestValue

def splitByFeatVal(feature, value, dataSetMat):
    #左子樹的值大於根節點的值
    rg = dataSetMat[:, feature] > value
    lineIndex = nonzero(rg)[0] #獲取滿足條件的index
    leftChild = dataSetMat[lineIndex, :]

    rg = dataSetMat[:, feature] <= value
    lineIndex = nonzero(rg)[0]  # 獲取滿足條件的index
    #右子樹的值小於等於根節點的值
    rightChild = dataSetMat[lineIndex, :]

    return leftChild, rightChild


def createRegTree(dataSetMat):
    feature, value = chooseBestFeatVal2Split(dataSetMat)
    print("best feature:",feature,"loss:",value)
    # 如果無法分割,那麼返回葉節點的值,即所有dataSetMat的均值
    if feature == None: return mean(dataSetMat[:, -1])
    # 如果可以繼續分割,那麼繼續創建新的子樹
    regTree = {}
    regTree['featIndex'] = feature
    regTree['value'] = value
    leftChild, rightChild = splitByFeatVal(feature, value, dataSetMat)
    regTree['leftChild'] = createRegTree(leftChild)
    regTree['rightChild'] = createRegTree(rightChild)

    return regTree

會得到一棵樹:

{'featIndex': 1, 'value': 10.0, 'leftChild': {'featIndex': 1, 'value': 20.0, 'leftChild':  , 'rightChild': {'featIndex': 1, 'value': 15.0, 'leftChild': 15.266, 'rightChild': 10.27}}, 'rightChild': {'featIndex': 1, 'value': 5.0, 'leftChild': 5.07, 'rightChild': 0.078}}

sklearn實踐


def sklearn_regressiontree(dataSetMat):
   X = dataSetMat[:,0:2]
   y = dataSetMat[:,-1]
   clf = DecisionTreeRegressor()
   clf = clf.fit(X,y)
   dot_data = export_graphviz(clf, out_file=None)
   graph = graphviz.Source(dot_data)
   print(clf)
   graph.render("regressiontree")

得到的結果如下:
在這裏插入圖片描述

數據regData.txt

5.23 1 0.1
5.23 2 0.12
5.23 3 0.02
5.23 4 0.03
5.23 5 0.12
5.23 6 5.0
5.23 7 5.2
5.23 8 5.1
5.23 9 5.02
5.23 10 5.03
5.23 11 10.8
5.23 12 10.06
5.23 13 10.03
5.23 14 10.02
5.23 15 10.44
5.23 16 15.88
5.23 17 15.06
5.23 18 15.04
5.23 19 15.30
5.23 20 15.05
5.23 21 20.8
5.23 22 20.16
5.23 23 20.24
5.23 24 20.05
5.23 25 20.09

總結

  1. cart的核心在於選擇特徵
  2. 評估的指標是每個分支的y的平方差,也就是var*n
  3. 自己操作一下可以對特徵理解深化
  4. sklearn裏就是decisiontreeregressionor.fit(X,y)
  5. 可以利用graphviz可以轉成dot並render存儲。
  6. 注意conda安裝是conda install python-graphviz

參考文獻

https://scikit-learn.org/stable/modules/tree.html#regression

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