1. 程式人生 > 實用技巧 >【筆記】過擬合和欠擬合以及為什麼要對分為訓練資料集和測試資料集

【筆記】過擬合和欠擬合以及為什麼要對分為訓練資料集和測試資料集

過擬合和欠擬合以及為什麼要對分為訓練資料集和測試資料集

過擬合和欠擬合

有了多項式迴歸以後,就可以比較輕鬆地用線性迴歸來求解非線性的問題了,不過過於使用可能會導致過擬合和欠擬合

先使用實際的例子來說明過擬合和欠擬合

(在notebook中)

載入好包,建立好虛假的資料集x和y,設定隨機種子666,然後繪製出影象

  import numpy as np
  import matplotlib.pyplot as plt
  np.random.seed(666)
  x = np.random.uniform(-3.0,3.0,size=100)
  X = x.reshape(-1,1)
  y = 0.5 * x**2 + x + 2 + np.random.normal(0,1,size=100)
  plt.scatter(x,y)

影象如下

之前使用線性迴歸的時候可以發現是不好的,詳情見上篇內容

要注意,直接使用線性迴歸和使用了升維以後線上性迴歸這兩個方程的計算是不同的,因此衡量好壞可以使用均方誤差的方法

使用均方誤差來衡量

具體操作

載入上mean_squared_error這個方式,使用均方誤差來說,需要知道對於這個模型,其對於X預測的結果是怎樣的,然後再使用mean_squared_error,將y的真值以及預測的y傳入

  from sklearn.metrics import mean_squared_error

  y_predict = lin_reg.predict(X)
  mean_squared_error(y,y_predict)

結果如下(均方誤差為3.07左右)

使用多項式迴歸

使用pipeline的應用,設立一個PolynomialRegression的函式,就是對之前的管道進行了包裝,傳入degree後,返回pipeline,其中就是多項式迴歸的三步

  from sklearn.pipeline import Pipeline
  from sklearn.preprocessing import StandardScaler
  from sklearn.preprocessing import PolynomialFeatures

  def PolynomialRegression(degree):
      return Pipeline([
          ("poly",PolynomialFeatures(degree=degree)),
          ("std_scaler",StandardScaler()),
          ("lin_reg",LinearRegression())
      ])

首先建立一個多項式迴歸的物件,相應的其中degree為2,然後進行fit的操作

  poly2_reg = PolynomialRegression(degree=2)
  poly2_reg.fit(X,y)

結果為

這樣就可以進行預測了,在預測結束以後使用均方誤差的方法得到均方誤差

  y2_predict = poly2_reg.predict(X)
  mean_squared_error(y,y2_predict)

結果為(均方誤差為1.098左右,顯然比線性迴歸要小)

繪製出影象

  plt.scatter(x,y)
  plt.plot(np.sort(x),y2_predict[np.argsort(x)],color='r')

影象為(可以發現與之前的是一致的)

試一下傳入更大的degree來看一下均方誤差

  poly100_reg = PolynomialRegression(degree=100)
  poly100_reg.fit(X,y)

  y100_predict = poly100_reg.predict(X)
  mean_squared_error(y,y100_predict)

情況如下(可知比degree為2要小不少)

繪製一下影象

  plt.scatter(x,y)
  plt.plot(np.sort(x),y100_predict[np.argsort(x)],color='r')

影象為(抖得挺厲害)

這個曲線只是原有的點之間對應的預測值連接出來的結果,不過很多地方可能沒有資料點,所以連線的結果和原來的曲線是不一樣的,我們還原出原本的曲線

設定一個樣本資料及x_plot,使其在-3到3之間均勻的取一百個值並將其變為100行1列的矩陣,然後將這個新的樣本集傳進去得到y_plot,然後對其進行繪製(設定好範圍)

  X_plot = np.linspace(-3,3,100).reshape(100,1)
  y_plot = poly100_reg.predict(X_plot)

  plt.scatter(x,y)
  plt.plot(X_plot[:,0],y_plot,color='r')
  plt.axis([-3,3,-1,10])

影象如下

由於資料樣本是均勻取值的,所以不會出現兩點之間間隔太大,這樣,就得出了真正的曲線

很顯然,degree傳入的越高,結果越好,畢竟只要找到讓所有的點都在的曲線即可,只是這樣會使得degree特別的高

那麼問題來了

這個結果從均方誤差的角度來看,是更好的,但是這真的是一個更好的反映樣本走勢的曲線嗎?雖然使用了非常高維的資料,使得所有的點獲得的誤差更小了,但是這個曲線不是需要的,其為了擬合所有的給定的樣本點,變得太複雜了,這種就稱其為過擬合,機器學習一般解決的是這種

直接使用一根直線的方式很顯然沒有很好的反映出原始資料的樣本特徵,其太過簡單了,這種就可以稱為欠擬合

通過觀察這個曲線可以知道,其稱為過擬合是因為雖然得到了曲線且使用這條曲線來預測的話相應的誤差就變小了,但是如果有一個新的樣本的話,假設一個樣本x為2.4,那麼根據這個曲線預測出的值可能y為0.4左右,可以發現,這個預測的結果和之前的樣本點不在一個趨勢上,很容易將這個預測值作為錯誤值

