機器學習之線性迴歸及代碼示例

一、線性迴歸

線性迴歸一般用來做連續值的預測,預測的結果爲一個連續值。因訓練時學習樣本不僅要提供學習的特徵向量X,而且還要提供樣本的實際結果(標記label),所以它是一種有監督學習。其中 X={x0,x1,...,xn}

線性迴歸需要學習得到的是一個映射關係 fXy ,即當給定新的待預測樣本時,我們可以通過這個映射關係得到一個測試樣本 X 的預測值 y

在線性迴歸中,假定輸入X和輸出y之間具有線性相關的關係。
例如當特徵向量 X 中只有一個特徵時,需要學習到的函數應該是一個一元線性函數 y=a+bx

當情況複雜時,考慮 X 存在n個特徵的情形下,我們往往需要得到更多地係數。我們將 Xy 的映射函數記作函數hθ(X)

hθ(X)=i=0nθixi=θTX

其中,爲了在映射函數hΘ(X)中保留常數項,令 x0 爲1,所以特徵向量X={1,x1,x2,...,xn} ,特徵係數向量 θ={θ0,θ1,θ2,...,θn}

當給定一個訓練集數據的情況,可以通過某個算法,學習出來一個線性的映射函數 hθ(X) 來求得預測值 y

二、損失函數

在需要通過學習得到的映射函數 hθ(X) 中,需要通過訓練集得到特徵係數向量 θ={θ0,θ1,θ2,...,θn}

那怎麼得到所需的特徵係數向量?怎麼保證得到的特徵係數向量是足夠好?這裏會有一個評判標準:損失函數。

根據特徵向量係數 θ ,可有損失函數 J(θ) 如下 :

J(θ)=12mi=1m(hθ(X(i))y(i))2

其中 hθ(X) 爲需要學習到的函數,m 爲訓練集樣本的個數,Xi 表示訓練集中第 i 個樣本的特徵向量,yi 表示第 i 個樣本中的標籤。

爲了得到預測值 hθ(Xi)yi 的絕對值,在公式上使用了平方數。爲了平均每個樣本的損失,在公式上對損失和進行除以 m 操作,,再除以 2 是爲了之後的求導計算。

三、梯度下降算法求解

批量隨機梯度下降BGD

在上面,找到了一個特徵係數向量 θ 好壞的損失函數 J(θ) 。爲了迎合這樣的評判標準得到較好的 θ ,需對損失函數值進行最小化,即讓損失函數在樣本中的損失最小。

對於損失函數 J(θ) ,可以發現 θ 是一個關於 \theta 的凸函數。

梯度下降就是一個不斷地最小化損失函數的過程。從圖像上來看,先初始化 θi 爲某個值,然後讓 θi 沿着J J(θ)θi 的偏導方向不斷地走,直達走到底部收斂爲止,最後就可以得到 J(θ) 最小時的那個 θi 的值。

這個不斷迭代的過程猶如一個不斷下山的過程,我們可以得到圖中關於 θi 的迭代函數,其中 α 爲每次下山的步長。

這裏寫圖片描述

θi 小於最低處的值的時,對其的偏導爲負,在迭代過程中, θi 不斷地增大以逼近最低處的值;當 θi 大於最低處的值時,對其的偏導爲正, θi 會不斷地做減法以逼近最低處的值。所以當步長 α 較小時, θi 會收斂於最低處的值。通常,我們將 α 叫做學習率 (learning rate)。

對於特徵向量係數 θ 中的每個元素 θi ,可以通過上面的迭代公式,它們都會往各自偏導的方向“下山”,“下山”的方向(梯度)爲偏導的方向,按照這樣的下山方向,下山的速度會更快一點。梯度下降算法就是這麼的一個“下山”的過程。

對下山迭代公式進行展開,可得到具體求得 θi 的公式:


這裏寫圖片描述

其中,x(i)0 爲第 i 個訓練樣本的第 0 個特徵值,爲保持 hθ(X) 的常數項,通常令 x(i)0 爲1。hθ(X) 爲在迭代過程中通過 θ 得到的預測函數,它的表達式會跟隨着 θ 的變化而不斷地變化。

