機器學習實戰筆記-預測數值型數據:回歸


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

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

回歸的目的是預測數值型的目標值。最直接的辦法是依據輸人寫出一個目標值的計算公式。 
假如你想要預測姐姐男友汽車的功率大小,可能會這么計算: 
HorsePower = 0.0015* annualSalary - 0.99* hoursListeningToPublicRadio 
這就是所謂的回歸方程(regression equation),其中的0.0015和-0.99稱作回歸系數(regression weights) ,求這些回歸系數的過程就是回歸。一旦有了這些回歸系數,再給定輸人,做預測就非常容易了。具體的做法是用回歸系數乘以輸人值,再將結果全部加在一起,就得到了預測值

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

應當怎樣從一大堆數據里求出回歸方程呢?假定輸人數據存放在矩陣X中,而回歸系數存放在向量w中。那么對於給定的數據x1 , 預測結果將會通過Y1=XT1w給出。現在的問題是,手里有一些x和對應的y,怎樣才能找到w呢?一個常用的方法就是找出使誤差最小的w,這里的誤差是指預測值和真實值之間的差值,使用該誤差的簡單累加將使得正差值和負差值相互抵消,所以我們采用平方誤差

平方誤差可以寫作: 

$\sum_{i=1}^{n}$


用矩陣表示還可以寫做(yxw)T(yxw)。如果對w求導,得到XT(YXw),令其等於零,解出w如下: 

w=(XTX)1XTy


w上方的小標記表示,這是當前可以估計出的w最優解。從現有數據上估計出的w可能並不是數據中的真實城值,所以這里使用了一個“帽”符號來表示它僅是w的一個最佳估計。(不知道怎么把冒號添上去,汗) 
值得注意的是,上述公式中包含(XTX1), 也就是需要對矩陣求逆,因此這個方程只在逆矩陣存在的時候適用。然而,矩陣的逆可能並不存在,因此必須要在代碼中對此作出判斷。 
上述的最佳w求解是統計學中的常見問題,除了矩陣方法外還有很多其他方法可以解決。通過調用沖Numpy庫里的矩陣方法,我們可以僅使用幾行代碼就完成所需功熊。該方法也稱作OLS,意 思是”普通最小二乘法”(ordinary least squares)。

 

下面看看實際效果,對於圖8-1中的散點圖,下面來介紹如何給出該數據的最佳擬合直線。 
這里寫圖片描述

標准回歸函數和數據導人函數,代碼如下所示:

#general function to parse tab -delimited floats
def loadDataSet(fileName):
    #get number of fields,默認最后一列為y      
    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
    #防止xTx無法求逆矩陣
    if linalg.det(xTx) == 0.0:
        print "This matrix is singular, cannot do inverse"
        return
    #利用之前推導出的公式求出w
    ws = xTx.I * (xMat.T*yMat)
    return ws

 

測試截圖如下: 
這里寫圖片描述 
變量ws存放的就是回歸系數。在用內積來預測y的時候,第一維將乘以前面的常數X0,第二維將乘以輸人變量X1。因為前面假定了X0=1,所以最終會得到y =ws[0]+ws [1]*X1。這里的y實際是預測出的,為了和真實的y值區分開來,我們將它記為yHat 。下面使用新的ws值計算yHat: 
這里寫圖片描述 
注意,如果直線上的數據點次序混亂,繪圖時將會出現問題,所以首先要將點按照升序排列,使用sort方法。 
這里寫圖片描述

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

在python中 ,Numpy庫提供了相關系數的計算方法:可以通過命令corrcoef(yEstimate,yActual)來計算預測值和真實值的相關性。下面我們就在前面的數據集上做個實驗。 
這里寫圖片描述 
該矩陣包含所有兩兩組合的相關系數。可以看到,對角線上的數據是1.0,因為yMat和自己的匹配是最完美的,而YHat和YMat的相關系數為0.98。

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

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

w=(XTWX)1XTWy


