參考:《機器學習實戰》
原始碼地址以及資料:https://github.com/JieruZhang/MachineLearninginAction_src

1. 標準線性迴歸(LR)

y = X w + b \mathbf{y} = \mathbf{X}w + b
i = 1 N ( y i x i T w ) 2 平方誤差:\sum_{i=1}^N (y_i - x_i^Tw)^2
w ^ = ( X T X ) 1 X T y 導數為零求得:\hat{w} = (\mathbf{X}^T\mathbf{X})^{-1}\mathbf{X}^Ty
目標:最小平方誤差
注意,需要先判斷矩陣是否可逆,判斷方法:行列式不為零。

from numpy import *
import matplotlib.pyplot as plt

#讀取資料
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

#標準線性迴歸函式
def standRegres(xArr, yArr):
    xMat = mat(xArr)
    yMat = mat(yArr).T
    xTx = xMat.T * xMat
    #判斷行列式為零,則無法求逆
    if linalg.det(xTx) == 0:
        print('the matrix is singular, cannot do inverse')
        return
    ws = (xTx).I * (xMat.T*yMat)
    return ws

#擬合數據
xArr, yArr = loadDataSet('ex0.txt')
ws = standRegres(xArr, yArr)
xMat = mat(xArr)
yMat = mat(yArr)
yHat = xMat*ws
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xMat[:,1].flatten().A[0], yMat.T[:,0].flatten().A[0])
xCopy = xMat.copy()
xCopy.sort(0)
yHat = xCopy*ws
ax.plot(xCopy[:,1], yHat)
plt.show()

在這裡插入圖片描述

計算 y ^ \hat{y} y y 之間的相關係數,用來判斷預測值和實際值的匹配程度。

yHat = xMat*ws
corrcoef(yHat.T, yMat)
array([[1.        , 0.97223133],
       [0.97223133, 1.        ]])

2. 區域性加權線性迴歸(LWLR)

標準線性迴歸依據最小均方誤差(MSE),則可能會導致欠擬合。因此可以在估計中加入偏差,降低預測的均方誤差。
區域性加權線性迴歸:給待遇測點附近每個點賦予一定的權重,然後繼續進行最小均方差迴歸。設定一個權重矩陣 W W ,給每個資料點賦予一個權重。可以使用 來給附近的點賦予更大的權值,常用的是高斯核:
w ( i , i ) = e x p ( x ( i ) x 2 k 2 ) w(i,i) = exp(\frac{|x^(i) - x|}{-2k^2})
權重 W \mathbf{W} 只含有對角元素,點x與x(i)越近,上述權重越大,k為一個使用者自定義的引數。
隨後計算線性模型的權重 w ^ \hat{w} :
w ^ = ( X T W X ) 1 X T W y \hat{w} = (\mathbf{X^T}\mathbf{W}\mathbf{X})^{-1}\mathbf{X}^T\mathbf{W}y

def lwlr(testPoint,xArr, yArr, k = 1.0):
    xMat = mat(xArr)
    yMat = mat(yArr).T
    m = shape(xMat)[0]
    #設定權重初始對角矩陣,為每一個樣本點初始化了權重值為1
    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('cannot 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)
    #依次預測測試資料的yHat的值
    for i in range(m):
        yHat[i] = lwlr(testArr[i], xArr, yArr, k)
    return yHat

#測試一下
xArr, yArr = loadDataSet('ex0.txt')
#得到所有點的估計
yHat0 = lwlrTest(xArr, xArr, yArr, 0.1)
yHat1 = lwlrTest(xArr, xArr, yArr, 0.01)
yHat = lwlrTest(xArr, xArr, yArr, 0.003)
#繪製圖像
xMat = mat(xArr)
srtInd = xMat[:,1].argsort(0)
xSort = xMat[srtInd][:,0,:]
fig = plt.figure()

ax = fig.add_subplot(311)
ax.plot(xSort[:,1], yHat0[srtInd])
ax.scatter(xMat[:,1].flatten().A[0], mat(yArr).T.flatten().A[0], s = 2, c = 'red')

ax = fig.add_subplot(312)
ax.plot(xSort[:,1], yHat1[srtInd])
ax.scatter(xMat[:,1].flatten().A[0], mat(yArr).T.flatten().A[0], s = 2, c = 'red')

ax = fig.add_subplot(313)
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=0.1, k= 0.01, k = 0.003.
由上圖知,區域性加權線性迴歸可以更好的擬合數據。且k值越小(越精細),擬合程度越好。但是k值過小會導致過擬合。

#預測鮑魚年齡

#計算平方誤差
def ressError(yArr, yHatArr):
    return ((yArr-yHatArr)**2).sum()

#不同的核大小對應的誤差
abX, abY = loadDataSet('abalone.txt')
yHat01 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 0.1)
yHat1 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 1)
yHat10 = lwlrTest(abX[0:99], abX[0:99], abY[0:99], 10)
print('k=0.1, error:', ressError(abY[0:99],yHat01.T))
print('k=1, error:', ressError(abY[0:99],yHat1.T))
print('k=10, error:', ressError(abY[0:99],yHat10.T))
k=0.1, error: 56.78868743050092
k=1, error: 429.89056187038
k=10, error: 549.1181708827924

由上述結果顯示,核越小,誤差越小。但是核太小可能會導致 過擬合,對新資料的效果差。
假設我們使用資料集的前100個數據作為擬合點來擬合數據集的後100個數據。

yHat01 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 0.1)
yHat1 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 1)
yHat10 = lwlrTest(abX[100:199], abX[0:99], abY[0:99], 10)
print('k=0.1, error:', ressError(abY[100:199],yHat01.T))
print('k=1, error:', ressError(abY[100:199],yHat1.T))
print('k=10, error:', ressError(abY[100:199],yHat10.T))
ws = standRegres(abX[0:99], abY[0:99])
yHat = mat(abX[100:199])*ws
print('standard LR error:', ressError(abY[100:199], yHat.T.A))
k=0.1, error: 57913.51550155911
k=1, error: 573.5261441895982
k=10, error: 517.5711905381903
standard LR error: 518.6363153245542

由上述結果顯示,對於新資料,k=10的擬合效果較好,反而k=0.1的誤差最大,也就是產生了過擬合。而且標準線性迴歸的效果也不比lwlr差。

3.過擬合

線性迴歸可能會導致過擬合的風險,因此需要在損失函式後面新增正則項,來限制迴歸引數的規模。這種限制在某種程度上也起到了特徵選擇的作用。線性迴歸一般可以使用:嶺迴歸(ridge),lasso迴歸,前向逐步迴歸。
嶺迴歸對應了L2正則項。lasso迴歸對應了L1正則項。而前向逐步迴歸則是一種貪心演算法,通過對某特徵增加或減小來判斷該特徵的變化是否會導致整個模型的誤差的變化,因此可以起到特徵選擇的作用。

3.1 嶺迴歸

損失函式:
i = 1 N ( y i x i T w ) 2 + λ w 2 \sum_{i=1}^N (y_i - x_i^Tw)^2 + \lambda||w||_2