當學習率 α 很小時,我們往往需要迭代更多次纔可以“下山”達到山底,θ 收斂很慢;當學習率a很大時,“下山”步子邁大後,往往會在山底的兩邊跳躍,可能無法到達山底,θ 會出現震盪,導致無法收斂。
一般情況下, α 可設爲0.01,0.005等,也可以在迭代的過程中不斷自適應地修改 α 的值,例如可以在迭代過程中不斷地減小 α 的值。

其他梯度下降算法

a. 批量梯度下降算法BGD中θi 的更新要用到所有的樣本數,需要計算所有樣本彙總的誤差來進行梯度下降:

θi=θiα1mi=1m(hθ(X(i))y(i))X(i)j

這樣訓練速度會隨着樣本數量 m 的增加而變得緩慢。

b. 隨機梯度下降法SGD:利用每個樣本的損失函數對 θi 求偏導得到對應的梯度,來更新 θi 的值,每次計算一個樣本的誤差來進行梯度下降:

for  i=1  to  mθi=θiα(hθ(X(i))y(i))X(i)j

隨機梯度下降算法通過每個樣本來迭代更新一次,當一些訓練樣本爲噪聲樣本時,會導致SGD並不是向着整體最優化方向迭代。因此SGD算法在解空間的搜索比較盲目,但在大體上是往着最優值方向移動的。

c. 小批量梯度下降法MBGD:MBGD是前面兩個算法SGD和BGD的一個折中,既要保證算法的計算速度,又要保證參數訓練的最優性,每次需要計算一小批量樣本的彙總誤差來進行梯度下降。MBGD對 θi 的迭代如下所示:

for  k=0,10,20,...,90θi=θiα110i=kk+9(hθ(X(i))y(i))X(i)j

上式設置的一個batch大小爲 10 個訓練樣本,不同的任務所設置的batch大小也有所不同,可把 batch的大小可看一個超參數。

梯度下降的注意

在使用梯度下降求解最優化的解時,要注意對特徵進行歸一化,這樣可以提升模型的收斂速度。考慮兩維的情況:假如樣本只有兩個特徵時,一個特徵取值於 100 到 1000 之間,另一個特徵取值於0到1之間,可在 (θ1,θ2) 平面上可以畫出目標函數的等高線:


這裏寫圖片描述

可得到一個個窄長的類橢圓等高線,導致在梯度下降時不斷地走“之”路線,增長了迭代時間。

若對特徵進行歸一化後,每個特徵之間具有相近的尺度,在 (θ1,θ2) 平面得到的是類圓等高線,梯度下降方向直指圓心,可幫助梯度下降算法更快地收斂。

四、過渡到多項式迴歸

通過上面的線性迴歸,我們可以得到一個線性的映射函數 hθ(X)

hθ(X)=i=0nθixi=θTX

對於 x1,x2,...,xn 這些特徵,可以使用 多項式特徵 的方法,將多個特徵進行組合,得到多次項的特徵。

這樣一來,hθ(X) 依然還是個線性函數,不過與之線性的不是原本的特徵,而是與 多項式特徵和原來的特徵 線性相關。通過這樣的方法,可將線性迴歸過渡到多項式迴歸,不過這樣做同時也會導致特徵的維度變高,也會增加一些沒必要的特徵。

五、過擬合的正則化

當我們的損失函數 J(θ) 在樣本中損失較大時,會出現欠擬合的情況,即對樣本的預測值和樣本的實際結果值由較大的差距。

當我們的損失函數 J(θ) 在樣本中損失約等於0 時,這時 hθ(X) 圖像穿過樣本的每一個點,這樣會出現過擬合的情況,缺乏泛化能力,函數波動比較大,對待預測的樣本預測能力比較弱。


這裏寫圖片描述

對於過擬合問題,可以使用 正則化 的方法,即在原來的損失函數 J(θ) 上加上一個“尾巴”:

無正則化時的損失函數:

J(θ)=12mi=1m(hθ(X(i))y(i))2

L1正則化下的損失函數:

J(θ)=12mi=1m(hθ(X(i))y(i))2+λ||θ||1

||θ||1=θi

L2正則化下的損失函數:

J(θ)=12mi=1m(hθ(X(i))y(i))2+λ||θ||22

||θ||2=θ2i