其中w是一個矩陣,用來給每個數據點賦予權重。 
LWLR使用”核”(與支持向量機中的核類似)來對附近的點賦予更高的權重。核的類型可以自由選擇,最常用的核就是高斯核,高斯核對應的權重如下: 

w(i,i)=exp(|xix|2k2)


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

 

局部加權線性回歸函數,代碼如下:

def lwlr(testPoint,xArr,yArr,k=1.0):
    #將二維數組xMat, yMat轉化為矩陣
    xMat = mat(xArr); yMat = mat(yArr).T
    m = shape(xMat)[0]
    #加權矩陣,初始化為m維單位矩陣
    weights = mat(eye((m)))
    for j in range(m):                      #next 2 lines create weights matrix
        #循環求出testPoint與矩陣中每行向量的差
        diffMat = testPoint - xMat[j,:]
        #利用權重公式求出testPoint對應的此矩陣的權重
        weights[j,j] = exp(diffMat*diffMat.T/(-2.0*k**2))
    #依然利用上節的公式求出xTx,不同的是對xMat加上了權重,即每行為x1乘以對應的權重
    xTx = xMat.T * (weights * xMat)
    #測試xTx是否可逆
    if linalg.det(xTx) == 0.0:
        print("This matrix is singular, cannot do inverse")
        return
    #依然利用上節的公式求ws,不同在於每行的x和y都分別乘以對應的權重
    ws = xTx.I * (xMat.T * (weights * yMat))
    #返回局部加權預測值
    return testPoint * ws

def lwlrTest(testArr,xArr,yArr,k=1.0):  #loops over all the data points and applies lwlr to each one
    m = shape(testArr)[0]
    yHat = zeros(m)
    for i in range(m):
        #循環利用lwlr函數計算出testArr每行向量對應的ws,算出加權預測值
        yHat[i] = lwlr(testArr[i],xArr,yArr,k)
    return yHat

 

測試代碼如下: 
這里寫圖片描述 
測試截圖如下,當k=0.003時
這里寫圖片描述 
當k=0.01時
這里寫圖片描述 
當k=1時
這里寫圖片描述

當k=1.0時權重很大,如同將所有的數據視為等權重,得出的最佳擬合直線與標准的回歸一致。使用k=0.01得到了非常好的效果,抓住了數據的潛在模式使用k=0.003納人了太多的噪聲點,擬合的直線與數據點過於貼近,過擬合。 
局部加權線性回歸也存在一個問題,即增加了計算量,因為它對每個點做預測時都必須使用整個數據集。k=0.01時可以得到很好的估計,但是同時看一下k=0.01的情況,就會發現大多數據點的權重都接近零如果避免這些計算將可以減少程序運行時間,從而緩解因計算量增加帶來的問題

8.3 示例:預測鮑魚的年齡

接下來,我們將回歸用於真實數據。在data目錄下有一份來自UCI數據集合的數據,記錄了鮑魚(一種介殼類水生動物)的年齡。鮑魚年齡可以從鮑魚殼的層數推算得到。 
計算預測值和實際值方差的代碼如下:

def rssError(yArr,yHatArr): #yArr and yHatArr both need to be arrays
    return ((yArr-yHatArr)**2).sum()

測試截圖如下: 
這里寫圖片描述 
可以看到,使用較小的核將得到較低的誤差。那么,為什么不在所有數據集上都使用最小的核呢?這是因為使用最小的核將造成過擬合,對新數據不一定能達到最好的預測效果。下面看看它們在新數據上面的表現: 
測試截圖如下: 
這里寫圖片描述 
從上述結果可以看到,在上面的三個參數中,核大小等於10時的測試誤差最小,但它在訓練集上的誤差卻是最大的。接下來再來和簡單的線性回歸做個比較: 
這里寫圖片描述 
簡單線性回歸達到了與局部加權線性回歸類似的效果。這也表明一點,必須在未知數據上比較效果才能選取到最佳模型。那么最佳的核大小是10嗎?或許是,但如果想得到更好的效果,應該用10個不同的樣本集做10次測試來比較結果。

