機器學習系列(三十六)——迴歸決策樹與決策樹總結

本篇主要內容:迴歸決策樹原理、迴歸樹學習曲線、決策樹總結

迴歸決策樹原理

迴歸決策樹樹是用於迴歸的決策樹模型,迴歸決策樹主要指CART算法, 同樣也爲二叉樹結構。以兩個特徵預測輸出的迴歸問題爲例,迴歸樹的原理是將特徵平面劃分成若干單元,每一個劃分單元都對應一個特定的輸出。因爲每個結點都是yes和no的判斷,所以劃分的邊界是平行於座標軸的。對於測試數據,我們只要將特徵按照決策過程將其歸到某個單元,便得到對應的迴歸輸出值。

如上圖所示的劃分和相應的迴歸樹,如果現在新來一個數據的特徵是(6,7.5),按照迴歸樹,它對應的迴歸結果就是C5。節點的劃分的過程也就是樹的建立過程,每劃分一次,隨即確定劃分單元對應的輸出,也就多了一個結點。當根據相應的約束條件終止劃分的時候,最終每個單元的輸出也就確定了,輸出也就是葉結點。這看似和分類樹差不多,實則有很大的區別。劃分點的尋找和輸出值的確定是迴歸決策樹的兩個核心問題。
一個輸入空間的劃分的誤差是用真實值和劃分區域的預測值的最小二乘來衡量的:
\sum_{x_{i}\in R_{m}}(y_{i}-f(x_{i}))^2

其中,f(x_i)是每個劃分單元的預測值,這個預測值是該單元內每個樣本點的值的某種組合,比如可取均值:
f(x_{i})=c_{m}=ave(y_{i}|x_{i}\in R_{m})

(輸入特徵空間劃分爲R_1,R_2,...,R_m
那麼求解最優劃分即是求解最優化問題:
min_{j,s}[min_{c_{1}}\sum_{x_{i}\in R_{1}(j,s)}(y_{i}-c_{1})^2+min_{c_{2}}\sum_{x_{i}\in R_{2}(j,s)}(y_{i}-c_{2})^2]

其中,R_1(j,s)=\langle x|x^{j}\leq s\rangleR_2(j,s)=\langle x|x^{j}\leq s\rangle是每次劃分形成的兩個區域。
關於該最優化問題的求解這裏不再介紹,下面直接使用skleaen中的決策迴歸樹來看一下決策樹的迴歸效果,數據集使用Boston房價數據:

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
boston=datasets.load_boston()
x=boston.data
y=boston.target

from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,random_state=666)
from sklearn.tree import DecisionTreeRegressor
dt_reg=DecisionTreeRegressor()
dt_reg.fit(x_train,y_train)
dt_reg.score(x_test,y_test)

不進行調參的話,可以看到在測試集上R方是0.59,顯然這是不太好的結果,但是一個有趣的現象是,在訓練集上:

R方值是1.0,也就是在訓練集上決策樹預測的迴歸結果完全吻合毫無偏差,這顯然是過擬合。這個例子也說明了決策樹算法是非常容易產生過擬合的,當然我們可以通過調參來緩解過擬合。


學習曲線

下面繪製學習曲線來直觀看一下決策樹迴歸模型的表現,首先繪製基於MSE的學習曲線:

from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import mean_squared_error

def plot_learning_curve(algo, X_train, X_test, y_train, y_test):
    train_score = []
    test_score = []
    for i in range(1, len(X_train)+1):
        algo.fit(X_train[:i], y_train[:i])
    
        y_train_predict = algo.predict(X_train[:i])
        train_score.append(mean_squared_error(y_train[:i], y_train_predict))
    
        y_test_predict = algo.predict(X_test)
        test_score.append(mean_squared_error(y_test, y_test_predict))
        
    plt.plot([i for i in range(1, len(X_train)+1)], 
                               np.sqrt(train_score), label="train")
    plt.plot([i for i in range(1, len(X_train)+1)], 
                               np.sqrt(test_score), label="test")
    plt.legend()
    plt.show()
    
plot_learning_curve(DecisionTreeRegressor(), X_train, X_test, y_train, y_test)

學習曲線如下:

再繪製基於R方的學習曲線:

from sklearn.metrics import r2_score
def plot_learning_curve_r2(algo, X_train, X_test, y_train, y_test):
    train_score = []
    test_score = []
    for i in range(1, len(X_train)+1):
        algo.fit(X_train[:i], y_train[:i])
    
        y_train_predict = algo.predict(X_train[:i])
        train_score.append(r2_score(y_train[:i], y_train_predict))
    
        y_test_predict = algo.predict(X_test)
        test_score.append(r2_score(y_test, y_test_predict))
        
    plt.plot([i for i in range(1, len(X_train)+1)], 
                               train_score, label="train")
    plt.plot([i for i in range(1, len(X_train)+1)], 
                               test_score, label="test")
    plt.legend()
    plt.axis([0, len(X_train)+1, -0.1, 1.1])
    plt.show()
    
plot_learning_curve_r2(DecisionTreeRegressor(), X_train, X_test, y_train, y_test)

上面兩種都是在默認情況下也就是不進行決策樹深度和葉子節點個數等條件的限制得到的結果。發現在訓練集上,如果不進行限制,可以做到0偏差,這是明顯的過擬合。接下來調節參數再繪製學習曲線,爲節約篇幅,只調節決策樹深度這一個參數,而且只繪製基於R方的學習曲線:
max_depth=1時

plot_learning_curve_r2(DecisionTreeRegressor(max_depth=1), X_train, X_test, y_train, y_test)

max_depth=3時

plot_learning_curve_r2(DecisionTreeRegressor(max_depth=3), X_train, X_test, y_train, y_test)

max_depth=5時

plot_learning_curve_r2(DecisionTreeRegressor(max_depth=5), X_train, X_test, y_train, y_test)

隨着深度的增加,模型複雜度越來越高,過擬合現象也越來越明顯,可以測試,當max_depth=20時,在訓練集上又爲一條y=1的無偏差直線。有興趣的仍然可以修改其它參數繪製學習曲線。


決策樹總結

決策樹的侷限性:

  • 決策樹最嚴重的侷限性是決策樹生成的決策邊界是平行於座標軸的直線的組合,旋轉數據集則決策邊界會改變,因此決策不穩定;
  • 另外決策樹對個別數據敏感。(這幾乎是所有非參數學習算法的弊端之一)

使用本系列上篇文章中的鳶尾花數據,來看一下決策樹對個別數據敏感會導致的結果,在本系列上篇文章中,使用信息熵劃分,其餘參數默認情況下繪製的決策邊界是:

接着我們刪除索引爲138的數據,再來繪製決策邊界:

X_new = np.delete(x,138,axis=0)
y_new = np.delete(y,138)
dt_clf2 = DecisionTreeClassifier(max_depth=2,criterion="entropy")
dt_clf2.fit(X_new,y_new)#用數據訓練模型

plot_decision_boundary(dt_clf2,axis=[0.5,7.5,0,3])
plt.scatter(x[y==0,0],x[y==0,1])
plt.scatter(x[y==1,0],x[y==1,1])
plt.scatter(x[y==2,0],x[y==2,1])
plt.show()

發現此時的決策邊界已經完全不同了,而這僅僅只是一個數據點的影響。


綜上我們知道決策樹實際是一種不夠穩定的算法,它的表現極度依賴調參和數據,不過雖然決策樹本身不是一種高效的機器學習算法,但是它們基於集成學習的組合——隨機森林(RF)卻是一個很魯棒的機器學習算法,這將在下篇開始介紹。

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