1. 程式人生 > >正則化線性迴歸的方差與偏差!

正則化線性迴歸的方差與偏差!

利用正則化線性迴歸模型來了解偏差和方差的特徵

例項:

首先根據資料建立線性迴歸模型,模型能夠根據水庫液位的變化來預測大壩的排水量,然後通過調整引數等方法來學習偏差和方差的一些特性。

1.概念

偏差:度量了學習演算法的期望預測與真實結果的偏離程度,即刻畫了學習演算法本身的擬合能力;

方差:度量了同樣大小的訓練集的變動所導致的學習效能的變化,即刻畫了資料擾動所造成的影響(模型的穩定性)。

2.構建正則化線性模型

(1)載入資料

開啟資料集 ex5data1.mat ,資料集包含了以下內容:

名稱 維度 X (12, 1) y (12, 1) Xtest (21, 1) ytest (21, 1) Xval (21, 1) yval (21, 1) 其中:

X,y 是 訓練集 ,用來訓練模型;

Xval,yval 是 交叉驗證集 ,主要用來確定模型中的超引數,如正則化引數 lambda ;

Xtest,ytest 是 測試集 ,主要評估模型的泛化能力。

(2)將 X,y 視覺化

橫軸表示水位的變化,縱軸表示水流量,單純的線性迴歸也能計算出模型,但那樣做偏差特別大,擬合程度很差,即出現 欠擬合 。

正則化線性迴歸的方差與偏差!

 

Plot X and y

參考程式碼:def plotXY(self, x, y):
 plt.scatter(x, y, marker='x', color='r')
 plt.xlabel('Change in water level(x)')
 plt.ylabel('Water flowing out of the dam (y)')
 plt.show()

(3)計算正則化線性迴歸損失函式J

損失函式公式如下,注意正則化項是 從下標1 開始計算的,lambda就是正則化係數,它控制模型複雜度的"懲罰"力度。

正則化線性迴歸的方差與偏差!

 

Cost J

(4)計算正則化線性迴歸梯度Gradient

損失函式J對 theta0和theta1 的偏導數定義如下,即要計算的梯度公式。

正則化線性迴歸的方差與偏差!

 

Gradient

參考程式碼:def linearRegCostFunction(self, theta, x, y, lamda):
 m = x.shape[0]
 theta = theta.reshape((x.shape[1], 1))
 cost = np.sum((x.dot(theta)-y)**2)/(2*m)
 regular = lamda/(2*m)*np.sum(theta[1::]**2)
 J = cost + regular
 return J
def linearRegGradient(self, theta, x, y, lamda):
 m = y.shape[0]
 theta = theta.reshape((x.shape[1], 1))
 grad = np.zeros((x.shape[1], 1))
 grad[0] = 1/m*(x[:, 0:1].T.dot(x.dot(theta)-y))
 grad[1::] = 1/m*(x[:, 1::].T.dot(x.dot(theta)-y)) + lamda/m*theta[1::]
 return grad

(5)擬合線性迴歸

當我們根據上述公式計算損失函式和梯度得到正確的結果後,我們利用之前提到的 scipy.optimize 中的 minimize 函式來計算最優的 theta0 和 theta1 ,計算得到 theta 的結果後,將 lambda 的值設為 0 ,因為當前模型只有 theta1 和 theta2 兩個值,模型很簡單,沒必要設定正則化項。

將得到的 theta 值同 X 相乘,得到預測的 y_predict ,將結果進行繪製,得到擬合的結果,如下圖。

可以看出得到的結果並不好,在接下來的內容裡,我們逐步討論。

正則化線性迴歸的方差與偏差!

 

Fit X and y

參考程式碼:def plotTrainingLine(self, x, y):
 theta = self.trainLinearReg(x, y, 0)
 plt.scatter(self.x, self.y, marker='+', color='r')
 plt.plot(self.x, x.dot(theta), '--', linewidth=2)
 plt.xlabel('Change in water level (x)')
 plt.ylabel('Water flowing out of the dam (y)')
 plt.legend(['Trained', 'Original'])
 plt.show()

3.方差和偏差

(1)繪製學習曲線

繪製 訓練集和交叉驗證集的誤差 與 訓練樣本數量 之間的學習曲線, 注意: 訓練集誤差和交叉驗證集誤差都沒有計算正則化項,後面我們單獨討論正則化係數對誤差造成的影響。

觀察下圖,隨著樣本數量逐漸增加,訓練集和交叉驗證集之間的誤差仍然是較大的,這就反應了模型的 高偏差問題 ,即 欠擬合 。

因為模型太簡單了,不能很好的擬合我們的資料,接下來我們將訓練模型修改,建立一個8次多項式。

正則化線性迴歸的方差與偏差!

 

Learning curve for Linear Reg