本例展示了如何使用局部加權線性回歸來構建模型,可以得到比普通線性回歸更好的效果。局部加權線性回歸的問題在於,每次必須在整個數據集上運行。也就是說為了做出預測,必須保存所有的訓練數據。下面將介紹另一種提高預測精度的方法,並分析它的優勢所在。

8.4 縮減系數來”理解”數據

如果數據的特征比樣本點還多應該怎么辦?是否還可以使用線性回歸和之前的方法來做預測?答案是否定的,即不能再使用前面介紹的方法。這是因為在計算(XTX)1的時候會出錯。 
為了解決這個問題,統計學家引入了嶺回歸(ridge regression)的概念,這就是本節將介紹的第一種縮減方法。接着lasso法,該方法效果很好但計算復雜。本節最后介紹了第二種縮減方法,稱為前向逐步回歸,可以得到與lasso差不多的效果,且更容易實現。

8.4.1 嶺回歸

簡單說來,嶺回歸就是在矩陣XTX上加一個λI從而使得矩陣非奇異,進而能對XTX+λI求逆。其中矩陣I是一個mm的單位矩陣,對角線上元素全為1 ,其他元素全為0。而λ是一個用戶定義的數值,后面會做介紹。在這種情況下,回歸系數的計算公式將變成: 

w=(XTX+λI)1XTy


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

 

嶺回歸中的嶺是什么? 
嶺回歸使用了單位矩陣乘以常量λ,我們觀察其中的單位矩陣, 可以看到值1貫穿整個對角線,其余元素全是0。形象地,在0構成的平面上有一條1組成的“嶺”,這就是嶺回歸中的“嶺”的由來 
縮減方法可以去掉不重要的參數,因此能更好地理解數據。此外,與簡單的線性回歸相比,縮減法能取得更好的預測效果

嶺回歸,代碼如下所示:

#根據嶺回歸公式計算出ws
def ridgeRegres(xMat,yMat,lam=0.2):
    xTx = xMat.T*xMat
    denom = xTx + eye(shape(xMat)[1])*lam
    #如果lam設定為0的時候一樣可能會產生錯誤,所以這里仍需要做一個檢查
    if linalg.det(denom) == 0.0:
        print("This matrix is singular, cannot do inverse")
        return
    ws = denom.I * (xMat.T*yMat)
    return ws

def ridgeTest(xArr,yArr):
    xMat = mat(xArr); yMat=mat(yArr).T
    #mean表示求出y的均值
    yMean = mean(yMat,0)
    yMat = yMat - yMean     #to eliminate X0 take mean off of Y
    #regularize X's
    #求出xArr各列均值
    xMeans = mean(xMat,0)   #calc mean then subtract it off
    #求出xArr各列方差
    xVar = var(xMat,0)      #calc variance of Xi then divide by it
    xMat = (xMat - xMeans)/xVar
    numTestPts = 30
    #wMat表示嶺回歸系數矩陣,初始化為(30,n)維零數組
    wMat = zeros((numTestPts,shape(xMat)[1]))
    #循環30次,根據不同的lambda填充ws
    for i in range(numTestPts):
        ws = ridgeRegres(xMat,yMat,exp(i-10))
        wMat[i,:]=ws.T
    return wMat

 

處理完成后就可以在30個不同的λ下調用ridgeRegres()函數。注意,這里的λ應以指數級變化,這樣可以看出λ在取非常小的值時和取非常大的值時分別對結果造成的影響。最后將所有的回歸系數輸出到一個矩陣並返回。

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

8.4.2 lasso

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

k=1nw2k<=λ

 

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

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

k=1n|wk|<=λ


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

 

8.4.3 前向逐步回歸

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

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

前向逐步線性回歸,代碼如下:

def regularize(xMat):#regularize by columns
    inMat = xMat.copy()
    inMeans = mean(inMat,0)   #calc mean then subtract it off
    inVar = var(inMat,0)      #calc variance of Xi then divide by it
    inMat = (inMat - inMeans)/inVar
    return inMat

