训练softmax分类器实例_第四章.模型训练
迄今為止,我們只是把機器學習模型及其大多數訓練算法視為黑盒。但是如果你做了前面幾章的一些練習,你可能會驚訝于你可以在不知道任何關于背后原理的情況下完成很多工作:優化一個回歸系統,改進一個數字圖像分類器,甚至從頭開始建立一個垃圾郵件分類器,所有這些都不知道它們的實際工作原理。事實上,在很多情況下,你其實并不需要知道這些實現細節。
然而,深刻理解事物的工作原理,可以幫助你快速找到合適的模型、正確的訓練算法,以及一組適合目標任務的超參數。除此之外,也會幫助你更有效地調試問題和進行錯誤分析。最后,本章所討論的大部分主題將對理解、構建和訓練神經網絡(將在本書第二部分中討論)至關重要。
在本章中,我們將從線性回歸模型開始,因為它是最簡單的模型之一。我們將討論兩種非常不同的訓練方法。
- 使用 "閉式 "方程,直接計算出最適合模型與訓練集的模型參數(即在訓練集上最小化成本函數的模型參數)。
- 使用一種梯度下降(GD)的迭代優化方法,通過逐步調整模型參數,使訓練集上的成本函數最小化,并最終收斂到與第一種方法相同的參數。我們來看一下梯度下降的幾個變體,在第二部分學習神經網絡時將會反復使用這些變體:Batch GD、Mini-batch GD和Stochastic GD。
然后,我們將討論一下多項式回歸,這是一個比較復雜的模型,可以擬合非線性的數據集。由于這個模型的參數比線性回歸更多,所以容易對訓練數據過擬合,我們將討論如何使用學習曲線來檢測是否存在這種情況,以及幾種可以降低過擬合風險的正則化方法。
最后,我們再著重討論兩個常用于分類任務的模型:Logistic回歸和Softmax回歸。
線性回歸
在第一章中,我們研究了一個簡單的生活滿意度回歸模型:
life_satisfaction
GDP_per_capita.該模型為單個輸入特征GDP_per_capita的線性函數,其中
和 為模型的參數。更一般地,線性模型是通過簡單地計算輸入特征的加權平均,并加上一個偏置項(也稱為截距項)來進行預測,如公式4-1所示:
其中:- 是模型的預測值
- 是模型輸入的特征數量
- 是第 個特征的取值
- 是第 個模型參數
上述公式用矢量化的形式可以寫得更簡潔,如公式4-2所示:
其中:- 是模型的參數向量,包含了偏置項 和權重項 到
- 是實例的特征向量,包含了特征 到 ,其中 始終為常數
但我們應該如何訓練呢?回顧一下,訓練模型意味著找出在訓練集上最適合模型的參數。為此,我們首先需要一個目標函數,正如我們在第二章中所看到,回歸模型最常用的度量標準是均方根誤差(RMSE)。為了訓練線性回歸模型,我們需要尋找最小化RMSE的
值,而在實踐中,更簡單的方法是最小化均方誤差(MSE),因為它們具有一致的結果。在線性假設
下,模型在訓練集 上 的計算如公式4-3所示:正規方程
為了找到最小化成本函數的
值,可以通過閉式解,這就是所謂的正規方程(方程4-4)。其中:- 為最小化代價函數的模型參數的取值
- 為標簽取值的向量,包含 到
現在我們來生成一些類似線性關系的數據
import numpy as npX = 2 * np.random.rand(100, 1) y = 4 + 3 * X + np.random.randn(100, 1)我們可以使用正規方程進行求解,只需使用NumPy的線性代數模塊(np.linalg)中的inv()函數來計算矩陣的逆,并使用dot()方法進行矩陣乘法。
X_b = np.c_[np.ones((100, 1)), X] # add x0 = 1 to each instance theta_best = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y)我們用來生成數據的函數是
。讓我們看看求解得出的方程是什么。theta_best # array([[4.21509616], # [2.77011339]])我們本來希望
和 ,但是已經足夠接近了,因為噪聲的影響,無法恢復原始函數的精確參數。我們來做些預測:
X_new = np.array([[0], [2]]) X_new_b = np.c_[np.ones((2, 1)), X_new] # add x0 = 1 to each instance y_predict = X_new_b.dot(theta_best) y_predict # array([[4.21509616], # [9.75532293]])plt.plot(X_new, y_predict, "r-") plt.plot(X, y, "b.") plt.axis([0, 2, 0, 15]) plt.show()使用Scikit-Learn進行線性回歸非常簡單。
from sklearn.linear_model import LinearRegressionlin_reg = LinearRegression() lin_reg.fit(X, y) lin_reg.intercept_, lin_reg.coef_ # (array([4.21509616]), array([[2.77011339]])) lin_reg.predict(X_new) # array([[4.21509616], # [9.75532293]])LinearRegression類是基于scipy.linalg.lstsq()函數( "最小二乘法"),你可以直接調用它。
theta_best_svd, residuals, rank, s = np.linalg.lstsq(X_b, y, rcond=1e-6) theta_best_svd # array([[4.21509616], # [2.77011339]])這個函數計算
,其中 是 的偽逆,你可以使用np.linalg.pinv()直接計算。np.linalg.pinv(X_b).dot(y) # array([[4.21509616], # [2.77011339]])偽逆本身的計算是通過奇異值分解(SVD)的方法進行的,該方法將訓練集矩陣
分解成三個矩陣相乘 (見 numpy.linalg.svd())。偽逆的計算公式為 ,為了計算矩陣 ,首先將矩陣 中所有小于預先設定閾值的值都設置為 ,并將非零值替換為其倒數,最后將矩陣進行轉置。這種方法比計算正規方程更有效率,事實上,如果矩陣 不可逆(即奇異),如 或一些特征是多余的,正規方程可能就不起作用了,但偽逆總是存在的。現在我們來看看一種非常不同的訓練線性回歸模型的方法,這種方法更適合于有大量特征或訓練實例太多,無法放入內存的情況。
梯度下降
梯度下降是一種通用的優化算法,能夠為各種問題找到最優解,梯度下降的一般思想是反復調整參數來最小化成本函數。
假設你由于濃霧而在山中迷失,你只能感覺到腳下地面的坡度,要想快速到達谷底,一個好的策略就是沿著坡度最陡的方向下山。這正是 "梯度下降 "的思想:通過計算誤差函數關于參數向量
的局部梯度,并沿著梯度下降的方向前進,一旦梯度為零,你就達到了最低點!具體來說,你先隨機初始化參數
的取值,然后逐步調整,每次一小步,每一步都試圖減少成本函數(如MSE),直到算法收斂到最小值(見圖4-3)。梯度下降法的一個重要參數是步長的大小,由超參數學習率所決定。如果學習率太小,那么算法就必須經過多次迭代才能收斂,也就意味著需要很長的時間進行訓練(見圖4-4)。
另一方面,如果學習率太高,你可能會跳過山谷,最后到了另一邊,甚至可能比之前更高了,這可能會導致算法發散(見圖4-5)。
最后需要注意的是,并不是所有的代價函數都是規則的碗狀結構。代價函數的曲面可能會有洞、山脊、鞍部和各種不規則的地形,這使得收斂到最小值變得困難。圖4-6顯示了梯度下降的兩個主要挑戰。如果隨機初始化從左邊開始,那么它將收斂到局部最小值,這顯然不如全局最小值。而如果從右邊開始,那么它將需要很長的時間來跨越鞍部,并且如果你過早地停止訓練,將永遠無法到達全局最小值。
幸運的是,線性回歸模型的MSE代價函數恰好是一個凸函數,這意味著如果你在曲線上選取任意兩點,則連接它們的線段永遠不會與曲線交叉,也就是說沒有局部最小值,只有一個全局最小值。此外,它也是一個連續可導的函數,這兩個特點可以保證梯度下降法會無限接近全局最小值。
事實上,即使代價函數具有碗的形狀,如果特征具有非常不同的尺度大小,它可以是一個拉長的碗。圖4-7顯示了在特征1和2具有相同尺度的情況下,以及在特征1的取值比特征2小得多的情況下,梯度下降法在訓練集上的迭代過程。
正如你所看到的,在左圖中,梯度下降法直接向最小值運動,從而快速到達最小值,而在右圖中,先是向著與全局最小值方向幾乎正交的方向運動,最后沿著一個幾乎平坦的山谷移動,雖然最終依然會到達最小值,但這需要很長的時間。
當使用梯度下降法時,你應該確保所有的特征都具有相似的尺度(例如,使用Scikit-Learn的StandardScaler類),否則會花費很長的時間來收斂。這張圖也說明了訓練一個模型意味著尋找一個模型參數的組合來最小化訓練集上的代價函數。而這個過程是在模型的參數空間中進行的:一個模型的參數越多,意味著這個空間的維度就越多,搜索的難度也就越大(大海撈針)。幸運的是,由于在線性回歸的情況下,成本函數是凸的,搜索就變得容易多了。
批量梯度下降
為了實現梯度下降,你需要計算代價函數關于每個模型參數
的梯度。換句話說,你需要計算如果改變 一點點,代價函數會有多大變化。這就是所謂的偏導數。代價函數關于參數 的偏導數 的計算如公式4-5所示:與其單獨計算這些偏導數,不如使用公式4-6一次性計算完梯度向量,記為 n
abla_{theta} MSE(theta) ,其包含了代價函數的所有偏導數。請注意,上述公式在梯度下降的每一步中都涉及整個訓練集 的計算!這就是為什么該算法被稱為 "批量梯度下降"(Batch Gradient Descent):它在每一步都使用所有訓練數據(實際上,完全梯度下降可能是一個更好的名字)。因此,在非常大的訓練集上,它的速度會變得非常慢,但是,梯度下降法隨著特征數量的增加具有良好的可擴展性:當面對幾十萬個特征的時侯,使用梯度下降法訓練線性回歸模型要比使用正規方程或SVD分解法快得多。一旦有了梯度向量,其指向為上坡,我們所需要做的就是反方向下坡。這意味著從
中減去 。 這就是學習率 的作用所在:將梯度向量乘以 來確定下坡的步長(公式4-7)。我們來看看該算法的快速實現:
eta = 0.1 # learning rate n_iterations = 1000 m = 100theta = np.random.randn(2,1) # random initializationfor iteration in range(n_iterations):gradients = 2/m * X_b.T.dot(X_b.dot(theta) - y)theta = theta - eta * gradientstheta # array([[4.21509616], # [2.77011339]])這正是正規方程所求的! 梯度下降法的效果非常好,但如果你使用了不同的學習率
呢?圖4-8展示了使用三種不同學習率的梯度下降的前10步(虛線代表起始)。Figure 4-8. Gradient Descent with various learning rates為了找到一個好的學習率,你可以使用網格搜索(見第2章)。但是,您可能希望限制迭代次數,以便網格搜索可以淘汰那些需要太長時間才能收斂的模型。
你可能想知道如何設置迭代次數。如果它太低,當算法停止時,你仍然會離最優解很遠;但如果它太高,你會浪費時間,而模型參數卻不再改變。一個簡單的解決方案是設置一個非常大的迭代次數,但當梯度向量變得很小,也就是說,當它的范數變得小于一個很小的數值
(稱為容忍度)時,中斷算法,因為此時梯度下降已經(幾乎)達到最小值了。隨機梯度下降
批量梯度下降的主要問題是其每一步都使用整個訓練集來計算梯度,所有當訓練集很大時,迭代的速度就會非常慢。相反地,隨機梯度下降法在每一步都會在訓練集中隨機挑選一個實例,并僅基于該實例來計算梯度,顯然,每次只處理一個實例會使算法的速度快得多。
另一方面,由于其隨機性的特點,該算法比Batch Gradient Descent的規律性要差得多:代價函數不是緩緩下降到達最小值,而是上下跳動,總體上下降。隨著時間的推移,它最終會非常接近最小值,但是就算到了最小值,也會繼續跳動,永遠不會穩定下來(見圖4-9)。所以當算法停止時,最終得到的參數值是好的,但并不是最優的。
當代價函數非常不規則時(如圖4-6),這實際上可以幫助算法跳出局部最小值,所以隨機梯度下降比批量梯度下降有更好的機會找到全局最小值。
因此,隨機性對于跳出局部最優值是有利的,但也意味著算法永遠無法在最小值處安頓下來。解決該難題的一個辦法是學習率衰減。步數一開始很大(有助于加速并跳出局部最小值),然后逐漸減小,讓算法在全局最小值處穩定下來。這個過程類似于模擬退火,這種算法的靈感來自于冶金學中的退火過程,即熔融的金屬被緩慢冷卻。決定每次迭代時學習率的函數稱為學習調度。如果學習率降低得太快,可能會卡在局部最小值,甚至中途停止,但如果學習率降低得太慢,可能會在最小值附近跳動很長時間,如果此時過早地停止訓練,最終得到的將是一個次優的解。
下面這段代碼使用了一個簡單的學習調度實現了隨機梯度下降。
n_epochs = 50 m = len(X_b) t0, t1 = 5, 50 # learning schedule hyperparametersdef learning_schedule(t):return t0 / (t + t1)theta = np.random.randn(2,1) # random initializationfor epoch in range(n_epochs):for i in range(m):random_index = np.random.randint(m)xi = X_b[random_index:random_index+1]yi = y[random_index:random_index+1]gradients = 2 * xi.T.dot(xi.dot(theta) - yi)eta = learning_schedule(epoch * m + i)theta = theta - eta * gradientstheta # array([[4.21076011], # [2.74856079]])根據傳統慣例,我們以
次迭代為一輪,每一輪稱為一個輪次(epoch)。上述的批量梯度下降對整個訓練集進行了 次迭代,而隨機梯度下降只對整個訓練集進行了50個輪次,就達到了一個相當不錯的解。圖4-10展示了訓練的前20步:
Figure 4-10. The first 20 steps of Stochastic Gradient Descent注意到,由于實例是隨機選取的,所以在每個epoch中, 有些實例可能會被選取幾次,而有些實例可能根本不會被選取。如果你想確保訓練過程使用到每一個實例,一種方法是對訓練集進行洗牌(確保將輸入特征和標簽一起洗牌),然后逐個實例進行迭代。不過,這種方法一般收斂比較慢。
基于Scikit-Learn的隨機梯度下降進行線性回歸,可以使用 SGDRegressor 類,該類默認為優化平方誤差代價函數。
下面的代碼運行最大epoch數為1,000,或者直到損失下降小于0.001為止(max_iter=1000, tol=1e-3),初始學習率為0.1(eta0=0.1),使用默認的學習調度(與前面的不同),不使用任何正則化(penalty=None)。
from sklearn.linear_model import SGDRegressorsgd_reg = SGDRegressor(max_iter=1000, tol=1e-3, penalty=None, eta0=0.1, random_state=42) sgd_reg.fit(X, y.ravel())sgd_reg.intercept_, sgd_reg.coef_ # (array([4.24365286]), array([2.8250878]))小批量梯度下降
最后我們要講的梯度下降算法叫做小批量梯度下降。如果你知道了批量梯度下降和隨機梯度下降,應該就容易理解了:在每一步,小批量梯度下降并不是基于完整的訓練集或僅僅基于單個實例來計算梯度,而是在小批量的隨機實例集上計算梯度。與隨機梯度下降相比,小批量梯度下降的主要優勢在于,你可以從矩陣運算的硬件優化中獲得性能提升,尤其是在使用GPU時。
此外,與隨機梯度下降相比,小批量梯度下降在參數空間的進展并沒有那么不穩定,尤其是在相當大的小批量下。因此,小批量梯度下降最終會比隨機梯度下降更接近最小值,但同時它可能會更難脫離局部最小值。圖4-11展示了三種梯度下降算法在訓練過程中的路徑,它們最終都在集中最小值附近,但批量梯度下降的路徑實際上停在了最小值處,而隨機梯度下降和小批量梯度下降都繼續在最小值附近擺動。但是,別忘了,批量梯度下降每走一步都需要大量的時間,并且如果你使用一個合適的學習調度,隨機梯度下降和小批量梯度下降也會達到最小值。
讓我們比較一下到目前為止我們討論過的線性回歸的算法(回憶一下,
是訓練實例的數量, 是特征的數量)。多項式回歸
如果你的數據比直線更復雜怎么辦?你可以使用線性模型來擬合非線性數據,一個簡單的方法是將每個特征的冪級添加為新特征,然后在這個擴展的特征集上訓練一個線性模型,這就是所謂的多項式回歸。
我們來看一個例子,首先,我們根據一個簡單的二次方程(加上一些噪聲,見圖4-12),生成一些非線性數據。
m = 100 X = 6 * np.random.rand(m, 1) - 3 y = 0.5 * X**2 + X + 2 + np.random.randn(m, 1)顯然,直線永遠無法正確擬合這些數據,因此,我們使用 Scikit- Learn 的 PolynomialFeatures類來轉換我們的訓練數據,并將訓練集中每個特征的平方作為新的特征添加進來(本例中只有一個特征)。
from sklearn.preprocessing import PolynomialFeatures poly_features = PolynomialFeatures(degree=2, include_bias=False) X_poly = poly_features.fit_transform(X) X[0] # array([-0.75275929]) X_poly[0] # array([-0.75275929, 0.56664654])現在你可以對這個擴展的訓練數據擬合一個LinearRegression模型(圖4-13)。
lin_reg = LinearRegression() lin_reg.fit(X_poly, y) lin_reg.intercept_, lin_reg.coef_ # (array([1.78134581]), array([[0.93366893, 0.56456263]]))還不錯,預測模型為:
,實際模型為: .需要注意的是,當數據具有多個特征時,多項式回歸能夠找到特征之間的交互關系(這是普通線性回歸模型無法做到的)。這是因為PolynomialFeatures還可以將所有特征的組合到給定的次數。例如,如果有兩個特征
和 ,PolynomialFeatures中的degree=3,則除了添加特征 外,還會添加組合特征 。PolynomialFeatures(degree=d)將包含 個特征的數組轉換成包含 個特征的數組。學習曲線
如果你嘗試高次的多項式回歸,你可能會比普通線性回歸更好地擬合訓練數據。圖4-14將300次的多項式模型應用于前面的訓練數據,并將結果與單純的線性模型和四元模型(二次多項式)進行比較。注意到300次的多項式模型是搖擺不定的,并盡可能接近訓練實例。
高次多項式回歸模型對訓練數據嚴重過擬合,而線性模型則欠擬合。在這種情況下,最具泛化能力的模型是二次模型,這是合理的的,因為數據是用二次模型生成的。但是在一般情況下,你不會知道數據是由什么函數生成的,所以你該如何決定你的模型應該有多復雜?如何判斷你的模型對數據是過擬合還是欠擬合?
在第2章中,我們使用交叉驗證來獲得模型泛化性能的估計。如果一個模型在訓練數據上表現良好,但根據交叉驗證的指標,它的泛化性能很差,那么你的模型就是過擬合。如果在這兩方面都表現不佳,那么就是欠擬合,這是判斷一個模型太簡單或太復雜的一種方法。
另一種方法是觀察學習曲線:這些曲線是模型在訓練集和驗證集上的性能相對于訓練集大小(或迭代次數)的函數。下面的代碼定義了一個函數,給定一些訓練數據,繪制模型的學習曲線。
from sklearn.metrics import mean_squared_error from sklearn.model_selection import train_test_splitdef plot_learning_curves(model, X, y):X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=10)train_errors, val_errors = [], []for m in range(1, len(X_train)):model.fit(X_train[:m], y_train[:m])y_train_predict = model.predict(X_train[:m])y_val_predict = model.predict(X_val)train_errors.append(mean_squared_error(y_train[:m], y_train_predict))val_errors.append(mean_squared_error(y_val, y_val_predict))plt.plot(np.sqrt(train_errors), "r-+", linewidth=2, label="train")plt.plot(np.sqrt(val_errors), "b-", linewidth=3, label="val")plt.legend(loc="upper right", fontsize=14) # not shown in the bookplt.xlabel("Training set size", fontsize=14) # not shownplt.ylabel("RMSE", fontsize=14) # not shownlin_reg = LinearRegression() plot_learning_curves(lin_reg, X, y) plt.axis([0, 80, 0, 3]) # not shown in the book plt.show() # not shown這是個欠擬合的模型,值得解釋一下。首先,我們來看看在訓練數據上的表現:當訓練集中只有一兩個實例時,模型可以完美地擬合它們,這就是為什么曲線從零開始。但是隨著新的實例加入到訓練集中,模型就不可能完美地擬合訓練數據了,這既是因為數據是有噪聲的,也是因為數據根本不是線性的。所以訓練數據上的誤差會一直上升,直到達到一個平穩的狀態,這時向訓練集添加新的實例并不會使平均誤差有多大的變化。現在我們來看看模型在驗證數據上的表現,當模型在極少的訓練實例上進行訓練時,它無法進行正確的泛化,這就是為什么最初的驗證誤差相當大。然后,當模型被展示更多的訓練實例時,它就會學習,因此驗證誤差會慢慢下降。然而,直線又不能很好地對數據進行建模,所以誤差也最終會達到一個平穩狀態,非常接近另一條曲線。
這些學習曲線是典型的模型欠擬合,兩條曲線都達到了一平緩,很接近,而且相當高。
如果你的模型對訓練數據欠擬合,添加更多的訓練實例也無濟于事,你需要使用更復雜的模型或添加更好的特征。我們來看看10次多項式模型在相同數據上的學習曲線(圖4-16)。
from sklearn.pipeline import Pipelinepolynomial_regression = Pipeline([("poly_features", PolynomialFeatures(degree=10, include_bias=False)),("lin_reg", LinearRegression()),])plot_learning_curves(polynomial_regression, X, y) plt.axis([0, 80, 0, 3]) # not shown plt.show()這些學習曲線看起來有點像之前的曲線,但有兩個非常重要的區別。
- 訓練數據的誤差比線性回歸模型低得多
- 曲線之間存在差距。這意味著模型在訓練數據上的表現明顯好于驗證數據,這是過擬合的標志,然而,如果你使用更大的訓練集,兩條曲線會繼續變得更接近。
改進過擬合模型的一種方法是給它輸入更多的訓練數據,直到驗證誤差逼近訓練誤差。
統計學和機器學習的一個重要理論成果是,一個模型的泛化誤差可以用三個不同的誤差之和來表示。偏差:這部分泛化誤差是由于錯誤的假設造成的,比如假設數據是線性的,而實際上是二次元的。高偏差的模型很有可能是對訓練數據欠擬合。方差:這部分是由于模型對訓練數據的微小變化過于敏感。一個具有高自由度的模型(如高次多項式模型)很可能具有較高的方差,從而對訓練數據過擬合。
隨機誤差:這部分是由于數據本身的噪聲。減少這部分誤差的唯一方法是清理數據(如修復數據源,如破損的傳感器,或檢測并去除異常值)。
增加一個模型的復雜性通常會增加其方差,減少其偏差。相反,降低一個模型的復雜性會增加它的偏差,減少它的方差。這就是為什么它被稱為權衡。
正則化線性回歸
正如我們在第1章和第2章中所看到的,減少過擬合的一個方法是對模型進行正則化(即約束模型):模型的自由度越少,它就越難對數據過擬合。正則化多項式模型的一個簡單方法是減少多項式的次數。
對于線性模型來說,正則化通常是通過約束模型的權重來實現的。我們現在來看一下嶺回歸、拉索回歸和彈性網絡三種不同的權重約束方式。
嶺回歸
嶺回歸(也叫Tikhonov正則化)是線性回歸的正則化版本:在代價函數中加入
的正則化項,迫使學習算法不僅要擬合數據,而且要使模型權重盡可能小。需要注意的是,正則化項只在訓練期間添加到代價函數中,當模型訓練完成后,你要使用非正則化的性能度量來評估模型的性能。訓練時使用的代價函數與測試時使用的性能度量標準往往是不同的。除了正則化之外,另一個原因是,一個好的代價函數應該具有良好優化的導數,而用于測試的性能度量應該盡可能地接近最終目標。例如,分類模型通常使用對數損失等代價函數進行訓練,但是使用精度/召回率進行評估。超參數
控制了對模型的正則化程度,如果 ,則嶺回歸就是線性回歸,如果 非常大,則所有的權重最終都會非常接近于零,結果就是一條平坦的線穿過數據的平均值。公式4-8給出了嶺回歸的代價函數:注意到偏置項
是不需要正則化的。在進行嶺回歸之前,對數據進行縮放(例如,使用StandardScaler)是很重要的,因為它對輸入特征的尺度很敏感,實際上大多數正則化模型都是如此。圖4-17展示了使用不同的懲罰系數
對一些線性數據進行訓練的嶺回歸模型。在左圖中,使用簡單的Ridge模型,進行線性擬合;在右圖中,首先使用PolynomialFeatures(degree=10)對數據進行擴展,然后使用StandardScaler對數據進行縮放,最后對得到的特征應用嶺回歸模型。注意到,增加 會導致曲線更平坦,從而減少模型的方差,但相應地會增加其偏差。與線性回歸一樣,我們也可以通過計算閉式解或梯度下降來訓練嶺回歸模型。方程4-9為對應的閉式解,其中
為 的單位矩陣,只是左上角取值為0,因為其對應偏置項。下面使用閉式解來訓練Scikit-Learn的嶺回歸:
from sklearn.linear_model import Ridge ridge_reg = Ridge(alpha=1, solver="cholesky", random_state=42) ridge_reg.fit(X, y) ridge_reg.predict([[1.5]]) # array([[1.55071465]])也可以使用隨機梯度下降法:
ridge_reg = Ridge(alpha=1, solver="sag", random_state=42) ridge_reg.fit(X, y) ridge_reg.predict([[1.5]]) # array([[1.5507201]])# SGD sgd_reg = SGDRegressor(penalty="l2", max_iter=1000, tol=1e-3, random_state=42) sgd_reg.fit(X, y.ravel()) sgd_reg.predict([[1.5]]) # array([1.47012588])其中超參數penalty設置要使用的正則化項的類型,指定 "l2 "表示希望SGD在代價函數中添加一個正則化項,取值為權重向量的
范數的一半。拉索回歸
拉索回歸是線性回歸的另一個正則化版本:就像嶺回歸一樣,它在代價函數中增加了一個正則化項,但它使用的是權重向量的
范數(見公式4-10)。與圖4-17類似,將嶺模型替換為拉索模型,并使用較小的
值,結果如圖4-18所示:拉索回歸的一個重要特點是它傾向于淘汰不重要的特征的權重(即將其設置為零)。例如,圖4-18中右圖中的虛線(
)看起來是二次的,而且幾乎是線性的,即所有高次的特征的權重都等于零。換句話說,拉索回歸會自動進行特征選擇,并輸出一個稀疏模型。你可以通過看圖4-19來了解為什么會這樣:軸代表模型的兩個參數,背景輪廓代表不同的損失函數。在左上角的圖中,等高線代表了
損失( ),當接近任何一個軸時,其取值都會線性下降。例如,如果你初始化模型參數為 和 ,使用 "梯度下降 法"將使兩個參數均等地遞減(用黃色虛線表示),因此, 將首先達到 (因為它一開始就接近 ),之后,梯度下降會順著軸滾下去,直到達到 (會有一點跳動,因為 范數的梯度永遠不會接近0:每個參數的梯度不是 就是 )。在右上角的圖中,等高線代表了拉索回歸的代價函數(即MSE代價函數加上 損失),白色的小圓圈顯示了梯度下降法優化模型參數的路徑,這些參數初始化在 和 附近。我們再一次注意到路徑快速到達 ,然后沿著軸滾動,并圍繞全局最優值(紅色方塊)跳動。如果我們增大 ,全局最優值將沿黃色虛線向左移動,而如果我們減小 ,全局最優值將向右移動(本例中,無正則化MSE的最優參數為 和 )。底部的兩張圖顯示的是同樣的情況,只是懲罰項換成了
損失。在左下角的圖中,可以看到 損失會隨著距離原點的距離而減少,所以梯度下降走了一條直達原點的路徑。在右下角的圖中,等高線代表了嶺回歸的代價函數(即MSE代價函數加上 損失)。這與拉索回歸有兩個主要區別:首先,當參數接近全局最優時,梯度會變小,所以梯度下降自然會變慢,這有助于收斂(因為沒有反彈);其次,當你增大 時,最佳參數(紅色方塊)會越來越接近原點,但它們永遠不會被完全消除(即其中一個取值為 )。在使用拉索回歸時,為了避免梯度下降在最后繞著最優值跳動,你需要在訓練中逐漸降低學習率(它仍然會繞著最優值跳動,但步子會越來越小,所以會收斂)。拉索回歸的代價函數在
時不可導,但是如果我們在 時使用次梯度向量 代替的話,梯度下降仍然可以正常工作。公式4-11給出了一個次梯度的計算:from sklearn.linear_model import Lasso lasso_reg = Lasso(alpha=0.1) lasso_reg.fit(X, y) lasso_reg.predict([[1.5]]) # array([1.53788174])彈性網絡
彈性網絡是介于嶺回歸和拉索回歸之間,正則化項是它們正則化項的簡單混合,并通過混合比
進行控制,當 時,彈性網絡相當于嶺回歸,當 時,相當于拉索回歸(見公式4-12)。那么,什么時候應該使用普通的線性回歸(即不進行任何正則化)、Ridge、Lasso和Elastic Net?事實上最好還是要有一點正則化,即盡量避免使用純線性回歸。Ridge是一個很好的初始模型,但如果你懷疑只有幾個特征是有用的,你應該更偏向于Lasso或Elastic Net,因為它們傾向于將無用特征的權重降到零。一般來說,Elastic Net比Lasso更適用,因為當特征的數量大于訓練實例的數量,或者有幾個特征具有很強的相關性時,Lasso可能會表現得很不穩定。
from sklearn.linear_model import ElasticNet elastic_net = ElasticNet(alpha=0.1, l1_ratio=0.5, random_state=42) elastic_net.fit(X, y) elastic_net.predict([[1.5]]) # array([1.54333232])早停
對迭代學習算法(如梯度下降)進行正則化的另一個方法是:當驗證誤差達到最小值時,立即停止訓練,這就是所謂的早停。圖4-20展示了一個復雜的模型(在本例中,是一個高階多項式回歸模型)使用批量梯度下降法進行訓練,隨著時間的推移,模型在訓練集和驗證集的預測誤差(RMSE)都在下降。然而過了一段時間,驗證誤差不再減少,開始回升,這說明模型已經開始對訓練數據過擬合,你只要在驗證誤差達到最小值時就停止訓練即可。就是這樣一種簡單高效的正則化技術,Geoffrey Hinton稱其為 "美妙的免費午餐"。
對于隨機和小批量的梯度下降,曲線并不是那么平滑,可能很難知道你是否已經達到了最小值。一個解決方案是,只有在驗證誤差超過最小值一段時間后(即當你確信模型不會有更好的表現時)才停止,然后返回驗證誤差最小的模型參數。from sklearn.base import clonepoly_scaler = Pipeline([("poly_features", PolynomialFeatures(degree=90, include_bias=False)),("std_scaler", StandardScaler())])X_train_poly_scaled = poly_scaler.fit_transform(X_train) X_val_poly_scaled = poly_scaler.transform(X_val)sgd_reg = SGDRegressor(max_iter=1, tol=-np.infty, warm_start=True,penalty=None, learning_rate="constant", eta0=0.0005, random_state=42)minimum_val_error = float("inf") best_epoch = None best_model = None for epoch in range(1000):sgd_reg.fit(X_train_poly_scaled, y_train) # continues where it left offy_val_predict = sgd_reg.predict(X_val_poly_scaled)val_error = mean_squared_error(y_val, y_val_predict)if val_error < minimum_val_error:minimum_val_error = val_errorbest_epoch = epochbest_model = clone(sgd_reg)注意到熱重啟參數warm_start=True,當調用fit() 函數時,會接著上次訓練的結果繼續訓練,而不是重新訓練。
邏輯回歸
正如我們在第一章中所討論的那樣,一些回歸算法可以用于分類(反之亦然)。邏輯回歸通常用于估計一個實例屬于特定類別的概率(例如,這封郵件是垃圾郵件的概率是多少?)如果估計的概率大于50%,那么該模型就預測該實例屬于該類(稱為正類,標簽為 "1"),否則就預測它不屬于該類(即屬于負類,標簽為 "0")。
概率估計
那么,邏輯回歸是如何工作的呢?如線性回歸模型一樣,邏輯回歸模型計算的是輸入特征的加權和(加上一個偏置項),但它并不像線性回歸模型那樣直接輸出結果,而是輸出這個結果的logistic(見公式4-13)。
是輸出在 和 之間的 函數(即S形)。 它的定義如公式4-14和圖4-21所示:只要邏輯回歸模型估計出一個實例
屬于正類的概率 ,就可以很容易地進行預測(見公式4-15):代價函數及其訓練
我們知道了邏輯回歸模型如何估計概率并進行預測了,但是如何訓練呢?訓練的目的是尋找參數向量
,使得模型對正的實例( )估計出較高的概率取值,而對負的實例( )估計出較低的概率取值。公式4-16給出了單個訓練實例 的代價函數:這個代價函數是合理的,因為當
接近 時, 會急劇增大,所以對于一個正例,如果模型估計的概率接近 ,代價會很大;類似地,對于一個負例,如果模型估計的概率接近 ,代價也會很大。另一方面,當 接近于 時, 會接近于 ,所以對于估計概率接近于 的負例或估計概率接近于 的正例,其代價將接近于 ,這正是我們想要的。整個訓練集的代價函數是所有訓練實例的代價的平均。它可以寫成一個單一的表達式,即對數損失,如式4-17所示:
壞消息是,并沒有已知的閉式方程來求解最小化這個代價函數的
值。好消息是,這個代價函數是凸的,所以梯度下降法(或其他任何優化算法)能夠保證找到全局最小值。代價函數關于模型參數 的偏導數由式4-18給出:這個式子看起來很像式4-5:對于每個實例,計算其預測誤差,并將其乘以
個特征的取值,最后計算所有訓練實例的平均值。只要有了包含所有偏導數的梯度向量,就可以在批量梯度下降算法中使用它。決策邊界
我們用鳶尾花數據集來了解Logistic回歸,該數據集包含了150朵三種不同品種(Iris setosa, Iris versicolor, Iris virginica)的鳶尾花的萼片和花瓣的長度與寬度(見圖4-22)。
我們先嘗試建立一個僅根據花瓣寬度來檢測花朵是否為Iris virginica的二分類器,首先我們來加載數據:
from sklearn import datasets iris = datasets.load_iris() list(iris.keys()) # ['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename'] X = iris["data"][:, 3:] # petal width y = (iris["target"] == 2).astype(np.int) # 1 if Iris virginica, else 0然后我們訓練一個邏輯回歸模型:
from sklearn.linear_model import LogisticRegression log_reg = LogisticRegression(solver="lbfgs", random_state=42) log_reg.fit(X, y)我們來看看模型對于花瓣寬度從0到3厘米不等的花朵的預測概率分布(圖4-23)。
X_new = np.linspace(0, 3, 1000).reshape(-1, 1) y_proba = log_reg.predict_proba(X_new) decision_boundary = X_new[y_proba[:, 1] >= 0.5][0]plt.figure(figsize=(8, 3)) plt.plot(X[y==0], y[y==0], "bs") plt.plot(X[y==1], y[y==1], "g^") plt.plot([decision_boundary, decision_boundary], [-1, 2], "k:", linewidth=2) plt.plot(X_new, y_proba[:, 1], "g-", linewidth=2, label="Iris virginica") plt.plot(X_new, y_proba[:, 0], "b--", linewidth=2, label="Not Iris virginica") plt.text(decision_boundary+0.02, 0.15, "Decision boundary", fontsize=14, color="k", ha="center") plt.arrow(decision_boundary, 0.08, -0.3, 0, head_width=0.05, head_length=0.1, fc='b', ec='b') plt.arrow(decision_boundary, 0.92, 0.3, 0, head_width=0.05, head_length=0.1, fc='g', ec='g') plt.xlabel("Petal width (cm)", fontsize=14) plt.ylabel("Probability", fontsize=14) plt.legend(loc="center left", fontsize=14) plt.axis([0, 3, -0.02, 1.02]) plt.show()Iris virginica(三角形)的花瓣寬度從
到 厘米不等,而其他鳶尾花(正方形)的花瓣寬度一般較小,從 到 厘米不等。對于花瓣寬度,如果在 厘米以上,則分類器高度確信該花是Iris virginica,而如果在 厘米以下,則它高度確信該花不是Iris virginica。對于兩者之間的情況,分類器是不確定的,但是,如果你讓它預測類別(使用predict()方法而不是predict_proba()方法),它將返回最有可能的類。因此,在 厘米左右有一個決策邊界,該處兩個概率都等于50%:如果花瓣寬度高于 厘米,分類器就會預測這朵花是Iris virginica,否則就會預測它不是。log_reg.predict([[1.7], [1.5]]) # array([1, 0])圖4-24展示了相同的數據集,但這次展示的是兩個特征:花瓣寬度和長度。訓練完成后,邏輯回歸分類器就可以根據這兩個特征來估計新樣本是Iris virginica的概率。虛線代表模型估計概率為50%的點:這是模型的決策邊界,并注意到,這是一個線性邊界。每條平行線代表模型輸出特定概率的點,從15%(左下)到90%(右上),根據模型,右上角線以外的所有花都有90%以上的概率是Iris virginica。
如同其他線性模型一樣,邏輯回歸模型可以使用
或 懲罰來進行正則化,事實上,Scikit-Learn默認會增加一個 的懲罰項。控制Scikit-Learn中LogisticRegression模型正則化強度的超參數不是 ,而是其倒數: , 的值越大,模型的正則化強度越低。Softmax
邏輯回歸模型可以推廣為支持多分類的情形,這就是所謂的Softmax回歸。
其思想很簡單:當給定一個實例
時,Softmax回歸模型首先計算出每個類 的分數 ,然后通過對分數應用softmax函數來得到每個類的概率。計算 的方程應該很熟悉,因為它和線性回歸的方程一樣(見方程4-19):注意到,每個類都有自己的專用參數向量
,所有這些向量通常以行的形式存儲在一個參數矩陣 中。只要計算出實例
的每一類對應的分數,就可以通過softmax函數來計算實例屬于類 的概率(公式4-20)。該函數計算每個分數的指數,然后將其標準化(除以所有指數的總和)。其中:
- 為類別數
- 為實例 的所有類別的得分向量
- 為實例 屬于類別 的概率
與邏輯回歸一樣,Softmax回歸也是預測估計概率最高的類(簡單來說就是得分最高的類),如公式4-21所示:
我們的目標是訓練一個對目標類具有高概率取值的模型。最小化式4-22所示的代價函數,即交叉熵,是可以達到目標的,因為當模型對目標類的估計概率較低時,它會對模型進行懲罰。交叉熵經常用來衡量預測類概率與目標類的匹配程度。
其中:- 表示第 個實例是否屬于類別 ,其取值為 或
該代價函數關于
的梯度向量由式4-23給出:現在,你可以計算每個類的梯度向量,然后使用梯度下降(或任何其他優化算法)找到最小化代價函數的參數矩陣
。X = iris["data"][:, (2, 3)] # petal length, petal width y = iris["target"]softmax_reg = LogisticRegression(multi_class="multinomial",solver="lbfgs", C=10, random_state=42) softmax_reg.fit(X, y)圖4-25展示了由此產生的決策邊界,用背景顏色表示。注意,任何兩個類之間的決策邊界都是線性的。圖中還顯示了Iris versicolor的概率,用曲線表示(例如,標有0.450的線代表45%的概率邊界)。
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的训练softmax分类器实例_第四章.模型训练的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python数据分析_上海交大学姐带你1
- 下一篇: 实时获取vuex更新的新数据_京东手机销