1.引言        

       學(xué)過(guò)數(shù)據(jù)結(jié)構(gòu)的同學(xué)對(duì)二叉樹(shù)應(yīng)該不陌生:二叉樹(shù)是一個(gè)連通的無(wú)環(huán)圖,每個(gè)節(jié)點(diǎn)最多有兩個(gè)子樹(shù)的樹(shù)結(jié)構(gòu)。如下圖(一)就是一個(gè)深度k=3的二叉樹(shù)。

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)       Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

            (圖一)                                                          (圖二)

       二元決策樹(shù)與此類似。不過(guò)二元決策樹(shù)是基于屬性做一系列二元(是/否)決策。每次決策從下面的兩種決策中選擇一種,然后又會(huì)引出另外兩種決策,依次類推直到葉子節(jié)點(diǎn):即最終的結(jié)果。也可以理解為是對(duì)二叉樹(shù)的遍歷,或者很多層的if-else嵌套。

        這里需要特別說(shuō)明的是:二元決策樹(shù)中的深度算法與二叉樹(shù)中的深度算法是不一樣的。二叉樹(shù)的深度是指有多少層,而二元決策樹(shù)的深度是指經(jīng)過(guò)多少層計(jì)算。以上圖(一)為例,二叉樹(shù)的深度k=3,而在二元決策樹(shù)中深度k=2。

        圖二就是一個(gè)二元決策樹(shù)的例子,其中最關(guān)鍵的是如何選擇切割點(diǎn):即X[0]<=-0.075中的-0.0751是如何選擇出來(lái)的?

 

2.二元決策樹(shù)切割點(diǎn)的選擇

       切割點(diǎn)的選擇是二元決策樹(shù)最核心的部分,其基本思路是:遍歷所有數(shù)據(jù),嘗試每個(gè)數(shù)據(jù)作為分割點(diǎn),并計(jì)算此時(shí)左右兩側(cè)的數(shù)據(jù)的離差平方和,并從中找到最小值,然后找到離差平方和最小時(shí)對(duì)應(yīng)的數(shù)據(jù),它就是最佳分割點(diǎn)。下面通過(guò)具體的代碼講解這一過(guò)程:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

import numpyimport matplotlib.pyplot as plot#建立一個(gè)100數(shù)據(jù)的測(cè)試集nPoints = 100#x的取值范圍:-0.5~+0.5的nPoints等分xPlot = [-0.5+1/nPoints*i for i in range(nPoints + 1)]#y值:在x的取值上加一定的隨機(jī)值或者叫噪音數(shù)據(jù)#設(shè)置隨機(jī)數(shù)算法生成數(shù)據(jù)時(shí)的開(kāi)始值,保證隨機(jī)生成的數(shù)值一致numpy.random.seed(1)##隨機(jī)生成寬度為0.1的標(biāo)準(zhǔn)正態(tài)分布的數(shù)值##上面的設(shè)置是為了保證numpy.random這步生成的數(shù)據(jù)一致y = [s + numpy.random.normal(scale=0.1) for s in xPlot]#離差平方和列表sumSSE = []for i in range(1, len(xPlot)):    #以xPlot[i]為界,分成左側(cè)數(shù)據(jù)和右側(cè)數(shù)據(jù)
    lhList = list(xPlot[0:i])
    rhList = list(xPlot[i:len(xPlot)])    #計(jì)算每側(cè)的平均值
    lhAvg = sum(lhList) / len(lhList)
    rhAvg = sum(rhList) / len(rhList)    #計(jì)算每側(cè)的離差平方和
    lhSse = sum([(s - lhAvg) * (s - lhAvg) for s in lhList])
    rhSse = sum([(s - rhAvg) * (s - rhAvg) for s in rhList])    #統(tǒng)計(jì)總的離差平方和,即誤差和
    sumSSE.append(lhSse + rhSse)##找到最小的誤差和minSse = min(sumSSE)##產(chǎn)生最小誤差和時(shí)對(duì)應(yīng)的數(shù)據(jù)索引idxMin = sumSSE.index(minSse)##打印切割點(diǎn)數(shù)據(jù)及切割點(diǎn)位置print("切割點(diǎn)位置:"+str(idxMin)) ##49print("切割點(diǎn)數(shù)據(jù):"+str(xPlot[idxMin]))##-0.010000000000000009##繪制離差平方和隨切割點(diǎn)變化而變化的曲線plot.plot(range(1, len(xPlot)), sumSSE)
plot.xlabel('Split Point Index')
plot.ylabel('Sum Squared Error')
plot.show()

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

 

3.使用二元決策樹(shù)擬合數(shù)據(jù)

    這里使用sklearn.tree.DecisionTreeRegressor函數(shù)。下面只顯示了主要代碼,數(shù)據(jù)生成部分同上:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

from sklearn import treefrom sklearn.tree import DecisionTreeRegressor##使用二元決策樹(shù)擬合數(shù)據(jù):深度為1##說(shuō)明numpy.array(xPlot).reshape(1, -1):這是傳入?yún)?shù)的需要:list->narraysimpleTree = DecisionTreeRegressor(max_depth=1)
simpleTree.fit(numpy.array(xPlot).reshape(-1,1), numpy.array(y).reshape(-1,1))##讀取訓(xùn)練后的預(yù)測(cè)數(shù)據(jù)y_pred  = simpleTree.predict(numpy.array(xPlot).reshape(-1,1))##繪圖plot.figure()
plot.plot(xPlot, y, label='True y')
plot.plot(xPlot, y_pred, label='Tree Prediction ', linestyle='--')
plot.legend(bbox_to_anchor=(1,0.2))
plot.axis('tight')
plot.xlabel('x')
plot.ylabel('y')
plot.show()

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

    結(jié)果如下圖:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

                                         (圖三)

   當(dāng)深度依次為2(圖四)、深度為6(圖5)時(shí)的結(jié)果:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)      Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

                                 (圖四)                                                                                (圖五)

 