def stageWise(xArr,yArr,eps=0.01,numIt=100):
    xMat = mat(xArr); yMat=mat(yArr).T
    #mean()函數表示對矩陣求均值,axis=0指定按列求均值
    yMean = mean(yMat,0)
    yMat = yMat - yMean     #can also regularize ys but will get smaller coef
    #對xMat進行標准化處理,標准化處理函數為regularize()
    xMat = regularize(xMat)
    m,n=shape(xMat)
    #返回所有迭代中ws的變化情況,初始化為(numIt,n)維矩陣
    returnMat = zeros((numIt,n)) #testing code remove
    #回歸系數ws初始化為(n,1)維零數組
    ws = zeros((n,1)); wsTest = ws.copy(); wsMax = ws.copy()
    #迭代numIt次,每次迭代,循環n*2次(每個特征有增大和減小兩個方向),找出令rssError最小的方向(哪個特征,對應增大還是減小),保存ws,下次迭代在ws基礎上做更新
    for i in range(numIt):
        print(ws.T)
        lowestError = 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

 

測試截圖如下: 
這里寫圖片描述 
這里寫圖片描述 
上述結果中值得注意的是w1w6都是0 ,這表明它們不對目標值造成任何影響,也就是說這些特征很可能是不需要的。另外,在參數eps(步長)設置為0.01的情況下,一段時間后系數就已經飽和並在特定值之間來回震盪,這是因為步長太大的緣故。這里會看到,第一個權重在0.04和0.05之間來回震盪

下面試着用更小的步長和更多的步數,測試結果截圖如下: 
這里寫圖片描述 
繪制returnMat,截圖如下: 
這里寫圖片描述
接下來把這些結果與最小二乘法進行比較,后者的結果可以通過如下代碼獲得: 
這里寫圖片描述 
可以看到在5000次迭代以后,逐步線性回歸算法與常規的最小二乘法效果類似。 
逐步線性回歸算法的實際好處並不在於能繪出這樣漂亮的圖,主要的優點在於它可以幫助人們理解現有的模型並做出改進當構建了一個模型后,可以運行該算法找出重要的特征,這樣就有可能及時停止對那些不重要特征的收集。最后,如果用於測試,該算法每100次迭代后就可以構建出一個模型,可以使用類似於10折交叉驗證的方法比較這些模型,最終選擇使誤差最小的模型

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

8.5 權衡偏差與方差

任何時候,一旦發現模型和測量值之間存在差異,就說出現了誤差。當考慮模型中的“噪聲”或者說誤差時,必須考慮其來源。你可能會對復雜的過程進行簡化,這將導致在模型和測量值之間出現“噪聲”或誤差,若無法理解數據的真實生成過程,也會導致差異的發生。另外,測量過程本身也可能產生“噪聲”或者問題。下面舉一個例子,8.1節和8.2節處理過一個從文件導人的二維數據。實話來講,這個數據是我自己造出來的,其具體的生成公式如下: 

y=3.0+1.7x+0.1sin(30x)+0.06N(0,1)


其中N(0,1)是一個均值為0、方差為1的正態分布。在8.1節中,我們嘗試過僅用一條直線來擬合上述數據。不難想到,直線所能得到的最佳擬合應該是3.0+1.7x這一部分。這樣的話,誤差部分就是0.1sin(30x)+0.06N(0,1)。在8.2節和8.3節,我們使用了局部加權線性回歸來試圖捕捉數據背后的結構。該結構擬合起來有一定的難度,因此我們測試了多組不同的局部權重來找到具有最小測試誤差的解。 
圖8-8給出了訓練誤差和測試誤差的曲線圖,上面的曲線就是測試誤差,下面的曲線是訓練誤差。根據8.3節的實驗我們知道:如果降低核的大小,那么訓練誤差將變小。從圖8-8來看,從左到右就表示了核逐漸減小的過程。 
這里寫圖片描述 
一般認為,上述兩種誤差由三個部分組成:偏差、測量誤差和隨機噪聲。在8.2節和8.3節,我們通過引人了三個越來越小的核來不斷增大模型的方差。

 

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

