我們在學習機器學習的時候會經常聽到正則化(Regularization),其一般是用於改善或者減少過度擬合問題。
下圖是一個回歸問題的例子:
第一個模型是一個線性模型,欠擬合,不能很好地適應我們的訓練集;第三個模型是一
個四次方的模型,過於強調擬合原始數據,而丟失了算法的本質:預測新數據。我們可以看
出,若給出一個新的值使之預測,它將表現的很差,是過擬合,雖然能非常好地適應我們的
訓練集但在新輸入變量進行預測時可能會效果不好;而中間的模型似乎最合適。
分類問題中也存在這樣的問題:
就以多項式理解,x 的次數越高,擬合的越好,但相應的預測的能力就可能變差。
問題是,如果我們發現了過擬合問題,應該如何處理?
- 丟棄一些不能幫助我們正確預測的特征。可以是手工選擇保留哪些特征,或者使用一些模型選擇的算法來幫忙(例如 PCA)
- 正則化。 保留所有的特征,但是減少參數的大小(magnitude)。
在sklearn里實現正則化
X = [[1,-1,2], [2,0,0], [0,1,-1]] # 使用L2正則化 from sklearn.preprocessing import normalize X1 = normalize(X, norm = 'l2') # 使用L1正則化 from sklearn.preprocessing import Normalizer normalizer = Normalizer(norm= 'l1') X2 = normalizer.fit_transform(X)
normalize()參數:
- X : 需要正則化的特征
- norm : 設置范數,‘l1’, ‘l2’, 或者’max’, 默認是’l2’
- return_norm : boolean, 默認False,如果為True將會返回計算后的norm參數
正則化的結果如圖:
L1正則化/Lasso
L1正則化將系數w的l1范數作為懲罰項加到損失函數上,由於正則項非零,這就迫使那些弱的特征所對應的系數變成0。因此L1正則化往往會使學到的模型很稀疏(系數w經常為0),這個特性使得L1正則化成為一種很好的特征選擇方法。
Scikit-learn為線性回歸提供了Lasso,為分類提供了L1邏輯回歸。
下面的例子在波士頓房價數據上運行了Lasso,其中參數alpha是通過grid search進行優化的。
from sklearn.linear_model import Lasso from sklearn.preprocessing import StandardScaler from sklearn.datasets import load_boston boston = load_boston() scaler = StandardScaler() X = scaler.fit_transform(boston["data"]) Y = boston["target"] names = boston["feature_names"] lasso = Lasso(alpha=.3) lasso.fit(X, Y) print "Lasso model: ", pretty_print_linear(lasso.coef_, names, sort = True)
可以看到,很多特征的系數都是0。如果繼續增加alpha的值,得到的模型就會越來越稀疏,即越來越多的特征系數會變成0。
然而,L1正則化像非正則化線性模型一樣也是不穩定的,如果特征集合中具有相關聯的特征,當數據發生細微變化時也有可能導致很大的模型差異。
L2正則化/Ridge regression
L2正則化將系數向量的L2范數添加到了損失函數中。由於L2懲罰項中系數是二次方的,這使得L2和L1有着諸多差異,最明顯的一點就是,L2正則化會讓系數的取值變得平均。對於關聯特征,這意味着他們能夠獲得更相近的對應系數。還是以Y=X1+X2為例,假設X1和X2具有很強的關聯,如果用L1正則化,不論學到的模型是Y=X1+X2還是Y=2X1,懲罰都是一樣的,都是2alpha。但是對於L2來說,第一個模型的懲罰項是2 alpha,但第二個模型的是4*alpha。可以看出,系數之和為常數時,各系數相等時懲罰是最小的,所以才有了L2會讓各個系數趨於相同的特點。
可以看出,L2正則化對於特征選擇來說一種穩定的模型,不像L1正則化那樣,系數會因為細微的數據變化而波動。所以L2正則化和L1正則化提供的價值是不同的,L2正則化對於特征理解來說更加有用:表示能力強的特征對應的系數是非零。
回過頭來看看3個互相關聯的特征的例子,分別以10個不同的種子隨機初始化運行10次,來觀察L1和L2正則化的穩定性。
from sklearn.linear_model import Ridge from sklearn.metrics import r2_score size = 100 #We run the method 10 times with different random seeds for i in range(10): print "Random seed %s" % i np.random.seed(seed=i) X_seed = np.random.normal(0, 1, size) X1 = X_seed + np.random.normal(0, .1, size) X2 = X_seed + np.random.normal(0, .1, size) X3 = X_seed + np.random.normal(0, .1, size) Y = X1 + X2 + X3 + np.random.normal(0, 1, size) X = np.array([X1, X2, X3]).T lr = LinearRegression() lr.fit(X,Y) print "Linear model:", pretty_print_linear(lr.coef_) ridge = Ridge(alpha=10) ridge.fit(X,Y) print "Ridge model:", pretty_print_linear(ridge.coef_) print
Random seed 0 Linear model: 0.728 * X0 + 2.309 * X1 + -0.082 * X2 Ridge model: 0.938 * X0 + 1.059 * X1 + 0.877 * X2
Random seed 1 Linear model: 1.152 * X0 + 2.366 * X1 + -0.599 * X2 Ridge model: 0.984 * X0 + 1.068 * X1 + 0.759 * X2
Random seed 2 Linear model: 0.697 * X0 + 0.322 * X1 + 2.086 * X2 Ridge model: 0.972 * X0 + 0.943 * X1 + 1.085 * X2
Random seed 3 Linear model: 0.287 * X0 + 1.254 * X1 + 1.491 * X2 Ridge model: 0.919 * X0 + 1.005 * X1 + 1.033 * X2
Random seed 4 Linear model: 0.187 * X0 + 0.772 * X1 + 2.189 * X2 Ridge model: 0.964 * X0 + 0.982 * X1 + 1.098 * X2
Random seed 5 Linear model: -1.291 * X0 + 1.591 * X1 + 2.747 * X2 Ridge model: 0.758 * X0 + 1.011 * X1 + 1.139 * X2
Random seed 6 Linear model: 1.199 * X0 + -0.031 * X1 + 1.915 * X2 Ridge model: 1.016 * X0 + 0.89 * X1 + 1.091 * X2
Random seed 7 Linear model: 1.474 * X0 + 1.762 * X1 + -0.151 * X2 Ridge model: 1.018 * X0 + 1.039 * X1 + 0.901 * X2
Random seed 8 Linear model: 0.084 * X0 + 1.88 * X1 + 1.107 * X2 Ridge model: 0.907 * X0 + 1.071 * X1 + 1.008 * X2
Random seed 9 Linear model: 0.714 * X0 + 0.776 * X1 + 1.364 * X2 Ridge model: 0.896 * X0 + 0.903 * X1 + 0.98 * X2
可以看出,不同的數據上線性回歸得到的模型(系數)相差甚遠,但對於L2正則化模型來說,結果中的系數非常的穩定,差別較小,都比較接近於1,能夠反映出數據的內在結構。
#coding:utf-8 from __future__ import division import sys reload(sys) sys.setdefaultencoding('utf-8') import time start_time = time.time() import pandas as pd # 輸入訓練樣本的特征以及目標值,分別存儲在變量X_train與y_train之中。 X_train = [[6], [8], [10], [14], [18]] y_train = [[7], [9], [13], [17.5], [18]] # 從sklearn.linear_model中導入LinearRegression。 from sklearn.linear_model import LinearRegression # 使用默認配置初始化線性回歸模型。 regressor = LinearRegression() # 直接以披薩的直徑作為特征訓練模型。 regressor.fit(X_train, y_train) # 導入numpy並且重命名為np。 import numpy as np # 在x軸上從0至25均勻采樣100個數據點。 xx = np.linspace(0, 26, 100) xx = xx.reshape(xx.shape[0], 1) # 以上述100個數據點作為基准,預測回歸直線。 yy = regressor.predict(xx) # 對回歸預測到的直線進行作圖。 import matplotlib.pyplot as plt plt.scatter(X_train, y_train) plt1, = plt.plot(xx, yy, label="Degree=1") plt.axis([0, 25, 0, 25]) plt.xlabel('Diameter of Pizza') plt.ylabel('Price of Pizza') plt.legend(handles = [plt1]) plt.show() # 輸出線性回歸模型在訓練樣本上的R-squared值。 print 'The R-squared value of Linear Regressor performing on the training data is', regressor.score(X_train, y_train) # 從sklearn.preproessing中導入多項式特征產生器 from sklearn.preprocessing import PolynomialFeatures # 使用PolynominalFeatures(degree=2)映射出2次多項式特征,存儲在變量X_train_poly2中。 poly2 = PolynomialFeatures(degree=2) X_train_poly2 = poly2.fit_transform(X_train) # 以線性回歸器為基礎,初始化回歸模型。盡管特征的維度有提升,但是模型基礎仍然是線性模型。 regressor_poly2 = LinearRegression() # 對2次多項式回歸模型進行訓練。 regressor_poly2.fit(X_train_poly2, y_train) # 從新映射繪圖用x軸采樣數據。 xx_poly2 = poly2.transform(xx) # 使用2次多項式回歸模型對應x軸采樣數據進行回歸預測。 yy_poly2 = regressor_poly2.predict(xx_poly2) # 分別對訓練數據點、線性回歸直線、2次多項式回歸曲線進行作圖。 plt.scatter(X_train, y_train) plt1, = plt.plot(xx, yy, label='Degree=1') plt2, = plt.plot(xx, yy_poly2, label='Degree=2') plt.axis([0, 25, 0, 25]) plt.xlabel('Diameter of Pizza') plt.ylabel('Price of Pizza') plt.legend(handles = [plt1, plt2]) plt.show() # 輸出2次多項式回歸模型在訓練樣本上的R-squared值。 print 'The R-squared value of Polynominal Regressor (Degree=2) performing on the training data is', regressor_poly2.score(X_train_poly2, y_train) # 從sklearn.preprocessing導入多項式特征生成器。 from sklearn.preprocessing import PolynomialFeatures # 初始化4次多項式特征生成器。 poly4 = PolynomialFeatures(degree=4) X_train_poly4 = poly4.fit_transform(X_train) # 使用默認配置初始化4次多項式回歸器。 regressor_poly4 = LinearRegression() # 對4次多項式回歸模型進行訓練。 regressor_poly4.fit(X_train_poly4, y_train) # 從新映射繪圖用x軸采樣數據。 xx_poly4 = poly4.transform(xx) # 使用4次多項式回歸模型對應x軸采樣數據進行回歸預測。 yy_poly4 = regressor_poly4.predict(xx_poly4) # 分別對訓練數據點、線性回歸直線、2次多項式以及4次多項式回歸曲線進行作圖。 plt.scatter(X_train, y_train) plt1, = plt.plot(xx, yy, label='Degree=1') plt2, = plt.plot(xx, yy_poly2, label='Degree=2') plt4, = plt.plot(xx, yy_poly4, label='Degree=4') plt.axis([0, 25, 0, 25]) plt.xlabel('Diameter of Pizza') plt.ylabel('Price of Pizza') plt.legend(handles = [plt1, plt2, plt4]) plt.show() print 'The R-squared value of Polynominal Regressor (Degree=4) performing on the training data is',regressor_poly4.score(X_train_poly4, y_train) # 准備測試數據。 X_test = [[6], [8], [11], [16]] y_test = [[8], [12], [15], [18]] # 使用測試數據對線性回歸模型的性能進行評估。 regressor.score(X_test, y_test) # 使用測試數據對2次多項式回歸模型的性能進行評估。 X_test_poly2 = poly2.transform(X_test) regressor_poly2.score(X_test_poly2, y_test) # 使用測試數據對4次多項式回歸模型的性能進行評估。 X_test_poly4 = poly4.transform(X_test) regressor_poly4.score(X_test_poly4, y_test) # 從sklearn.linear_model中導入Lasso。 from sklearn.linear_model import Lasso # 從使用默認配置初始化Lasso。 lasso_poly4 = Lasso() # 從使用Lasso對4次多項式特征進行擬合。 lasso_poly4.fit(X_train_poly4, y_train) # 對Lasso模型在測試樣本上的回歸性能進行評估。 print lasso_poly4.score(X_test_poly4, y_test) # 輸出Lasso模型的參數列表。 print lasso_poly4.coef_ # 回顧普通4次多項式回歸模型過擬合之后的性能。 print regressor_poly4.score(X_test_poly4, y_test) # 回顧普通4次多項式回歸模型的參數列表。 print regressor_poly4.coef_ # 輸出上述這些參數的平方和,驗證參數之間的巨大差異。 print np.sum(regressor_poly4.coef_ ** 2) # 從sklearn.linear_model導入Ridge。 from sklearn.linear_model import Ridge # 使用默認配置初始化Riedge。 ridge_poly4 = Ridge() # 使用Ridge模型對4次多項式特征進行擬合。 ridge_poly4.fit(X_train_poly4, y_train) # 輸出Ridge模型在測試樣本上的回歸性能。 print ridge_poly4.score(X_test_poly4, y_test) # 輸出Ridge模型的參數列表,觀察參數差異。 print ridge_poly4.coef_ # 計算Ridge模型擬合后參數的平方和。 print np.sum(ridge_poly4.coef_ ** 2)
"D:\Program Files\Python27\python.exe" D:/PycharmProjects/sklearn/模型正則化.py The R-squared value of Linear Regressor performing on the training data is 0.910001596424 The R-squared value of Polynominal Regressor (Degree=2) performing on the training data is 0.98164216396 The R-squared value of Polynominal Regressor (Degree=4) performing on the training data is 1.0 0.83889268736 [ 0.00000000e+00 0.00000000e+00 1.17900534e-01 5.42646770e-05 -2.23027128e-04] 0.809588079575 [[ 0.00000000e+00 -2.51739583e+01 3.68906250e+00 -2.12760417e-01 4.29687500e-03]] 647.382645692 0.837420175937 [[ 0. -0.00492536 0.12439632 -0.00046471 -0.00021205]] 0.0154989652035