全文共14118字,預計學習時長30分鐘或更長
想了解如何使用numpy在tensorflow或pytorch中實現優化算法,以及如何使用matplotlib創建精美的動畫?
本文將討論如何實現梯度下降優化技術的不同變體,以及如何使用matplotlib將用於這些變體更新規則的運作可視化出來。
本文的內容和結構基於 One-Fourth Labs。
梯度下降是優化神經網絡最常用的技術之一。梯度下降算法是通過向相對於網絡參數的目標函數梯度的相反方向移動來更新參數。
運用Numpy在Python中實現
照片來源:Unsplash,克里斯托弗·高爾
編碼部分將討論以下主題。
• Sigmoid神經元類
• 總體設置——何爲數據、模型、任務
• 繪圖功能——3D和輪廓圖
• 個體算法及其執行方式
在開始實現梯度下降之前,首先需要輸入所需的庫。從mpl_toolkits.mplot3d輸入的Axes3D提供了一些基本的3D繪圖(散點、曲面、直線、網格)工具。它並非最快或功能最完整的3D庫,而是Matplotlib附帶的。還從 Matplotlib輸入colors和colormap(cm)。我們想要製作動畫圖來演示每種優化算法的工作原理,所以我們輸入animation和rc來讓圖表看起來美觀。爲了顯示HTML,在Jupyter Notebook中成線性排列。最後爲了計算目的來輸入numpy,這項計算任務很繁重。
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import matplotlib.colors
from matplotlib import animation, rc
from IPython.display import HTML
import numpy as np
實施Sigmoid神經元
爲了實現梯度下降優化技術,以sigmoid神經元(邏輯函數)爲例,看看梯度下降的不同變體是如何學習參數“ w”和“ b”的。
Sigmoid神經元複查
Sigmoid神經元類似於感知機神經元(perceptron neuron),因爲對於每個輸入xi,其都有與輸入相關的權重wi。權重表明了輸入在決策過程中的重要性。來自sigmoid的輸出不同於感知機模型,其輸出不是0或1,而是一個介於0到1之間的實數值,可以解釋爲概率。最常用的sigmoid 函數是邏輯函數,它具有“ S”形曲線的特徵。
Sigmoid神經元標註(邏輯函數)
學習算法
學習算法的目標是確定參數(w和b)的最佳可能值,以使模型的整體損失(平方誤差損失)儘可能最小化。
對w和b進行隨機初始化。然後,對數據中的所有觀測值進行迭代。使用sigmoid函數找到每個觀測值相應的預測結果,並計算均方誤差損失。基於損失值,將更新權重,以使在新參數下模型的整體損失將小於模型的當前損失。
Sigmoid神經元類
在開始分析梯度下降算法的不同變體之前,將在名爲SN的類中構建模型。
class SN:
#constructor
def __init__(self, w_init, b_init, algo):
self.w = w_init
self.b = b_init
self.w_h = []
self.b_h = []
self.e_h = []
self.algo = algo
#logistic function
def sigmoid(self, x, w=None, b=None):
if w is None:
w = self.w
if b is None:
b = self.b
return 1. / (1. + np.exp(-(w*x + b)))
#loss function
def error(self, X, Y, w=None, b=None):
if w is None:
w = self.w
if b is None:
b = self.b
err = 0
for x, y in zip(X, Y):
err += 0.5 * (self.sigmoid(x, w, b) - y) ** 2
return err
def grad_w(self, x, y, w=None, b=None):
if w is None:
w = self.w
if b is None:
b = self.b
y_pred = self.sigmoid(x, w, b)
return (y_pred - y) * y_pred * (1 - y_pred) * x
def grad_b(self, x, y, w=None, b=None):
if w is None:
w = self.w
if b is None:
b = self.b
y_pred = self.sigmoid(x, w, b)
return (y_pred - y) * y_pred * (1 - y_pred)
def fit(self, X, Y,
epochs=100, eta=0.01, gamma=0.9, mini_batch_size=100, eps=1e-8,
beta=0.9, beta1=0.9, beta2=0.9
):
self.w_h = []
self.b_h = []
self.e_h = []
self.X = X
self.Y = Y
if self.algo == 'GD':
for i in range(epochs):
dw, db = 0, 0
for x, y in zip(X, Y):
dw += self.grad_w(x, y)
db += self.grad_b(x, y)
self.w -= eta * dw / X.shape[0]
self.b -= eta * db / X.shape[0]
self.append_log()
elif self.algo == 'MiniBatch':
for i in range(epochs):
dw, db = 0, 0
points_seen = 0
for x, y in zip(X, Y):
dw += self.grad_w(x, y)
db += self.grad_b(x, y)
points_seen += 1
if points_seen % mini_batch_size == 0:
self.w -= eta * dw / mini_batch_size
self.b -= eta * db / mini_batch_size
self.append_log()
dw, db = 0, 0
elif self.algo == 'Momentum':
v_w, v_b = 0, 0
for i in range(epochs):
dw, db = 0, 0
for x, y in zip(X, Y):
dw += self.grad_w(x, y)
db += self.grad_b(x, y)
v_w = gamma * v_w + eta * dw
v_b = gamma * v_b + eta * db
self.w = self.w - v_w
self.b = self.b - v_b
self.append_log()
elif self.algo == 'NAG':
v_w, v_b = 0, 0
for i in range(epochs):
dw, db = 0, 0
v_w = gamma * v_w
v_b = gamma * v_b
for x, y in zip(X, Y):
dw += self.grad_w(x, y, self.w - v_w, self.b - v_b)
db += self.grad_b(x, y, self.w - v_w, self.b - v_b)
v_w = v_w + eta * dw
v_b = v_b + eta * db
self.w = self.w - v_w
self.b = self.b - v_b
self.append_log()
#logging
def append_log(self):
self.w_h.append(self.w)
self.b_h.append(self.b)
self.e_h.append(self.error(self.X, self.Y))
#constructor
def __init__(self, w_init, b_init, algo):
self.w = w_init
self.b = b_init
self.w_h = []
self.b_h = []
self.e_h = []
self.algo = algo
init__函數(構造函數)有助於將sigmoid神經元的參數初始化爲w權重和b偏差。這個函數有三個參數:
• w_init,b_init,這些取參數“w”和“b”的初始值,而非隨機設置參數,將其設置爲特定值。這樣能夠通過可視化不同初始點來理解算法的執行方式。有些算法在某些參數下陷入局部最小值。
• algo指出使用何種梯度下降算法的變體來發現最佳參數。
在此函數中,我們對參數進行初始化,並定義了三種帶有後綴'_h'的新數組變量,表示它們是歷史變量,以跟蹤權重(w_h)、偏差(b_h)和誤差(e_h)的值是如何隨着sigmoid神經元學習參數而變化的。
def sigmoid(self, x, w=None, b=None):
if w is None:
w = self.w
if b is None:
b = self.b
return 1. / (1. + np.exp(-(w*x + b)))
有一個sigmoid函數,它接受輸入x-強制參數,並計算輸入的邏輯函數及參數。該函數還接受其他兩個可選參數。
• w & b,以“ w”和“ b”用作參數,它有助於根據特定的參數值來計算sigmoid函數的值。如果未傳遞這些參數,它將使用已學的參數值來計算邏輯函數。
def error(self, X, Y, w=None, b=None):
if w is None:
w = self.w
if b is None:
b = self.b
err = 0
for x, y in zip(X, Y):
err += 0.5 * (self.sigmoid(x, w, b) - y) ** 2
return err
下面,有error 函數,輸入X和Y作爲強制參數和可選參數 ,像sigmoid函數一樣。在這個函數中,通過每個數據點進行迭代,並使用sigmoid函數計算實際特徵值和預測特徵值之間的累積均方誤差。正如在sigmoid函數中看到的,它支持在指定參數值下計算誤差。
def grad_w(self, x, y, w=None, b=None):
.....
def grad_b(self, x, y, w=None, b=None):
.....
接下來,將定義兩個函數grad_w和grad_b。輸入“x”和“y”作爲強制參數,有助於分別計算sigmoid相對於參數“w”和“b”輸入的梯度。還有兩個可選參數,計算指定參數值處的梯度。
def fit(self, X, Y, epochs=100, eta=0.01, gamma=0.9, mini_batch_size=100, eps=1e-8,beta=0.9, beta1=0.9, beta2=0.9):
self.w_h = []
.......
接下來,定義“ fit”法,它接受輸入“ X”,“ Y”和其他一系列參數。每當將其用於梯度下降算法的特定變體時,都會解釋這些參數。該函數首先初始化歷史記錄變量並設置本地輸入變量以存儲輸入參數數據。
然後,對於該函數支持的每個算法,有一堆不同的“if-else”語句。依據選擇的算法,將在fit法中實現梯度下降。在本文的後半部分,將詳細解釋這些實現。
def append_log(self):
self.w_h.append(self.w)
self.b_h.append(self.b)
self.e_h.append(self.error(self.X, self.Y))
最後,有theappend_log函數,用以存儲各時期各梯度下降的參數值和損失函數值。
繪圖設置
本節將定義一些配置參數,使用簡單的二維toy數據集來模擬梯度下降更新規則。還定義了一些函數,創建三維和二維圖併爲其設置動畫,可視化更新規則的運作。這種設置有助於針對不同起點、不同超參數設置和不同梯度下降變量的繪圖/動畫更新規則運行不同的實驗。
#Data
X = np.asarray([3.5, 0.35, 3.2, -2.0, 1.5, -0.5])
Y = np.asarray([0.5, 0.50, 0.5, 0.5, 0.1, 0.3])
#Algo and parameter values
algo = 'GD'
w_init = 2.1
b_init = 4.0
#parameter min and max values- to plot update rule
w_min = -7
w_max = 5
b_min = -7
b_max = 5
#learning algorithum options
epochs = 200
mini_batch_size = 6
gamma = 0.9
eta = 5
#animation number of frames
animation_frames = 20
#plotting options
plot_2d = True
plot_3d = False
首先,採用一個簡單的二維玩具數據集,它包括兩個輸入和兩個輸出。在第5行定義一個字符串變量algo,它接受要執行的算法類型。初始化第6-7行中的參數'w'和'b',以指示算法的開始位置。
從第9-12行開始,設置參數的極限,即signoid神經元在指定範圍內搜索最佳參數的範圍。這些精心挑選的數字,用以說明梯度下降更新規則的運作。接下來將設置超參數的值,將某些變量特定於某些算法。在我們討論算法實現的時候,我會對此進行討論。最後,從19-22行開始,明確製作動畫或繪製更新規則所需的變量。
sn = SN(w_init, b_init, algo)
sn.fit(X, Y, epochs=epochs, eta=eta, gamma=gamma, mini_batch_size=mini_batch_size)
plt.plot(sn.e_h, 'r')
plt.plot(sn.w_h, 'b')
plt.plot(sn.b_h, 'g')
plt.legend(('error', 'weight', 'bias'))
plt.title("Variation of Parameters and loss function")
plt.xlabel("Epoch")
plt.show()
設置好配置參數後,將SN類進行初始化,然後使用配置參數調用fit法。此外,繪製三個歷史變量,用以將參數和損失函數值在各個時期之間的變化進行可視化。
3D和2D繪圖設置
if plot_3d:
W = np.linspace(w_min, w_max, 256)
b = np.linspace(b_min, b_max, 256)
WW, BB = np.meshgrid(W, b)
Z = sn.error(X, Y, WW, BB)
fig = plt.figure(dpi=100)
ax = fig.gca(projection='3d')
surf = ax.plot_surface(WW, BB, Z, rstride=3, cstride=3, alpha=0.5, cmap=cm.coolwarm, linewidth=0, antialiased=False)
cset = ax.contourf(WW, BB, Z, 25, zdir='z', offset=-1, alpha=0.6, cmap=cm.coolwarm)
ax.set_xlabel('w')
ax.set_xlim(w_min - 1, w_max + 1)
ax.set_ylabel('b')
ax.set_ylim(b_min - 1, b_max + 1)
ax.set_zlabel('error')
ax.set_zlim(-1, np.max(Z))
ax.view_init (elev=25, azim=-75) # azim = -20
ax.dist=12
title = ax.set_title('Epoch 0')
首先爲了創建3D繪圖,要在“ w”和“ b”的最小值和最大值之間創建256個相等間隔的值來創建網格,如第2-5行所示。使用網格通過調用Sigmoid類中的error函數來計算這些值的誤差(第5行)SN。在第8行創建軸手柄以創建三維繪圖。
爲了創建3D繪圖,使用ax.plot_surface函數,通過設置rstride和cstride,指定採樣點和數據的頻率,創建關於權重和誤差的表面圖。接下來,使用ax.contourf函數,通過將誤差值指定爲“ Z”方向(第9-10行),在表面頂部繪製相對於權重和偏差的誤差輪廓。在11-16行中,爲每個軸設置標籤,併爲所有三維設置軸限。正在繪製三維繪圖,所以需要定義視點。在第17–18行中爲繪圖設置了一個視點,該視點在“ z”軸上的高度爲25度,距離爲12個單位。
def plot_animate_3d(i):
i = int(i*(epochs/animation_frames))
line1.set_data(sn.w_h[:i+1], sn.b_h[:i+1])
line1.set_3d_properties(sn.e_h[:i+1])
line2.set_data(sn.w_h[:i+1], sn.b_h[:i+1])
line2.set_3d_properties(np.zeros(i+1) - 1)
title.set_text('Epoch: {: d}, Error: {:.4f}'.format(i, sn.e_h[i]))
return line1, line2, title
if plot_3d:
#animation plots of gradient descent
i = 0
line1, = ax.plot(sn.w_h[:i+1], sn.b_h[:i+1], sn.e_h[:i+1], color='black',marker='.')
line2, = ax.plot(sn.w_h[:i+1], sn.b_h[:i+1], np.zeros(i+1) - 1, color='red', marker='.')
anim = animation.FuncAnimation(fig, func=plot_animate_3d, frames=animation_frames)
rc('animation', html='jshtml')
anim
基於靜態三維繪圖,想要可視化該算法的動態操作,該操作是由用於參數和誤差函的歷史變量在算法的各個時期捕獲。要創建梯度下降算法的動畫,將使用通過傳遞自定義函數plot_animate_3d作爲參數之一的animation.FuncAnimation函數,並指定創建動畫所需的幀數。plot_animate_3d函數plot_animate_3d爲“ w”和“ b”的相應值更新參數值和誤差值。在第7行的相同函數中,將文本設置爲顯示該特定時期的誤差值。最後,爲了在線顯示動畫,調用rc函數以在jupyter筆記本中呈現HTML內容。
類似於三維繪圖,可以創建一個函數用以繪製二維等高線圖。
if plot_2d:
W = np.linspace(w_min, w_max, 256)
b = np.linspace(b_min, b_max, 256)
WW, BB = np.meshgrid(W, b)
Z = sn.error(X, Y, WW, BB)
fig = plt.figure(dpi=100)
ax = plt.subplot(111)
ax.set_xlabel('w')
ax.set_xlim(w_min - 1, w_max + 1)
ax.set_ylabel('b')
ax.set_ylim(b_min - 1, b_max + 1)
title = ax.set_title('Epoch 0')
cset = plt.contourf(WW, BB, Z, 25, alpha=0.8, cmap=cm.bwr)
plt.savefig("temp.jpg",dpi = 2000)
plt.show()
def plot_animate_2d(i):
i = int(i*(epochs/animation_frames))
line.set_data(sn.w_h[:i+1], sn.b_h[:i+1])
title.set_text('Epoch: {: d}, Error: {:.4f}'.format(i, sn.e_h[i]))
return line, title
if plot_2d:
i = 0
line, = ax.plot(sn.w_h[:i+1], sn.b_h[:i+1], color='black',marker='.')
anim = animation.FuncAnimation(fig, func=plot_animate_2d, frames=animation_frames)
rc('animation', html='jshtml')
anim
算法的實現
本節將實現梯度下降算法的不同變體,並生成三維和二維動畫圖。
Vanilla梯度下降
梯度下降算法是通過向相對於網絡參數的目標函數梯度的相反方向移動來更新參數。
參數更新規則由下式給出:
梯度下降更新規則:
for i in range(epochs):
dw, db = 0, 0
for x, y in zip(X, Y):
dw += self.grad_w(x, y)
db += self.grad_b(x, y)
self.w -= eta * dw / X.shape[0]
self.b -= eta * db / X.shape[0]
self.append_log()
在批量梯度下降中,迭代所有訓練數據點,並計算參數“ w”和“ b”的梯度累積和。然後根據累積梯度值和學習率更新參數值。
要執行梯度下降算法,請如下所示更改配置設置。
X = np.asarray([0.5, 2.5])
Y = np.asarray([0.2, 0.9])
algo = 'GD'
w_init = -2
b_init = -2
w_min = -7
w_max = 5
b_min = -7
b_max = 5
epochs = 1000
eta = 1
animation_frames = 20
plot_2d = True
plot_3d = True
在配置設置中,設置變量algo 爲‘GD’,表明在sigmoid神經元中執行vanilla梯度下降算法,以便找到最佳參數值。設置好配置參數後,將繼續執行SN類“fit”法,訓練小型數據上的sigmoid神經元。
梯度下降的歷史
上圖顯示了在算法學習最佳參數的同時,誤差、權重和偏差的歷史值在不同階段之間的變化方式。圖中需要注意的關鍵一點是,在初始階段,誤差值徘徊在0.5左右,但在200個階段後,誤差幾乎達到零。
如果想要繪製三維或二維動畫,則可以設置布爾變量plot_2d和 plot_3d。展示三維誤差面在對應值“w”和“b”下的外觀。學習算法的目標是向誤差/損失最小的深藍色區域移動。
爲了可視化動態執行的算法,可以使用函數plot_animate_3d生成動畫。播放動畫時,可以看到該階段的編號和相應的誤差值。
如果想要放慢動畫的速度,可以通過單擊視頻控件中的減號來實現,如上動畫所示。同樣,可以爲二維等高線圖生成動畫,以查看算法如何向全局最小值移動。
基於Momentum的梯度下降
在Momentum GD中,以先前梯度和當前梯度指數衰減的累積平均值移動。
Momentum GD的代碼如下:
v_w, v_b = 0, 0
for i in range(epochs):
dw, db = 0, 0
for x, y in zip(X, Y):
dw += self.grad_w(x, y)
db += self.grad_b(x, y)
v_w = gamma * v_w + eta * dw
v_b = gamma * v_b + eta * db
self.w = self.w - v_w
self.b = self.b - v_b
self.append_log()
基於Momentum GD,涵蓋了歷史變量,以便跟蹤先前的梯度值。變量gamma表示需要給算法施加多少Momentum。變量v_w和 v_b用來計算基於歷史記錄和當前梯度的運動。在每個階段結束時,調用append_log函數來存儲參數和損失函數值的歷史記錄。
爲sigmoid神經元執行Momentum GD,需要修改配置設置,如下所示:
X = np.asarray([0.5, 2.5])
Y = np.asarray([0.2, 0.9])
algo = 'Momentum'
w_init = -2
b_init = -2
w_min = -7
w_max = 5
b_min = -7
b_max = 5
epochs = 1000
mini_batch_size = 6
gamma = 0.9
eta = 1
animation_frames = 20
plot_2d = True
plot_3d = True
變量algo被設置爲“Momentum”,以示想要使用Momentum GD爲sigmoid神經元找到最佳參數;另一個重要的變化是gamma變量,該變量用於控制在學習算法中Momentum的所需量。Gamma值在0-1之間變化。設置好配置參數後,將繼續執行SN類“ fit”法來訓練toy數據上的sigmoid神經元。
Momentum GD的變化
由圖可見,累積的歷史Momentum GD在極小值內外波動,權重和偏差項的值也出現了一些波動。
Nesterov加速梯度下降
在Nesterov加速梯度下降過程中,希望在根據當前梯度值採取另一步驟之前,瞭解是否接近最小值,從而避免出現過沖問題。
Momentum GD的代碼如下:
v_w, v_b = 0, 0
for i in range(epochs):
dw, db = 0, 0
v_w = gamma * v_w
v_b = gamma * v_b
for x, y in zip(X, Y):
dw += self.grad_w(x, y, self.w - v_w, self.b - v_b)
db += self.grad_b(x, y, self.w - v_w, self.b - v_b)
v_w = v_w + eta * dw
v_b = v_b + eta * db
self.w = self.w - v_w
self.b = self.b - v_b
self.append_log()
NAG GD代碼的主要變化是v_w和 v_b的計算。在Momentum GD中,一步完成這些變量的計算,但在NAG中,分兩個步驟進行計算。
v_w = gamma * v_w
v_b = gamma * v_b
for x, y in zip(X, Y):
dw += self.grad_w(x, y, self.w - v_w, self.b - v_b)
db += self.grad_b(x, y, self.w - v_w, self.b - v_b)
v_w = v_w + eta * dw
v_b = v_b + eta * db
第一部分中,在遍歷數據之前,將gamma與歷史變量相乘,然後使用self.w和 self.b中減去的歷史值來計算梯度。只需要設置algo變量爲“NAG”。可以生成三維或二維動畫,以查看NAG GD與Momentum GD在達到全局最小值方面有何差異。
Mini-Batch and Stochastic梯度下降
無需一次查看所有數據點,將整個數據分爲多個子集。針對數據的每個子集,計算子集中存在的每個點的導數,並更新參數。不存在針對損失函數計算整個數據的導數,而是將其近似爲更少的點或較小的mini-batch size。這種批量計算梯度的方法稱爲 “ Mini-Batch 梯度下降”。
Mini-Batch GD的代碼如下所示:
for i in range(epochs):
dw, db = 0, 0
points_seen = 0
for x, y in zip(X, Y):
dw += self.grad_w(x, y)
db += self.grad_b(x, y)
points_seen += 1
if points_seen % mini_batch_size == 0:
self.w -= eta * dw / mini_batch_size
self.b -= eta * db / mini_batch_size
self.append_log()
dw, db = 0, 0
在Mini Batch中,遍歷整個數據並使用變量points_seen跟蹤已看到的點數。如果看到的點數是mini-batch size 的倍數,那麼是正在更新sigmoid神經元的參數。在特殊情況下,當mini-batch size等於1時,它將成爲隨機梯度下降。要執行Mini-Batch GD,只需要將算法變量設置爲“MiniBatch”。可以生成3D或2D動畫,以瞭解Mini-Batch GD在達到全局最小值方面與Momentum GD有何差異。
AdaGrad梯度下降
AdaGrad隱藏的主要動機是針對數據集中不同特徵的自適應學習率,即不是針對數據集中所有特徵,使用相同的學習率,而是不同特徵採用不同學習率。
Adagrad的代碼如下所示:
v_w, v_b = 0, 0
for i in range(epochs):
dw, db = 0, 0
for x, y in zip(X, Y):
dw += self.grad_w(x, y)
db += self.grad_b(x, y)
v_w += dw**2
v_b += db**2
self.w -= (eta / np.sqrt(v_w) + eps) * dw
self.b -= (eta / np.sqrt(v_b) + eps) * db
self.append_log()
在Adagrad中,保持梯度的平方和,然後將學習率除以歷史值的平方根更新參數。這裏並非靜態學習,而是密集和稀疏型的動態學習。生成圖/動畫的機制與上文相同。此處的想法是使用不同的toy數據集和不同的超參數配置。
RMSProp梯度下降
在RMSProp中,不同於AdaGrad中的梯度總和,梯度歷史是根據指數衰減的平均值計算的,這有助於防止密集型的分母快速增長。
RMSProp的代碼如下所示:
v_w, v_b = 0, 0
for i in range(epochs):
dw, db = 0, 0
for x, y in zip(X, Y):
dw += self.grad_w(x, y)
db += self.grad_b(x, y)
v_w = beta * v_w + (1 - beta) * dw**2
v_b = beta * v_b + (1 - beta) * db**2
self.w -= (eta / np.sqrt(v_w) + eps) * dw
self.b -= (eta / np.sqrt(v_b) + eps) * db
self.append_log()
在AdaGrad代碼中唯一的更改是更新變量v_w和 v_b的方式。在AdaGrad中,v_w和v_b,從第一階段開始,總是按每個參數的梯度平方遞增,但在RMSProp 中,v_w和 v_b是使用“gamma”的超參數指數衰減的梯度加權和。要執行RMSProp GD,只需將algo變量設置爲“RMSProp”。可以生成3D或2D動畫,以查看RMSProp GD在達到全局最小值方面與 AdaGrad GD有何差異。
Adam梯度下降
Adam 擁有兩個歷史記錄,“mₜ”和Momentum GD中使用的歷史類似,“vₜ”和RMSProp中使用的歷史類似。
在運作中,Adam執行偏差校正。它對“mₜ”和“vₜ”使用以下等式:
偏差校正
偏差校正可確保在訓練開始時不會出現怪異的行爲。Adam的要點是,它結合了Momentum GD(在溫和地區移動更快)和RMSProp GD(調整學習率)的優勢。
Adam GD的代碼如下所示:
v_w, v_b = 0, 0
m_w, m_b = 0, 0
num_updates = 0
for i in range(epochs):
dw, db = 0, 0
for x, y in zip(X, Y):
dw = self.grad_w(x, y)
db = self.grad_b(x, y)
num_updates += 1
m_w = beta1 * m_w + (1-beta1) * dw
m_b = beta1 * m_b + (1-beta1) * db
v_w = beta2 * v_w + (1-beta2) * dw**2
v_b = beta2 * v_b + (1-beta2) * db**2
m_w_c = m_w / (1 - np.power(beta1, num_updates))
m_b_c = m_b / (1 - np.power(beta1, num_updates))
v_w_c = v_w / (1 - np.power(beta2, num_updates))
v_b_c = v_b / (1 - np.power(beta2, num_updates))
self.w -= (eta / np.sqrt(v_w_c) + eps) * m_w_c
self.b -= (eta / np.sqrt(v_b_c) + eps) * m_b_c
self.append_log()
在Adam優化器中,計算m_w & m_b來跟蹤momentum 歷史,並計算v_w & v_b以衰減分母並阻止其快速增長,就像在RMSProp中一樣。
之後,對基於Momentum和RMSProp的歷史變量實施偏差校正。一旦計算出參數“ w”和“ b”的校正值,將使用這些值來更新參數值。
執行Adam梯度下降算法,請如下所示更改配置設置。
X = np.asarray([3.5, 0.35, 3.2, -2.0, 1.5, -0.5])
Y = np.asarray([0.5, 0.50, 0.5, 0.5, 0.1, 0.3])
algo = 'Adam'
w_init = -6
b_init = 4.0
w_min = -7
w_max = 5
b_min = -7
b_max = 5
epochs = 200
gamma = 0.9
eta = 0.5
eps = 1e-8
animation_frames = 20
plot_2d = True
plot_3d = False
變量algo被設置爲“Adam”,表示使用Adam GD爲sigmoid神經元找到最佳參數;另一個重要的變化是gamma 變量,該變量用於控制學習算法所需的momentum。Gamma值在0-1之間變化。設置配置參數後,將繼續執行SN類“ fit”法以訓練toy數據上的sigmoid神經元。
Adam GD中的參數變化
創建2D等高線動畫,該動畫顯示Adam GD學習通向全局極小值的路徑的方式。
Adam GD動畫
不同於RMSProp案例,沒有太多波動。尤其在最初幾個階段之後,更加確定地朝着最小值移動。
關於如何使用Numpy實現優化技術的討論到此結束。
實踐學習
本文中介紹了不同的情況,使用了帶有靜態初始化點的toy數據集。但是可以使用不同的初始化點,並針對每個初始化點,使用不同的算法,看看在超參數中需要進行何種調整。本文討論的全部代碼都在GitHub存儲庫中。隨意分類或下載。最棒的是,可以直接在google colab中運行代碼,而不必擔心安裝軟件包。
https://github.com/Niranjankumar-c/GradientDescent_Implementation?source=post_page-----809e7ab3bab4----------------------
綜上所述,本文介紹瞭如何通過採取簡單的sigmoid神經元實現梯度算法的不同變種。此外,還了解了如何爲每個變體(顯示學習算法如何找到最佳參數)創建精美的3D或2D動畫。