方差是可以度量的。如果從鮑魚數據中取一個隨機樣本集(例如取其中100個數據)並用線性模型擬合,將會得到一組回歸系數。同理,再取出另一組隨機樣本集並擬合,將會得到另一組回歸系數。這些系數間的差異大小也就是模型方差大小的反映。上述偏差與方差折中的概念在機器學習十分流行並且反復出現。 
下一節將介紹上述理論的應用:首先從拍賣站點抽取一些數據,再使用一些回歸法進行實驗來為數據找到最佳的嶺回歸模型。這樣就可以通過實際效果來看看偏差和方差間的折中效果

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

 用回歸法預測樂高套裝的價格 
  (1)收Google Shopping的API收集數據。 
  (2)准備數據:從返回的JSON數據中抽取價格。 
  (3)分析數據:可視化並觀察數據。 
  (4)訓練算法:構建不同的模型,釆用逐步線性回歸和直接的線性回歸模型。 
  (5)測試算法:使用交叉驗證來測試不同的模型,分析哪個效果最好。 
  (6)使用算法:這次練習的目標就是生成數據模型。

在這個例子中,我們將從不同的數據集上獲取價格,然后對這些數據建立回歸模型。需要做的第一件事就是如何獲取數據。

8.6.1 收集數據:使用Google購物的API

Google已經為我們提供了一套購物的API來抓取價格。在使用處1之前,需要注冊一個Goolge的賬號,然后訪問Google API的控制台來確保購物API可用。完成之后就可以發送HTTP請求,API將以JSON格式返回所需的產品信息。Python提供了JSON解析模塊,我們可以從返回的JSON格式里整理出所需數據。詳細的API介紹可以參見:http://code.google.com/apis/shopping/search/v1/getting_started.html

購物信息的獲取函數,代碼如下:

def searchForSet(retX, retY, setNum, yr, numPce, origPrc):
    sleep(10)
    myAPIstr = 'AIzaSyD2cR2KFyx12hXu6PFU-wrWot3NXvko8vY'
    searchURL = 'https://www.googleapis.com/shopping/search/v1/public/products?key=%s&country=US&q=lego+%d&alt=json' % (myAPIstr, setNum)
    #通過google api獲取對應商品編號的數據
    pg = urllib2.urlopen(searchURL)
    #將數據轉化為json格式
    retDict = json.loads(pg.read())
    #遍歷json數據,填充retX,retY
    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)

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)

 

測試截圖如下:(寫的時候沒有翻牆。。。) 
這里寫圖片描述

8.6.2 訓練算法:建立模型

使用線性回歸得到的模型如下: 
$55319.9727.59Year0.00268NumPieces11.22NewOrUsed+2.57originalprice 
這個模型的預測效果非常好,但模型本身並不能令人滿意。它對於數據擬合得很好,但看上去沒有什么道理。從公式看,套裝里零部件越多售價反而會越低。另外,該公式對新套裝也有一定的懲罰。 
下面使用縮減法中一種,即嶺回歸再進行一次實驗。前面討論過如何對系數進行縮減,但這 
次將會看到如何用縮減法確定最佳回歸系數

交叉驗證測試嶺回歸,代碼如下:

