1. 程式人生 > 實用技巧 >【筆記】視覺化模型誤差之學習曲線

【筆記】視覺化模型誤差之學習曲線

學習曲線

過擬合和欠擬合以及為什麼要對分為訓練資料集和測試資料集中繪製了模型複雜度曲線,那麼如果還想用別的方法視覺化過擬合和欠擬合的關係的話,可以使用學習曲線

什麼是學習曲線?

學習曲線描述的就是隨著訓練樣本的逐漸增多,演算法訓練出的模型的表現能力的變化

具體實現體現一下

(在notebook中)

使用熟悉的配方熟悉的味道來繪製圖形

  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)

影象如下

先對資料集進行分割,隨機的種子設為10

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

此時在預設情況下,X_train中含有75個元素(預設取的25%的資料)

那麼學習曲線的繪製其實就是對於這75個訓練資料,每一次都多一些訓練資料來訓練模型,以此來觀察得到的模型在訓練資料集和測試資料集上的表現是怎樣的

使用線性

使用一個迴圈,從只給一個數據訓練開始,到最後給75個數據訓練,每一次都要訓練一個模型,在迴圈中每一次都例項化一個lin_reg,然後對lin_reg進行fit關於訓練的資料x和y的前i個元素,相應的訓練完以後就開始預測,對預測結果來說,分成y_train_predict和y_test_predict,每一次預測的結果在相應的資料集上的對應的均方誤差,執行程式以後,對效能變化的曲線進行繪製,x就是1到75的每個值,相應的y值就使用np.sqrt(對應的誤差)來表示,對訓練和測試都繪製出來

  from sklearn.metrics import mean_squared_error
  from sklearn.linear_model import LinearRegression

  train_score = []
  test_score = []
  for i in range(1,76):
      lin_reg = LinearRegression()
      lin_reg.fit(X_train[:i],y_train[:i])

      y_train_predict = lin_reg.predict(X_train[:i])
      train_score.append(mean_squared_error(y_train[:i],y_train_predict))

      y_test_predict = lin_reg.predict(X_test)
      test_score.append(mean_squared_error(y_test,y_test_predict))

  plt.plot([i for i in range(1,76)],np.sqrt(train_score),label="train")
  plt.plot([i for i in range(1,76)],np.sqrt(test_score),label="test")
  plt.legend()

結果如下,這就是基於線性迴歸來說的樣本資料的學習曲線

大體趨勢上,很明顯的可以看出來在訓練資料集上的誤差是逐漸升高的,開始快,後面累計的小,相應的比較穩定,對於訓練資料集來說,開始樣本比較少的時候,誤差非常的大,當樣本到一定程度以後,誤差就會減小,最後相對穩定,整體上學習曲線就是這樣的趨勢

將上述程式碼提煉成一個函式plot_learning_curve,只需要傳入機器學習演算法和資料集即可得到對應的演算法的學習曲線,不同之處在於對繪製圖像的時候將範圍進行了限定,y是0到4,x為0到X的訓練資料集的數量

  def plot_learning_curve(algo,X_train,X_test,y_train,y_test):
      train_score = []
      test_score = []
      for i in range(1,len(X_train)+1):
          algo.fit(X_train[:i],y_train[:i])

          y_train_predict = algo.predict(X_train[:i])
          train_score.append(mean_squared_error(y_train[:i],y_train_predict))

          y_test_predict = algo.predict(X_test)
          test_score.append(mean_squared_error(y_test,y_test_predict))
    
      plt.plot([i for i in range(1,len(X_train)+1)],
                            np.sqrt(train_score),label="train")
      plt.plot([i for i in range(1,len(X_train)+1)],
                            np.sqrt(test_score),label="test")
      plt.legend()
      plt.axis([0,len(X_train)+1,0,4])

呼叫函式以後,即可得到

  plot_learning_curve(LinearRegression(),X_train,X_test,y_train,y_test)

結果如下(欠擬合)

使用多項式迴歸

多項式迴歸如下:

  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,然後呼叫函式

  poly2_reg = PolynomialRegression(degree=2)
  plot_learning_curve(poly2_reg,X_train,X_test,y_train,y_test)

結果如下(正合適)

可以發現趨勢是大致和線性一致的,不過線性最後的穩定位置比多項式的最後的穩定位置要大

若傳入degree為20

  poly20_reg = PolynomialRegression(degree=20)
  plot_learning_curve(poly20_reg,X_train,X_test,y_train,y_test)

結果如下(過擬合)

大致趨勢還是一樣,但是其最後穩定的時候訓練資料和測試資料的差距還是比較大的,可以發現是在訓練資料集上已經擬合的很好了,但是在測試資料集上,誤差還是很大的,這種一般就是過擬合的情況

對於欠擬合來說,比最佳的情況的穩定的位置要高一些,說明無論對於哪個資料集,其誤差都是比較大的

對於過擬合來說,訓練資料集的誤差是更低的,但是問題是測試資料集的誤差比較大,離訓練資料集比較遠,這就說明這個模型的泛化能力比較弱,對於新的資料,誤差是比較大的