程式扎記: [ ML In Action ] Predicting numeric values : regression - Linear regression (2)

標籤

2012年9月7日 星期五

[ ML In Action ] Predicting numeric values : regression - Linear regression (2)

Problem : 
從 Linear regression (1) 我們知道可以透過 Linear regression 幫我們去預測一個連續的值, 但這樣的 Model 往往會因為 underfit 造成失真. 例如請參考下面兩個 data set, 經過 Linear regression 得到的 weight 會是相同的 [0, 2.0] : 
 

Locally weighted linear regression : 
因此我們需要一些額外的訊息來幫我們解決失真的問題, 這邊引入的方法是 "locally weighted linear regression (LWLR)". 其實說白了就是透過左右鄰點來提供資訊, 針對每一點的 weighting 作一些微調. 因此我們計算出來的 weighting 不在是單一值; 原先的公式也稍微變更成下面式子 : 
 

而引入的紅色 W 即是由臨點帶入參考的 weighting, 讓建立出來的 weight matrix 能更貼近 data set 的分布. 下面的函數 lwlr() 可以根據測試點自動建立紅色的 W 矩陣 (weights) 並將計算後預測的連續值返回 : 
  1. def lwlr(testPoint,xArr,yArr,k=1.0):  
  2.     xMat = mat(xArr); yMat = mat(yArr).T  
  3.     m = shape(xMat)[0]  
  4.     weights = mat(eye((m)))  
  5.     for j in range(m):                      #next 2 lines create weights matrix  
  6.         diffMat = testPoint - xMat[j,:]     #  
  7.         weights[j,j] = exp(diffMat*diffMat.T/(-2.0*k**2))  
  8.     xTx = xMat.T * (weights * xMat)  
  9.     if linalg.det(xTx) == 0.0:  
  10.         print "This matrix is singular, cannot do inverse"  
  11.         return  
  12.     ws = xTx.I * (xMat.T * (weights * yMat))  
  13.     return testPoint * ws  
接著你可以如下預測 xArr 中某一點預測的 y 值 : 
>>> import regression
>>> xArr, yArr = regression.loadDataSet('ex0.txt') # 載入 training data
>>> regression.lwlr(xArr[0], xArr, yArr) # 預測 xArr[0] 點的 y 值
matrix([[ 3.12204471]])

如果你要一次預測一堆點, 則可以利用下面的方法 : 
  1. def lwlrTest(testArr,xArr,yArr,k=1.0):  #loops over all the data points and applies lwlr to each one  
  2.     m = shape(testArr)[0]  
  3.     yHat = zeros(m)  
  4.     for i in range(m):  
  5.         yHat[i] = lwlr(testArr[i],xArr,yArr,k)  
  6.     return yHat  
如果你需要的是返回一個類似 weighting 的東西, 則可以考慮下面的函數 lwlrWeight(). 傳入 training 用的 xArr, yArr 與選定的 k 值(預設是1) 後, 此方法會返回另一個函數物件, 該函數物件可以根據你提供的 x 座標(如 xArr[0]) 預測 y 座標的值 : 
  1. def lwlrWeight(xArr,yArr,k=1.0):  
  2.     xMat = mat(xArr); yMat = mat(yArr).T  
  3.     def _w(testPoint):  
  4.         m = shape(xMat)[0]  
  5.         weights = mat(eye((m)))  
  6.         for j in range(m):                      #next 2 lines create weights matrix  
  7.             diffMat = testPoint - xMat[j,:]     #  
  8.             weights[j,j] = exp(diffMat*diffMat.T/(-2.0*k**2))  
  9.         xTx = xMat.T * (weights * xMat)  
  10.         if linalg.det(xTx) == 0.0:  
  11.             print "This matrix is singular, cannot do inverse"  
  12.             return  
  13.         ws = xTx.I * (xMat.T * (weights * yMat))  
  14.         return testPoint * ws  
  15.     return _w  
使用方法如下 : 
>>> wf = regression.lwlrWeight(xArr, yArr)
>>> wf(xArr[0]) # 預測 xArr[0] 點的 y 值
matrix([[ 3.12204471]])

如果你想觀察預測值形成的線型與 data set 的分布狀況, 可以使用下面函數 : 
  1. def drawLWLRChart(xArr, yArr, lwlrWS):  
  2.     xMat = mat(xArr)  
  3.     yMat = mat(yArr)  
  4.   
  5.     import matplotlib.pyplot as plt  
  6.     fig = plt.figure()  
  7.     ax = fig.add_subplot(111)  
  8.     ax.scatter(xMat[:,1].flatten().A[0], yMat.T[:,0].flatten().A[0])  
  9.     xCopy = xMat.copy()  
  10.     xCopy.sort(0)  
  11.     yHat = zeros([200,1])  
  12.     m = shape(xMat)[0]  
  13.     for i in range(m):  
  14.         yHat[i][0] = lwlrWS(xCopy[i])  
  15.     ax.plot(xCopy[:,1], yHat)  
  16.     plt.show()  
將剛剛得到的 lwlr 的 weighting function 加上 xArr, yArr 傳入該方法 : 
>>> wf = regression.lwlrWeight(xArr, yArr)
>>> regression.drawLWLRChart(xArr, yArr, wf)

可以發現跟 Linear regression (1) 得到的結果差不多, 不過你可以藉由調小 k 值得到更貼近 data set 分布的線形 : 
>>> wf = regression.lwlrWeight(xArr, yArr, 0.01) # 使用 k 值 = 0.01 (預設是1)
>>> regression.drawLWLRChart(xArr, yArr, wf)

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!