Python機器學習及實踐——進階篇3(模型正則化之欠擬合與過擬合)

一個需要記住的重要事項:任何機器學習模型在訓練集上的性能表現,都不能作爲其對未知測試數據預測能力的評估。

這裏講詳細及時什麼是模型的泛化力以及如何保證模型的泛化力,一次會闡述模型複雜度與泛化力的關係以及使用L1範數正則化與L2範數正則化加強模型的泛化力,避免模型參數過擬合。

 

所謂擬合,是指機器學習模型在訓練的過程中,通過更新參數,使得模型不斷契合訓練集的過程。本篇將使用一個“比薩餅價格預測“的例子來說明。

如下圖一所示,美國一家比薩餅店出售不同尺寸的比薩,其中每種直徑都對應一個報價,我們所要做的就是設計一個學習模型,可以有效地根據圖2中比薩的直徑特徵來預測售價。

目前已知有5組訓練數據、4組測試數據,並且其中測試數據的比薩報價未知。根據我們的經驗,如果只考慮比薩的尺寸與售價的關係,那麼使用線性迴歸模型比較直觀,如代碼1所示:

# 輸入訓練樣本的特徵以及目標值,分別存儲在變量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)

The R-squared value of Linear Regressor performing on the training data is 0.910001596424

根據代碼輸出的圖,以及當前模型在訓練集上的表現(R-squared值爲0.9100),我們進一步猜測,也許比薩餅的面積與售價的線性關係更加明顯。因此我們試圖將遠特徵升高一個維度,使用(2次)多項式迴歸對訓練樣本進行擬合,如代碼2所示:

# 從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)

The R-squared value of Polynominal Regressor (Degree=2) performing on the training data is 0.98164216396

果然升高了特徵維度之後,模型在訓練樣本上的性能表現更加突出,R-squared值從0.910上升至0.982。並且根據輸出的圖所示,2次多項式迴歸曲線比起線性迴歸直線,對訓練數據的擬合程度增加了許多,由此我們更加大膽地升高特徵維度,如代碼3所示,增加到4次多項式。

# 從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)

The R-squared value of Polynominal Regressor (Degree=4) performing on the training data is 1.0

如圖所示,4次多項式曲線幾乎完全擬合了所有的訓練數據點,對應的R-squared值也爲1.0,但是如果這時覺得已經找到了完美的模型,就高興過早了。

下圖揭示了比薩的真實價格。

Testing Instance Diameter(in inches) Price(in U.S. dollars)
1 6 8
2 8 12
3 11 15
4 16 18
# 準備測試數據。
X_test = [[6], [8], [11], [16]]
y_test = [[8], [12], [15], [18]]

# 使用測試數據對線性迴歸模型的性能進行評估。
regressor.score(X_test, y_test)

0.80972683246686095

# 使用測試數據對2次多項式迴歸模型的性能進行評估。
X_test_poly2 = poly2.transform(X_test)
regressor_poly2.score(X_test_poly2, y_test)

0.86754436563450543

# 使用測試數據對4次多項式迴歸模型的性能進行評估。
X_test_poly4 = poly4.transform(X_test)
regressor_poly4.score(X_test_poly4, y_test)

0.8095880795781909

 

如果我們使用代碼4評估上述3種模型在測試集上的表現,並將輸出對比之前在訓練集上的擬合情況,做成圖4:結果令人咋舌:當模型複雜度很低時,模型不僅沒有對訓練集有良好的擬合狀態,而且在測試集上也表現平平,這種情況叫欠擬合;但是當我們一味追求很高的模型複雜度,儘管模型幾乎完全擬合了所有的訓練數據,但模型也變得非常波動,幾乎喪失了對未知數據的預測能力,這種情況叫過擬合。這兩種情況都是缺乏模型泛化力的表現。

特徵多項式次數 訓練集R-squared值 測試集R-squared值
Degree=1 0.9100 0.8097
Degree=2 0.9816 0.8675
Degree=4 1.0000 0.8096

由此可見,雖然我們不斷追求更好的模型泛化力,但是因爲未知數據無法預測,所以又期望模型可以充分利用訓練數據,避免欠擬合。這就要求在增加模型複雜度、提高在可觀測數據上的性能表現的同時,又兼顧模型的泛化力,防止發生過擬合情況。爲了平衡這兩難的選擇,我們通常採用兩種模型正則化的方法,分別是L1正則和L2正則。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章