【機器學習】多項式迴歸

:在上一篇的通常線性迴歸中,使用的假設函數是一元一次方程,也就是二維平面上的一條直線。可是不少時候可能會遇到直線方程沒法很好的擬合數據的狀況,這個時候能夠嘗試使用多項式迴歸。多項式迴歸中,加入了特徵的更高次方(例如平方項或立方項),也至關於增長了模型的自由度,用來捕獲數據中非線性的變化。添加高階項的時候,也增長了模型的複雜度。隨着模型複雜度的升高,模型的容量以及擬合數據的能力增長,能夠進一步下降訓練偏差,但致使過擬合的風險也隨之增長。html

 

 

圖A,模型複雜度與訓練偏差及測試偏差之間的關係git

 

0. 多項式迴歸的通常形式


在多項式迴歸中,最重要的參數是最高次方的次數。設最高次方的次數爲$n$,且只有一個特徵時,其多項式迴歸的方程爲:github

$$ \hat{h} = \theta_0 + \theta_1 x^1 + \ ... \  + \theta_{n-1} x^{n-1} +  \theta_n x^n $$app

若是令$x_0 = 1$,在多樣本的狀況下,能夠寫成向量化的形式:機器學習

$$\hat{h} = X \cdot \theta$$函數

其中$X$是大小爲$m \cdot (n+1)$的矩陣,$\theta$是大小爲$(n+1) \cdot 1$的矩陣。在這裏雖然只有一個特徵$x$以及$x$的不一樣次方,可是也能夠將$x$的高次方當作一個新特徵。與多元迴歸分析惟一不一樣的是,這些特徵之間是高度相關的,而不是一般要求的那樣是相互對立的。學習

 在這裏有個問題在剛開始學習線性迴歸的時候困擾了本身好久:若是假設中出現了高階項,那麼這個模型仍是線性模型嗎?此時看待問題的角度不一樣,獲得的結果也不一樣。若是把上面的假設當作是特徵$x$的方程,那麼該方程就是非線性方程;若是當作是參數$\theta$的方程,那麼$x$的高階項均可以看作是對應$\theta$的參數,那麼該方程就是線性方程。很明顯,在線性迴歸中採用了後一種解釋方式。所以多項式迴歸仍然是參數的線性模型。測試

 

1. 多項式迴歸的實現


下面主要使用了numpy、scipy、matplotlib和scikit-learn,全部使用到的函數的導入以下:ui

1 import numpy as np
2 from scipy import stats
3 import matplotlib.pyplot as plt
4 from sklearn.preprocessing import PolynomialFeatures
5 from sklearn.linear_model import LinearRegression
6 from sklearn.metrics import mean_squared_error

下是使用的數據是使用$y = x^2 + 2$並加入一些隨機偏差生成的,只取了10個數據點:spa

 1 data = np.array([[ -2.95507616,  10.94533252],
 2        [ -0.44226119,   2.96705822],
 3        [ -2.13294087,   6.57336839],
 4        [  1.84990823,   5.44244467],
 5        [  0.35139795,   2.83533936],
 6        [ -1.77443098,   5.6800407 ],
 7        [ -1.8657203 ,   6.34470814],
 8        [  1.61526823,   4.77833358],
 9        [ -2.38043687,   8.51887713],
10        [ -1.40513866,   4.18262786]])
11 m = data.shape[0]  # 樣本大小
12 X = data[:, 0].reshape(-1, 1)  # 將array轉換成矩陣
13 y = data[:, 1].reshape(-1, 1)
14 plt.plot(X, y, "b.")
15 plt.xlabel('X')
16 plt.ylabel('y')
17 plt.show()

這些數據點plot出來,以下圖:

圖1-1,原始數據

1.1 直線方程擬合

