作者 | xiaoyu
知乎 | https://zhuanlan.zhihu.com/pypcfx
介紹 | 一個半路轉行的數據挖掘工程師
線性迴歸作爲監督學習中經典的迴歸模型之一,是初學者入門非常好的開始。宏觀上考慮理解性的概念,我想我們在初中可能就接觸過,y=ax,x爲自變量,y爲因變量,a爲係數也是斜率。如果我們知道了a係數,那麼給我一個x,我就能得到一個y,由此可以很好地爲未知的x值預測相應的y值。這很符合我們正常邏輯,不難理解。那統計學中的線性迴歸是如何解釋的呢?
對於統計模型線性迴歸,我想從以下六個方面來展開,並分兩篇文章進行詳細解讀:
- 線性迴歸模型定義
- 線性迴歸的損失函數
- 線性迴歸參數估計
- 線性迴歸預測
- 線性迴歸擬合優度
- 線性迴歸假設檢驗
- 線性迴歸診斷
▌線性迴歸模型定義
線性迴歸按變量數量的多少可以分爲:一元線性迴歸(簡單線性迴歸)和多元線性迴歸。
一元線性迴歸,也就是有一個自變量,其模型可以表示如下:
公式中參數解釋如下:
x:自變量
y:因變量
β 0:截距
β 1:變量回歸係數
ϵ:誤差項的隨機變量1
這些參數中,(β 0+β 1x)反映了由於x的變化而引起的y的線性變化;ϵ反映了除了x和y之間的線性關係之外的隨機因素對y的影響,是不能由x和y之間的線性關係所解釋的變異性。可以這麼來理解ϵ:我們對y的預測是不可能達到與真實值完全一樣的,這個真實值只有上帝知道,因此必然會產生誤差,我們就用ϵ來表示這個無法預測的誤差。
同樣的,多元線性迴歸模型的表示如下:
我們通過引入了ϵ可以讓模型達到完美狀態,也就是理論的迴歸模型。但是我們要如何定義這個無法預測的誤差項呢?爲此,偉人們提出了一些假設條件:
在統計學中,高斯-馬爾可夫定理陳述的是:在誤差零均值,同方差,且互不相關的線性迴歸模型中,迴歸係數的最佳無偏線性估計(BLUE)就是最小方差估計。
總結一下,有如下幾個主要的假設條件:
(1)誤差項ϵ是一個期望爲0的隨機變量,即E(ϵ)=0
(2)對於自變量的所有值,ϵ的方差σ^2 都相同
(3)誤差項ϵ是一個服從正態分佈的隨機變量,且相互獨立,即ϵ~N(0,σ^2 )
ϵ正態性意味着對於給定的自變量,因變量y也是一個服從正態分佈的隨機變量。根據迴歸模型的假設,有如下多元迴歸方程:
▌線性迴歸的損失函數
從樣本數據考慮,如果想讓我們預測值儘量準確,那麼我們就必須讓真實值與預測值的差值最小,即讓誤差平方和ϵ最小,用公式來表達即:
用平方而沒用誤差絕對值是因爲:平方對於後續求導比較方便。
雖然我們得到了損失函數,但是如果從統計理論的角度出發來推導損失函數,我認爲更有說服力,也能更好地理解線性迴歸模型,以及爲什麼開始要提出那些假設條件。
根據上面假設條件:ϵ 服從均值爲0,方差爲σ的正態分佈,且獨立,因此隨機變量ϵ 的概率密度函數(正態分佈的概率密度函數)爲:
我們把前面的多元線性迴歸模型簡單地變換一下,如下:
然後將得到的ϵ公式帶入上面概率密度函數:
有了概率密度函數,我們自然會想到用最大似然估計推導損失函數:
然後我們將似然函數取對數,這樣可以將概率密度的乘法轉換爲加法:
再然後我們對似然函數取最大值,即最大化似然函數:
這樣我們就從統計理論的角度得到了我們要找的損失函數,與我們最小化誤差平方和得到的結果是一樣的,也從側面證實了前面提出假設的正確性。因此,多元線性迴歸模型的損失函數爲:
公式裏的1/2對損失函數沒有影響,只是爲了能抵消求導後的乘數2。
▌線性迴歸參數估計
損失函數只是一種策略,有了策略我們還要用適合的算法進行求解。在線性迴歸模型中,求解損失函數就是求與自變量相對應的各個迴歸係數和截距。有了這些參數,我們才能實現模型的預測(輸入x,給出y)。
對於誤差平方和損失函數的求解方法有很多,典型的如最小二乘法,梯度下降等。下面我們分別用這兩種方法來進行求解。
最小二乘法
最小二乘法可以將誤差方程轉化爲有確定解的代數方程組(其方程式數目正好等於未知數的個數),從而可求解出這些未知參數。這個有確定解的代數方程組稱爲最小二乘法估計的正規方程。
我們將代數方程組用矩陣來代替可以簡化推導過程,以及代碼實現。
這裏會涉及到矩陣求導的用法,詳細介紹請看下面wiki的參考鏈接:
https://en.wikipedia.org/wiki/Matrix_calculus#Scalar-by-vector_identities
我們令上面得到的公式等於0,即可得到最終的求解:
Python中對於矩陣的各種操作可以通過Numpy庫的一些方法來實現,非常方便。但在這個代碼實現中需要注意:X矩陣不能爲奇異矩陣,否則是無法求解矩陣的逆的。下面是手擼最小二乘法的代碼實現部分。
def standRegres(xArr,yArr): """ 函數說明:計算迴歸係數w Parameters: xArr - x數據集 yArr - y數據集 Returns: ws - 迴歸係數 """ xMat = np.mat(xArr); yMat = np.mat(yArr).T #根據文中推導的公示計算迴歸係數 xTx = xMat.T * xMat if np.linalg.det(xTx) == 0.0: print("矩陣爲奇異矩陣,不能求逆") return ws = xTx.I * (xMat.T*yMat) return ws
梯度下降法
梯度下降是另一種常用的方法,可以用來求解凸優化問題。它的原理有別於最小二乘法,它是通過一步步迭代(與最小二乘法的區別在後面介紹)求解,不斷逼近正確結果,直到與真實值之差小於一個閾值,從而得到最小化損失函數的模型參數值的。它的公式如下:
對於損失函數的梯度(即求偏導的過程),上面在最小二乘法部分已經給出推導過程和結果。不同的是,我們不會將公式等於0來求極值,而是帶入上面梯度下面公式來迭代完成求解,以下是梯度下降矩陣形式的最終求解結果。
最小二乘法 vs 梯度下降法
通過上面推導,我們不難看出,二者都對損失函數的迴歸係數進行了求偏導,並且所得到的推導結果是相同的,那麼究竟哪裏不同呢?
如果仔細觀察,可以觀察到:最小二乘法通過使推導結果等於0,從而直接求得極值,而梯度下降則是將推導結果帶入迭代公式中,一步一步地得到最終結果。簡單地說,最小二乘法是一步到位的,而梯度下降是一步步進行的。
因而通過以上的異同點,總結如下
最小二乘法:
- 得到的是全局最優解,因爲一步到位,直接求極值,因而步驟簡單
- 線性迴歸的模型假設,這是最小二乘方法的優越性前提,否則不能推出最小二乘是最佳(即方差最小)的無偏估計
梯度下降法:
- 得到的是局部最優解,因爲是一步步迭代的,而非直接求得極值
- 既可以用於線性模型,也可以用於非線性模型,沒有特殊的限制和假設條件
▌線性迴歸預測
上面我們已經手擼了最小二乘法和梯度下降法求解誤差平方和損失函數的過程,即我們通過以上算法已經得到了我們想要的參數值。當然,我們也可以使用statsmodels或者sklearn庫中已經被封裝好了的模型來進行預測。不過,爲了更好的瞭解模型,優化算法,而不僅僅是做一個調包俠,我們最好對每種算法都自己實現一遍。
爲了更好的說明整個建模到預測的過程,我們通過一個例子來詳細說明。對於一個數據集,我們通過自己手擼的最小二乘法來建模,求解參數然後進行預測。
class LeastSquared(object): def __init__(self): self.xArr = [] self.yArr = [] self.params = [] self.y_predict = [] def fit(self,xArr,yArr): self.xArr = xArr self.yArr = yArr xMat = np.mat(xArr) yMat = np.mat(yArr).T xTx = xMat.T*xMat if np.linalg.det(xTx) == 0.0: print('矩陣爲奇異矩陣') params = xTx.I*(xMat.T*yMat) self.params = params def predict(self,x_new): y_predict = x_new*self.params self.y_predict = y_predict return y_predict
可以看到這是一個簡單的二維平面,藍色代表一個變量X和因變量Y的散點圖,紅色是我們通過最小二乘法擬合出來的直線。如果是多自變量,那麼擬合結果將是一個平面,或者超平面。使用這個模型,我們就能對未知的X值進行預測。
然後,我們在x的範圍內再取10個隨機數,並進行預測感受一下。
# 生成最小二乘法類 xArr, yArr = loadDataSet('ex0.txt') ls = LeastSquared() ls.fit(xArr,yArr) #訓練模型 y_predict = ls.predict(xArr) #預測模型 # 在x範圍內,隨機生成10個新的x值 x_min = np.min(np.array(xArr)[:,1]) x_max = np.max(np.array(xArr)[:,1]) x_random = np.random.uniform(x_min,x_max,[10,1]) x_new = np.c_[np.ones(10),x_random.flatten()] y_new = ls.predict(x_new) y_new = y_new.flatten().tolist()[0] x_new = x_random.flatten().tolist() # 可視化 n = len(xArr) xcord = [];ycord = [];y_hat = [] for i in range(n): xcord.append(xArr[i][1]) ycord.append(yArr[i]) y_hat.append(y_predict.tolist()[i][0]) fig = plt.figure() #添加subplot ax = fig.add_subplot(111) #繪製樣本點 ax.plot(xcord, y_hat, c = 'red') ax.scatter(xcord, ycord, s = 20, c = 'blue',alpha = .5) ax.scatter(x_new,y_new, s=150, c='r', alpha = 0.8) #繪製title plt.title('LeastSquareMethod') plt.xlabel('X') plt.show()
這時我們看到,生成的10個隨機數都在我們的擬合直線上,對應的y值就是我們的預測值。同樣的,我們也手擼了梯度下降算法進行的求解過程,二者得到的結果參數幾乎相等。二者可視化效果如下所示(可以看到兩個擬合直線是重合的,紅色和綠色):
二者所得參數對比如下,其中梯度下降迭代了500次,可以看到參數結果是幾乎一樣的。
#最小二乘法 ls.params >>matrix([[3.00774324], [1.69532264]]) #梯度下降法,迭代500次 gd.params >>matrix([[3.00758726], [1.69562035]])
▌總結
本篇主要介紹了線性迴歸的前幾個部分:模型定義假設,模型參數估計,模型預測。但是預測完模型之後,我們並不知道結果時好時壞,並且我們也不知道開始的假設是否成立,這些內容涉及模型擬合優度,模型假設檢驗,和模型診斷,將在下一篇進行介紹。