機器學士實戰(筆記):第 8 章 預測數值型數據:回歸

第二部分 利用回歸預測數值型數據

第 8 章 預測數值型數據:回歸

[TOC]

本章內容

  • 線性回歸
  • 局部加權線性回歸
  • 嶺回歸和逐步線性回歸
  • 預測鮑魚年齡和玩具售價

1. 用線性回歸找到最佳擬合直線

線性回歸:

  • 優點:結果易于理解,計算熵不復雜。
  • 缺點:對非線性的數據擬合不好。
  • 適用數類型:數值型和標稱型數據。

回歸的目的是預測數值型的目標值。最直接的辦法是依據輸入寫成一個目標值的計算公式。

y=a_1*x_1+a_2*x_2

這就是回歸方程,其中的 a1 和 a2 稱作回歸系數(regression weights),求這些回歸系數的過程就是回歸。一旦有了這些回歸系數,再給定輸入,做預測就非常容易了,具體的做法是用回歸系數乘以輸入值,再將結果全部加在一起,就得到了預測值。(這里的回歸都是指線性回歸(linear regression)

回歸的一般方法:

  1. 收集數據:采用任意方法收集數據。
  2. 準備數據:回歸需要數值型數據,標稱型數據將被轉成二值型數據。
  3. 分析數據:繪制出數據的可視化二維圖將有助于對數據做出理解和分析,在采用縮減法求得新回歸系數之后,可以將新擬合線繪制在圖上作為對比。
  4. 訓練算法:找到回歸系數。
  5. 測試算法:使用 R2 或者預測值和數據的擬合度,來分析模型的效果。
  6. 使用算法:使用回歸,可以在給定輸入的時候預測出一個數值,這是對分類方法的提升,因為這樣可以預測連續型數值而不僅僅是離散的類別標簽。

應當怎樣從一大堆數據里求出回歸方程呢?假定輸入數據存放在矩陣 X 中,而回歸系數存放在向量 W 中,那么對于給定的數據 x1,預測結果將會通過

Y_1=X^T_1 W

給出。現在的問題是,有一些 x 個對應的 Y,怎樣才能找到 w 呢?一個常用的方法就是找出誤差最小的 w。這里的誤差是指預測 y 值和真實 y 值之間的差值,使用該差值的簡單累加將使得正差值和發差值相互抵消,所以使用平方誤差公式:

\sum^m_{i=1}(y_i-x^T_i w)^2

用矩陣表示為:

(y-Xw)^T(y-Xw)

如果對 w 求導,得到

X^T(y-Xw)

令其等于零,解出 w 如下:

\hat{w}=(X^T X)^{-1}X^T y

w 上方的小標記表示,這是當前可以估計出的 w 的最優解。

值得注意的是,上述公式中包含

X^T X^{-1}

,也就是需要對矩陣求逆,因此這個方程只在逆矩陣存在的時候適用(代碼中可以用偽逆矩陣)。上述的最佳 w 求解的統計學中的常見問題,除了矩陣方法外還有其他方法可以解決。該方法也稱為 OLS,即普通最小二乘法(ordinary least squares)

對于圖8-1的散點圖,下面來介紹如何給出該數據的最佳擬合直線。

圖8-1

添加 loadDataSet() 函數:

def loadDataSet(fileName):
    """
    讀取文本文件
    :param fileName:
    :return:
    """
    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

添加 standRegres 函數:

def standRegres(xArr, yArr):
    """計算最佳擬合直線"""
    xMat = np.mat(xArr)
    yMat = np.transpose(np.mat(yArr))
    xTx = np.transpose(xMat) * xMat
    if np.linalg.det(xTx) == 0.0: # 計算 xTx 的行列式,為零則不能計算逆矩陣
        print("this matrix is singular,cannot do inverse")
        return
    ws = xTx.I * (xMat.T * yMat)
    # ws = np.linalg.solve(xTx, xMat.T * yMat) # 偽逆矩陣,可以不判斷行列式是否為零
    return ws

測試:

xArr,yArr = loadDataSet('ex0.txt')
ws = standRegres(xArr,yArr) # 回歸系數
# print(ws) # [[ 3.00774324] [ 1.69532264]]
xMat = np.mat(xArr)
yMat = np.mat(yArr)
yHat = xMat * ws # 預測的 y 值: y=ws[0]+ws[1]*X1
print(np.cov(yHat.T, yMat)) # 協方差 [[ 0.24664409  0.24664409] [ 0.24664409  0.25345439]]
print(np.corrcoef(yHat.T, yMat)) # 相關系數 [[ 1.  0.98647356] [ 0.98647356  1.  ]]

# 繪制數據集散點圖和最佳擬合直線圖
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()
圖8-2

幾乎任一數據集都可以用上述方法建立模型,那么,如何判斷這些模型的好壞呢?比較一下圖8-3 的兩個子圖,如果在兩個數據集上分別作線性回歸,將得到完全一樣的模型(擬合直線)。顯然兩個數據是不一樣的,那么模型分別在二者上的效果如何?我們當如何比較這些效果的好壞?有兩種方法可以計算預測值 yHat 序列和真實值 y 序列的匹配程度,那就是計算這兩個序列的相關系數。

圖8-3

在 numpy 中提供了 corrcoef 來計算預測值與真實值的相關性.

協方差:

Cov(X,Y)=\frac{1}{m}\sum^m_{i=1}(x_i-\bar{x})(y_i-\bar{y})

相關系數:

r=\frac{Cov(X,Y)}{\sqrt{\delta_x}\sqrt{\delta_y}}

其中,

\sqrt{\delta_x}$$、$$\sqrt{\delta_y}

表示 X、Y 的方差,并且 |r|<=1。

該矩陣包含所有兩兩組合的相關系數。可以看到,對角線上的數據是 1.0,因為 yMat 和自己的匹配是最完美的,而 yHat 和 yMat 的相關系數為 0.98.

2. 局部加權線性回歸

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

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

\hat{w}=(X^T WX)^{-1}X^TWy

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

LWLR 使用“核”來對附近的點賦予更高的權重。核的類型可以自由選擇,最常用的核就是高斯核,其對應的權重如下:

w(i,i)=exp(\frac{|x^{(i)}-x|}{-2k^2})

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

圖8-4

添加 lwlr() 函數:

def lwlr(testPoint, xArr, yArr, k=1.0):
    """
    局部加權線性回函數
    :param testPoint:
    :param xArr:
    :param yArr:
    :param k:用于控制衰減速度
    :return:
    """
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    m = np.shape(xMat)[0]
    weights = np.mat(np.eye((m))) # 創建對角矩陣,階數等于樣本點個數
    for j in range(m):
        # 隨著樣本點與待預測點距離的遞增,權重值大小以指數級衰減
        diffMat = testPoint - xMat[j,:]
        weights[j,j] = np.exp(diffMat*diffMat.T/(-2.0*k**2)) # 參數 k,控制衰減的速度
    xTx = xMat.T * (weights * xMat)
    if np.linalg.det(xTx) == 0.0:
        print('this matrix is singular, cannot do inverse')
    ws = xTx.I * (xMat.T * (weights * yMat))
    return testPoint * ws

添加 lwlrTest() 函數:

def lwlrTest(testArr, xArr, yArr, k=1.0):
    """
    用于為數據集中每個點調用 lwlr(),這有助于求解 k 的大小
    :param testArr:
    :param xArr:
    :param yArr:
    :param k:
    :return:
    """
    m = np.shape(testArr)[0]
    yHat = np.zeros(m)
    for i in range(m):
        yHat[i] = lwlr(testArr[i], xArr, yArr, k)
    return yHat

測試:

xArr, yArr = loadDataSet('ex0.txt')
yHat = lwlrTest(xArr, xArr, yArr, 0.01) # k=1, 0.01, 0.003
xMat = np.mat(xArr)
srtInd = xMat[:,1].argsort(0)
xSort = xMat[srtInd][:,0,:]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(xSort[:,1], yHat[srtInd])
ax.scatter(xMat[:,1].flatten().A[0], np.mat(yArr).T.flatten().A[0], s=2, c='red')
plt.show()

可以觀察到如圖8-5所示的效果。圖8-5 給出了 k 在不同取值下的結果圖。

圖8-5

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

3. 示例:預測鮑魚的年齡

在data目錄下有一份來自 UCI 數據集合的數據,記錄了鮑魚的年齡。鮑魚年齡可以從鮑魚殼的層數推算得到。

預測代碼:

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(rssError(abY[0:99], yHat01.T)) # 56.7886874305
print(rssError(abY[0:99], yHat1.T)) # 429.89056187
print(rssError(abY[0:99], yHat10.T)) # 549.118170883

可以看到,使用較小的核將得到較低的誤差,那么,為什么不在所有數據集上都使用最小的核呢?這是因為 使用最小的核將造成過擬合,對新數據不一定能達到最好的預測效果。下面就來看看它們在新數據上的表現:

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(rssError(abY[100:199], yHat01.T)) # 57913.5155016
print(rssError(abY[100:199], yHat1.T)) # 573.52614419
print(rssError(abY[100:199], yHat10.T)) # 517.571190538

從上述結果可以看到,核大小等于 10 時的測試誤差最小,但它在訓練集上的誤差卻最大的。接下來和簡單的線性回歸做個比較:

ws = standRegres(abX[0:99], abY[0:99])
yHat = np.mat(abX[100:199]) * ws
print(rssError(abY[100:199], yHat.T.A)) # 518.636315325

簡單線性回歸達到了與局部加權線性回歸類似的結果,這也表明一點,必須在未知數據上比較效果才能選取到最佳模型。

4. 縮減系數來“理解”數據

如果數據的特征比樣本點還多應該怎么辦?是否還可以使用線性回歸和之前的額方法來做預測?答案是否定的,即不能再使用前面介紹的方法。這是因為在計算

(X^TX)^{-1}

的時候會出錯。

如果特征比樣本點還多 (n>m) 也就是說輸入數據的矩陣 X 不是滿秩矩陣。非滿秩矩陣在求逆時會出問題。

這節介紹的三種方法就是來解決這個問題。

4.1 嶺回歸

嶺回歸(ridge regression)就是在矩陣 $$X^TX$$ 上加一個 $$\lambda I$$ 從而使得矩陣非奇異,進而能對 $$X^T X+\lambda I$$ 求逆。其中矩陣 I 是一個 m*m 的單位矩陣。而 lambda 是一個用戶定義數值,后面會做介紹。在這種情況下, 回歸系數的計算公式將變成:

\hat{w}=(X^T X+\lambda I)^{-1}X^T y

嶺回歸最先用來處理特征多余樣本數的情況,現在也用于在估計中加入偏差,從而得到更好的估計。這里通過引入 lambda 來限制了所有 w 之和,通過引入該懲罰項,能夠減少不重要的參數,這個技術在統計學中也叫做縮減(shrinkage)

添加 ridgeRegres() 函數:

def ridgeRegres(xMat, yMat, lam=0.2):
    """
    計算回歸系數
    :param xMat: 
    :param yMat: 
    :param lam: 
    :return: 
    """
    xTx = xMat.T * xMat
    denom = xTx + np.eye(np.shape(xMat)[1]) * lam # 
    if np.linalg.det(denom) == 0.0:
        print("this matrix is singular,cannot do inverse")
        return
    ws = denom.I * (xMat.T * yMat)
    # ws = np.linalg.solve(xTx + np.eye(np.shape(xMat)[1]) * lam, xMat.T * yMat) # 用偽逆矩陣求解
    return ws

添加 ridgeTest() 函數:

def ridgeTest(xArr, yArr):
    """
    在一組lam上測試結果
    :param xArr:
    :param yArr:
    :return:
    """
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    yMean = np.mean(yMat, 0) # 求均值
    yMat = yMat - yMean
    xMeans = np.mean(xMat, 0) # 求均值
    xVar = np.var() # 求方差
    xMat = (xMat - xMeans)/xVar # 數據標準化:所有特征都減去各自的均值并除以方差
    numTestPts = 30
    wMat = np.zeros((numTestPts, np.shape(xMat)[1]))
    for i in range(numTestPts):
        ws = ridgeRegres(xMat, yMat, np.exp(i-10))
        wMat[i,:] = ws.T
    return wMat

下面看一下鮑魚數據集上的運行結果:

abX, abY = loadDataSet('abalone.txt')
ridgeWeights = ridgeTest(abX, abY)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(ridgeWeights)
plt.show()

這樣就得到了 30 個不同 lambda 所對應回歸系數。圖8-6是所見的額效果圖:

圖8-6

該圖繪制出了回歸系數與 log(lam) 的關系。在最左邊,即 lambda 最小時,可以得到所有系數的原始值(與線性回歸一致);而在右邊,系數全部縮減為 0;在中間部分的某值將可以取得最好的預測效果。為了定量地找到最佳參數值,還需要進行交叉驗證。另外,要判斷哪些變量對結果預測最具有影響力,在圖8-6 中觀察它們對應的系數大小就可以。

4.2 lasso

不難證明,在增加如下約束時,普通的最小二乘法回歸會得到與嶺回歸的一樣的公式:

\sum^n_{k=1}w^2_k \leq \lambda

上式限定了所有回歸系數的平方和不能大于 lambda。使用普通的最小二乘法回歸在當兩個或更多的特征相關時,可能會得出一個很大的正系數和一個很大的負系數。正式因為上述限制條件的存在,使用嶺回歸可以避免這個問題。

與嶺回歸類似,另一個縮減方法 lasso 也對回歸系數做了限定,對應的約束條件如下:

\sum^n_{k=1}|w_k| \leq \lambda

唯一的不同點在于,這個約束條件使用絕對值取代了平方和。在 lambda 足夠小時,一些系數會因此被迫縮減到 0,這個特性可以幫助我們更好的理解數據,這兩個約束條件在公式上看起來相差無幾,但細微的變化卻極大的增加了計算復雜度(為了在這個新的約束條件下解出回歸系數,需要使用二次規劃算法)。下面將介紹一個更為簡單的方法來得到結果,該方法叫做前向逐步回歸

4.3 前向逐步回歸

前向逐步回歸算法可以得到與 lasso 差不多的效果,但更加簡單。它屬于一種貪心算法,即每一步都盡可能減少誤差。一開始,所有的權重都設為 1,然后每一步所做的決策時對某個權重增加或減少一個很小的值。

該算法的偽代碼如下:

數據標準化,使其分布滿足 0 均值和單位方差
在每輪迭代過程中:
    設置當前最小誤差啊 lowestError 為正無窮
    對每個特征:
        增大或縮小:
            改變一個系數得到一個新的 W
            計算新 W 下的誤差
            如果誤差 Error 小于當前最小誤差 lowestError:設置 Wbest 等于當前的 W
        將 W 設置為新的 WBest

添加 regularize() 函數:

def regularize(xMat):
    """
    把特征按照均值為0,方差為1進行標準化
    :param xMat:
    :return:
    """
    inMat = xMat.copy()
    inMeans = np.mean(inMat, 0)
    inVar = np.var(inMat, 0)
    inMat = (inMat - inMeans)/inVar
    return inMat

添加 stageWise() 函數:

def stageWise(xArr, yArr, eps=0.01, numIt=100):
    """
    逐步線性回歸算法
    :param xArr:
    :param yArr:
    :param eps: 步長
    :param numIt: 迭代次數
    :return:
    """
    xMat =np.mat(xArr)
    yMat = np.mat(yArr).T
    yMean = np.mean(yMat, 0)
    yMat = yMat - yMean
    xMat = regularize(xMat)
    m,n = np.shape(xMat)
    returnMat = np.zeros((numIt,n))
    ws = np.zeros((n,1))
    wsTest = ws.copy() # 為了實現貪心算法建立了ws的兩個副本
    wsMax = ws.copy()
    for i in range(numIt): # 迭代
        print('ws.T:',ws.T)
        lowestError = float('inf')
        for j in range(n):
            for sign in [-1,1]: # 兩次循環,分別計算增加或減少該特征對誤差的影響
                wsTest = ws.copy()
                wsTest[j] += eps*sign
                yTest = xMat*wsTest
                rssE = rssError(yMat.A, yTest.A) # 得到平方誤差
                if rssE < lowestError: # 經過與所有的誤差比較后取最小的誤差
                    lowestError = rssE
                    wsMax = wsTest
        ws = wsMax.copy()
        returnMat[i,:] = ws.T
    return returnMat

測試一下實際效果:

xArr, yArr = loadDataSet('abalone.txt')
# print(stageWise(xArr,yArr,0.01,200))
print(stageWise(xArr,yArr,0.001,5000))

與最小二乘法進行比較,發現在5000次迭代后,逐步線性回歸算法與常規的最小二乘法效果類似。使用 0.005 的 epsilon 值并經過 1000 次迭代后的結果參見圖8-7:

圖8-7

逐步線性回歸算法的實際好處并不在于能繪出圖8-7這樣漂亮的圖,主要的優點在于它可以幫助人們理解現有的模型并作出改進。當構建一個模型后,可以運行該算法找出重要的特征,這樣就有可能及時停止對那些不重要特征的收集。最后,如果用于測試,該算法每 100 次迭代后就可以構建出一個模型,可以使用類似于 10 折交叉驗證比較這些模型,最終選擇使誤差最小的模型。

當應用縮減方法(如逐步線性回歸或嶺回歸)時,模型也就增加了**偏差(bias),與此同時卻減小了模型的方差。

5. 權衡偏差與方差

圖8-8給出了訓練誤差和測試誤差的曲線圖,上面的曲線圖就是測試誤差,下面的曲線是訓練誤差。根據8.3節的實驗我們知道:如果降低核的大小,那么訓練誤差將變小。從圖8-8來看,從左到右就表示了核逐漸減小的過程。

圖8-8

一般認為,上述兩種誤差由三個部分組成:偏差、測量誤差和隨機噪聲。在8.2節和8.3節,我們通過引入了三個越來越小的核來不斷增大模型方差。

8.4節介紹了縮減法,可以將一些系數縮減成很小的值或直接縮減為0,這是一個增大模型偏差的例子。通過把一些特征的回歸系數縮減到 0 ,同時也就減少了模型的復雜度。例子中有8個特征,消除了其中兩個后不僅使模型更易理解,同時還降低了預測誤差。圖8-8左側是參數縮減過于嚴厲的結果,右側是無縮減的效果。

6. 示例:預測樂高玩具套裝的價格

一種樂高套裝基本上在幾年后就會停產,但樂高的收藏著之間仍會在停產后彼此交易,為了給樂高套裝估價,下面將用本章的回歸技術來建立一個預測模型。

示例:用回歸法預測樂高套裝的價格:

  1. 收集數據:用 google shopping 的 api 收集數據。
  2. 準備數據:從返回的 json 數據中抽取價格。
  3. 分析數據:可視化并觀察數據。
  4. 訓練算法:構建不同的模型,采用逐步線性回歸和直接的線性回歸模型。
  5. 測試算法:使用交叉驗證來測試不同的模型,分析哪個效果更好。
  6. 使用算法:這次練習的目的就是生成數據模型。

6.1 收集數據:使用 google 購物的 api

詳細 api 介紹可參見:http://code.google.com/apis/shopping/search/v1/gettting_started.html

添加 searchForSet() 函數:

from time import sleep
import json
import urllib

def searchForSet(retX, retY, setNum, yr, numPce, origPrc):
    """
    調用google 購物 api 并保證數據抽取的正確性
    :param retX:
    :param retY:
    :param setNum:
    :param yr:
    :param numPce:
    :param origPrc:
    :return:
    """
    sleep(10) # 防止短時間內有過多的 api 調用
    myAPIstr = 'get from code.google.com'
    searchURL = 'http://www.googleapis.com/shopping/search/v1/public/products?key=%s&country=US&q=lego+%d&alt=json' % (myAPIstr,setNum)
    pg = urllib.request.urlopen(searchURL)
    retDict = json.loads(pg.read())
    for i in range(len(retDict['items'])):
        try:
            currItem = retDict['items'][i]
            if currItem['product']['condition'] == 'new': # 判斷是否新產品
                newFlag = 1
            else: newFlag = 0
            listOfInv = currItem['product']['inventories']
            for item in listOfInv:
                sellingPrice = item['price']
                if sellingPrice > origPrc * 0.5: # 如果一個套裝的價格比原始價格低一半以上就認為是不完整,過濾掉
                    print("%d\t%d\t%d\t%f\t%f" % (yr, numPce, newFlag, origPrc, sellingPrice))
                    retX.append([yr, numPce, newFlag, origPrc])
                    retY.append(sellingPrice)
        except:
            print('problem with item %d' % i)

測試(無法訪問google api):

def setDataCollect(retX, retY):
    searchForSet(retX, retY, 8288, 2006, 800, 49.99)
    searchForSet(retX, retY, 10030, 2002, 3096, 269.99)
    searchForSet(retX, retY, 10179, 2007, 5195, 499.99)
    searchForSet(retX, retY, 10181, 2007, 3428, 199.99)
    searchForSet(retX, retY, 10189, 2008, 5922, 299.99)
    searchForSet(retX, retY, 10196, 2009, 3263, 249.99)

lgX = []
lgY = []
setDataCollect(lgX, lgY)

6.2 訓練算法:建立模型

上一節從網上收集到了一些真實的數據,下面將為這些數據構建一個模型。構建的模型可以對售價作出預測,并幫助我們理解現有數據。

使用常規的線性回歸:

# print(np.shape(lgX)) # (58, 4)
lgX1 = np.mat(np.ones((58,5))) # 創建一個全 1 的矩陣
lgX1[:,1:5] = np.mat(lgX) # 將原數據矩陣 lgX 復制到新數據矩陣 lgX1 的第 1 到第 5 列
ws = standRegres(lgX1, lgY) # 回歸算法
print(lgX1[0]*ws)
print(lgX1[-1]*ws)
print(lgX1[43]*ws)

使用嶺回歸確定最佳回歸系數:

def crossValidation(xArr, yArr, numVal=10):
    """
    交叉驗證測試嶺回歸
    :param xArr:
    :param yArr:
    :param numVal: 交叉驗證的次數
    :return:
    """
    m = len(yArr)
    indexList = range(m)
    errorMat = np.zeros((numVal, 30))
    for i in range(numVal): # 數據分為訓練集和測試集
        trainX = []
        trainY = []
        testX = []
        testY = []
        np.random.shuffle(indexList) # 對數據混洗
        for j in range(m):
            if j < m*0.9:
                trainX.append(xArr[indexList[j]])
                trainY.append(yArr[indexList[j]])
            else:
                testX.append(xArr[indexList[j]])
                testY.append(yArr[indexList[j]])
    wMat = ridgeTest(trainX, trainY) # wMat 保存嶺回歸中的所有回歸系數
    for k in range(30):
        # 用訓練時的參數將測試數據標準化
        matTestX = np.mat(testX)
        matTrainX = np.mat(trainX)
        meanTrain = np.mean(matTrainX, 0)
        varTrain = np.var(matTrainX, 0)
        matTestX = (matTestX - meanTrain)/varTrain
        yEst = matTestX * np.mat(wMat[k,:]).T + np.mean(trainY)
        errorMat[i,k] = rssError(yEst.T.A, np.array(testY))
    meanErrors = np.mean(errorMat, 0)
    minMean = float(min(meanErrors))
    bestWeights = wMat[np.nonzero(meanErrors==minMean)]
    xMat = np.mat(xArr);
    yMat = np.mat(yArr).T
    meanX = np.mean(xMat, 0)
    varX = np.var(xMat, 0)
    unReg = bestWeights/varX
    print("the best model from Ridge regression is :\n", unReg) # 標準化后的數據還原
    print("with constant term:",-1 * sum(np.multiply(meanX, unReg)) + np.mean(yMat))

運行:

crossValidation(lgX, lgY, 10)

我們本期望找到一個更易于理解的模型,顯然沒有達到預期結果。為了達到這一點,我們來看一下在縮減過程中回歸系數是如何變化的,輸入一下的命令:

print(ridgeTest(lgX, lgY)) 

這些系數是經過不同程度的縮減得到的。

這種分析方法似的我們可以挖掘大量數據的內在規律。在特征比較多的時候,可以指出哪些特征時關鍵的,而哪些特征時不重要的。

7. 本章小結

在回歸方程中,求得特征對應的最佳回歸系數的方法是最小化誤差的平方差。給定輸入矩陣 X,如果 $X^TX$ 的逆存在并可以求得的話,回歸法對可以直接使用。數據集上計算出的回歸方程并不一定意味著它是最佳,可以使用預測值 yHat 和原始值 y 的相關性來度量回歸方程的好壞。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374