下面先用直線方程擬合上面的數據點:

 1 lin_reg = LinearRegression()
 2 lin_reg.fit(X, y)
 3 print(lin_reg.intercept_, lin_reg.coef_)  # [ 4.97857827] [[-0.92810463]]
 4 
 5 X_plot = np.linspace(-3, 3, 1000).reshape(-1, 1)
 6 y_plot = np.dot(X_plot, lin_reg.coef_.T) + lin_reg.intercept_
 7 plt.plot(X_plot, y_plot, 'r-')
 8 plt.plot(X, y, 'b.')
 9 plt.xlabel('X')
10 plt.ylabel('y')
11 plt.savefig('regu-2.png', dpi=200)

圖1-2,直線擬合的效果

可使用函數"mean_squared_error"來計算偏差(使用前面介紹過的Mean squared error, MSE):

h = np.dot(X.reshape(-1, 1), lin_reg.coef_.T) + lin_reg.intercept_
print(mean_squared_error(h, y)) # 3.34

 

1.2 使用多項式方程

爲了擬合2次方程,須要有特徵$x^2$的數據,這裏可使用函數"PolynomialFeatures"來得到:

1 poly_features = PolynomialFeatures(degree=2, include_bias=False)
2 X_poly = poly_features.fit_transform(X)
3 print(X_poly)

結果以下:

[[-2.95507616  8.73247511]
 [-0.44226119  0.19559496]
 [-2.13294087  4.54943675]
 [ 1.84990823  3.42216046]
 [ 0.35139795  0.12348052]
 [-1.77443098  3.1486053 ]
 [-1.8657203   3.48091224]
 [ 1.61526823  2.60909145]
 [-2.38043687  5.66647969]
 [-1.40513866  1.97441465]]

利用上面的數據作線性迴歸分析:

 1 lin_reg = LinearRegression()
 2 lin_reg.fit(X_poly, y)
 3 print(lin_reg.intercept_, lin_reg.coef_)  # [ 2.60996757] [[-0.12759678  0.9144504 ]]
 4 
 5 X_plot = np.linspace(-3, 3, 1000).reshape(-1, 1)
 6 X_plot_poly = poly_features.fit_transform(X_plot)
 7 y_plot = np.dot(X_plot_poly, lin_reg.coef_.T) + lin_reg.intercept_
 8 plt.plot(X_plot, y_plot, 'r-')
 9 plt.plot(X, y, 'b.')
10 plt.show()

第3行獲得了訓練後的參數,即多項式方程爲$h = -0.13x + 0.91x^2 + 2.61$ (結果中係數的順序與$X$中特徵的順序一致),以下圖所示:

圖1-3:2次多項式方程與原始數據的比較

利用多項式迴歸,代價函數MSE的值降低到了0.07。經過觀察代碼,能夠發現訓練多項式方程與直線方程惟一的差異是輸入的訓練集$X$的差異。在訓練直線方程時直接輸入了$X$的值,在訓練多項式方程的時候,還添加了咱們計算出來的$x^2$這個「新特徵」的值(因爲$x^2$徹底是由$x$的值肯定的,所以嚴格意義上來說此時該模型只有一個特徵$x$)。

此時有個很是有趣的問題:假如一開始獲得的數據就是上面代碼中"X_poly"的樣子,且不知道$x_1$與$x_2$之間的關係。此時至關於咱們有10個樣本,每一個樣本具備$x_1, x_2$兩個不一樣的特徵。這時假設函數爲:$$\hat{h} = \theta_0 + \theta_1 x_1 + \theta_2 x_2$$

直接按照二元線性迴歸方程來訓練,也能夠獲得上面一樣的結果($\theta$的值)。若是在相同狀況下,收集到了新的數據,能夠直接帶入上面的方程進行預測。惟一不一樣的是,咱們不知道$x_2 = x_1^2$這個隱含在數據內部的關係,全部也就沒法畫出圖1-3中的這條曲線。一旦瞭解到了這兩個特徵之間的關係,數據的維度就從3維降低到了2維(包含截距項$\theta_0$)。

 

 2. 持續下降訓練偏差與過擬合