L1正則化、L2正則化也稱爲Lasso正則化、Ridge正則化,其中 λ 爲模型的超參數。

正則化後的損失函數不僅要最小化預測值與樣本點實際值間的差距了,還得最小化參數向量 θ 。當 hθ(X) 函數圖像穿過所有樣本點時,函數圖像波動會非常大,(平方項、立方項等高次項的)的權重 θi 會很大,這樣便會導致它的損失函數並非最小的。

正則化後,不僅要求 hθ(X) 接近更多的點,還要控制(高次項的)θi 值,使得控制函數不能波動太大,而且正則化之後損失函數無法等於0。

L1正則化往往會使得一些 θi 值趨近於0(這可用於特徵的選擇),L2正則化往往會使得 θi 值之間差別不會太大,θi 值之間的變化比較穩定。

六、最小二乘法求解

根據特徵向量係數 θ ,可有損失函數 J(θ) 的矩陣表示 如下 :

J(θ)=12mi=1m(X(i)θy(i))2=12m(XθY)T(XθY)

其中 Xm×n 的矩陣, Ym×1 的矩陣,θ 爲長度爲 n 的列向量, m 爲訓練樣本的個數,n 爲樣本的特徵維度,需要計算得到的 θ 爲:

θ=argminθJ(θ)

由最小二乘法,令 J(θ) 對向量 θ 的一階導數爲零:

J(θ)θ=1mXT(XθY)=0

因此可得:XTXθ=XTY ,其中 XTX 是一個 n×n 的矩陣,當 XTX 可逆時,可通過公式得到

θ=(XTX)1XTY

XTX 可逆時, XTX 須爲滿秩(秩 爲 n)。

Xm×n 的矩陣,其秩爲 mn 中最小的一個數,爲保證 XTX 滿秩,m 需大於 n ,即訓練樣本的個數需大於樣本的特徵維度。

上述的 XTX 不一定是可逆的,而當 J(θ) 加上正則化項之後:

J(θ)=12mi=1m(hθ(X(i))y(i))2+λ||θ||22

由最小二乘法,令 J(θ)θ 的一階導數爲零:

J(θ)θ=1mXT(XθY)+2λθ=0

可得到

θ=(XTX+2λI)1XTY

其中 I 爲單位矩陣,因 XTX 爲半正定矩陣,而 2λI 爲正定矩陣,因此 XTX+2λI 爲正定矩陣,總是可以求得其逆 (XTX+2λI)1 ,可以通過式子得到 θ 的值。

七、代碼示例

  • 在sklearn的房價數據上使用線性迴歸,多項式迴歸:
from sklearn import datasets
boston = datasets.load_boston() # 加載房價數據
X = boston.data
y = boston.target
print X.shape
print y.shape
(506L, 13L)
(506L,)
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y ,test_size = 1/3.,random_state = 8)
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_score

# 線性迴歸
lr = LinearRegression(normalize=True,n_jobs=2) 
scores = cross_val_score(lr,X_train,y_train,cv=10,scoring='neg_mean_squared_error') #計算均方誤差
print scores.mean()

lr.fit(X_train,y_train)
lr.score(X_test,y_test)
-24.4294227766

0.6922002690740765
from sklearn.preprocessing import PolynomialFeatures
# 多項式迴歸

for k in range(1,4):
    lr_featurizer = PolynomialFeatures(degree=k) # 用於產生多項式 degree:最高次項的次數
    print '-----%d-----' % k

    X_pf_train = lr_featurizer.fit_transform(X_train)
    X_pf_test = lr_featurizer.transform(X_test)

    pf_scores = cross_val_score(lr,X_pf_train,y_train,cv=10,scoring='neg_mean_squared_error')
    print pf_scores.mean()

    lr.fit(X_pf_train,y_train)
    print lr.score(X_pf_test,y_test)
    print lr.score(X_pf_train,y_train)
-----1-----
-24.4294227766
0.692200269074
0.755769163218
-----2-----
-22.7848754621
0.833095620158
0.93821030015
-----3-----
-6325.76073853
-16.550355583
1.0
當 k=1 時,迴歸爲線性迴歸;當 k=2 時,迴歸爲多項式迴歸,模型的效果比線性迴歸要好一點。
當 k=3 時,已經出現過擬合現象,模型在訓練集上得分很高,然而在驗證集上得分很低,從交叉驗證的得分結果也可以看得出來
# 正則化解決k=3的過擬合現象