4.二元決策樹(shù)的過(guò)度擬合

        二元決策樹(shù)同普通最小二乘法一樣,都存在擬合過(guò)度的情況,如圖五所示,幾乎看不到預(yù)測(cè)值的曲線,這就是擬合過(guò)度了。判斷是否擬合過(guò)度有兩種方法:

        1)觀察結(jié)果圖。這個(gè)很好理解,就是直接看繪制的對(duì)比圖。

        2)比較決策樹(shù)終止節(jié)點(diǎn)的數(shù)目與數(shù)據(jù)的規(guī)模。生產(chǎn)圖五的曲線的深度是6(最深會(huì)有7層),即會(huì)有26=64個(gè)節(jié)點(diǎn),而數(shù)據(jù)集中一共才有100個(gè)數(shù)據(jù),也就是說(shuō)有很多節(jié)點(diǎn)是只包括一個(gè)數(shù)據(jù)的。

 

5.二元決策樹(shù)深度的選擇

       一般是通過(guò)不同深度二元決策樹(shù)的交叉驗(yàn)證(前面已講過(guò)原理)來(lái)確定最佳深度的,基本思路:

       1)確定深度列表:

       2)設(shè)置采用幾折交叉驗(yàn)證

       3)計(jì)算每折交叉驗(yàn)證時(shí)的樣本外數(shù)據(jù)的均方誤差

       4)繪圖,觀察結(jié)果

       下面就通過(guò)深度分別為1~7的10折交叉驗(yàn)證來(lái)檢驗(yàn)下最佳深度。

  

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

import numpy 
import matplotlib.pyplot as plotfrom sklearn import treefrom sklearn.tree import DecisionTreeRegressor#建立一個(gè)100數(shù)據(jù)的測(cè)試集nPoints = 100#x的取值范圍:-0.5~+0.5的nPoints等分xPlot = [-0.5+1/nPoints*i for i in range(nPoints + 1)]#y值:在x的取值上加一定的隨機(jī)值或者叫噪音數(shù)據(jù)#設(shè)置隨機(jī)數(shù)算法生成數(shù)據(jù)時(shí)的開(kāi)始值,保證隨機(jī)生成的數(shù)值一致numpy.random.seed(1)##隨機(jī)生成寬度為0.1的標(biāo)準(zhǔn)正態(tài)分布的數(shù)值##上面的設(shè)置是為了保證numpy.random這步生成的數(shù)據(jù)一致y = [s + numpy.random.normal(scale=0.1) for s in xPlot]##測(cè)試數(shù)據(jù)的長(zhǎng)度nrow = len(xPlot)##設(shè)置二元決策樹(shù)的深度列表depthList = [1, 2, 3, 4, 5, 6, 7]##每個(gè)深度下的離差平方和xvalMSE = []##設(shè)置n折交叉驗(yàn)證nxval = 10##外層循環(huán):深度循環(huán)for iDepth in depthList:    ##每個(gè)深度下的樣本外均方誤差
    oosErrors =0    ##內(nèi)層循環(huán):交叉驗(yàn)證循環(huán)
    for ixval in range(nxval+1):        #定義訓(xùn)練集和測(cè)試集標(biāo)簽
        xTrain = []  #訓(xùn)練集
        xTest = []   #測(cè)試集
        yTrain = []  #訓(xùn)練集標(biāo)簽
        yTest = []   #測(cè)試集標(biāo)簽

        for a in range(nrow):            ##如果采用a%ixval==0的方式寫(xiě),會(huì)有除數(shù)為0的錯(cuò)誤
            if a%nxval != ixval%nxval:
                xTrain.append(xPlot[a])
                yTrain.append(y[a])            else :
                xTest.append(xPlot[a])
                yTest.append(y[a])                
        ##深度為max_depth=iDepth的訓(xùn)練
        treeModel = DecisionTreeRegressor(max_depth=iDepth)        ##轉(zhuǎn)換參數(shù)類型
        treeModel.fit(numpy.array(xTrain).reshape(-1, 1), numpy.array(yTrain).reshape(-1, 1))        ##讀取預(yù)測(cè)值:使用測(cè)試集獲取樣本外誤差
        treePrediction = treeModel.predict(numpy.array(xTest).reshape(-1, 1))        ##離差列表:使用測(cè)試標(biāo)簽獲取樣本外誤差
        error = [yTest[r] - treePrediction[r] for r in range(len(yTest))]        ##每個(gè)深度下的樣本外均方誤差和
        oosErrors += sum([e * e for e in error])   
    #計(jì)算每個(gè)深度下的樣本外平均離差平方和
    mse = oosErrors/nrow    ##添加到離差平方和列表    xvalMSE.append(mse)    
##繪圖---樣本外離差和的平方平均值隨深度變化的曲線plot.plot(depthList, xvalMSE)
plot.axis('tight')
plot.xlabel('Tree Depth')
plot.ylabel('Mean Squared Error')
plot.show()

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

結(jié)果如圖:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

                     (圖六)

       從圖中可以看出,當(dāng)深度為3時(shí)的效果最好,下面我們把深度調(diào)成3,觀察結(jié)果(為了效果調(diào)整了上面代碼的顏色值):

Android培訓(xùn),安卓培訓(xùn),手機(jī)開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)                                   (圖七) 

http://www.cnblogs.com/lc1217/p/6739612.html