上篇博客是針對普通線性迴歸往往存在欠擬合的情況,採用多項式擴展的方式,從而映射到多維空間來擬合。多項式擴展的時候,如果指定的階數比較大,那麼有可能導致過擬合。也就是模型太契合訓練數據了。數據上表現就是參數過多、過大。過擬合在實際機器學習應用中是普遍存在的。
正則化的引入
對於前面提到的目標函數:
現在,我們要防止過擬合,可以通過調節參數值,讓參數越小越好。也可以說參數越小,模型越簡單。比如說除了兩個參數外,其他的參數都小到爲0了,此時完全可以只關心這兩個參數的情況了,從而從高維空間降到了低維空間。
爲了防止數據過擬合,也就是的θ值在樣本空間中不能過大/過小,可以在目標函數之上增加一個平方和損失:
加上的這項後半部分,也可寫成 或 ||θ||(也叫二範式)這就是正則項: 這裏這個正則項叫做 L2-norm,有的地方也叫懲罰項,就是原始J(θ)的一個懲罰值。
兩部分加在一起最小化,是我們要達到的目的。第一項是凸函數,第二項也是凸函數,而且是兩個開口向上的凸的函數,疊加後還是凸函數。也就存在最小值,且在極值的位置,即求導爲0的地方。最終求得的結果:
是不是很熟悉?沒錯這就是《機器學習(迴歸一)——線性迴歸-最小二乘》中爲了防止不可逆或者過擬合的問題存在,可以增加額外數據影響,使得最終的矩陣是可逆的,從而得到的式子。
常見線性迴歸正則化模型
故:爲了解決過擬合問題,我們可以選擇在損失函數中加入懲罰項(對於系統過磊的懲罰),主要分爲L1-norm和L2-norm,以及他倆結合的線性迴歸模型:
- 使用L2正則的線性迴歸模型就稱爲Ridge迴歸(嶺迴歸)
- 使用L1正則的線性迴歸模型就稱爲LASSO迴歸(Least Absolute Shrinkage and Selection Operator)
- 同時使用L1正則和L2正則的線性迴歸模型就稱爲 Elasitc Net 算法(彈性網絡算法)
各種線性迴歸過擬合顯示
這裏自己手動生成數據,來看看各種不同的線性迴歸模型在過擬合時的顯示效果。
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,並引入超參λ來進行調整