溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

學習日志---樹回歸(回歸樹,模型樹)

發(fā)布時間:2020-07-15 14:36:19 來源:網(wǎng)絡 閱讀:1217 作者:wukong0716 欄目:開發(fā)技術

CART算法的樹回歸:

返回的每個節(jié)點最后是一個最終確定的平均值。

#coding:utf-8

import numpy as np

# 加載文件數(shù)據(jù)
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

#在dataset中選擇特征為feature的這一列,以value值分成兩部分
def binSplitDataSet(dataSet, feature, value):
    mat0 = dataSet[np.nonzero(dataSet[:,feature] > value)[0],:][0]
    mat1 = dataSet[np.nonzero(dataSet[:,feature] <= value)[0],:][0]
    return mat0,mat1

#計算此矩陣的最后一列結果的平均值,用平均值來當做最后的返回結果,后面的模型樹返回的是一個 線性模型
def regLeaf(dataSet):
    return np.mean(dataSet[:,-1])

#計算dataset結果的混亂程度,用方差反應,因為是連續(xù)數(shù)據(jù)
def regErr(dataSet):
    return np.var(dataSet[:,-1]) * np.shape(dataSet)[0]

#選擇最佳的分離特征和該特征的分離點
#這里的ops是預先的給定值,1是差別太小就不分了,4是分開后的各自樣本數(shù),太小就舍去,這是一  種預剪枝方法
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 = np.shape(dataSet)
    #the choice of the best feature is driven by Reduction in RSS error from mean
    S = errType(dataSet)
    bestS = np.inf; bestIndex = 0; bestValue = 0
    #循環(huán)所有的特征
    for featIndex in range(n-1):
        #循環(huán)該特征下的所有特征值
        for splitVal in set(dataSet[:,featIndex]):
            mat0, mat1 = binSplitDataSet(dataSet, featIndex, splitVal)
            #如果更具這個特征值分成的兩類有一個小與預先給定值,說明分類太偏,則不考慮
            if (np.shape(mat0)[0] < tolN) or (np.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) #exit cond 2
    mat0, mat1 = binSplitDataSet(dataSet, bestIndex, bestValue)
    if (np.shape(mat0)[0] < tolN) or (np.shape(mat1)[0] < tolN):  #exit cond 3
        return None, leafType(dataSet)
    return bestIndex,bestValue              

#創(chuàng)建樹
def createTree(dataSet, leafType=regLeaf, errType=regErr, ops=(1,4)):   
    feat, val = chooseBestSplit(dataSet, leafType, errType, ops)       
    if feat == None: 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


myDat = loadDataSet('ex0.txt')
myMat = np.mat(myDat)
result = createTree(myMat)
print result

結果:

{'spInd': 1, 'spVal': matrix(` 0`.`39435`), 'right': {'spInd': 1, 'spVal': matrix(` 0`.`197834`), 'right': -0.023838155555555553, 'left': 1.0289583666666666}, 'left': {'spInd': 1, 'spVal': matrix(` 0`.`582002`), 'right': 1.980035071428571, 'left': {'spInd': 1, 'spVal': matrix(` 0`.`797583`), 'right': 2.9836209534883724, 'left': 3.9871631999999999}}}

結果的意思是:第幾個特征,以多大作為特征值分開,分成左右,依次分下去。

這個算法很好,但是對數(shù)據(jù)的分類太過于高,容易造成過擬合。因此要采用剪枝技術。

通過降低決策樹的復雜度來避免過擬合的過程稱為剪枝。


#判斷obj是否是一個子樹
def isTree(obj):
    return (type(obj).__name__=='dict')

#用于坍塌處理,當測試數(shù)據(jù)集是空是,則取整個樹的平均值
def getMean(tree):
    if isTree(tree['right']): tree['right'] = getMean(tree['right'])
    if isTree(tree['left']): tree['left'] = getMean(tree['left'])
    return (tree['left']+tree['right'])/2.0

#剪枝函數(shù)
def prune(tree, testData):
    
    #如果測試數(shù)據(jù)集為空,則坍塌處理
    if np.shape(testData)[0] == 0: return getMean(tree)   
    
    #如果左或者右是樹,則把測試數(shù)據(jù)集根據(jù)決策樹進行分割
    if (isTree(tree['right']) or isTree(tree['left'])):
        lSet, rSet = binSplitDataSet(testData, tree['spInd'], tree['spVal'])
    
    #如果左側是樹,則把數(shù)據(jù)集和子樹帶入繼續(xù)找
    if isTree(tree['left']): tree['left'] = prune(tree['left'], lSet)
    #同理
    if isTree(tree['right']): tree['right'] =  prune(tree['right'], rSet)
    #if they are now both leafs, see if we can merge them
    #如果左右都是節(jié)點,則計算節(jié)點誤差
    if not isTree(tree['left']) and not isTree(tree['right']):
        lSet, rSet = binSplitDataSet(testData, tree['spInd'], tree['spVal'])
        #計算不合并的誤差
        errorNoMerge = sum(np.power(lSet[:,-1] - tree['left'],2)) + sum(np.power(rSet[:,-1] - tree['right'],2))
        treeMean = (tree['left']+tree['right'])/2.0
        #計算將當前兩個葉子節(jié)點合并后的誤差
        errorMerge = sum(np.power(testData[:,-1] - treeMean,2))
        if errorMerge < errorNoMerge:
            print "merging"
            #可以合并就返回平均值
            return treeMean
        #不可以合并就返回樹,不變
        else: return tree
    else: return tree

一般來說都是預剪枝和后剪枝合并使用


模型樹

每個節(jié)點是一個線性模型

其他基本一樣:

#對數(shù)據(jù)集進行線性回歸
def linearSolve(dataSet):
    m,n = np.shape(dataSet)
    X = np.mat(np.ones((m,n))); Y = np.mat(np.ones((m,1)))
    #有一列是常數(shù)項,因此要多出一列放置常數(shù)項
    X[:,1:n] = dataSet[:,0:n-1]; Y = dataSet[:,-1]
    xTx = X.T*X
    if np.linalg.det(xTx) == 0.0:
        raise NameError('This matrix is singular, cannot do inverse,\n\
        try increasing the second value of ops')
    ws = xTx.I * (X.T * Y)
    return ws,X,Y

#產(chǎn)生針對該數(shù)據(jù)集的線性模型
#相當于上面的regLeaf函數(shù)
def modelLeaf(dataSet):
    ws,X,Y = linearSolve(dataSet)
    return ws

#產(chǎn)生針對該數(shù)據(jù)集的線性模型,并計算誤差返回
#相當于上面的regErr函數(shù),計算模型的誤差,如果分后和不分的誤差差不多則選擇不分
def modelErr(dataSet):
    ws,X,Y = linearSolve(dataSet)
    yHat = X * ws
    return sum(np.power(Y - yHat,2))

模型樹回歸很好,而且可以用作預測

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI