梯度下降是數據科學的基礎,無論是深度學習還是機器學習。深入了解梯度下降原理一定會對你今後的工作有所幫助。
你將真正了解這些超參數的作用以及處理使用此算法可能遇到的問題。
然而,梯度下降並不局限於一種算法。另外兩種流行的梯度下降(隨機和小批量梯度下降)建立在主要算法的基礎上,你可能會看到比普通批量梯度下降更多的算法。因此,我們也必須對這些算法有一個堅實的了解,因為它們有一些額外的超參數,當算法沒有達到期望的性能時,我們需要理解和分析這些超參數。
雖然理論對於深入理解手頭的算法至關重要,但梯度下降的實際編碼及其不同的「變體」可能是一項困難的任務。為了完成這項任務,本文的格式如下:
簡要概述每種算法的作用算法的代碼對規範不明確部分的進一步解釋我們將使用著名的波士頓住房數據集,它是預先內置在scikit learn中的。我們還將從頭開始構建一個線性模型。
首先做一些基本的導入。我不打算在這裡做EDA,因為這不是本文的真正目的。
import numpy as npimport pandas as pd import plotly.express as pxfrom sklearn.datasets import load_bostonfrom sklearn.metrics import mean_squared_error為了看到數據是什麼樣子,我將把數據轉換成一個數據幀並顯示輸出。
data = load_boston()df = pd.DataFrame(data['data'],columns=data['feature_names'])df.insert(13,'target',data['target'])df.head(5)
現在定義特徵(X)和目標(y),我們還將定義參數向量,將其命名為thetas,並將它們初始化為零。
X,y = df.drop('target',axis=1),df['target']thetas = np.zeros(X.shape[1])成本函數
回想一下,成本函數是衡量模型性能的東西,也是梯度下降的目標。我們將使用的代價函數稱為均方誤差。公式如下:
def cost_function(X,Y,B): predictions = np.dot(X,B.T) cost = (1/len(Y)) * np.sum((predictions - Y) ** 2) return cost在這裡,我們將輸入、標籤和參數作為輸入,並使用線性模型進行預測,得到成本,然後返回。如果第二行讓你困惑,回想一下線性回歸公式:
所以我們基本上是得到每個特徵和它們相應權重之間的點積。如果你仍不理解,可以看看這個視頻:https://www.youtube.com/watch?v=kHwlB_j7Hkc 。
現在讓我們測試一下成本函數,看看它是否真的有效。為了做到這一點,我們將使用scikit learn的均方誤差,得到結果,並將其與我們的算法進行比較。
mean_squared_error(np.dot(X,thetas.T),y)OUT: 592.14691169960474cost_function(X,y,thetas)OUT: 592.14691169960474我們的成本函數起作用了!
特徵縮放
特徵縮放是線性模型(線性回歸、KNN、SVM)的重要預處理技術。本質上,特徵被縮小到更小的範圍,並且特徵也在一定的範圍內。可以這樣考慮特徵縮放:
你有一座很大的建築物你希望保持建築的形狀,但希望將其調整為較小的比例特徵縮放通常用於以下場景:
如果一個算法使用歐幾裡德距離,那麼由於歐幾裡德距離對較大的量值敏感,因此需要對特徵進行縮放特徵縮放還可以用於數據標準化特徵縮放還可以提高算法的速度雖然有許多不同的特徵縮放方法,但我們將使用以下公式構建MinMaxScaler的自定義實現:
由於上述原因,我們將使用縮放。
X_norm = (X - X.min()) / (X.max() - X.min())X = X_norm這裡沒什麼特別的,我們只是把公式翻譯成代碼。現在,節目真正開始了:梯度下降!
梯度下降
具體地說,梯度下降是一種優化算法,它通過迭代遍歷數據並獲得偏導數來尋求函數的最小值(在我們的例子中是MSE)。
如果這有點複雜,試著把梯度下降想像成是一個人站在山頂上,他試著以最快的速度從山上爬下來,沿著山的負方向不斷地「走」,直到到達底部。
現在梯度下降有不同的版本,但是你會遇到最多的是:
批量梯度下降隨機梯度下降法小批量梯度下降現在我們將按順序討論、實現和分析每一項,讓我們開始吧!
批量梯度下降
批量梯度下降可能是你遇到的第一種梯度下降類型。在這篇文章中並不是很理論化(可以參考以前的文章:https://medium.com/@vagifaliyev/gradient-descent-clearly-explained-in-python-part-1-the-troubling-theory-49a7fa2c4c06 ),但實際上它計算的是整個(批處理)數據集上係數的偏導數。你可能已經猜到這樣做很慢了。
我們的數據集很小,所以可以像這樣實現批量梯度下降:
def batch_gradient_descent(X,Y,theta,alpha,iters): cost_history = [0] * iters # 初始化歷史損失列表 for i in range(iters): prediction = np.dot(X,theta.T) theta = theta - (alpha/len(Y)) * np.dot(prediction - Y,X) cost_history[i] = cost_function(X,Y,theta) return theta,cost_history要說明一些術語:
alpha:這是指學習率。
iters:迭代運行的數量。
太好了,現在讓我們看看結果吧!
batch_theta,batch_history=batch_gradient_descent(X,y,theta,0.05,500)不是很快,但也不是很慢。我們用新的和改進的參數來可視化成本:
cost_function(X,y,batch_theta)OUT: 27.537447130784262哇,從592到27!這只是梯度下降的力量的一瞥!讓我們對迭代次數的成本函數進行可視化:
fig = px.line(batch_history,x=range(5000),y=batch_history,labels={'x':'no. of iterations','y':'cost function'})fig.show()
好的,看看這個圖表,在大約100次迭代之後達到了一個大的下降,之後一直在逐漸減少。批量梯度下降到此結束。
優點
有效且曲線平滑最準確,最有可能達到全局最低值缺點
對於大型數據集可能會很慢計算成本高隨機梯度下降法
這裡不是計算整個訓練集的偏導數,而是只計算一個隨機樣本(隨機意義上的隨機)。
這是很好的,因為計算只需要在一個訓練示例上進行,而不是在整個訓練集上進行,這使得計算速度更快,而且對於大型數據集來說非常理想。
然而由於其隨機性,隨機梯度下降並不像批量梯度下降那樣具有平滑的曲線,雖然它可以返回良好的參數,但不能保證達到全局最小值。
學習率調整
解決隨機梯度下降問題的一種方法是學習率調整。
基本上,這會逐漸降低學習率。因此,學習率一開始很大(這有助於避免局部極小值),當學習率接近全局最小值時,學習率逐漸降低。但是你必須小心:
如果學習速率降低得太快,那麼算法可能會陷入局部極小,或者在達到最小值的一半時停滯不前;如果學習速率降低太慢,可能會在很長一段時間內跳轉到最小值附近,仍然無法得到最佳參數。現在,我們將使用簡易的學習率調整策略實現隨機梯度下降:
t0,t1 = 5,50 # 學習率超參數def learning_schedule(t): return t0/(t+t1) def stochastic_gradient_descent(X,y,thetas,n_epochs=30): c_hist = [0] * n_epochs # 歷史成本 for epoch in range(n_epochs): for i in range(len(y)): random_index = np.random.randint(len(Y)) xi = X[random_index:random_index+1] yi = y[random_index:random_index+1] prediction = xi.dot(thetas) gradient = 2 * xi.T.dot(prediction-yi) eta = learning_schedule(epoch * len(Y) + i) thetas = thetas - eta * gradient c_hist[epoch] = cost_function(xi,yi,thetas) return thetas,c_hist現在運行函數:
sdg_thetas,sgd_cost_hist = stochastic_gradient_descent(X,Y,theta)這樣就行了!現在讓我們看看結果:
cost_function(X,y,sdg_thetas)OUT:29.833230764634493從592到29,但是請注意:我們只進行了30次迭代。批量梯度下降,500次迭代後得到27次!這只是對隨機梯度下降的非凡力量的一瞥。
我們用一個圖再次將其可視化:
由於這是一個小數據集,批量梯度下降就足夠了,但這只是顯示了隨機梯度下降的力量。
優點:
與批量梯度下降相比更快更好地處理更大的數據集缺點:
在某個最小值上很難跳出並不總是有一個清晰的圖,可以在一個最小值附近反彈,但永遠不會達到最佳的最小值小批量梯度下降
好了,快到了,還有一個要通過!現在,在小批量梯度下降中,我們不再計算整個訓練集或隨機樣本的偏導數,而是在整個訓練集的小子集上計算。
這給了我們比批量梯度下降更快的速度,因為它不像隨機梯度下降那樣隨機,所以我們更接近於最小值。然而,它很容易陷入局部極小值。
同樣,為了解決陷入局部最小值的問題,我們將在實現中使用簡易的學習率調整。
np.random.seed(42) # 所以我們得到相同的結果t0, t1 = 200, 1000def learning_schedule(t): return t0 / (t + t1) def mini_batch_gradient_descent(X,y,thetas,n_iters=100,batch_size=20): t = 0 c_hist = [0] * n_iters for epoch in range(n_iters): shuffled_indices = np.random.permutation(len(y)) X_shuffled = X_scaled[shuffled_indices] y_shuffled = y[shuffled_indices] for i in range(0,len(Y),batch_size): t+=1 xi = X_shuffled[i:i+batch_size] yi = y_shuffled[i:i+batch_size] gradient = 2/batch_size * xi.T.dot(xi.dot(thetas) - yi) eta = learning_schedule(t) thetas = thetas - eta * gradient c_hist[epoch] = cost_function(xi,yi,thetas) return thetas,c_hist運行並獲得結果:
mini_batch_gd_thetas,mini_batch_gd_cost = mini_batch_gradient_descent(X,y,theta)以及新參數下的成本函數:
cost_function(X,Y,mini_batch_gd_thetas)OUT: 27.509689139167012真的很棒。我們運行了1/5的迭代,得到了一個更好的分數!
再畫出函數:
感謝閱讀!