讀古今文學網 > 機器學習實戰 > 8.2 局部加權線性回歸 >

8.2 局部加權線性回歸

線性回歸的一個問題是有可能出現欠擬合現象,因為它求的是具有最小均方誤差的無偏估計。顯而易見,如果模型欠擬合將不能取得最好的預測效果。所以有些方法允許在估計中引入一些偏差,從而降低預測的均方誤差。

其中的一個方法是局部加權線性回歸(Locally Weighted Linear Regression,為LWLR)。在該算法中,我們給待預測點附近的每個點賦予一定的權重;然後與8.1節類似,在這個子集上基於最小均方差來進行普通的回歸。與kNN一樣,這種算法每次預測均需要事先選取出對應的數據子集。該算法解出回歸係數w的形式如下:

其中w是一個矩陣,用來給每個數據點賦予權重。

LWLR使用「核」(與支持向量機中的核類似)來對附近的點賦予更高的權重1 。核的類型可以自由選擇,最常用的核就是高斯核,高斯核對應的權重如下:

1. 讀者要注意區分這裡的權重W和回歸係數w;與kNN一樣,該加權模型認為樣本點距離越近,越可能符合同一個線性模型。——譯者注

這樣就構建了一個只含對角元素的權重矩陣w,並且點xx(i)越近,w(i,i)將會越大。上述公式包含一個需要用戶指定的參數k,它決定了對附近的點賦予多大的權重,這也是使用LWLR時唯一需要考慮的參數,在圖8-4中可以看到參數k與權重的關係。

圖8-4 每個點的權重圖(假定我們正預測的點是x = 0.5),最上面的圖是原始數據集,第二個圖顯示了當k = 0.5時,大部分的數據都用於訓練回歸模型;而最下面的圖顯示當k = 0.01時,僅有很少的局部點被用於訓練回歸模型

下面看看模型的效果,打開文本編輯器,將程序清單8-2的代碼添加到文件regression.py中。

程序清單8-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.0:
        print \"This matrix is singular, cannot do inverse\"
        return
    ws = xTx.I * (xMat.T * (weights * yMat))
        return testPoint * ws
def 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  
  

程序清單8-2中代碼的作用是,給定x空間中的任意一點,計算出對應的預測值yHat。函數lwlr的開頭與程序清單8-1類似,讀入數據並創建所需矩陣,之後創建對角權重矩陣weights❶。權重矩陣是一個方陣,階數等於樣本點個數。也就是說,該矩陣為每個樣本點初始化了一個權重。接著,算法將遍歷數據集,計算每個樣本點對應的權重值:隨著樣本點與待預測點距離的遞增,權重將以指數級衰減❷。輸入參數k控制衰減的速度。與之前的函數standRegress一樣,在權重矩陣計算完畢後,就可以得到對回歸係數ws的一個估計。

程序清單8-2中的另一個函數是lwlrTest,用於為數據集中每個點調用lwlr,這有助於求解k的大小。

下面看看實際效果,將程序清單8-2的代碼加入到regression.py中並保存,然後在Python提示符下輸入如下命令:

>>> reload(regression)
<module \'regression\' from \'regression.py\'>
  

如果需要重新載入數據集,則輸入:

>>> xArr,yArr=regression.loadDataSet(\'ex0.txt\')  
  

可以對單點進行估計:

>>> yArr[0]
3.1765129999999999
>>> regression.lwlr(xArr[0],xArr,yArr,1.0)
matrix([[ 3.12204471]])
>>> regression.lwlr(xArr[0],xArr,yArr,0.001)
matrix([[ 3.20175729]]) 
  

為了得到數據集裡所有點的估計,可以調用lwlrTest函數:

>>> yHat = regression.lwlrTest(xArr, xArr, yArr,0.003)
  

下面繪出這些估計值和原始值,看看yHat的擬合效果。所用的繪圖函數需要將數據點按序排列,首先對 xArr排序:

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])
[<matplotlib.lines.Line2D object at 0x03639550>]
>>> ax.scatter(xMat[:,1].flatten.A[0], mat(yArr).T.flatten.A[0] , s=2,c=\'red\')
<matplotlib.collections.PathCollection object at 0x03859110>
>>> plt.show
  

可以觀察到如圖8-5所示的效果。圖8-5給出了k在三種不同取值下的結果圖。當k = 1.0時權重很大,如同將所有的數據視為等權重,得出的最佳擬合直線與標準的回歸一致。使用k = 0.01得到了非常好的效果,抓住了數據的潛在模式。下圖使用k = 0.003納入了太多的噪聲點,擬合的直線與數據點過於貼近。所以,圖8-5中的最下圖是過擬合的一個例子,而最上圖則是欠擬合的一個例子。下一節將對過擬合和欠擬合進行量化分析。

圖8-5 使用3種不同的平滑值繪出的局部加權線性回歸結果。上圖中的平滑參數k = 1.0,中圖k = 0.01,下圖k = 0.003。可以看到,k = 1.0時的模型效果與最小二乘法差不多,k = 0.01時該模型可以挖出數據的潛在規律,而k = 0.003時則考慮了太多的噪音,進而導致了過擬合現象

局部加權線性回歸也存在一個問題,即增加了計算量,因為它對每個點做預測時都必須使用整個數據集。從圖8-5可以看出,*k *= 0.01時可以得到很好的估計,但是同時看一下圖8-4中k = -0.01的情況,就會發現大多數據點的權重都接近零。如果避免這些計算將可以減少程序運行時間,從而緩解因計算量增加帶來的問題。

到此為止,我們已經介紹了找出最佳擬合直線的兩種方法,下面用這些技術來預測鮑魚的年齡。