線性回歸
用線性回歸找到最佳擬合直線
回歸的目的是預測數值型數據,根據輸入寫出一個目標值的計算公式,這個公式就是回歸方程(regression equation),變量前的係數(比如一元一次方程)稱為回歸係數(regression weights)。求這些回歸係數的過程就是回歸。
假設輸入數據存放在矩陣X 中,回歸係數存放在向量w 中,那麼對於數據X 1 的預測結果可以用Y 1 =X T 1 w 得出。我們需要找到使誤差最小的w ,但是如果使用誤差之間的累加的話,那么正負誤差將會抵消,起不到效果,所以採用平方誤差。如下:
∑ i=1 n (y i −x T i w) 2
用矩陣表示:(Y−Xw) T (Y−Xw) 。對W 求導得到X T (Y−Xw) ,令其等於零得到w 的最佳估計:w ^ =(X T X) −1 X T y
首先我們導入數據,代碼如下:from numpy import *def loadDataSet(fileName): numFeat = len(open(fileName).readline().split('\t'))-1 dataMat = []; labelMat = [] fr = open(fileName) for line in fr.readlines(): lineArr = [] curLine = line.strip().split('\t') for i in range(numFeat): lineArr.append(float(curLine[i])) dataMat.append(lineArr) labelMat.append(float(curLine[-1])) return dataMat, labelMat
這部分數據是由tap分隔,並且最後一個值是目標值。接下來計算回歸係數:
def standRegres(xArr, yArr): xMat = mat(xArr); yMat = mat(yArr).T xTx = xMat.T*xMat if linalg.det(xTx) == 0.0: print "This matrix is singular, cannot do inverse" return ws = xTx.I * (xMat.T*yMat) return ws
該函數首先讀入x 和y 數組,然後將它們轉換成矩陣,之後根據公式計算平方誤差,注意,需要對X T X 求逆,此時需要判斷它的行列式是否為0,行列式等於0的矩陣無法求逆,可以直接使用linalg.det() 來計算行列式。好了,我們來看看效果,首先讀入數據:
xArr,yArr=loadDataSet('ex0.txt')
我們先看看前兩條數據:
print xArr[0:2][[1.0, 0.067732], [1.0, 0.42781]]
第一個值總是等於1.0,也就是x 0 ,假定偏移量是一個常數,第二個值是x 1 。
現在看看計算回歸係數函數效果:
ws = standRegres(xArr,yArr) print ws[[ 3.00774324] [ 1.69532264]]
現在得到了回歸係數,那麼我們可以通過回歸方程進行預測yHat:
xMat = mat(xArr)yMat = mat(yArr)yHat = xMat*ws
為方便觀察,我們畫出數據集散點圖:
import matplotlib.pyplot as pltfig = plt.figure()ax = fig.add_subplot(111)ax.scatter(xMat[:,1].flatten().A[0], yMat.T[:,0].flatten().A[0])
現在繪製最佳擬合直線,那麼需要畫出預測值yHat,如果直線上數據點次序混亂,繪圖時將會出現問題,所以要將點按照升序排序:
xCopy = xMat.copy()xCopy.sort(0)yHat=xCopy*wsax.plot(xCopy[:,1], yHat)plt.show()
我們來看看效果:
最佳擬合直線方法將數據視為直線進行建模,但圖中的數據集似乎還有更好的擬合方式。
局部加權線性回歸
線性回歸可能出現欠擬合的現象,如上圖所示,因為它求的是具有最小均方誤差的無偏差估計。所以有些方法允許在估計中引入一些偏差,從而降低預測的均方誤差。其中一個方法就是使用局部加權線性回歸(Locally Weighted Linear Regression,LWLR),我們給待測點附近的每個點賦予一定的權重,是用此方法解出的回歸係數如下:
w ^ =(X T WX) −1 X T Wy
其中W 是一個矩陣,用來給每個數據點賦予權重。LWLR使用「核」來對附近的點賦予更高的權重,核的類型可以自由選擇,最常用的核就是高斯核,高斯核對應的權重如下:
w(i,i)=exp(|x i −x|−2k 2 )
下面來編寫局部加權線性回歸:def lwlr(testPoint, xArr, yArr, k = 1.0): xMat = mat(xArr); yMat = mat(yArr).T m = shape(xMat)[0] weights = mat(eye((m))) for j in range(m): diffMat = testPoint - xMat[j,:] weights[j,j] = exp(diffMat*diffMat.T/(-2.0*k**2)) xTx = xMat.T * (weights * xMat) if linalg.det(xTx) == 0: print "This matrix is singular, cannot do inverse" return ws = xTx.I * (xMat.T * (weights * yMat)) return testPoint * wsdef lwlrTest(testArr, xArr, yArr, k=1.0): m = shape(testArr)[0] yHat = zeros(m) for i in range(m): yHat[i] = lwlr(testArr[i],xArr,yArr,k) return yHat
lwlr()函數得到的是單點的估計,為得到所有點的估計,可以調用lwlrTest()函數。
yHat = lwlrTest(xArr,xArr,yArr,1.0)
下面繪出估計值和原始值,看看yHat的擬合效果。需要將數據點按序排列。
xMat = mat(xArr)srtInd = xMat[:,1].argsort(0)xSort = xMat[srtInd][:,0,:]
然後使用Matplotlib繪圖:
fig = plt.figure() ax = fig.add_subplot(111) ax.plot(xSort[:,1],yHat[srtInd]) ax.scatter(xMat[:,1].flatten().A[0], mat(yArr).T.flatten().A[0], s = 2, c = 'red') plt.show()
當k=1.0時,權重很大,相當於將所有數據視為等權重,得出的最佳擬合直線與標準線性回歸一致:
當使用k=0.01時:
yHat = lwlrTest(xArr,xArr,yArr,0.01)
結果如下:
當使用k=0.003時:
yHat = lwlrTest(xArr,xArr,yArr,0.003)
結果如下:
由上三種結果可以看出,在k=1.0時,得出的擬合曲線欠擬合,k=0.01時,曲線擬合效果不錯,而k=0.003時,又有些過擬合。