在上面實現多項式迴歸的過程當中,經過引入高階項$x^2$,訓練偏差從3.34降低到了0.07,減少了將近50倍。那麼訓練偏差是否還有進一步降低的空間呢?答案是確定的,經過繼續增長更高階的項,訓練偏差能夠進一步下降。經過嘗試,當最高階項爲$x^{11}$時,訓練偏差爲3.11e-23,幾乎等於0了。

下面是測試不一樣degree的過程:

 1 # test different degree and return loss
 2 def try_degree(degree, X, y):
 3     poly_features_d = PolynomialFeatures(degree=degree, include_bias=False)
 4     X_poly_d = poly_features_d.fit_transform(X)
 5     lin_reg_d = LinearRegression()
 6     lin_reg_d.fit(X_poly_d, y)
 7     return {'X_poly': X_poly_d, 'intercept': lin_reg_d.intercept_, 'coef': lin_reg_d.coef_}
 8 
 9 degree2loss_paras = []
10 for i in range(2, 20):
11     paras = try_degree(i, X, y)
12     h = np.dot(paras['X_poly'], paras['coef'].T) + paras['intercept']
13     _loss = mean_squared_error(h, y)
14     degree2loss_paras.append({'d': i, 'loss': _loss, 'coef': paras['coef'], 'intercept': paras['intercept']})
15 
16 min_index = np.argmin(np.array([i['loss'] for i in degree2loss_paras]))
17 min_loss_para = degree2loss_paras[min_index]
18 print(min_loss_para)  # 
19 X_plot = np.linspace(-3, 1.9, 1000).reshape(-1, 1)
20 poly_features_d = PolynomialFeatures(degree=min_loss_para['d'], include_bias=False)
21 X_plot_poly = poly_features_d.fit_transform(X_plot)
22 y_plot = np.dot(X_plot_poly, min_loss_para['coef'].T) + min_loss_para['intercept']
23 fig, ax = plt.subplots(1, 1)
24 ax.plot(X_plot, y_plot, 'r-', label='degree=11')
25 ax.plot(X, y, 'b.', label='X')
26 plt.xlabel('X')
27 plt.ylabel('y')
28 ax.legend(loc='best', frameon=False)
29 plt.savefig('regu-4-overfitting.png', dpi=200)

輸出爲:

{'coef': array([[  0.7900162 ,  26.72083627,   4.33062978,  -7.65908434,
          24.62696711,  12.33754429, -15.72302536,  -9.54076366,
           1.42221981,   1.74521649,   0.27877112]]),
 'd': 11,
 'intercept': array([-0.95562816]),
 'loss': 3.1080267005676934e-23}

畫出的函數圖像以下:

圖2-1:degree=11時的函數圖像

由圖2-1能夠看到,此時函數圖像穿過了每個樣本點,全部的訓練樣本都落在了擬合的曲線上,訓練偏差接近與0。 能夠說是近乎完美的模型了。可是,這樣的曲線與咱們最開始數據的來源(一個二次方程加上一些隨機偏差)差別很是大。若是從相同來源再取一些樣本點,使用該模型預測會出現很是大的偏差。相似這種訓練偏差很是小,可是新數據點的測試偏差很是大的狀況,就叫作模型的過擬合。過擬合出現時,表示模型過於複雜,過多考慮了當前樣本的特殊狀況以及噪音(模型學習到了當前訓練樣本非全局的特性),使得模型的泛化能力降低。

出現過擬合通常有如下幾種解決方式:

  • 下降模型複雜度,例如減少上面例子中的degree;
  • 降維,減少特徵的數量;
  • 增長訓練樣本;
  • 添加正則化項.

防止模型過擬合是機器學習領域裏最重要的問題之一。鑑於該問題的廣泛性和重要性,在知足要求的狀況下,能選擇簡單模型時應該儘可能選擇簡單的模型。

 

Reference


http://scikit-learn.org/stable/modules/linear_model.html

Géron A. Hands-on machine learning with Scikit-Learn and TensorFlow: concepts, tools, and techniques to build intelligent systems[M]. " O'Reilly Media, Inc.", 2017. github

https://www.arxiv-vanity.com/papers/1803.09820/

相關文章
相關標籤/搜索