1. 線性模型
有監督學習是透過已知的樣本產生預測模型的學習方法,任何有監督學習模型都可被想象成一個函式:
其中,\(x_1,x_2,x_3...x_n\)是模型的n維的特徵值,\(y\)是要預測的目標值/分類,當\(y\)是可列舉的型別時,對應分類問題(classification);\(y\)為連續值時,該模型解決迴歸問題(regression)。
線性迴歸(Linear Regression)在機器學習中被用來解決學習特徵和目標值都是連續值型別的問題,可定義為多項式函式:
線性模型學習的目標就是確定\(w_0 w_1..w_n\)的值,使\(y\)可以根據特徵值直接透過函式計算得到,\(w_0\)成為截距,\(w_1...w_n\)被成為迴歸係數(coefficient)。
公式(2)可以有其他變體,注意:是否是線性模型取決於其被要求的係數\(w_0...w_n\)之間是否為線性關係,與樣本特徵變數\(x_0..x_n\)的形式無關。e.g.以下前者為線性模型,後者為非線性模型。
2. 最小二乘法
最簡單的線性模型演算法便是最小二乘法(Ordinary Least Squares, OLS),透過樣本真值與預測值之間的方差和來達到計算出\(w_1,w_2..w_n\)的目的,即:
arg min
就是使後面這個式子達到最小值時的變數的取值,其中\(\hat{y}\)是樣本預測值,\(y\)是樣本中的真值(ground truth)
在python使用
import numpy as np
from sklearn import linear_model
x = np.array([[0, 1], [3, -2], [2, 3]]) # 訓練樣本特徵
y = np.array([0.5, 0.3, 0.9]) # 訓練樣本目標值
reg = linear_model.LinearRegression() # 最小二乘法迴歸物件
reg.fit(x, y) # 訓練、擬合
print("intercept_:", reg.intercept_) # 讀取截距
# intercept_: 0.36666666666666686
print("coef_:", reg.coef_) # 讀取回歸引數
# coef_: [0.06666667 0.13333333]
reg.predict([[1, 2], [-3, 9]]) # 預測
# array([0.7 , 1.36666667])
透過intercept_
和coef_
屬性可知道上述程式碼擬合的線性模型為:
最小二乘的不足
隨著特徵維度的增加,模型的求得的引數\(w_0 w_2...w_n\)的值顯著增加,OLS始終試圖最小化公式的值,因此為了擬合訓練資料中很小的\(x\)的值差異產生的較大\(y\)值差異,必須使用較大的\(w\)值,結果是任何一個微小的變化都會導致最終預測目標值大幅度變化,即過擬合
。請看:
# 測試資料
def make_data(nDim):
x0 = np.linspace(1, np.pi, 50) # 一個維度的特徵
x = np.vstack([[x0,], [i**x0 for i in range(2, nDim+1)]]) # nDim個維度的特徵
y = np.sin(x0) + np.random.normal(0, 0.15, len(x0)) # 目標值
return x.transpose(), y
x, y = make_data(12)
使用最小二乘法擬合:
def linear_regression():
dims = [1,3,6,12] # 需要訓練的維度
for idx, i in enumerate(dims):
plt.subplot(2, int(len(dims)/2), idx+1)
reg = linear_model.LinearRegression()
sub_x = x[:, 0:i] # 取x中前i個維度的特徵
reg.fit(sub_x, y) # 訓練
plt.plot(x[:,0], reg.predict(sub_x)) # 繪製模型
plt.plot(x[:,0], y, '.')
plt.title("dim %s"%i)
print("dim %d :"%i)
print("intercept_: %s"%(reg.intercept_)) # 檢視截距引數
print("coef_: %s"%(reg.coef_)) # 檢視回歸引數
plt.show()
linear_regression()
## 輸出
# dim 1 :
# intercept_: 1.5190767802159115
# coef_: [-0.39131134]
# dim 3 :
# intercept_: 1.1976858427101735
# coef_: [ 1.91353693 -1.39387114 0.16182502]
# dim 6 :
# intercept_: -221.70126917849046
# coef_: [-104.46259453 520.30421899 -573.61812163 429.72658285 -181.37364277
# 32.5448376 ]
# dim 12 :
# intercept_: -7992016.158635169
# coef_: [-1.83126880e+06 5.87461130e+07 -2.99765476e+08 9.72248992e+08
# -1.92913886e+09 2.18748658e+09 -8.81323910e+08 -1.12973621e+09
# 2.01475457e+09 -1.40758872e+09 4.94058411e+08 -7.17485861e+07]
可以明顯看到dim=12
已經過擬合了
3. 嶺迴歸
嶺迴歸(Ridge Regression 或稱Tikhonov Regularization)由俄羅斯科學家Tikhonov提出,透過改變回歸目標係數,達到了控制迴歸引數隨著維度瘋狂增長的目的。新的迴歸函式:
與OLS
的差別在於將\(\alpha\sum w^2\)加入最小化目標(也被稱為L2懲罰項,即L2 Penalty),其中\(\alpha\)是一個可調節的超引數,\(w\)是線性模型中的所有引數。
Ridge與原來的LinearRegression類使用方法相似,只是在初始化物件需要超引數α
def ridge_regression():
alphas = [1e-15, 1e-12, 1e-5, 1, ] # α引數
for idx, i in enumerate(alphas):
plt.subplot(2, int(len(alphas)/2), idx+1)
reg = linear_model.Ridge(alpha=i) # 嶺迴歸模型
sub_x = x[:, 0:12] # 取x中前i個維度的特徵
reg.fit(sub_x, y) # 訓練
plt.plot(x[:,0], reg.predict(sub_x)) # 繪製模型
plt.plot(x[:,0], y, '.')
plt.title("dim=12, alpha=%e"%i)
print("alpha %e :"%i)
print("intercept_: %s"%(reg.intercept_)) # 檢視截距引數
print("coef_: %s"%(reg.coef_)) # 檢視回歸引數
plt.show()
ridge_regression()
#輸出結果
# dim 1 :
# intercept_: 1.5190767802159115
# coef_: [-0.39131134]
# dim 3 :
# intercept_: 1.1976858427101735
# coef_: [ 1.91353693 -1.39387114 0.16182502]
# dim 6 :
# intercept_: -221.70126917849046
# coef_: [-104.46259453 520.30421899 -573.61812163 429.72658285 -181.37364277
# 32.5448376 ]
# dim 12 :
# intercept_: -7992016.158635169
# coef_: [-1.83126880e+06 5.87461130e+07 -2.99765476e+08 9.72248992e+08
# -1.92913886e+09 2.18748658e+09 -8.81323910e+08 -1.12973621e+09
# 2.01475457e+09 -1.40758872e+09 4.94058411e+08 -7.17485861e+07]
比較OLS模型的12維結果,模型引數\(w\)顯著降低。並且\(\alpha\)引數大小與訓練結果成反比:\(\alpha\)越大,迴歸引數越小,模型越平緩。
3. Lasso迴歸
Ridge已經可以解決了多特徵情況下引數太大導致的過度擬合,但在該模型中,無論\(\alpha\)多大,迴歸模型都存在絕對值極小的引數,難以到達零值,這部分引數對最終預測結果影響甚微但消耗計算資源,因此需要將不重要的特徵引數計算為零,即壓縮特徵。這便是Lasso迴歸:
對上面的程式碼略作修改
def lasso_regression():
alphas = [1e-12, 1e-5, 1, 10] # α引數
for idx, i in enumerate(alphas):
plt.subplot(2, int(len(alphas)/2), idx+1)
reg = linear_model.Lasso(alpha=i) # Lasso模型
sub_x = x[:, 0:12] # 取x中前12個維度的特徵
reg.fit(sub_x, y) # 訓練
plt.plot(x[:,0], reg.predict(sub_x)) # 繪製模型
plt.plot(x[:,0], y, '.')
plt.title("dim=12, alpha=%e"%i)
print("alpha %e :"%i)
print("intercept_: %s"%(reg.intercept_)) # 檢視截距引數
print("coef_: %s"%(reg.coef_)) # 檢視回歸引數
plt.show()
lasso_regression()
## 輸出
# alpha 1.000000e-12 :
# intercept_: 0.6298258513945978
# coef_: [ 1.22542814e+00 -4.54947716e-01 -3.93085543e-02 -5.49725917e-03
# -5.88010501e-04 2.47220823e-04 3.36442748e-04 2.85372733e-04
# 2.20237636e-04 1.66111569e-04 1.25225041e-04 9.51371122e-05]
# alpha 1.000000e-05 :
# intercept_: 0.6275025198172812
# coef_: [ 1.21848162e+00 -4.48693464e-01 -4.01246675e-02 -5.58492834e-03
# -6.02833485e-04 2.41969131e-04 3.37470151e-04 2.86500656e-04
# 2.21159502e-04 1.66823390e-04 1.25767364e-04 9.55513093e-05]
# alpha 1.000000e+00 :
# intercept_: 0.9304502324995079
# coef_: [-0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
# -0.00000000e+00 -0.00000000e+00 -0.00000000e+00 -0.00000000e+00
# -0.00000000e+00 -6.76060251e-04 -7.30714077e-05 -0.00000000e+00]
# alpha 1.000000e+01 :
# intercept_: 0.9076753246287685
# coef_: [-0. -0. -0. -0. -0. -0.
# -0. -0. -0. -0. -0. -0.0004185]
參考文獻
[1]劉長龍. 從機器學習到深度學習[M]. 1. 電子工業出版社, 2019.3.