接上一篇文章【線性迴歸——二維線性迴歸方程(證明和代碼實現)】
前言:
博主前面一篇文章講述了二維線性迴歸問題的求解原理和推導過程,以及使用python自己實現算法,但是那種方法只能適用於普通的二維平面問題,今天博主來講一下線性迴歸問題中更爲通用的方法,也是我們實際開發中會經常用到的一個數學模型,常用的解法就是最小二次乘法和梯度下降法.博主今天對最小二乘法進行推導並使用Python代碼自定義實現,廢話不多說,開始吧:
一、公式推導
- 假如現在有一堆這樣的數據(x1,y1),(x2,y2),…,(xn,yn),然後我們已經通過某種方式得到了數據所對應的模型y^=θ0+θ1x,但是因爲 y^ 畢竟是通過訓練模型所得到的預測值,所以 y^ 與 y 之間必定存在誤差如圖所示:
也就是說存在一個這樣的等式 ω=y^−y 其中 ω 的值代表誤差值.現在不妨我們再來將訓練數據換一下變成(x11,x12,…,x1m,y1),(x21,x22,,…,x2m,y1),…,(xn1,xn2,…,xnm,yn)這裏有n項數據,其中每項數據的前 m 個數據代表特徵值,最後的一個數據代表標籤值(也就是真實的 y 值),對這個數據我們通過線性模型也可以將數據對應的模型寫出來:y^=θ0+θ1x1+θ2x2+⋯+θmxm不妨簡化一下換成矩陣的形式表示:
y^=i=0∑nθixi=θTx
其中θ=⎣⎢⎢⎢⎡θ0θ1⋮θm⎦⎥⎥⎥⎤,xi=⎣⎢⎢⎢⎡xi0xi1⋮xim⎦⎥⎥⎥⎤,xi0=1,我們再把 y^ 換一個符號表示:
yθ(xi)=i=0∑mθixi=θTxi
上面這個函數方程即表示我們的擬合曲線,再結合我們前面分析的誤差結論可知(因爲誤差值可正可負,這裏加減就無所謂了):
yi=θTxi+ωi
等式中的 右端項 yi 代表真實的標籤值,等式的左端項 θTxi+ωi 就代表預測值加上誤差值
上圖這個圖應該很眼熟吧,沒錯就是表示正態分佈(也稱高斯分佈)的統計圖,其實現實生活中,誤差的波動性也大多遵循這個規律.是什麼意思呢?我們可以把圖中的橫座標想作誤差值,而縱座標想成概率值,那麼從圖中我們可以發現一個很有意思的規律,誤差值的絕對值越大那麼它出現的概率反而會越小越趨近於0,誤差值在0附近時可以看它們出現的概率是最大的,也就是說那種極大或者極小的誤差值是佔少數的.我們現在要引出一個函數也就是高斯分佈的概率密度函數:
f(x−μ)=2πσ1e(−2σ2(x−μ)2)
不懂概率密度函數的小夥伴也別急,你就把這個函數的自變量想成事件,然後函數的值想作此事件發生的概率即可;
- 既然誤差遵循這個規律,那麼我們就可以把前面我們得到的模型公式和概率密度函數聯合起來就有:
⎩⎪⎨⎪⎧yi=θTxi+ωif(x−μ)=2πσ1e(−2σ2(x−μ)2)
將 ωi 代入概率密度函數就有:
f(ωi)=2πσ1e(−2σ2(yi−θTxi)2)
我們通常習慣用 p(A∣B) 來表示B事件發生後A事件發生的概率,前面的公式也就可以變成:
p(yi∣xi;θ)=2πσ1e(−2σ2(yi−θTxi)2)
上式即表示我們通過訓練模型得到準確值的概率,這個式子只是求單個數據得到真實值的概率,我們現在要來求全部數據都預測正確的概率,因爲每個預測事件都是相互獨立,的所以求全部正確概率只需要將所有單個的概率乘起來即可,那麼就有:
L(θ)=i=1∏np(yi∣xi;θ)=i=1∏n2πσ1e(−2σ2(yi−θTxi)2)
這個函數有個官方的名稱叫做似然函數,它表示參數 θ 所對應的模型預測值全部與真實值相匹配的概率,也可以把它看做預測模型與數據的真實模型的相似度,顯然看着上面的式子都很頭大,很難計算,不妨將它轉化一下.兩邊同時取對數:
lnL(θ)=lni=1∏n2πσ1e(−2σ2(yi−θTxi)2)
它表示對數似然函數,這裏圖方便後面化簡就取以 e 爲底的對數,去了對數之後不妨來看有什麼好處,我們來化簡一下:
因爲對數內部的乘法可以展開爲外部的加法即 log(ab)=loga+logb,所以:
lnL(θ)=ln2πσ1e(−2σ2(y1−θTx1)2)+⋯+ln2πσ1e(−2σ2(yn−θTxn)2)
把它在弄好看一點:
lnL(θ)=i=1∑nln2πσ1e(−2σ2(yi−θTxi)2)
再次利用對數的乘法變加法性質展開得:
lnL(θ)=nln2πσ1+i∑nlne(−2σ2(yi−θTxi)2)
我們都知對數有這樣一個性質: logaan=n 那麼就有:
lnL(θ)=nln2πσ1+i∑n[−2σ2(yi−θTxi)2]
再把常數因子提到外邊來就有:
lnL(θ)=nln2πσ1−σ2121i∑n(yi−θTxi)2
爲了便於觀察,我們把常數項用 t 來代替掉:
lnL(θ)=t1−t221i∑n(yi−θTxi)2
因爲 lnL(θ) 表示的是訓練模型與真實模型相似值,越大表明訓練模型越趨近於真實模型,根據方程我們可以知道,等式右端第一項 t1(nln2πσ1) 爲一個非負常數,第二項中t2(2σ21)也爲非負數,顯然 t221i∑n(yi−θTxi)2 也爲非負數,所以一個正的常數項減去一個非負變量,似然函數的值越大越好,也就是需要讓後面這個非負變量越小那麼似然函數的值也就越大,於是就有:
J(θ)=21i∑n(yi−θTxi)2
現在我們目標就是求 maxJ(θ),我們根據表達式可以看出這是一個開口向上的二次函數,那麼它必定存在一個極值點,而且這個極值點就是函數的最小值,所以我們只需對 J(θ) 進行求導,然後使其等於 0 即可,我們先將上面的式子換一種方式表達:
我們令:
X=⎣⎢⎡x11⋮xn1⋯⋱⋯x1m⋮xnm⎦⎥⎤,Y=⎣⎢⎡y1⋮yn⎦⎥⎤
那麼就有:
J(θ)=21(Xθ−Y)T(Xθ−Y)
#######################補充說明(如果上面等式你已經理解是如何來到可以跳過下面的內容)#####################
可能有小夥伴不知道是如何變換過來的,其實你只要懂矩陣的乘法就可以了,很明顯:
Xθ=⎣⎢⎡x11⋮xn1⋯⋱⋯x1m⋮xnm⎦⎥⎤⎣⎢⎡θ1⋮θm⎦⎥⎤=⎣⎢⎡x11θ1+⋮xn1θ1+⋯⋱⋯+x1mθm⋮+xnmθm⎦⎥⎤=⎣⎢⎡θTx1⋮θTxn⎦⎥⎤即它的結果是一個 n 行 1 列的矩陣,那麼:
(Xθ−Y)=⎣⎢⎡θTx1⋮θTxn⎦⎥⎤−⎣⎢⎡y1⋮yn⎦⎥⎤=⎣⎢⎡θTx1−y1⋮θTxn−yn⎦⎥⎤
所以:
(Xθ−Y)T(Xθ−Y)=[θTx1−y1,⋯θTxn−yn]⎣⎢⎡θTx1−y1⋮θTxn−yn⎦⎥⎤=(θTx1−y1)2+⋯+(θTxn−yn)2=i∑n(yi−θTxi)2
############################################ 結束 #################################################
- 現在我們對 J(θ) 進行求偏導那麼就有:
δθδ(J(θ))=21δθδ((Xθ−Y)T(Xθ−Y))
將轉置放在裏面來(注意在對含有乘法的項轉置時要將矩陣變換順序):
δθδ(J(θ))=21δθδ((θTXT−YT)(Xθ−Y))
使用分配律將括號展開:
δθδ(J(θ))=21δθδ(θTXTXθ−YTXθ−θTXTY+YTY)
在最終求導前要先介紹三個矩陣求導的性質:
性質一:
如果有 f(A)=ATBA 那麼 δAδ(f(A))=BA+BTA
性質二:
如果有 f(A)=BTA 那麼 δAδ(f(A))=B
性質三:
如果有 f(A)=ATBC 那麼 δAδ(f(A))=BC
有了上面三個性質,現在就可以計算上面式子的導數了(先做一下變換,方便使用上面的性質)
δθδ(J(θ))=21δθδ(θT(XTX)θ−(XTY)Tθ−θTXTY+YTY)
利用性質一,二,三可得:
δθδ(J(θ))=21[((XTX)θ+(XTX)Tθ)−XTY−XTY]
將轉置放在擴號中去:
δθδ(J(θ))=21[(XTXθ+XTXθ)−XTY−XTY]
合併同類項:
δθδ(J(θ))=21(2XTXθ−2XTY)=XTXθ−XTY
因爲我們要求極值點,所以令導數等於0即可:
δθδ(J(θ))=XTXθ−XTY=0
移項得:
XTXθ=XTY
兩邊同時乘以一個(XTX)−1:
θ=(XTX)−1XTY
這樣我們便得到了θ的計算方法
二、代碼實現
- 有了計算方式,那麼使用代碼來實現就簡單了
- 先定義計算 θ 的函數
import matplotlib.pyplot as plt
import numpy as np
def train_model(x_data,y_data):
matrix_x = np.matrix(x_data)
matrix_y = np.matrix(y_data)
matrix_x_T = matrix_x.T
t1 = np.dot(matrix_x_T,matrix_x)
t1_I = t1.I
t2 = np.dot(t1_I,matrix_x_T)
theta = np.dot(t2,matrix_y)
return theta
def predict(model,x_data):
return np.dot(np.matrix(x_data),model)
from sklearn.datasets import load_boston
boston_data = load_boston().data
train_x = boston_data[:,:-1]
ones_x = np.ones(train_x.shape[0])
train_x = np.column_stack((ones_x,train_x))
train_y = boston_data[:,-1:]
model = train_model(train_x,train_y)
model.shape
p_y = predict(model,train_x)
p_y = np.sort(np.array(p_y)[:,0])
plt.scatter(np.arange(0,506),np.sort(boston_data[:,-1]),marker="x",c="r",label="label_data")
plt.plot(np.arange(0,506),p_y,label="predict_data")
plt.legend()
寫在最後的話:
感覺這篇文章寫了好久,寫這篇文章發現了幾個自身的問題,矩陣的求導方法還不是很熟悉,矩陣的有一些基本性質有遺忘現象.
感覺線性代數和高數還是要時不時的回去複習一下,不然會越學越吃力,怎麼說了寫博客還是收益挺多了,凡是不能圖快,要是地基都沒打牢,房子搭在高也是會倒的,萬丈高樓平地起.嗯.
最後如果有小夥伴發現文章有什麼錯誤的地方,歡迎指出來,將非常感謝!!!(__)