lr_featurizer = PolynomialFeatures(degree=3) # 用於產生多項式 degree:最高次項的次數
X_pf_train = lr_featurizer.fit_transform(X_train)
X_pf_test = lr_featurizer.transform(X_test)

# LASSO迴歸:
from sklearn.linear_model import Lasso

for a in range(0,0.0005):
    print '----%f-----'% a
    lasso = Lasso(alpha=a,normalize=True)
    pf_scores = cross_val_score(lasso,X_pf_train,y_train,cv=10,scoring='neg_mean_squared_error')
    print pf_scores.mean()

    lasso.fit(X_pf_train,y_train)
    print lasso.score(X_pf_test,y_test)
    print lasso.score(X_pf_train,y_train)
----0.000000-----
-29.3011407539


D:\Users\cxm\Anaconda2\lib\site-packages\ipykernel\__main__.py:15: UserWarning: With alpha=0, this algorithm does not converge well. You are advised to use the LinearRegression estimator


0.768272379891
0.965488575704
----0.000500-----
-11.2829107107
0.837992634283
0.943520416421
對比 alpha=0 和 alpha = 0.0005的情況,發現Lasso正則化處理後,模型的評價會提高很多。
# 正則化解決k=3的過擬合現象

lr_featurizer = PolynomialFeatures(degree=3) # 用於產生多項式 degree:最高次項的次數
X_pf_train = lr_featurizer.fit_transform(X_train)
X_pf_test = lr_featurizer.transform(X_test)

from sklearn.linear_model import Ridge

# 嶺迴歸
for a in [0,0.005]:
    print '----%f-----'% a
    ridge = Ridge(alpha=a,normalize=True)
    pf_scores = cross_val_score(ridge,X_pf_train,y_train,cv=10,scoring='neg_mean_squared_error')
    print pf_scores.mean()

    ridge.fit(X_pf_train,y_train)
    print ridge.score(X_pf_test,y_test)
    print ridge.score(X_pf_train,y_train)
----0.000000-----
-6143.74385842
-16.4626220878
1.0
----0.005000-----
-14.0438948376
0.823709164706
0.948990485145
對比 alpha=0 和 alpha = 0.0005的情況,發現Ridge正則化處理後,模型的評價會提高很多。

  • tensorflow實現正則化解決過擬合現象
from sklearn.datasets import load_boston
from sklearn.metrics import mean_squared_error
X_data = load_boston().data
Y_data = load_boston().target.reshape(-1,1)
print (X_data.shape,Y_data.shape)
((506, 13), (506, 1))
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X_data, Y_data ,test_size = 1/3.,random_state = 8)
from sklearn.preprocessing import PolynomialFeatures
pl_featurizer = PolynomialFeatures(degree=2) # 用於產生多項式 degree:最高次項的次數
X_train = pl_featurizer.fit_transform(X_train)
X_test = pl_featurizer.transform(X_test)
print X_train.shape
(337, 105)
import tensorflow as tf
X = tf.placeholder(tf.float32,[None,105])
Y = tf.placeholder(tf.float32,[None,1])
tf_W_L1 = tf.Variable(tf.zeros([105,1]))
tf_b_L1 = tf.Variable(tf.zeros([1,1]))
pred = tf.matmul(X,tf_W_L1)+tf_b_L1
print pred
Tensor("add_6:0", shape=(?, 1), dtype=float32)
# L1正則化:
loss = tf.reduce_mean(tf.pow(pred-Y,2))*0.5 +0.001*tf.reduce_sum(tf_W_L1)
train_step = tf.train.AdamOptimizer(0.01).minimize(loss)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for i in range(20000):
        sess.run(train_step,feed_dict={X:X_train,Y: y_train})
        if(i%10000==0):
            print mean_squared_error(y_train,pred.eval(feed_dict={X:X_train}).flatten().reshape(-1,1))
    print 'test mse: '
    print mean_squared_error(y_test,pred.eval(feed_dict={X:X_test}).flatten().reshape(-1,1))
42669456.628
10.4569020596
test mse: 
15.7365063927
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章