機器學習實戰第8章預測數值型數據:回歸
1.簡單的線性回歸
假定輸入數據存放在矩陣X中,而回歸系數存放在向量W中,則對於給定的數據X1,預測結果將會是
這裏的向量都默認為列向量
現在的問題是手裏有一些x和對應的y數據,怎樣才能找到W呢?一個常用的方法是找到使誤差最小的W,這裏的誤差是指預測y值與真實y值之間的差值,使用該誤差的簡單累加將使得正差值和負差值相互抵消,所以我們采用平方誤差。
平方誤差可以寫做:
用矩陣表示可以寫成
使用上式對w進行求導:
具體可參考https://blog.csdn.net/nomadlx53/article/details/50849941
令上述導數為0,即可得
2. 局部加權線性回歸
線性回歸的一個問題是有可能出現欠擬合現象,因為它求的是具有小均方誤差的無偏估計。顯而易見,如果模型欠擬合將不能取得好的預測效果。所以有些方法允許在估計中引入一 些偏差,從而降低預測的均方誤差。
其中的一個方法是局部加權線性回歸(Locally Weighted Linear Regression,LWLR)。在該方法中,我們給待預測點附近的每個點賦予一定的權重。與kNN一樣,這種算法每次預測均需要事先選取出對應的數據子集。該算法解除回歸系數W的形式如下:
LWLR使用"核"(與支持向量機中的核類似)來對附近的點賦予更高的權重。核的類型可以自由選擇,最常用的核就是高斯核,高斯核對應的權重如下:
其中k需要用戶設定,它決定了對附近的點賦予多大的權重,控制衰減速度
下面是代碼
from numpy import mat,linalg,corrcoef,shape,eye,exp,zeros import matplotlib.pyplot as plt import numpy as np 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 """ 函數說明:線性回歸 ParameterS: xArr - x數據集 yArr - y數據集 Returns: ws - 回歸系數 """ def standRegres(xArr, yArr): xMat = mat(xArr); yMat = mat(yArr).T xTx = xMat.T*xMat # linalg.det()計算行列式,若為0,則不可逆 if linalg.det(xTx) == 0.0: print("This matrix is singular, cannot do inverse") return #回歸系數ws = (X^TX)^-1X^Ty ws = xTx.I * (xMat.T * yMat) return ws def plotStandRegres(xMat, yMat, ws): fig = plt.figure() ax = fig.add_subplot(111) xCopy = xMat.copy() yHat = xCopy * ws # flatten()函數Return a copy of the array collapsed into one dimension. # .A將矩陣轉化為數組 ax.scatter(xMat[:, 1].flatten().A[0], yMat.T[:, 0].flatten().A[0]) ax.plot(xCopy[:, 1], yHat,‘r‘) plt.show() """ 函數說明:局部加權線性回歸 Parameters: testPoint - 測試點,xArr - x數據集,yArr - y數據集, k - 高斯核的參數,自定義,控制衰減速度 Returns: 測試點y值 """ def lwlr(testPoint, xArr, yArr, k=1.0): xMat = mat(xArr) # m * n yMat = mat(yArr).T # m * 1 m = shape(xMat)[0] weights = mat(eye(m)) #生成對角矩陣,對角線上元素全為1,其他為0 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 """ 函數說明:局部加權線性回歸測試函數 Parameters: testPoint - 測試x數據集,xArr - x數據集,yArr - y數據集, k - 高斯核的參數,自定義,控制衰減速度 Returns: yHat - 預測y數據集 """ 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 def plotLwlr(xMat, yMat): plt.rcParams[‘font.sans-serif‘] = [‘SimHei‘] plt.rcParams[‘axes.unicode_minus‘] = False strInd = xMat[:,1].argsort(0) #對x進行排序,並返回排序下標 xSort = xMat[strInd][:,0,:] yHat1 = lwlrTest(xArr, xArr, yArr, 1.0) yHat2 = lwlrTest(xArr, xArr, yArr, 0.01) yHat3 = lwlrTest(xArr, xArr, yArr, 0.003) fig, axs = plt.subplots(nrows=3, ncols=1, sharex=False, sharey=False, figsize=(10, 8)) axs[0].plot(xSort[:, 1], yHat1[strInd], c=‘red‘) axs[1].plot(xSort[:, 1], yHat2[strInd], c=‘red‘) axs[2].plot(xSort[:, 1], yHat3[strInd], c=‘red‘) axs[0].scatter(xMat[:, 1].flatten().A[0], yMat.T[:, 0].flatten().A[0]) axs[1].scatter(xMat[:, 1].flatten().A[0], yMat.T[:, 0].flatten().A[0]) axs[2].scatter(xMat[:, 1].flatten().A[0], yMat.T[:, 0].flatten().A[0]) #設置標題,x軸y軸label axs0_title_text = axs[0].set_title(u‘局部加權回歸曲線,k=1.0‘) axs1_title_text = axs[1].set_title(u‘局部加權回歸曲線,k=0.01‘) axs2_title_text = axs[2].set_title(u‘局部加權回歸曲線,k=0.003‘) plt.setp(axs0_title_text, size=8, weight=‘bold‘, color=‘red‘) plt.setp(axs1_title_text, size=8, weight=‘bold‘, color=‘red‘) plt.setp(axs2_title_text, size=8, weight=‘bold‘, color=‘red‘) plt.xlabel(‘X‘) plt.ylabel(‘Y‘) plt.show() if __name__ == ‘__main__‘: xArr, yArr = loadDataSet(‘ex0.txt‘) ws = standRegres(xArr, yArr) xMat = mat(xArr) yMat = mat(yArr) yHat = xMat * ws # 預測y值 plotStandRegres(xMat, yMat, ws) #計算相關系數 print(corrcoef(yHat.T, yMat)) plotLwlr(xMat, yMat)
簡單線性回歸的運行結果:
相關系數:
局部加權回歸:
從圖中可以看出在三種k的取值中,k=0.01的結果相對更好一些,k=0.003時會出現過擬合的情況。
局部加權回歸存在一個問題:增加了計算量,每次必須在整個數據集上運行
3.示例:預測鮑魚的年齡
""" 函數說明:計算誤差 """ def rssError(yArr,yHatArr): #yArr and yHatArr both need to be arrays return ((yArr-yHatArr)**2).sum() if __name__ == ‘__main__‘: xArr, yArr = regression.loadDataSet(‘abalone.txt‘) print(‘訓練集與測試集相同:局部加權線性回歸,核k的大小對預測的影響:‘) yHat01 = regression.lwlrTest(xArr[0:99], xArr[0:99], yArr[0:99], 0.1) yHat1 = regression.lwlrTest(xArr[0:99], xArr[0:99], yArr[0:99], 1) yHat10 = regression.lwlrTest(xArr[0:99], xArr[0:99], yArr[0:99], 10) print(‘k=0.1時,誤差大小為:‘,rssError(yArr[0:99], yHat01.T)) print(‘k=1時,誤差大小為:‘,rssError(yArr[0:99], yHat1.T)) print(‘k=10.1時,誤差大小為:‘,rssError(yArr[0:99], yHat10.T)) print(‘‘) print(‘訓練集與測試集不同:局部加權線性回歸,核k的大小對預測的影響:‘) yHat01 = regression.lwlrTest(xArr[100:199], xArr[0:99], yArr[0:99], 0.1) yHat1 = regression.lwlrTest(xArr[100:199], xArr[0:99], yArr[0:99], 1) yHat10 = regression.lwlrTest(xArr[100:199], xArr[0:99], yArr[0:99], 10) print(‘k=0.1時,誤差大小為:‘,rssError(yArr[100:199], yHat01.T)) print(‘k=1時,誤差大小為:‘,rssError(yArr[100:199], yHat1.T)) print(‘k=10時,誤差大小為:‘,rssError(yArr[100:199], yHat10.T)) print(‘‘) print(‘訓練集與測試集不同:簡單的線性歸:‘) ws = regression.standRegres(xArr[0:99], yArr[0:99]) # 8 * 1 yHat = xArr[100:199] * ws print(rssError(yArr[100:199],yHat.T.A))
從中可以看出使用訓練數據集做測試集時,k=0.1時的誤差最小,但使用不同訓練集的數據做測試數據集時,k=0.1的誤差最大,說明了可能出現了過擬合的情況,因此在選取k值時應選擇使測試誤差最小的值。同時我們可以看到,當k=1時,局部加權線性回歸和簡單的線性回歸得到的效果差不多。這也表明一點,必須在未知數據上比較效果才能選取到最佳模型。
參考:
《機器學習實戰第8章》
http://cuijiahua.com/blog/2017/11/ml_11_regression_1.html
機器學習實戰第8章預測數值型數據:回歸