局部加權線性迴歸算法原理分析和代碼實戰

局部加權線性迴歸算法原理分析和代碼實戰

前言

前面我們介紹了最基礎的線性迴歸算法,也提到了它容易出現欠擬合的現象。那麼,本文的局部加權線性迴歸將解決這個欠擬合問題。我覺得這個思路具有很好的遷移性,咱們一起好好看看。

項目源碼已上傳至GitHubb上,有需要的自取:項目地址
如果項目對你有些許幫助,不要忘記給個小⭐⭐

算法原理分析

1. 什麼叫局部加權?

使用“近朱者赤,近墨者黑”來形容這個算法再合適不過了。在你的社交圈子裏,離你距離越近,對你的影響就越大,離你距離越遠,對你的影響越小。我們以每一個數據爲一個社交中心,對於存在於該數據周圍的所有數據按照距離賦予權值,這裏我們使用高斯函數進行權值計算。

2. 什麼是高斯核函數?

也稱徑向基函數,簡稱RBF,定義爲空間中任意一點xi到x之間歐氏距離的單調函數。當兩個點距離很近時,這個值越大,當兩個點距離很遠時,距離很小。我們就使用這個函數來對數據賦予權值。

數據集普遍都具有很多屬性,所以一般的高斯核函數如下:

img

σ表示屬性值的方差

由於我們本次使用的數據集只有一個屬性,並且我們需要自己控制擬合程度,所以,我們對於上面的式子做出了一點改進:

img

這個w權值是一個方陣,其形狀大小有數據量決定。我們假設有m條數據量,那麼w權值的形狀大小爲(mxm)的方陣。

此方陣對角線存放權值,其餘全是0,這樣弄的原因是爲了計算表示的方便,我們看下面的式子:

img

img

接下來我以數據點x[0]= [1.0, 0.42781]爲數據中心點,對整個數據集計算權值,生成的圖像如下:

image-20200624145049225

從圖像我們可以看到,接近於0.4281的地方都比較高,都比較接近於1,離這個點越遠,其值就越小。

3. 加入權值後的線性迴歸公式

下面公式是在上一篇博客的公式推導基礎上,進行改進的。如果沒有看過,建議先去大致看一眼

線性迴歸算法擬合數據原理分析以及源代碼解析

  • 最小均方差公式

img

img

  • 化簡形式

img

  • 對F進行求導,得出α的表達式

img

局部加權線性迴歸代碼實戰

  1. 加載數據

首先我們先看一下文本數據的屬性有哪些

image-20200624113916873

圖中最座標紅色方塊是數據的常數1,用來求解常量,對應着就是我們前面公式的b。

中間黃色的是我們的屬性值α1.

最右邊白色的是我們數據的值,也就是y值。

我們來看一下數據的分佈情況:

image-20200624115800582

def LoadData(filename):
    dataMat = []
    labelMat = []
    with open(filename) as f:
        numFeat = len(f.readline().split('\t'))-1#這裏會導致忽略第一個數據
        f.seek(0)
        for line in f.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
  1. 首先從文件中加載進來數據
  2. 獲取數據的屬性個數numFeat
  3. 將數據的屬性值和y值分別放入到dataMat列表和labelMat列表中

2. 進行局部加權迴歸

def lwlr(testPoint,xArr,yArr,k=0.1):
    xMat = np.mat(xArr);yMat = np.mat(yArr).T#
    m = np.shape(xMat)[0]#數據個數
    #爲什麼創建方陣,是爲了實現給每個數據增添不同的權值
    weights = np.mat(np.eye(m))#初始化一個階數等於m的方陣,其對角線的值爲1,其餘值均爲0
    for j in range(m):
        diffMat = testPoint-xMat[j,:]
        weights[j,j] = np.exp(diffMat*diffMat.T/(-2.0*k**2)) #e的指數形式
    xTx = xMat.T*(weights*xMat)
    #print("weights",weights)
    if np.linalg.det(xTx)==0.0:#通過計算行列式的值來判是否可逆
        print("this matrix is singular,cannot do inverse")
        return
    ws = xTx.I*(xMat.T*(weights*yMat))
    return testPoint*ws
  1. 這裏testPoint 是中心點數據,將所有數據對求解w權值。
  2. 我們創建一個大小爲m,對角線爲1的方陣weights
  3. 通過一個循環,來計算testPoint 對數據中各個點的高斯函數計算
  4. 通過上面咱們推導的公式,進行求解α(代碼中是ws)
  5. 由於公式中,我們需要用到行列式的逆,所以,我們需要判斷行列式的值是否等於0,以此來判定是否可逆。
  6. 最後可以求出testPoint 的預測值

3. 循環求出所有數據點的預測值

def lwlrTest(testArr,xArr,yArr,k=1.0):
    m = np.shape(testArr)[0]
    yHat = np.zeros(m)
    for i in range(m):
        yHat[i] = lwlr(testArr[i],xArr,yArr,k)
    return yHat
  1. 這裏我們使用循環,對每個數據進行循環獲取其預測值,並將預測值存入到yHat數組中。

4. 圖形的顯示

def lwlshow(xArr,yMat,yHat):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    xCopy = np.array(xArr.copy())
    srtInd = xCopy[:,1].argsort(0)
    print("xSort",xCopy[srtInd])
    ax.plot(xCopy[srtInd][:,1],yHat[srtInd])
    yMat = np.array(yMat)
    ax.scatter(xCopy[srtInd][:,1].tolist(),yMat[srtInd],s=10,alpha=0.7)
    plt.show()
  1. 這裏我們首先對數據進行排序
  2. 然後將預測值用直線連接,將原數據點使用點的形式顯示

image-20200624155111212

這裏我們取k=0.01,顯示的圖像如上圖。

接下來,我們分別測試,當k=1、0.1、0.01、0.005的圖像分別是什麼樣的。

image-20200624161713286

從圖中我們能夠看到,隨着k的逐漸減小,原本擬合的直線逐漸的變形,慢慢的與數據開始擬合。

k=0.01的時候,擬合程度 剛剛好。當k=0.005的時候,就出現了過擬合。

總結

通過局部加權線性迴歸算法,我們基本解決了一般線性迴歸的欠擬合問題。當然這裏k的取值還是需要我們去多次嘗試,找到一個合適的值。

最後我想說一點的時,這個加權的思想可以用在很多地方,我也在反覆的想這個事情,希望之後學到新的算法時,能夠將這個思想添加進去,以達到更好的效果。

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章