參考程式碼:def learningCurve(self, x, y, xval, yval, lamda):
 m = x.shape[0]
 error_train = np.zeros((m, 1))
 error_val = np.zeros((m, 1))
 print("Training Examples	Train Error	Cross Validation Error
")
 for i in range(m):
 theta = self.trainLinearReg(x[:1+i, :], y[:1+i], lamda)
 error_train[i] = self.linearRegCostFunction(theta, x[:1+i, :], y[:1+i], 0)
 error_val[i] = self.linearRegCostFunction(theta, xval, yval, 0)
 print("		%d			%f		%f
"%(i, error_train[i], error_val[i]))
 return [error_train, error_val]
def plotLinerRCurve(self):
 error_train, error_val = self.learningCurve(self.x_plus_one, self.y, self.xval_plus_one, self.yval, 0)
 plt.xlim([0, 13])
 plt.ylim([0, 150])
 plt.plot([i for i in range(12)], error_train, 'r')
 plt.plot([i for i in range(12)], error_val, 'b')
 plt.title('Learning curve for linear regression')
 plt.xlabel('Number of training examples')
 plt.ylabel('Error')
 plt.legend(['Train', 'Cross Validation'])
 plt.show()

(2)建立多項式迴歸模型

由於一次線性模型太簡單,導致欠擬合,我們新增更多的"特徵",做一個  次多項式。 具體最高項應該設定成幾次這個問題,也是沒有定式,只能說多嘗試,找到較為合適的最高次數。

參考程式碼:

def polyFeatures(self, x, p):
 x_ploy = np.zeros((np.size(x), p), np.float32) # (12, 8)
 m = np.size(x) # 12
 for i in range(m):
 for j in range(p):
 x_ploy[i, j] = x[i]**(j+1)
 return x_ploy

(3)繪製多項式迴歸模型擬合曲線

根據上一部分得到的多項式,因為同樣是解決一個線性迴歸的問題,所以之前的損失函式和梯度計算函式仍可使用。

接下來利用優化函式計算得到最優的 theta 值,繪製擬合曲線,當設定正則化係數 lambda=0 的時候,意味這對模型複雜度沒有任何處理,得到下圖。

可以看出,我們的模型訓練的非常好,基本穿過了每一個點,訓練誤差肯定很小,但是在一些極值外,函式迅速的上升和下降,這就意味這我們設計的模型 過擬合 了,雖然對訓練資料擬合很好,但是不具有很好的泛化能力,也意味著模型不穩定。

正則化線性迴歸的方差與偏差!

 

Polynomial Reg Fit with lambda=0

觀察下 lambda=0 的情況下,訓練誤差和交叉驗證誤差的曲線,非常明顯,訓練誤差幾乎為0,而交叉驗證誤差卻很大,訓練集和交叉驗證集之間的空隙充分反應了模型的 高方差問題 。

正則化線性迴歸的方差與偏差!

 

Polynomial Reg Learning curve with lambda=0

此時我們讓正則化係數為1,再重複上面的試驗,我們再來看下你和曲線和學習曲線的圖形。

一目瞭然,多項式模型很好的擬合了資料,訓練誤差和交叉驗證集誤差曲線都隨著樣本數量的增加都 收斂於一個較小的值 ,因此我們認為當 lambda=1 時,得到的模型沒有 高偏差和高方差的問題 ,實際上也是模型在方差和偏差之間進行了很好的 折中 。

正則化線性迴歸的方差與偏差!

 

Polynomial Reg Fit with lambda=1

正則化線性迴歸的方差與偏差!

 

Learning curve with lambda=1

參考程式碼:def plotFit(self, x, mu, sigma, theta, p):
 x = np.arange(np.min(x) - 15, np.max(x) + 25, 0.05)
 x = x.reshape((-1, 1))
 x_poly = self.polyFeatures(x, p)
 x_poly = x_poly - mu
 x_poly = x_poly/sigma
 x_poly = np.hstack([np.ones((x_poly.shape[0], 1)), x_poly])
 plt.plot(x, x_poly.dot(theta), '--', linewidth=2)
 plt.title('Polynomial Regression Fit (lambda=0.00)')
 plt.xlabel('Change in water level (x)')
 plt.ylabel('Water flowing out of the dam (y)')
 plt.show()

(4)利用交叉驗證集選擇lambda

通過上面的試驗,我們可以知道,lambda明顯的影響著多項式正則化迴歸的訓練誤差和交叉驗證誤差。 當lambda為0甚至很小的時候,模型可以很好的擬合訓練集,不具備好的泛化能力,當lambda很大的時候,模型又不能很好的擬合數據,出現欠擬合問題 ,那麼到底怎樣選擇一個lambda的值呢?

我們根據上面的例項,選擇10個lambda的值,分別繪製每個lambda對應的訓練誤差和交叉驗證集誤差,

進群:960410445  即可獲取數十套PDF!

lambda_vec = {0; 0:001; 0:003; 0:01; 0:03; 0:1; 0:3; 1; 3; 10}

繪製得到下圖, 從圖中我們可以看出最好的lambda值出現在 3 附近,此時測試誤差值大約3.8599,結果已經相當不錯了。

正則化線性迴歸的方差與偏差!

 

Find the best lambda

參考程式碼:def validationCurveForLamdas(self, x, y, xval, yval, lamda_vec):
 error_train = np.zeros((len(lamda_vec), 1))
 error_val = np.zeros((len(lamda_vec), 1))
 print("Lambda		Train Error	Validation Error
")
 for i in range(len(lamda_vec)):
 lamda = lamda_vec[i]
 theta = self.trainLinearReg(x, y, lamda)
 error_train[i] = self.linearRegCostFunction(theta, x, y, 0)
 error_val[i] = self.linearRegCostFunction(theta, xval, yval, 0)
 print("		%d			%f		%f
" % (i, error_train[i], error_val[i]))
 return [error_train, error_val]