def crossValidation(xArr,yArr,numVal=10):
    m = len(yArr)                           
    indexList = range(m)
    #errorMat保存不同訓練樣本(默認為10次不同的訓練樣本和測試樣本)對應30個lambda(ridgeTest()將lambda按指數級遞減得到30個不同lambda對應的相關系數)的rssError,初始化為(10,30)維零矩陣
    errorMat = zeros((numVal,30))#create error mat 30columns numVal rows
    for i in range(numVal):
        trainX=[]; trainY=[]
        testX = []; testY = []
        #shuffle()表示對range(m)重新洗牌,即讓每次迭代對應的訓練集和測試集都不一樣
        random.shuffle(indexList)
        for j in range(m):#create training set based on first 90% of values in indexList
            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)    #get 30 weight vectors from ridge
        for k in range(30):#loop over all of the ridge estimates
            matTestX = mat(testX); matTrainX=mat(trainX)
            meanTrain = mean(matTrainX,0)
            varTrain = var(matTrainX,0)
            matTestX = (matTestX-meanTrain)/varTrain #regularize test with training params
            yEst = matTestX * mat(wMat[k,:]).T + mean(trainY)#test ridge results and store
            errorMat[i,k]=rssError(yEst.T.A,array(testY))
            #print errorMat[i,k]
    meanErrors = mean(errorMat,0)#calc avg performance of the different ridge weight vectors
    #求出最小的meanErrors
    minMean = float(min(meanErrors))
    #meanErros==minMean的那一列對應的即是使誤差最小的lambda,而這一列對應的即為找出最佳相關系數的wMat的行數
    bestWeights = wMat[nonzero(meanErrors==minMean)]
    #can unregularize to get model
    #when we regularized we wrote Xreg = (x-meanX)/var(x)
    #we can now write in terms of x not Xreg:  x*w/var(x) - meanX/var(x) +meanY
    xMat = mat(xArr); yMat=mat(yArr).T
    meanX = mean(xMat,0); varX = var(xMat,0)
    #注意,嶺回歸將xMat和yMat標准化了,而線性回歸不用標准化,所以我們要將數據"還原"
    unReg = bestWeights/varX
    print("the best model from Ridge Regression is:\n",unReg)
    print("with constant term: ",-1*sum(multiply(meanX,unReg)) + mean(yMat))

 

來看一下整體的運行效果,在%獅如叫丫中輸人程序清單8-6中的代碼並保存,然后執行如下命令: 
這里寫圖片描述 
可以看到,該結果與最小二乘法沒有太大差異。我們本期望找到一個更易於理解的模型,顯然沒有達到預期效果。為了達到這一點,我們來看一下在縮減過程中回歸系數是如何變化的,輸人下面的命令 
這里寫圖片描述

這些系數是經過不同程度的縮減得到的。首先看第1行,第4項比第2項的系數大5倍,比第1項大57倍。這樣看來,如果只能選擇一個特征來做預測的話,我們應該選擇第4個特征,也就是原始價格。如果可以選擇2個特征的話,應該選擇第4個和第2個特征。 
這種分析方法使得我們可以挖掘大量數據的內在規律。在僅有4個特征時,該方法的效果也許並不明顯;但如果有100個以上的特征,該方法就會變得十分有效:它可以指出哪些特征是關鍵的,而哪些特征是不重的

本章小結

與分類一樣,回歸也是預測目標值的過程。回歸與分類的不同點在於,前者預測連續型變量,而后者預測離散型變量。回歸是統計學中最有力的工具之一。在回歸方程里,求得特征對應的最佳回歸系數的方法是最小化誤差的平方和。給定輸人矩陣X,如果X的逆存在並可以求得的話,回歸法都可以直接使用。數據集上計算出的回歸方程並不一定意味着它是最佳的,可以便用預測值yHat和原始值y的相關性來度量回歸方程的好壞。 
當數據的樣本數比特征數還少時候,矩陣XTX的逆不能直接計算。即便當樣本數比特征數多時,XTX的逆仍有可能無法直接計算,這是因為特征有可能高度相關。這時可以考慮使用嶺回歸,因為當XTX的逆不能計算時,它仍保證能求得回歸參數。 
嶺回歸是縮減法的一種,相當於對回歸系數的大小施加了限制。另一種很好的縮減法是lasso。Lasso難以求解,但可以使用計算簡便的逐步線性回歸方法來求得近似結果。 
縮減法還可以看做是對一個模型增加偏差的同時減少方差偏差方差折中是一個重要的概念,可以幫助我們理解現有模型並做出改進,從而得到更好的模型。 
本章介紹的方法很有用。但有些時候數據間的關系可能會更加復雜,如預測值與特征之間是非線性關關系,這種情況下使用線性的模型就難以擬合


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM