機器學習(迴歸四)——線性迴歸-正則化

上篇博客是針對普通線性迴歸往往存在欠擬合的情況,採用多項式擴展的方式,從而映射到多維空間來擬合。多項式擴展的時候,如果指定的階數比較大,那麼有可能導致過擬合。也就是模型太契合訓練數據了。數據上表現就是參數過多、過大。過擬合在實際機器學習應用中是普遍存在的。

正則化的引入

對於前面提到的目標函數:
J(θ)=12i=1m(hθ(x(i))y(i))2J{(\theta)} = \frac{1}{2} \sum_{i=1}^m \left( h_\theta \left(x^{(i)} \right) -y^{(i)} \right)^2
現在,我們要防止過擬合,可以通過調節參數值,讓參數越小越好。也可以說參數越小,模型越簡單。比如說除了兩個參數外,其他的參數都小到爲0了,此時完全可以只關心這兩個參數的情況了,從而從高維空間降到了低維空間。

爲了防止數據過擬合,也就是的θ值在樣本空間中不能過大/過小,可以在目標函數之上增加一個平方和損失:
J(θ)=12i=1m(hθ(x(i))y(i))2+λi=1mθj2 J{(\theta)} = \frac{1}{2} \sum_{i=1}^m \left( h_\theta \left(x^{(i)} \right) -y^{(i)} \right)^2 + \lambda \sum_{i=1}^m \bm{\theta}^2_j
加上的這項後半部分,也可寫成 θTθθ^T θ 或 ||θ||(也叫二範式)這就是正則項:λi=1mθj2\lambda \sum_{i=1}^m \bm{\theta}^2_j 這裏這個正則項叫做 L2-norm,有的地方也叫懲罰項,就是原始J(θ)的一個懲罰值。

兩部分加在一起最小化,是我們要達到的目的。第一項是凸函數,第二項也是凸函數,而且是兩個開口向上的凸的函數,疊加後還是凸函數。也就存在最小值,且在極值的位置,即求導爲0的地方。最終求得的結果:
θ=(XTX+λI)1XTy \bm{\theta} = \left( X^T X + \lambda I \right)^{-1} X^T \bm{y}

是不是很熟悉?沒錯這就是《機器學習(迴歸一)——線性迴歸-最小二乘》中爲了防止不可逆或者過擬合的問題存在,可以增加額外數據影響,使得最終的矩陣是可逆的,從而得到的式子。

常見線性迴歸正則化模型

:爲了解決過擬合問題,我們可以選擇在損失函數中加入懲罰項(對於系統過磊的懲罰),主要分爲L1-norm和L2-norm,以及他倆結合的線性迴歸模型:

  1. 使用L2正則的線性迴歸模型就稱爲Ridge迴歸(嶺迴歸)
    J(θ)=12i=1m(hθ(x(i))y(i))2+λi=1mθj2        λ>0 J{(\theta)} = \frac{1}{2} \sum_{i=1}^m \left( h_\theta \left(x^{(i)} \right) -y^{(i)} \right)^2 + \lambda \sum_{i=1}^m \bm{\theta}^2_j \,\,\,\,\,\,\,\,\lambda>0
  2. 使用L1正則的線性迴歸模型就稱爲LASSO迴歸(Least Absolute Shrinkage and Selection Operator)
    J(θ)=12i=1m(hθ(x(i))y(i))2+λi=1mθj        λ>0 J{(\theta)} = \frac{1}{2} \sum_{i=1}^m \left( h_\theta \left(x^{(i)} \right) -y^{(i)} \right)^2 + \lambda \sum_{i=1}^m |\bm{\theta}_j | \,\,\,\,\,\,\,\,\lambda>0
  3. 同時使用L1正則和L2正則的線性迴歸模型就稱爲 Elasitc Net 算法(彈性網絡算法)
    J(θ)=12i=1m(hθ(x(i))y(i))2+λ(pi=1mθj+(1p)i=1mθj2)        {λ>0p[0,1] J{(\theta)} = \frac{1}{2} \sum_{i=1}^m \left( h_\theta \left(x^{(i)} \right) -y^{(i)} \right)^2 + \lambda \left( p\sum_{i=1}^m |\bm{\theta}_j | + (1-p)\sum_{i=1}^m \bm{\theta}^2_j\right) \,\,\,\,\,\,\,\, \begin{cases} \lambda>0 \\ p\in[0,1] \end{cases}

各種線性迴歸過擬合顯示

這裏自己手動生成數據,來看看各種不同的線性迴歸模型在過擬合時的顯示效果。

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import pandas as pd
import warnings
import sklearn
from sklearn.linear_model import LinearRegression, LassoCV, RidgeCV, ElasticNetCV
from sklearn.preprocessing import PolynomialFeatures#數據預處理,標準化
from sklearn.pipeline import Pipeline
from sklearn.linear_model.coordinate_descent import ConvergenceWarning

## 設置字符集,防止中文亂碼
mpl.rcParams['font.sans-serif']=[u'simHei']
mpl.rcParams['axes.unicode_minus']=False
## 攔截異常
warnings.filterwarnings(action = 'ignore', category=ConvergenceWarning)

## 創建模擬數據
np.random.seed(100)
np.set_printoptions(linewidth=1000, suppress=True)#顯示方式設置,每行的字符數用於插入換行符,是否使用科學計數法
N = 10
x = np.linspace(0, 6, N) + np.random.randn(N)
y = 1.8*x**3 + x**2 - 14*x - 7 + np.random.randn(N)
## 將其設置爲矩陣
x.shape = -1, 1
y.shape = -1, 1

## RidgeCV和Ridge的區別是:前者可以進行交叉驗證
models = [
    Pipeline([
            ('Poly', PolynomialFeatures(include_bias=False)),
            ('Linear', LinearRegression(fit_intercept=False))
        ]),
    Pipeline([
            ('Poly', PolynomialFeatures(include_bias=False)),
            # alpha給定的是Ridge算法中,L2正則項的權重值,也就是ppt中的蘭姆達
            # alphas是給定CV交叉驗證過程中,Ridge算法的alpha參數值的取值的範圍
            ('Linear', RidgeCV(alphas=np.logspace(-3,2,50), fit_intercept=False))
        ]),
    Pipeline([
            ('Poly', PolynomialFeatures(include_bias=False)),
            ('Linear', LassoCV(alphas=np.logspace(0,1,10), fit_intercept=False))
        ]),
    Pipeline([
            ('Poly', PolynomialFeatures(include_bias=False)),
            # la_ratio:給定EN算法中L1正則項在整個懲罰項中的比例,這裏給定的是一個列表;
            # 表示的是在CV交叉驗證的過程中,EN算法L1正則項的權重比例的可選值的範圍
            ('Linear', ElasticNetCV(alphas=np.logspace(0,1,10), l1_ratio=[.1, .5, .7, .9, .95, 1], fit_intercept=False))
        ])
]

## 線性模型過擬合圖形識別
plt.figure(facecolor='w')
degree = np.arange(1,N,4) # 階
dm = degree.size
colors = [] # 顏色
for c in np.linspace(16711680, 255, dm):
    colors.append('#%06x' % int(c))

model = models[0]
for i,d in enumerate(degree):
    plt.subplot(int(np.ceil(dm/2.0)),2,i+1)
    plt.plot(x, y, 'ro', ms=10, zorder=N)

    # 設置階數
    model.set_params(Poly__degree=d)
    # 模型訓練
    model.fit(x, y.ravel())
    
    lin = model.get_params('Linear')['Linear']
    output = u'%d階,係數爲:' % (d)
    # 判斷lin對象中是否有對應的屬性
    if hasattr(lin, 'alpha_'):
        idx = output.find(u'係數')
        output = output[:idx] + (u'alpha=%.6f, ' % lin.alpha_) + output[idx:]
    if hasattr(lin, 'l1_ratio_'):
        idx = output.find(u'係數')
        output = output[:idx] + (u'l1_ratio=%.6f, ' % lin.l1_ratio_) + output[idx:]
    print (output, lin.coef_.ravel())
    
    x_hat = np.linspace(x.min(), x.max(), num=100) ## 產生模擬數據
    x_hat.shape = -1,1
    y_hat = model.predict(x_hat)
    s = model.score(x, y)
    
    z = N - 1 if (d == 2) else 0
    label = u'%d階, 正確率=%.3f' % (d,s)
    plt.plot(x_hat, y_hat, color=colors[i], lw=2, alpha=0.75, label=label, zorder=z)
    
    plt.legend(loc = 'upper left')
    plt.grid(True)
    plt.xlabel('X', fontsize=16)
    plt.ylabel('Y', fontsize=16)

plt.tight_layout(1, rect=(0,0,1,0.95))
plt.suptitle(u'線性迴歸過擬合顯示', fontsize=22)
plt.show()

看一下不同階數時的參數及效果圖:

1階,係數爲: [30.38156963]
5階,係數爲: [-19.20808111 -0.21353395 3.43106275 -0.38668311 0.02765531]
9階,係數爲: [-109.41783246 187.72709809 -113.70069709 -4.58285729 36.05537154 -17.39656084 3.82839962 -0.4129825 0.01765462]

在這裏插入圖片描述

## 線性迴歸、Lasso迴歸、Ridge迴歸、ElasticNet比較
plt.figure(facecolor='w')
degree = np.arange(1,N, 2) # 階, 多項式擴展允許給定的階數
dm = degree.size
colors = [] # 顏色
for c in np.linspace(16711680, 255, dm):
    colors.append('#%06x' % int(c))
titles = [u'線性迴歸', u'Ridge迴歸', u'Lasso迴歸', u'ElasticNet']

for t in range(4):
    model = models[t]#選擇了模型--具體的pipeline(線性、Lasso、Ridge、EN)
    plt.subplot(2,2,t+1) # 選擇具體的子圖
    plt.plot(x, y, 'ro', ms=10, zorder=N) # 在子圖中畫原始數據點; zorder:圖像顯示在第幾層

    # 遍歷不同的多項式的階,看不同階的情況下,模型的效果
    for i,d in enumerate(degree):
        # 設置階數(多項式)
        model.set_params(Poly__degree=d)
        # 模型訓練
        model.fit(x, y.ravel())

        # 獲取得到具體的算法模型
        # model.get_params()方法返回的其實是一個dict對象,後面的Linear其實是dict對應的key
        # 也是我們在定義Pipeline的時候給定的一個名稱值
        lin = model.get_params()['Linear']
        # 打印數據
        output = u'%s:%d階,係數爲:' % (titles[t],d)
        # 判斷lin對象中是否有對應的屬性
        if hasattr(lin, 'alpha_'): # 判斷lin這個模型中是否有alpha_這個屬性
            idx = output.find(u'係數')
            output = output[:idx] + (u'alpha=%.6f, ' % lin.alpha_) + output[idx:]
        if hasattr(lin, 'l1_ratio_'): # 判斷lin這個模型中是否有l1_ratio_這個屬性
            idx = output.find(u'係數')
            output = output[:idx] + (u'l1_ratio=%.6f, ' % lin.l1_ratio_) + output[idx:]
        # line.coef_:獲取線性模型的參數列表,也就是我們ppt中的theta值,ravel()將結果轉換爲1維數據
        print (output, lin.coef_.ravel())

        # 產生模擬數據
        x_hat = np.linspace(x.min(), x.max(), num=100) ## 產生模擬數據
        x_hat.shape = -1,1
        # 數據預測
        y_hat = model.predict(x_hat)
        # 計算準確率
        s = model.score(x, y)

        # 當d等於5的時候,設置爲N-1層,其它設置0層;將d=5的這條線凸顯出來
        z = N + 1 if (d == 5) else 0
        label = u'%d階, 正確率=%.3f' % (d,s)
        plt.plot(x_hat, y_hat, color=colors[i], lw=2, alpha=0.75, label=label, zorder=z)
    
    plt.legend(loc = 'upper left')
    plt.grid(True)
    plt.title(titles[t])
    plt.xlabel('X', fontsize=16)
    plt.ylabel('Y', fontsize=16)
plt.tight_layout(1, rect=(0,0,1,0.95))
plt.suptitle(u'各種不同線性迴歸過擬合顯示', fontsize=22)
plt.show()

代碼看着挺多,其實主要是畫圖。再看一下運行效果及參數:
在這裏插入圖片描述
在這裏插入圖片描述
發現:到三階的時候,都擬合的特別好,但是到九階時,普通的線性迴歸,出了明顯的拐線。普通迴歸,到9階時,有多個比較大的係數,而其他的沒有出現這個情況;Lasso在高階時,出現了好多非常接近0的係數。

L1正則與L2正則對比

  • L2-norm中,由於對於各個維度的參數縮放是在一個圓內縮放的,不可能導致有維度參數變爲0的情況,那麼也就不會產生稀疏解;實際應用中,數據的維度中是存在噪音和冗餘的,稀疏的解可以找到有用的維度並且減少冗餘,提高迴歸預測的準確性和魯棒性(減少了overfitting)(L1-norm可以達到最終解的稀疏性的要求);

  • Ridge模型具有較高的準確性、魯棒性以及穩定性;LASSO模型具有較高的求解速度。(等於0的參數,我們就可以不用考慮,這個過程叫特徵選擇);

  • 如果既要考慮穩定性也考慮求解的速度,就使用Elasitc Net。

在這裏插入圖片描述

以兩個參數爲例,第一二個參數分別爲橫縱座標,到原點小於等t的圖形,分別是兩個陰影部分。(左圖是L1,右圖是L2)J(θ)類似於一個橢圓(ax² + by²=c)。
在上圖中就是一圈的橢圓線,等高線,橢圓上的值是相等的。中間的那個點是最優的解。
加上懲罰項過,最優,就是交點處。圖1,交點,可以在座標軸上,即參數可能出現0的情況。但圖2就不行。

總結:

普通的線性迴歸往往擬合效果不好,比如圖形是曲線的形式,可以做一個多項式擴展,變到高維空間。也可以說多項式擴展能解決線性迴歸模型欠擬合的情況。但多項式的階數如果太高,就會導致過擬合的情況,也就是訓練集上特別好,測試集不太理想。

對於過擬合可以使用L1或L2來解決,也就是在J(θ) 的基礎上把模型的複雜度加上,如嶺迴歸。

在線性迴歸中,可以理解爲就是θ值,我們把θ值加上去,但值有正有負所以出現了常用的兩種形式:L1 L2,並引入超參λ來進行調整

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章