概念
在回歸(一)中提到用最小二乘法求解回歸系數的過程中需要考慮特征矩陣是否可逆的問題,事實上當特征數量比樣本數量多的時候(樣本數m大於特征數n,X不是滿秩矩陣)就會遇到這個問題,這個時候標准線性回歸顯然就無從下手了
引入嶺回歸就是為了解決這個問題,它是最先用來處理特征數多余樣本數的算法。該算法的基本思想是在X
TX上加上一個
λ
I
使得矩陣非奇異,從而能夠對
X
T
X+
λ
I
求逆,其中I是一個n*n的單位矩陣,λ是一個超參數,需要用戶自己調試。I 作為一個對角的單位陣,由1組成的對角線就像一條在0矩陣中的嶺,這就是嶺回歸的由來。那么根據回歸(一)中的思路,回歸系數的求解公式變成如下所示:

事實上這是一種縮減(shrinkage)的算法,這種方法能夠通過系數反映出參數的重要程度,也就是說能夠把一些系數縮減成很小的值甚至零。這有點類似於降維,保留更少的特征能夠減少模型的復雜程度,便於理解。而且研究表明與簡單的線性回歸相比,縮減法能夠取得更好的預測效果。
代碼實現
需要指出的是,使用嶺回歸和縮減技術,首先需要對特征作標准化處理,使得每個特征具有相同的重要性,這樣才能從得到的系數中反應各個參數的重要程度。
演示所用的數據集是《機器學習實戰》第八張提供的abalone.txt數據,數據有八個特征,最后一列為目標值,
概覽如下:
代碼如下:
1 def ridgeRegres(xMat,yMat,lam=0.2): 2 ''' 3 嶺回歸,lam是需要調試的超參數 4 ''' 5 xTx = xMat.T*xMat 6 denom = xTx + eye(shape(xMat)[1])*lam 7 if linalg.det(denom) == 0.0: 8 print "This matrix is singular, cannot do inverse" 9 return 10 ws = denom.I * (xMat.T*yMat) 11 return ws 12 13 def ridgeTest(xArr,yArr): 14 xMat = mat(xArr); yMat=mat(yArr).T 15 yMean = mean(yMat,0) 16 yMat = yMat - yMean #to eliminate X0 take mean off of Y 17 #嶺回歸和縮減技術需要對特征作標准化處理,使每維特征具有相同的重要性 18 xMeans = mean(xMat,0) 19 xVar = var(xMat,0) 20 xMat = (xMat - xMeans)/xVar 21 numTestPts = 30 22 wMat = zeros((numTestPts,shape(xMat)[1])) 23 #在30個不同的lambda下計算,為了找出最優參數 24 for i in range(numTestPts): 25 ws = ridgeRegres(xMat,yMat,exp(i-10)) 26 wMat[i,:]=ws.T 27 return wMat
可以看到,為了找出最優的λ,ridgeTest()函數在30個不同的λ下調用嶺回歸。而且λ以指數變化,這樣可以看出在非常小和非常大的情況下分別對結果造成的影響。
使用如下代碼可以得到30組嶺回歸的權重:
xArr, yArr =loadDataSet('abalone.txt') ridgeWeight = ridgeTest(xArr,yArr)
下面我們可視化一下這30組系數的變化情況:
1 def plotRidgeWeight(ridgeWeight): 2 import matplotlib.pyplot as plt 3 fig = plt.figure() 4 ax = fig.add_subplot(111) 5 axisX = [(i - 10) for i in range(30)]#log(λ)作為橫坐標 6 ax.plot(axisX, ridgeWeight)
得到的圖形如下:

對於上圖,可以理解成每個lambda對應一組權重,隨着λ的增大,權重逐漸縮小。最佳的參數就在其中的某一組中。為了定量地找到最佳參數,還需要進行交叉驗證。接下來就介紹交叉驗證測試嶺回歸。
交叉驗證
一般地,交叉驗證就是通過把數據集分成若干等份,每一份輪流作測試集,其余數據作訓練集進行輪流訓練與測試。如果把數據集分成n份,就叫n-folds交叉驗證,這樣會得到n個錯誤率(或者准確率等其他評價指標),然后取這n個的平均值作為最終的結果。這樣做是為了得到更加准確的評價。
這里的交叉驗證是為了定量地找到嶺回歸的最佳參數,代碼如下:
def crossValidation(xArr, yArr, numFold = 10): ''' 交叉驗證,其中numFold是交叉的折數,默認為10折 ''' m = len(yArr) indexList = range(m) errorMat = zeros((numFold, 30))# 每一行都有30個λ得到的結果 for i in range(numFold): trainX = []; trainY = [] testX = []; testY = [] random.shuffle(indexList)# 把indexList打亂獲得隨機的選擇效果 for j in range(m):# 划分測試集和訓練集 if j < 0.9*m: trainX.append(xArr[indexList[j]]) trainY.append(yArr[indexList[j]]) else: testX.append(xArr[indexList[j]]) testY.append(yArr[indexList[j]]) # 30組系數,返回維度為30*8的數組 wMat = ridgeTest(trainX, trainY) # 對每一組系數求誤差 # 訓練數據做了怎么的處理,新的查詢數據就要做怎樣的處理才能帶入模型 for k in range(30): matTestX = mat(testX);matTrainX = mat(trainX) meanTrainX = mean(trainX, 0) varTrainX = var(matTrainX, 0) meanTrainY = mean(trainY, 0) matTestX = (matTestX - meanTrainX)/varTrainX yEst = matTestX * mat(wMat[k, :]).T + mean(trainY) errorMat[i, k] = rssError(yEst.T.A, array(testY)) meanErrors = mean(errorMat, 0) # 每個λ對應的平均誤差 minErrors = float(min(meanErrors)) # 找到最優的λ之后,選取最后一輪迭代的wMat的對應的λ的權重作為最佳權重 bestWeights = wMat[nonzero(meanErrors == minErrors)] xMat = mat(xArr); yMat = mat(yArr) meanX = mean(xMat, 0); varX = var(xMat, 0) # 為了和標准線性回歸比較需要對數據進行還原 unReg = bestWeights/varX print "the best model from Ridge Regression is:\n",unReg print "with constant term: ",-1*sum(multiply(meanX,unReg)) + mean(yMat)
執行
>>>crossValidation(xarr, yarr)
可得到如下結果:

總結
1.嶺回歸可以解決特征數量比樣本量多的問題
2.嶺回歸作為一種縮減算法可以判斷哪些特征重要或者不重要,有點類似於降維的效果
3.縮減算法可以看作是對一個模型增加偏差的同時減少方差