前面介紹了模型樹、回歸樹和一般的回歸方法,下面測試一下哪個模型最好。本節首先給出一些函數,它們可以在樹構建好的情況下對給定的輸入進行預測,之後利用這些函數來計算三種回歸模型的測試誤差。這些模型將在某個數據上進行測試,該數據涉及人的智力水平和自行車的速度的關係。
這裡的數據是非線性的,不能簡單地使用第8章的全局線性模型建模。當然這裡也需要聲明一下,此數據純屬虛構。
下面先給出在給定輸入和樹結構情況下進行預測的幾個函數。打開regTrees.py
並加入如下代碼。
程序清單9-5 用樹回歸進行預測的代碼
def regTreeEval(model, inDat):
return float(model)
def modelTreeEval(model, inDat):
n = shape(inDat)[1]
X = mat(ones((1,n+1)))
X[:,1:n+1]=inDat
return float(X*model)
def treeForeCast(tree, inData, modelEval=regTreeEval):
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):
m=len(testData)
yHat = mat(zeros((m,1)))
for i in range(m):
yHat[i,0] = treeForeCast(tree, mat(testData[i]), modelEval)
return yHat
對於輸入的單個數據點或者行向量,函數treeForeCast
會返回一個浮點值。在給定樹結構的情況下,對於單個數據點,該函數會給出一個預測值。調用函數treeForeCast
時需要指定樹的類型,以便在葉節點上能夠調用合適的模型 。參數modelEval
是對葉節點數據進行預測的函數的引用。函數treeForeCast
自頂向下遍歷整棵樹,直到命中葉節點為止。一旦到達葉節點,它就會在輸入數據上調用modelEval
函數,而該函數的默認值是regTreeEval
。
要對回歸樹葉節點進行預測,就調用函數regTreeEval
;要對模型樹節點進行預測時,就調用modelTreeEval
函數。它們會對輸入數據進行格式化處理,在原數據矩陣上增加第0列,然後計算並返回預測值。為了與函數modelTreeEval
保持一致, 儘管regTreeEval
只使用一個輸入,但仍保留了兩個輸入參數。
最後一個函數是createForCast
,它會多次調用treeForeCast
函數。由於它能夠以向量形式返回的一組預測值,因此該函數在對整個測試集進行預測時非常有用。下面很快會看到這一點。
接下來考慮圖9-6所示的數據。該數據是我從多個騎自行車的人那裡收集得到的。圖中給出騎自行車的速度和人的智商之間的關係。下面將基於該數據集建立多個模型並在另一個測試集上進行測試。對應的訓練集數據保存在文件bikeSpeedVsIq_train.txt
中,而測試集數據保存在文件bikeSpeedVsIq_test.txt
中。
圖9-6 人們騎自行車的速度和他們智商之間的關係數據。該數據用於比較樹回歸模型和普通的線性回歸模型
下面將為圖9-6的數據構建三個模型。首先,將程序清單9-5中的代碼保存為regTrees.py
,然後在Python提示符下輸入以下命令:
>>>reload(regTrees)
接下來,利用該數據創建一棵回歸樹:
>>> trainMat=mat(regTrees.loadDataSet(\'bikeSpeedVsIq_train.txt\'))
>>> testMat=mat(regTrees.loadDataSet(\'bikeSpeedVsIq_test.txt\'))
>>> myTree=regTrees.createTree(trainMat, ops=(1,20))
>>> yHat = regTrees.createForeCast(myTree, testMat[:,0])
>>> corrcoef(yHat, testMat[:,1],rowvar=0)[0,1]
0.96408523182221306
同樣地,再創建一棵模型樹:
>>> myTree=regTrees.createTree(trainMat, regTrees.modelLeaf,
regTrees.modelErr,(1,20))
>>> yHat = regTrees.createForeCast(myTree, testMat[:,0],
regTrees.modelTreeEval)
>>> corrcoef(yHat, testMat[:,1],rowvar=0)[0,1]
0.9760412191380623
我們知道,R2值越接近1.0越好,所以從上面的結果可以看出,這裡模型樹的結果比回歸樹好。下面再看看標準的線性回歸效果如何,這裡無須導入第8章的任何代碼,本章已實現過一個線性方程求解函數linearSolve
:
>>> ws,X,Y=regTrees.linearSolve(trainMat)
>>> ws
matrix([[ 37.58916794],
[ 6.18978355]])
為了得到測試集上所有的yHat
預測值,在測試數據上循環執行:
>>> for i in range(shape(testMat)[0]):
... yHat[i]=testMat[i,0]*ws[1,0]+ws[0,0]
...
最後來看一下R2值:
>>> corrcoef(yHat, testMat[:,1],rowvar=0)[0,1]
0.94346842356747584
可以看到,該方法在R2值上的表現上不如上面兩種樹回歸方法。所以,樹回歸方法在預測複雜數據時會比簡單的線性模型更有效,相信讀者對這個結論也不會感到意外。下面將展示如何對回歸模型進行定性的比較。
下面使用Python提供的框架來構建圖形用戶界面(GUI),讀者可以使用該GUI來探究不同的回歸工具。