第9章 樹回歸
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=default"></script>
樹回歸 概述
我們本章介紹 CART(Classification And Regression Trees, 分類回歸樹) 的樹構建算法。該算法既可以用于分類還可以用于回歸。
樹回歸 場景
我們在第 8 章中介紹了線性回歸的一些強大的方法,但這些方法創建的模型需要擬合所有的樣本點(局部加權線性回歸除外)。當數據擁有眾多特征并且特征之間關系十分復雜時,構建全局模型的想法就顯得太難了,也略顯笨拙。而且,實際生活中很多問題都是非線性的,不可能使用全局線性模型來擬合任何數據。
一種可行的方法是將數據集切分成很多份易建模的數據,然后利用我們的線性回歸技術來建模。如果首次切分后仍然難以擬合線性模型就繼續切分。在這種切分方式下,樹回歸和回歸法就相當有用。
除了我們在 第3章 中介紹的 決策樹算法,我們介紹一個新的叫做 CART(Classification And Regression Trees, 分類回歸樹) 的樹構建算法。該算法既可以用于分類還可以用于回歸。
1、樹回歸 原理
1.1、樹回歸 原理概述
為成功構建以分段常數為葉節點的樹,需要度量出數據的一致性。第3章使用樹進行分類,會在給定節點時計算數據的混亂度。那么如何計算連續型數值的混亂度呢?
在這里,計算連續型數值的混亂度是非常簡單的。首先計算所有數據的均值,然后計算每條數據的值到均值的差值。為了對正負差值同等看待,一般使用絕對值或平方值來代替上述差值。
上述做法有點類似于前面介紹過的統計學中常用的方差計算。唯一不同就是,方差是平方誤差的均值(均方差),而這里需要的是平方誤差的總值(總方差)。總方差可以通過均方差乘以數據集中樣本點的個數來得到。
1.2、樹構建算法 比較
我們在 第3章 中使用的樹構建算法是 ID3 。ID3 的做法是每次選取當前最佳的特征來分割數據,并按照該特征的所有可能取值來切分。也就是說,如果一個特征有 4 種取值,那么數據將被切分成 4 份。一旦按照某特征切分后,該特征在之后的算法執行過程中將不會再起作用,所以有觀點認為這種切分方式過于迅速。另外一種方法是二分切分法,即每次把數據集切分成兩份。如果數據的某特征值等于切分所要求的值,那么這些數據就進入樹的左子樹,反之則進入樹的右子樹。
除了切分過于迅速外, ID3 算法還存在另一個問題,它不能直接處理連續型特征。只有事先將連續型特征轉換成離散型,才能在 ID3 算法中使用。但這種轉換過程會破壞連續型變量的內在性質。而使用二元切分法則易于對樹構造過程進行調整以處理連續型特征。具體的處理方法是: 如果特征值大于給定值就走左子樹,否則就走右子樹。另外,二分切分法也節省了樹的構建時間,但這點意義也不是特別大,因為這些樹構建一般是離線完成,時間并非需要重點關注的因素。
CART 是十分著名且廣泛記載的樹構建算法,它使用二元切分來處理連續型變量。對 CART 稍作修改就可以處理回歸問題。第 3 章中使用香農熵來度量集合的無組織程度。如果選用其他方法來代替香農熵,就可以使用樹構建算法來完成回歸。
回歸樹與分類樹的思路類似,但是葉節點的數據類型不是離散型,而是連續型。
1.2.1、附加 各常見樹構造算法的劃分分支方式
還有一點要說明,構建決策樹算法,常用到的是三個方法: ID3, C4.5, CART.
三種方法區別是劃分樹的分支的方式:
- ID3 是信息增益分支
- C4.5 是信息增益率分支
- CART 是 GINI 系數分支
工程上總的來說:
CART 和 C4.5 之間主要差異在于分類結果上,CART 可以回歸分析也可以分類,C4.5 只能做分類;C4.5 子節點是可以多分的,而 CART 是無數個二叉子節點;
以此拓展出以 CART 為基礎的 “樹群” Random forest , 以 回歸樹 為基礎的 “樹群” GBDT 。
1.3、樹回歸 工作原理
1、找到數據集切分的最佳位置,函數 chooseBestSplit() 偽代碼大致如下:
對每個特征:
對每個特征值:
將數據集切分成兩份(小于該特征值的數據樣本放在左子樹,否則放在右子樹)
計算切分的誤差
如果當前誤差小于當前最小誤差,那么將當前切分設定為最佳切分并更新最小誤差
返回最佳切分的特征和閾值
2、樹構建算法,函數 createTree() 偽代碼大致如下:
找到最佳的待切分特征:
如果該節點不能再分,將該節點存為葉節點
執行二元切分
在右子樹調用 createTree() 方法
在左子樹調用 createTree() 方法
1.4、樹回歸 開發流程
(1) 收集數據:采用任意方法收集數據。
(2) 準備數據:需要數值型數據,標稱型數據應該映射成二值型數據。
(3) 分析數據:繪出數據的二維可視化顯示結果,以字典方式生成樹。
(4) 訓練算法:大部分時間都花費在葉節點樹模型的構建上。
(5) 測試算法:使用測試數據上的R^2值來分析模型的效果。
(6) 使用算法:使用訓練處的樹做預測,預測結果還可以用來做很多事情。
1.5、樹回歸 算法特點
優點:可以對復雜和非線性的數據建模。
缺點:結果不易理解。
適用數據類型:數值型和標稱型數據。
1.6、回歸樹 項目案例
1.6.1、項目概述
在簡單數據集上生成一棵回歸樹。
1.6.2、開發流程
收集數據:采用任意方法收集數據
準備數據:需要數值型數據,標稱型數據應該映射成二值型數據
分析數據:繪出數據的二維可視化顯示結果,以字典方式生成樹
訓練算法:大部分時間都花費在葉節點樹模型的構建上
測試算法:使用測試數據上的R^2值來分析模型的效果
使用算法:使用訓練出的樹做預測,預測結果還可以用來做很多事情
收集數據:采用任意方法收集數據
data1.txt 文件中存儲的數據格式如下:
0.036098 0.155096
0.993349 1.077553
0.530897 0.893462
0.712386 0.564858
0.343554 -0.371700
0.098016 -0.332760
準備數據:需要數值型數據,標稱型數據應該映射成二值型數據
分析數據:繪出數據的二維可視化顯示結果,以字典方式生成樹
基于 CART 算法構建回歸樹的簡單數據集
用于測試回歸樹的分段常數數據集
訓練算法: 構造樹的數據結構
def binSplitDataSet(dataSet, feature, value):
"""binSplitDataSet(將數據集,按照feature列的value進行 二元切分)
Description:在給定特征和特征值的情況下,該函數通過數組過濾方式將上述數據集合切分得到兩個子集并返回。
Args:
dataMat 數據集
feature 待切分的特征列
value 特征列要比較的值
Returns:
mat0 小于等于 value 的數據集在左邊
mat1 大于 value 的數據集在右邊
Raises:
"""
# # 測試案例
# print 'dataSet[:, feature]=', dataSet[:, feature]
# print 'nonzero(dataSet[:, feature] > value)[0]=', nonzero(dataSet[:, feature] > value)[0]
# print 'nonzero(dataSet[:, feature] <= value)[0]=', nonzero(dataSet[:, feature] <= value)[0]
# dataSet[:, feature] 取去每一行中,第1列的值(從0開始算)
# nonzero(dataSet[:, feature] > value) 返回結果為true行的index下標
mat0 = dataSet[nonzero(dataSet[:, feature] <= value)[0], :]
mat1 = dataSet[nonzero(dataSet[:, feature] > value)[0], :]
return mat0, mat1
# 1.用最佳方式切分數據集
# 2.生成相應的葉節點
def chooseBestSplit(dataSet, leafType=regLeaf, errType=regErr, ops=(1, 4)):
"""chooseBestSplit(用最佳方式切分數據集 和 生成相應的葉節點)
Args:
dataSet 加載的原始數據集
leafType 建立葉子點的函數
errType 誤差計算函數(求總方差)
ops [容許誤差下降值,切分的最少樣本數]。
Returns:
bestIndex feature的index坐標
bestValue 切分的最優值
Raises:
"""
# ops=(1,4),非常重要,因為它決定了決策樹劃分停止的threshold值,被稱為預剪枝(prepruning),其實也就是用于控制函數的停止時機。
# 之所以這樣說,是因為它防止決策樹的過擬合,所以當誤差的下降值小于tolS,或劃分后的集合size小于tolN時,選擇停止繼續劃分。
# 最小誤差下降值,劃分后的誤差減小小于這個差值,就不用繼續劃分
tolS = ops[0]
# 劃分最小 size 小于,就不繼續劃分了
tolN = ops[1]
# 如果結果集(最后一列為1個變量),就返回退出
# .T 對數據集進行轉置
# .tolist()[0] 轉化為數組并取第0列
if len(set(dataSet[:, -1].T.tolist()[0])) == 1: # 如果集合size為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)
# inf 正無窮大
bestS, bestIndex, bestValue = inf, 0, 0
# 循環處理每一列對應的feature值
for featIndex in range(n-1): # 對于每個特征
# [0]表示這一列的[所有行],不要[0]就是一個array[[所有行]]
for splitVal in set(dataSet[:, featIndex].T.tolist()[0]):
# 對該列進行分組,然后組內的成員的val值進行 二元切分
mat0, mat1 = binSplitDataSet(dataSet, featIndex, splitVal)
# 判斷二元切分的方式的元素數量是否符合預期
if (shape(mat0)[0] < tolN) or (shape(mat1)[0] < tolN):
continue
newS = errType(mat0) + errType(mat1)
# 如果二元切分,算出來的誤差在可接受范圍內,那么就記錄切分點,并記錄最小誤差
# 如果劃分后誤差小于 bestS,則說明找到了新的bestS
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)
# 對整體的成員進行判斷,是否符合預期
# 如果集合的 size 小于 tolN
if (shape(mat0)[0] < tolN) or (shape(mat1)[0] < tolN): # 當最佳劃分后,集合過小,也不劃分,產生葉節點
return None, leafType(dataSet)
return bestIndex, bestValue
# assume dataSet is NumPy Mat so we can array filtering
# 假設 dataSet 是 NumPy Mat 類型的,那么我們可以進行 array 過濾
def createTree(dataSet, leafType=regLeaf, errType=regErr, ops=(1, 4)):
"""createTree(獲取回歸樹)
Description:遞歸函數:如果構建的是回歸樹,該模型是一個常數,如果是模型樹,其模型師一個線性方程。
Args:
dataSet 加載的原始數據集
leafType 建立葉子點的函數
errType 誤差計算函數
ops=(1, 4) [容許誤差下降值,切分的最少樣本數]
Returns:
retTree 決策樹最后的結果
"""
# 選擇最好的切分方式: feature索引值,最優切分值
# choose the best split
feat, val = chooseBestSplit(dataSet, leafType, errType, ops)
# if the splitting hit a stop condition return val
# 如果 splitting 達到一個停止條件,那么返回 val
if feat is None:
return val
retTree = {}
retTree['spInd'] = feat
retTree['spVal'] = val
# 大于在右邊,小于在左邊,分為2個數據集
lSet, rSet = binSplitDataSet(dataSet, feat, val)
# 遞歸的進行調用,在左右子樹中繼續遞歸生成樹
retTree['left'] = createTree(lSet, leafType, errType, ops)
retTree['right'] = createTree(rSet, leafType, errType, ops)
return retTree
完整代碼地址: https://github.com/apachecn/MachineLearning/blob/master/src/python/9.RegTrees/regTrees.py
測試算法:使用測試數據上的R^2值來分析模型的效果
使用算法:使用訓練出的樹做預測,預測結果還可以用來做很多事情
2、樹剪枝
一棵樹如果節點過多,表明該模型可能對數據進行了 “過擬合”。
通過降低決策樹的復雜度來避免過擬合的過程稱為 剪枝(pruning)
。在函數 chooseBestSplit() 中提前終止條件,實際上是在進行一種所謂的 預剪枝(prepruning)
操作。另一個形式的剪枝需要使用測試集和訓練集,稱作 后剪枝(postpruning)
。
2.1、預剪枝(prepruning)
顧名思義,預剪枝就是及早的停止樹增長,在構造決策樹的同時進行剪枝。
所有決策樹的構建方法,都是在無法進一步降低熵的情況下才會停止創建分支的過程,為了避免過擬合,可以設定一個閾值,熵減小的數量小于這個閾值,即使還可以繼續降低熵,也停止繼續創建分支。但是這種方法實際中的效果并不好。
2.2、后剪枝(postpruning)
決策樹構造完成后進行剪枝。剪枝的過程是對擁有同樣父節點的一組節點進行檢查,判斷如果將其合并,熵的增加量是否小于某一閾值。如果確實小,則這一組節點可以合并一個節點,其中包含了所有可能的結果。合并也被稱作 塌陷處理
,在回歸樹中一般采用取需要合并的所有子樹的平均值。后剪枝是目前最普遍的做法。
后剪枝 prune() 的偽代碼如下:
基于已有的樹切分測試數據:
如果存在任一子集是一棵樹,則在該子集遞歸剪枝過程
計算將當前兩個葉節點合并后的誤差
計算不合并的誤差
如果合并會降低誤差的話,就將葉節點合并
2.3、剪枝 代碼
回歸樹剪枝函數
# 判斷節點是否是一個字典
def isTree(obj):
"""
Desc:
測試輸入變量是否是一棵樹,即是否是一個字典
Args:
obj -- 輸入變量
Returns:
返回布爾類型的結果。如果 obj 是一個字典,返回true,否則返回 false
"""
return (type(obj).__name__ == 'dict')
# 計算左右枝丫的均值
def getMean(tree):
"""
Desc:
從上往下遍歷樹直到葉節點為止,如果找到兩個葉節點則計算它們的平均值。
對 tree 進行塌陷處理,即返回樹平均值。
Args:
tree -- 輸入的樹
Returns:
返回 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
# 檢查是否適合合并分枝
def prune(tree, testData):
"""
Desc:
從上而下找到葉節點,用測試數據集來判斷將這些葉節點合并是否能降低測試誤差
Args:
tree -- 待剪枝的樹
testData -- 剪枝所需要的測試數據 testData
Returns:
tree -- 剪枝完成的樹
"""
# 判斷是否測試數據集沒有數據,如果沒有,就直接返回tree本身的均值
if shape(testData)[0] == 0:
return getMean(tree)
# 判斷分枝是否是dict字典,如果是就將測試數據集進行切分
if (isTree(tree['right']) or isTree(tree['left'])):
lSet, rSet = binSplitDataSet(testData, tree['spInd'], tree['spVal'])
# 如果是左邊分枝是字典,就傳入左邊的數據集和左邊的分枝,進行遞歸
if isTree(tree['left']):
tree['left'] = prune(tree['left'], lSet)
# 如果是右邊分枝是字典,就傳入左邊的數據集和左邊的分枝,進行遞歸
if isTree(tree['right']):
tree['right'] = prune(tree['right'], rSet)
# 上面的一系列操作本質上就是將測試數據集按照訓練完成的樹拆分好,對應的值放到對應的節點
# 如果左右兩邊同時都不是dict字典,也就是左右兩邊都是葉節點,而不是子樹了,那么分割測試數據集。
# 1. 如果正確
# * 那么計算一下總方差 和 該結果集的本身不分枝的總方差比較
# * 如果 合并的總方差 < 不合并的總方差,那么就進行合并
# 注意返回的結果: 如果可以合并,原來的dict就變為了 數值
if not isTree(tree['left']) and not isTree(tree['right']):
lSet, rSet = binSplitDataSet(testData, tree['spInd'], tree['spVal'])
# power(x, y)表示x的y次方
errorNoMerge = sum(power(lSet[:, -1] - tree['left'], 2)) + sum(power(rSet[:, -1] - tree['right'], 2))
treeMean = (tree['left'] + tree['right'])/2.0
errorMerge = sum(power(testData[:, -1] - treeMean, 2))
# 如果 合并的總方差 < 不合并的總方差,那么就進行合并
if errorMerge < errorNoMerge:
print "merging"
return treeMean
else:
return tree
else:
return tree
完整代碼地址: https://github.com/apachecn/MachineLearning/blob/master/src/python/9.RegTrees/regTrees.py
3、模型樹
3.1、模型樹 簡介
用樹來對數據建模,除了把葉節點簡單地設定為常數值之外,還有一種方法是把葉節點設定為分段線性函數,這里所謂的 分段線性(piecewise linear)
是指模型由多個線性片段組成。
我們看一下圖 9-4 中的數據,如果使用兩條直線擬合是否比使用一組常數來建模好呢?答案顯而易見。可以設計兩條分別從 0.0~0.3、從 0.3~1.0 的直線,于是就可以得到兩個線性模型。因為數據集里的一部分數據(0.00.3)以某個線性模型建模,而另一部分數據(0.31.0)則以另一個線性模型建模,因此我們說采用了所謂的分段線性模型。
決策樹相比于其他機器學習算法的優勢之一在于結果更易理解。很顯然,兩條直線比很多節點組成一棵大樹更容易解釋。模型樹的可解釋性是它優于回歸樹的特點之一。另外,模型樹也具有更高的預測準確度。
將之前的回歸樹的代碼稍作修改,就可以在葉節點生成線性模型而不是常數值。下面將利用樹生成算法對數據進行劃分,且每份切分數據都能很容易被線性模型所表示。這個算法的關鍵在于誤差的計算。
那么為了找到最佳切分,應該怎樣計算誤差呢?前面用于回歸樹的誤差計算方法這里不能再用。稍加變化,對于給定的數據集,應該先用模型來對它進行擬合,然后計算真實的目標值與模型預測值間的差值。最后將這些差值的平方求和就得到了所需的誤差。
3.2、模型樹 代碼
模型樹的葉節點生成函數
# 得到模型的ws系數:f(x) = x0 + x1*featrue1+ x3*featrue2 ...
# create linear model and return coeficients
def modelLeaf(dataSet):
"""
Desc:
當數據不再需要切分的時候,生成葉節點的模型。
Args:
dataSet -- 輸入數據集
Returns:
調用 linearSolve 函數,返回得到的 回歸系數ws
"""
ws, X, Y = linearSolve(dataSet)
return ws
# 計算線性模型的誤差值
def modelErr(dataSet):
"""
Desc:
在給定數據集上計算誤差。
Args:
dataSet -- 輸入數據集
Returns:
調用 linearSolve 函數,返回 yHat 和 Y 之間的平方誤差。
"""
ws, X, Y = linearSolve(dataSet)
yHat = X * ws
# print corrcoef(yHat, Y, rowvar=0)
return sum(power(Y - yHat, 2))
# helper function used in two places
def linearSolve(dataSet):
"""
Desc:
將數據集格式化成目標變量Y和自變量X,執行簡單的線性回歸,得到ws
Args:
dataSet -- 輸入數據
Returns:
ws -- 執行線性回歸的回歸系數
X -- 格式化自變量X
Y -- 格式化目標變量Y
"""
m, n = shape(dataSet)
# 產生一個關于1的矩陣
X = mat(ones((m, n)))
Y = mat(ones((m, 1)))
# X的0列為1,常數項,用于計算平衡誤差
X[:, 1: n] = dataSet[:, 0: n-1]
Y = dataSet[:, -1]
# 轉置矩陣*矩陣
xTx = X.T * X
# 如果矩陣的逆不存在,會造成程序異常
if linalg.det(xTx) == 0.0:
raise NameError('This matrix is singular, cannot do inverse,\ntry increasing the second value of ops')
# 最小二乘法求最優解: w0*1+w1*x1=y
ws = xTx.I * (X.T * Y)
return ws, X, Y
完整代碼地址: https://github.com/apachecn/MachineLearning/blob/master/src/python/9.RegTrees/regTrees.py
3.3、模型樹 運行結果
4、樹回歸 項目案例
4.1、項目案例1: 樹回歸與標準回歸的比較
4.1.1、項目概述
前面介紹了模型樹、回歸樹和一般的回歸方法,下面測試一下哪個模型最好。
這些模型將在某個數據上進行測試,該數據涉及人的智力水平和自行車的速度的關系。當然,數據是假的。
4.1.2、開發流程
收集數據:采用任意方法收集數據
準備數據:需要數值型數據,標稱型數據應該映射成二值型數據
分析數據:繪出數據的二維可視化顯示結果,以字典方式生成樹
訓練算法:模型樹的構建
測試算法:使用測試數據上的R^2值來分析模型的效果
使用算法:使用訓練出的樹做預測,預測結果還可以用來做很多事情
收集數據: 采用任意方法收集數據
準備數據:需要數值型數據,標稱型數據應該映射成二值型數據
數據存儲格式:
3.000000 46.852122
23.000000 178.676107
0.000000 86.154024
6.000000 68.707614
15.000000 139.737693
分析數據:繪出數據的二維可視化顯示結果,以字典方式生成樹
訓練算法:模型樹的構建
用樹回歸進行預測的代碼
# 回歸樹測試案例
# 為了和 modelTreeEval() 保持一致,保留兩個輸入參數
def regTreeEval(model, inDat):
"""
Desc:
對 回歸樹 進行預測
Args:
model -- 指定模型,可選值為 回歸樹模型 或者 模型樹模型,這里為回歸樹
inDat -- 輸入的測試數據
Returns:
float(model) -- 將輸入的模型數據轉換為 浮點數 返回
"""
return float(model)
# 模型樹測試案例
# 對輸入數據進行格式化處理,在原數據矩陣上增加第0列,元素的值都是1,
# 也就是增加偏移值,和我們之前的簡單線性回歸是一個套路,增加一個偏移量
def modelTreeEval(model, inDat):
"""
Desc:
對 模型樹 進行預測
Args:
model -- 輸入模型,可選值為 回歸樹模型 或者 模型樹模型,這里為模型樹模型
inDat -- 輸入的測試數據
Returns:
float(X * model) -- 將測試數據乘以 回歸系數 得到一個預測值 ,轉化為 浮點數 返回
"""
n = shape(inDat)[1]
X = mat(ones((1, n+1)))
X[:, 1: n+1] = inDat
# print X, model
return float(X * model)
# 計算預測的結果
# 在給定樹結構的情況下,對于單個數據點,該函數會給出一個預測值。
# modelEval是對葉節點進行預測的函數引用,指定樹的類型,以便在葉節點上調用合適的模型。
# 此函數自頂向下遍歷整棵樹,直到命中葉節點為止,一旦到達葉節點,它就會在輸入數據上
# 調用modelEval()函數,該函數的默認值為regTreeEval()
def treeForeCast(tree, inData, modelEval=regTreeEval):
"""
Desc:
對特定模型的樹進行預測,可以是 回歸樹 也可以是 模型樹
Args:
tree -- 已經訓練好的樹的模型
inData -- 輸入的測試數據
modelEval -- 預測的樹的模型類型,可選值為 regTreeEval(回歸樹) 或 modelTreeEval(模型樹),默認為回歸樹
Returns:
返回預測值
"""
if not isTree(tree):
return modelEval(tree, inData)
if inData[tree['spInd']] <= tree['spVal']:
if isTree(tree['left']):
return treeForeCast(tree['left'], inData, modelEval)
else:
return modelEval(tree['left'], inData)
else:
if isTree(tree['right']):
return treeForeCast(tree['right'], inData, modelEval)
else:
return modelEval(tree['right'], inData)
# 預測結果
def createForeCast(tree, testData, modelEval=regTreeEval):
"""
Desc:
調用 treeForeCast ,對特定模型的樹進行預測,可以是 回歸樹 也可以是 模型樹
Args:
tree -- 已經訓練好的樹的模型
inData -- 輸入的測試數據
modelEval -- 預測的樹的模型類型,可選值為 regTreeEval(回歸樹) 或 modelTreeEval(模型樹),默認為回歸樹
Returns:
返回預測值矩陣
"""
m = len(testData)
yHat = mat(zeros((m, 1)))
# print yHat
for i in range(m):
yHat[i, 0] = treeForeCast(tree, mat(testData[i]), modelEval)
# print "yHat==>", yHat[i, 0]
return yHat
完整代碼地址: https://github.com/apachecn/MachineLearning/blob/master/src/python/9.RegTrees/regTrees.py
測試算法:使用測試數據上的R^2值來分析模型的效果
R^2 判定系數就是擬合優度判定系數,它體現了回歸模型中自變量的變異在因變量的變異中所占的比例。如 R^2=0.99999 表示在因變量 y 的變異中有 99.999% 是由于變量 x 引起。當 R^2=1 時表示,所有觀測點都落在擬合的直線或曲線上;當 R^2=0 時,表示自變量與因變量不存在直線或曲線關系。
所以我們看出, R^2 的值越接近 1.0 越好。
使用算法:使用訓練出的樹做預測,預測結果還可以用來做很多事情
5、附加 Python 中 GUI 的使用
5.1、使用 Python 的 Tkinter 庫創建 GUI
如果能讓用戶不需要任何指令就可以按照他們自己的方式來分析數據,就不需要對數據做出過多解釋。其中一個能同時支持數據呈現和用戶交互的方式就是構建一個圖形用戶界面(GUI,Graphical User Interface),如圖9-7所示。
5.2、用 Tkinter 創建 GUI
Python 有很多 GUI 框架,其中一個易于使用的 Tkinter,是隨 Python 的標準版編譯版本發布的。Tkinter 可以在 Windows、Mac OS和大多數的 Linux 平臺上使用。
5.3、集成 Matplotlib 和 Tkinter
MatPlotlib 的構建程序包含一個前端,也就是面向用戶的一些代碼,如 plot() 和 scatter() 方法等。事實上,它同時創建了一個后端,用于實現繪圖和不同應用之間接口。
通過改變后端可以將圖像繪制在PNG、PDF、SVG等格式的文件上。下面將設置后端為 TkAgg (Agg 是一個 C++ 的庫,可以從圖像創建光柵圖)。TkAgg可以在所選GUI框架上調用Agg,把 Agg 呈現在畫布上。我們可以在Tk的GUI上放置一個畫布,并用 .grid()來調整布局。
5.4、用treeExplore 的GUI構建的模型樹示例圖
完整代碼地址: https://github.com/apachecn/MachineLearning/blob/master/src/python/9.RegTrees/treeExplore.py
6、樹回歸 小結
數據集中經常包含一些復雜的相關關系,使得輸入數據和目標變量之間呈現非線性關系。對這些復雜的關系建模,一種可行的方式是使用樹來對預測值分段,包括分段常數或分段直線。一般采用樹結構來對這種數據建模。相應地,若葉節點使用的模型是分段常數則稱為回歸樹,若葉節點使用的模型師線性回歸方程則稱為模型樹。
CART 算法可以用于構建二元樹并處理離散型或連續型數據的切分。若使用不同的誤差準則,就可以通過CART 算法構建模型樹和回歸樹。該算法構建出的樹會傾向于對數據過擬合。一棵過擬合的樹常常十分復雜,剪枝技術的出現就是為了解決這個問題。兩種剪枝方法分別是預剪枝(在樹的構建過程中就進行剪枝)和后剪枝(當樹構建完畢再進行剪枝),預剪枝更有效但需要用戶定義一些參數。
Tkinter 是 Python 的一個 GUI 工具包。雖然并不是唯一的包,但它最常用。利用 Tkinter ,我們可以輕輕松松繪制各種部件并安排它們的位置。另外,可以為 Tkinter 構造一個特殊的部件來顯示 Matplotlib 繪出的圖。所以,Matplotlib 和 Tkinter 的集成可以構建出更強大的 GUI ,用戶可以以更自然的方式來探索機器學習算法的奧妙。
- 作者:片刻 小瑤
- GitHub地址: https://github.com/apachecn/MachineLearning
- 版權聲明:歡迎轉載學習 => 請標注信息來源于 ApacheCN