也就是說,在過擬合的情況下,雖然將這些樣本點擬合的很好,但是來了新的樣本點的時候,對新的樣本點就沒法很好的預測了,可以說,這條曲線的泛化能力是非常弱的(泛化能力,可以看作為由此及彼的能力),得出這條曲線是為了更好的預測,而不是為了追求原先資料的擬合誤差小,我們真正需要的使這個模型的泛化能力好

訓練資料集與測試資料集分離的意義

那麼要使這個模型的泛化能力好應該怎麼做呢?

其實就是使用訓練測試資料集的分離,將原本的資料分為訓練資料集和測試資料集,而獲得模型只使用訓練資料集來獲得,如果使用訓練資料集獲得的模型對測試資料集的模型也有很不錯的預測結果的話,就可以說這個魔性的泛化能力強,反之,稱其為泛化能力弱,這就是測試資料集的更大的意義

具體實現提現其意義

(在notebook中)

首先使用下面的程式碼將之前的情況佈置出來

  import numpy as np
  import matplotlib.pyplot as plt

  np.random.seed(666)
  x = np.random.uniform(-3.0,3.0,size=100)
  X = x.reshape(-1,1)
  y = 0.5 * x**2 + x + 2 + np.random.normal(0,1,size=100)

  from sklearn.pipeline import Pipeline
  from sklearn.preprocessing import StandardScaler
  from sklearn.preprocessing import PolynomialFeatures
  from sklearn.linear_model import LinearRegression

  def PolynomialRegression(degree):
      return Pipeline([
          ("poly",PolynomialFeatures(degree=degree)),
          ("std_scaler",StandardScaler()),
          ("lin_reg",LinearRegression())
      ])

  poly100_reg = PolynomialRegression(degree=100)
  poly100_reg.fit(X,y)

  X_plot = np.linspace(-3,3,100).reshape(100,1)
  y_plot = poly100_reg.predict(X_plot)

  plt.scatter(x,y)
  plt.plot(X_plot[:,0],y_plot,color='r')
  plt.axis([-3,3,-1,10])

可以得到影象:

首先使用sklearn中的train_test_split來將資料集進行分割,種子為666

  from sklearn.model_selection import train_test_split
  X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=666)

之後操作與之前大致相同

首先先看一下對於線性迴歸來說,先例項化一個物件,然後fit訓練資料集,在進行對x_test的預測以後計算測試資料集和預測結果的均方誤差

  from sklearn.metrics import mean_squared_error

  lin_reg = LinearRegression()
  lin_reg.fit(X_train,y_train)
  y_predict = lin_reg.predict(X_test)
  mean_squared_error(y_test,y_predict)

結果為(對於線性迴歸來說,誤差為2.21)

使用多項式迴歸的方式,和之前的操作一樣

  poly2_reg = PolynomialRegression(degree=2)
  poly2_reg.fit(X_train,y_train)
  y2_predict = poly2_reg.predict(X_test)
  mean_squared_error(y_test,y2_predict)

結果為0.8左右(可以得出,在這種情況下,使用多項式迴歸的模型得到的結果的泛化能力比線性迴歸的結果是要好的)

設定成degree為100

  poly100_reg = PolynomialRegression(degree=100)
  poly100_reg.fit(X_train,y_train)
  y100_predict = poly100_reg.predict(X_test)
  mean_squared_error(y_test,y100_predict)

結果如下

這個誤差直接起飛,不過和上面的影象其實是吻合的

雖然在degree為100的時候,其對於訓練資料集的擬合是非常好的,但是面對新的資料的時候,其預測結果是相當差的,所以說這個模型對於預測來講是十分沒用的

上面的實驗實際上就是在測試模型的複雜度,對於多項式迴歸來說,階數越高,模型越複雜,對於機器演算法來說,對於訓練資料集來說,模型複雜度越高,模型的準確率越好,模型越複雜,對資料的擬合就越好,因此準確率也就高了,對於測試資料集來說,隨著模型負責度的提升,模型準確率會進行先升後降,即從欠擬合到合適再到過擬合的過程
(橫軸為模型複雜度,縱軸為模型準確率)

那麼就可以知道過擬合和欠擬合的大致意思了

欠擬合 underfitting

欠擬合是指模型的擬合程度不高,資料距離擬合曲線大部分較遠,或說模型沒有很好地捕捉到資料特徵,不能夠很好地擬合數據,即演算法訓練的模型不能完整的表述資料關係

過擬合 overfitting

過擬合是指為了得到追求極高的擬合而嚴格過頭的情況,即演算法所訓練的模型過多的表達了資料間的噪音關係,將這些噪音也作為特徵進行了訓練

可以舉一個簡單的例子,在機器學習的時候,如果說根據模型,認為只要帶眼睛,那就是狗,這很明顯就是欠擬合模型,太簡單了,但是也不能說,具備了狗的特徵以後,只有有金色毛髮的,才算狗,這就是過擬合了,好傢伙直接給其他顏色的狗開除狗籍,這肯定是不行的

過擬合和欠擬合是機器學習中一直需要注意和解決的重點,對於模型的目標,就變成找到泛化能力最好的地方,找到合適的模型複雜度,即找到模型對測試資料模型的最好的引數