前言
一元線性回歸只是讓我們好理解回歸這個概念。在實際中特征很少說只有一個。所有就有多元線性回歸。
概念
與一元線性回歸一樣,多元線性回歸自然是一個回歸問題。只不過一元線性回歸的方程可以寫成。
y=ax+b
多元線性回歸是
y=a1x1+a2X2+a3X3+...+anXn+b
相當于我們高中學(xué)的一元一次方程,變成了n元一次方程。因為y還是那個y。只是特征增多了。
模型的訓(xùn)練
多元線性回歸的訓(xùn)練方式與一元線性回歸方式基本相同。
1、設(shè)置一個損失函數(shù)。
2、想辦法讓潤濕函數(shù)最小。

其中

每一個i對應(yīng)一行的數(shù)據(jù)。
我們只需要找到合適的。

參數(shù)即可。
方便計算
為了方便算,我們把b換一下。

那么可以組成一個待解參數(shù)矩陣。

然而我們的輸入X對應(yīng)每個特征也可以組成一個矩陣。


于是我們很快發(fā)現(xiàn) X的每一行與O不同維。O的維度是1*n+1 (從0開始數(shù)起的)
X的一行的維度是是1Xn
那么我們只有在X矩陣加一列。那么加什么不會改變O1的值呢,那就是1了。
那么我們X矩陣就變成了

大家都知道矩陣的乘法是行*列然后每個元素想加。這里的yi就等于Xi 與 O的點乘,因為O是一個行向量,所以這里要轉(zhuǎn)置成列向量。

(備注:numpy.array 中的dot方法會自動轉(zhuǎn)換行、列向量,但是這里說原理要說清楚)。
那么可以得到

其中Xb就是X加一列的結(jié)果。O是一個列向量。所得到的y的預(yù)測值也是一個向量。
損失函數(shù)
我們再看損失函數(shù),長這樣。

其中的y是一個1 X m的向量,其中每一元素代表是一個標簽。y預(yù)測也是一個1 * m的向量。這個公式計算每一個樣本的誤差然后求和。那么我們可以直接轉(zhuǎn)換成矩陣的計算。
我們可以改為

我們只需要找到 合適的O就可以了。

找參數(shù)
我們只需要找到合適的cita就可以了。
正規(guī)方程解
正規(guī)方程解其實就是跟簡單線性回歸一樣求偏導(dǎo)數(shù)然后求極值點。這里多元的那就是求偏導(dǎo)數(shù)然后求極值點,但是多元的好麻煩。這里直接給出公式比較合適。

這個知道仔細推導(dǎo)其實意義并不大,因為我們在實際算法中不會去使用這個。我們都知道矩陣的乘法時間復(fù)雜度是O(n3)有很多人做了優(yōu)化也沒有降到O(n2),如果你能降低0.1個點就不得了。所以說這是一個世界性難題。大家都知道我們機器學(xué)習其實計算量是很大的,那么怎么能讓我們的計算變的快一點只有改變我們的策略。那就是梯度下降。
使用求正規(guī)方程解尋找參數(shù)代碼
def fit_normal(self,X_train,y_train):
assert X_train.shape[0] == y_train.shape[0],"你要一一對應(yīng)吧"
X_b = np.hstack([np.ones((len(X_train),1)),X_train])
self._theta=np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y_train) #就是那個公式
self.interception_=self._theta[0] #第0項就是那個截距b
self.coef_=self._theta[1:] #后面都是系數(shù)
梯度下降
在簡單線性回歸的時候說了梯度下降的思路。其實就是一種求極值的一種思路,就是在模型上隨機選一個點,然后向著梯度方向以一定步長挪動。最終能到達極值點。

在簡單線性回歸中就是x沿著x的導(dǎo)數(shù)這個梯度進行下降。
可以把loss函數(shù)定義一下

那么兩個參數(shù)的變化是

同樣的多元線性回歸是一樣的。其中參數(shù)都是cta
可以定義梯度

那么每次更新就是

那么關(guān)鍵就是求梯度這個矩陣。

可以看一下。

注意這里的X 是在第一列加了一個全1項的。

那么梯度矩陣可以寫為

我們會發(fā)現(xiàn)一個問題就是m越大,梯度就越大,梯度怎么會跟m(樣本的數(shù)量有關(guān)系呢,這顯然不合理)。所以損失函數(shù)應(yīng)該 加一個1/m這個常數(shù)來抵消這個影響。

那么梯度就變成了。

看代碼吧
def J(theta,X_b,y):
"""
定義損失函數(shù)
"""
try:
return np.sum((y- X_b.dot(theta))**2)/len(X_b) #=損失函數(shù)
except:
return float('inf')
def dJ(theta,X_b,y):
"""
求偏導(dǎo),計算梯度
"""
res = np.empty(len(theta))
res[0]=np.sum(X_b.dot(theta)-y)
for i in range(1,len(theta)):
res[i]=(X_b.dot(theta)-y).dot(X_b[:,i])
return res * 2/len(X_b)
def gradient_descent(X_b,y,initial_theta,eta,n_iters=1e4,epsilon=1e-8):
theta=initial_theta
i_iter=0
while i_iter < n_iters:#防止梯度不停的跳動,過了一定輪數(shù)就停止。
gradient = dJ(theta,X_b,y)
last_theta= theta
theta=theta - eta * gradient
if(abs(J(theta,X_b,y)-J(last_theta,X_b,y))<epsilon):# 不一定非要是0,接近0就可以了。
break
i_iter +=1
return theta