5分鐘快速掌握Adam優(yōu)化算法
梯度下降是一種優(yōu)化算法,遵循目標(biāo)函數(shù)的負(fù)梯度以定位函數(shù)的最小值。
梯度下降的局限性是,所有輸入變量都使用單個(gè)步長(學(xué)習(xí)率)。像AdaGrad和RMSProp這樣的梯度下降的擴(kuò)展會(huì)更新算法,以對每個(gè)輸入變量使用單獨(dú)的步長,但可能會(huì)導(dǎo)致步長迅速減小到非常小的值。自適應(yīng)運(yùn)動(dòng)估計(jì)算法(Adam)是梯度下降的擴(kuò)展,是AdaGrad和RMSProp等技術(shù)的自然繼承者,該技術(shù)可自動(dòng)為目標(biāo)函數(shù)的每個(gè)輸入變量調(diào)整學(xué)習(xí)率,并通過使用以指數(shù)方式降低梯度的移動(dòng)平均值以更新變量。
在本教程中,您將發(fā)現(xiàn)如何從頭開始使用Adam優(yōu)化算法開發(fā)梯度下降。完成本教程后,您將知道:
- 梯度下降是一種優(yōu)化算法,它使用目標(biāo)函數(shù)的梯度來導(dǎo)航搜索空間。
- 可以通過使用稱為Adam的偏導(dǎo)數(shù)的遞減平均值,將梯度下降更新為對每個(gè)輸入變量使用自動(dòng)自適應(yīng)步長。
- 如何從頭開始實(shí)施Adam優(yōu)化算法并將其應(yīng)用于目標(biāo)函數(shù)并評估結(jié)果。
教程概述
本教程分為三個(gè)部分:他們是:
- 梯度下降
- Adam優(yōu)化算法
- Adam梯度下降
二維測試問題
Adam的梯度下降優(yōu)化
Adam可視化
梯度下降
梯度下降是一種優(yōu)化算法。它在技術(shù)上稱為一階優(yōu)化算法,因?yàn)樗鞔_利用了目標(biāo)目標(biāo)函數(shù)的一階導(dǎo)數(shù)。一階導(dǎo)數(shù),或簡稱為“導(dǎo)數(shù)”,是目標(biāo)函數(shù)在特定點(diǎn)(例如,點(diǎn))上的變化率或斜率。用于特定輸入。如果目標(biāo)函數(shù)采用多個(gè)輸入變量,則將其稱為多元函數(shù),并且可以將輸入變量視為向量。反過來,多元目標(biāo)函數(shù)的導(dǎo)數(shù)也可以視為向量,通常稱為梯度。
梯度:多元目標(biāo)函數(shù)的一階導(dǎo)數(shù)。
對于特定輸入,導(dǎo)數(shù)或梯度指向目標(biāo)函數(shù)最陡峭的上升方向。
梯度下降是指一種最小化優(yōu)化算法,該算法遵循目標(biāo)函數(shù)的下坡梯度負(fù)值來定位函數(shù)的最小值。梯度下降算法需要一個(gè)正在優(yōu)化的目標(biāo)函數(shù)和該目標(biāo)函數(shù)的導(dǎo)數(shù)函數(shù)。目標(biāo)函數(shù)f()返回給定輸入集合的分?jǐn)?shù),導(dǎo)數(shù)函數(shù)f'()給出給定輸入集合的目標(biāo)函數(shù)的導(dǎo)數(shù)。梯度下降算法需要問題中的起點(diǎn)(x),例如輸入空間中的隨機(jī)選擇點(diǎn)。
假設(shè)我們正在最小化目標(biāo)函數(shù),然后計(jì)算導(dǎo)數(shù)并在輸入空間中采取一步,這將導(dǎo)致目標(biāo)函數(shù)下坡運(yùn)動(dòng)。下坡運(yùn)動(dòng)是通過首先計(jì)算輸入空間中的運(yùn)動(dòng)量來進(jìn)行的,計(jì)算方法是將步長(稱為alpha或?qū)W習(xí)率)乘以坡度。然后從當(dāng)前點(diǎn)減去該值,以確保我們逆梯度移動(dòng)或向下移動(dòng)目標(biāo)函數(shù)。
x(t)= x(t-1)–step* f'(x(t-1))
在給定點(diǎn)的目標(biāo)函數(shù)越陡峭,梯度的幅度越大,反過來,在搜索空間中采取的步伐也越大。使用步長超參數(shù)來縮放步長的大小。
步長(alpha):超參數(shù),控制算法每次迭代時(shí)相對于梯度在搜索空間中移動(dòng)多遠(yuǎn)。
如果步長太小,則搜索空間中的移動(dòng)將很小,并且搜索將花費(fèi)很長時(shí)間。如果步長太大,則搜索可能會(huì)在搜索空間附近反彈并跳過最優(yōu)值。
現(xiàn)在我們已經(jīng)熟悉了梯度下降優(yōu)化算法,下面讓我們看一下Adam算法。
Adam優(yōu)化算法
自適應(yīng)運(yùn)動(dòng)估計(jì)算法(簡稱“Adam”)是梯度下降優(yōu)化算法的擴(kuò)展。Diederik Kingma和Jimmy Lei Ba在2014年發(fā)表的題為“Adam:隨機(jī)優(yōu)化方法”的論文中描述了該算法。Adam旨在加速優(yōu)化過程,例如減少達(dá)到最佳狀態(tài)所需的功能評估次數(shù),或提高優(yōu)化算法的功能,例如產(chǎn)生更好的最終結(jié)果。這是通過為每個(gè)要優(yōu)化的輸入?yún)?shù)計(jì)算步長來實(shí)現(xiàn)的。重要的是,每個(gè)步長都將根據(jù)每個(gè)變量遇到的梯度(偏導(dǎo)數(shù))自動(dòng)調(diào)整搜索過程的吞吐量。
讓我們逐步介紹該算法的每個(gè)元素。首先,對于作為搜索一部分而被優(yōu)化的每個(gè)參數(shù),我們必須維持一個(gè)矩矢量和指數(shù)加權(quán)無窮大范數(shù),分別稱為m和v(真是希臘字母nu)。在搜索開始時(shí)將它們初始化為0.0。
m = 0
v = 0
該算法在從t=1開始的時(shí)間t內(nèi)迭代執(zhí)行,并且每次迭代都涉及計(jì)算一組新的參數(shù)值x,例如。從x(t-1)到x(t)。如果我們專注于更新一個(gè)參數(shù),這可能很容易理解該算法,該算法概括為通過矢量運(yùn)算來更新所有參數(shù)。首先,計(jì)算當(dāng)前時(shí)間步長的梯度(偏導(dǎo)數(shù))。
g(t)= f'(x(t-1))
接下來,使用梯度和超參數(shù)beta1更新第一時(shí)刻。
m(t)= beta1 * m(t-1)+(1 – beta1)* g(t)
然后,使用平方梯度和超參數(shù)beta2更新第二時(shí)刻。
v(t)= beta2 * v(t-1)+(1 – beta2)* g(t)^ 2
由于第一和第二力矩是用零值初始化的,所以它們是有偏的。接下來,對第一力矩和第二力矩進(jìn)行偏差校正,并以第一力矩為起點(diǎn):
mhat(t)= m(t)/(1 – beta1(t))
然后第二個(gè)時(shí)刻:
vhat(t)= v(t)/(1 – beta2(t))
注意,beta1(t)和beta2(t)指的是beta1和beta2超參數(shù),它們在算法的迭代過程中按時(shí)間表衰減??梢允褂渺o態(tài)衰減時(shí)間表,盡管該論文建議以下內(nèi)容:
beta1(t)= beta1 ^ t
beta2(t)= beta2 ^ t
最后,我們可以為該迭代計(jì)算參數(shù)的值。
x(t)= x(t-1)– alpha * mhat(t)/(sqrt(vhat(t))+ eps)
其中alpha是步長超參數(shù),eps是一個(gè)較小的值(epsilon),例如1e-8,可確保我們不會(huì)遇到被零除的誤差,而sqrt()是平方根函數(shù)。
注意,可以使用對本文中列出的更新規(guī)則進(jìn)行更有效的重新排序:
alpha(t)= alpha * sqrt(1 – beta2(t))/(1 – beta1(t)) x(t)= x(t-1)– alpha(t)* m(t)/(sqrt(v(t))+ eps)
回顧一下,該算法有三個(gè)超參數(shù),它們是:
- alpha:初始步長(學(xué)習(xí)率),典型值為0.001。
- beta1:第一個(gè)動(dòng)量的衰減因子,典型值為0.9。
- beta2:無窮大范數(shù)的衰減因子,典型值為0.999。
接下來,讓我們看看如何在Python中從頭開始實(shí)現(xiàn)該算法。
Adam梯度下降
在本節(jié)中,我們將探討如何使用Adam實(shí)現(xiàn)梯度下降優(yōu)化算法。
二維測試問題
首先,讓我們定義一個(gè)優(yōu)化函數(shù)。我們將使用一個(gè)簡單的二維函數(shù),該函數(shù)將每個(gè)維的輸入平方,并定義有效輸入的范圍(從-1.0到1.0)。
下面的Objective()函數(shù)實(shí)現(xiàn)了此功能
- # objective function
- def objective(x, y):
- return x**2.0 + y**2.0
我們可以創(chuàng)建數(shù)據(jù)集的三維圖,以了解響應(yīng)面的曲率。下面列出了繪制目標(biāo)函數(shù)的完整示例。
- # 3d plot of the test function
- from numpy import arange
- from numpy import meshgrid
- from matplotlib import pyplot
- # objective function
- def objective(x, y):
- return x**2.0 + y**2.0
- # define range for input
- r_min, r_max = -1.0, 1.0
- # sample input range uniformly at 0.1 increments
- xaxis = arange(r_min, r_max, 0.1)
- yaxis = arange(r_min, r_max, 0.1)
- # create a mesh from the axis
- x, y = meshgrid(xaxis, yaxis)
- # compute targets
- results = objective(x, y)
- # create a surface plot with the jet color scheme
- figure = pyplot.figure()
- axis = figure.gca(projection='3d')
- axis.plot_surface(x, y, results, cmap='jet')
- # show the plot
- pyplot.show()
運(yùn)行示例將創(chuàng)建目標(biāo)函數(shù)的三維表面圖。我們可以看到全局最小值為f(0,0)= 0的熟悉的碗形狀。
我們還可以創(chuàng)建函數(shù)的二維圖。這在以后要繪制搜索進(jìn)度時(shí)會(huì)很有幫助。下面的示例創(chuàng)建目標(biāo)函數(shù)的輪廓圖。
- # contour plot of the test function
- from numpy import asarray
- from numpy import arange
- from numpy import meshgrid
- from matplotlib import pyplot
- # objective function
- def objective(x, y):
- return x**2.0 + y**2.0
- # define range for input
- bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
- # sample input range uniformly at 0.1 increments
- xaxis = arange(bounds[0,0], bounds[0,1], 0.1)
- yaxis = arange(bounds[1,0], bounds[1,1], 0.1)
- # create a mesh from the axis
- x, y = meshgrid(xaxis, yaxis)
- # compute targets
- results = objective(x, y)
- # create a filled contour plot with 50 levels and jet color scheme
- pyplot.contourf(x, y, results, levels=50, cmap='jet')
- # show the plot
- pyplot.show()
運(yùn)行示例將創(chuàng)建目標(biāo)函數(shù)的二維輪廓圖。我們可以看到碗的形狀被壓縮為以顏色漸變顯示的輪廓。我們將使用該圖來繪制在搜索過程中探索的特定點(diǎn)。
現(xiàn)在我們有了一個(gè)測試目標(biāo)函數(shù),讓我們看一下如何實(shí)現(xiàn)Adam優(yōu)化算法。
Adam梯度下降優(yōu)化
我們可以將帶有Adam的梯度下降應(yīng)用于測試問題。首先,我們需要一個(gè)函數(shù)來計(jì)算此函數(shù)的導(dǎo)數(shù)。
f(x)= x ^ 2
f'(x)= x * 2
x ^ 2的導(dǎo)數(shù)在每個(gè)維度上均為x * 2。 derived()函數(shù)在下面實(shí)現(xiàn)了這一點(diǎn)。
- # derivative of objective function
- def derivative(x, y):
- return asarray([x * 2.0, y * 2.0])
接下來,我們可以實(shí)現(xiàn)梯度下降優(yōu)化。首先,我們可以選擇問題范圍內(nèi)的隨機(jī)點(diǎn)作為搜索的起點(diǎn)。假定我們有一個(gè)數(shù)組,該數(shù)組定義搜索范圍,每個(gè)維度一行,并且第一列定義最小值,第二列定義維度的最大值。
- # generate an initial point
- x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
- score = objective(x[0], x[1])
接下來,我們需要將第一時(shí)刻和第二時(shí)刻初始化為零。
- # initialize first and second moments
- m = [0.0 for _ in range(bounds.shape[0])]
- v = [0.0 for _ in range(bounds.shape[0])]
然后,我們運(yùn)行由“ n_iter”超參數(shù)定義的算法的固定迭代次數(shù)。
- ...
- # run iterations of gradient descent
- for t in range(n_iter):
- ...
第一步是使用導(dǎo)數(shù)()函數(shù)計(jì)算當(dāng)前解決方案的梯度。
- # calculate gradient
- gradient = derivative(solution[0], solution[1])
第一步是計(jì)算當(dāng)前參數(shù)集的導(dǎo)數(shù)。
- # calculate gradient g(t)
- g = derivative(x[0], x[1])
接下來,我們需要執(zhí)行Adam更新計(jì)算。為了提高可讀性,我們將使用命令式編程樣式一次執(zhí)行一個(gè)變量的這些計(jì)算。
在實(shí)踐中,我建議使用NumPy向量運(yùn)算以提高效率。
- ...
- # build a solution one variable at a time
- for i in range(x.shape[0]):
- ...
首先,我們需要計(jì)算力矩。
- # m(t) = beta1 * m(t-1) + (1 - beta1) * g(t)
- m[i] = beta1 * m[i] + (1.0 - beta1) * g[i]
然后是第二個(gè)時(shí)刻。
- # v(t) = beta2 * v(t-1) + (1 - beta2) * g(t)^2
- v[i] = beta2 * v[i] + (1.0 - beta2) * g[i]**2
然后對第一和第二時(shí)刻進(jìn)行偏差校正。
- # mhat(t) = m(t) / (1 - beta1(t))
- mmhat = m[i] / (1.0 - beta1**(t+1))
- # vhat(t) = v(t) / (1 - beta2(t))
- vvhat = v[i] / (1.0 - beta2**(t+1))
然后最后是更新的變量值。
- # x(t) = x(t-1) - alpha * mhat(t) / (sqrt(vhat(t)) + eps)
- x[i] = x[i] - alpha * mhat / (sqrt(vhat) + eps)
然后,針對要優(yōu)化的每個(gè)參數(shù)重復(fù)此操作。在迭代結(jié)束時(shí),我們可以評估新的參數(shù)值并報(bào)告搜索的性能。
- # evaluate candidate point
- score = objective(x[0], x[1])
- # report progress
- print('>%d f(%s) = %.5f' % (t, x, score))
我們可以將所有這些結(jié)合到一個(gè)名為adam()的函數(shù)中,該函數(shù)采用目標(biāo)函數(shù)和派生函數(shù)的名稱以及算法超參數(shù),并返回在搜索及其評估結(jié)束時(shí)找到的最佳解決方案。
下面列出了完整的功能。
- # gradient descent algorithm with adam
- def adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2, eps=1e-8):
- # generate an initial point
- x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
- score = objective(x[0], x[1])
- # initialize first and second moments
- m = [0.0 for _ in range(bounds.shape[0])]
- v = [0.0 for _ in range(bounds.shape[0])]
- # run the gradient descent updates
- for t in range(n_iter):
- # calculate gradient g(t)
- g = derivative(x[0], x[1])
- # build a solution one variable at a time
- for i in range(x.shape[0]):
- # m(t) = beta1 * m(t-1) + (1 - beta1) * g(t)
- m[i] = beta1 * m[i] + (1.0 - beta1) * g[i]
- # v(t) = beta2 * v(t-1) + (1 - beta2) * g(t)^2
- v[i] = beta2 * v[i] + (1.0 - beta2) * g[i]**2
- # mhat(t) = m(t) / (1 - beta1(t))
- mmhat = m[i] / (1.0 - beta1**(t+1))
- # vhat(t) = v(t) / (1 - beta2(t))
- vvhat = v[i] / (1.0 - beta2**(t+1))
- # x(t) = x(t-1) - alpha * mhat(t) / (sqrt(vhat(t)) + eps)
- x[i] = x[i] - alpha * mhat / (sqrt(vhat) + eps)
- # evaluate candidate point
- score = objective(x[0], x[1])
- # report progress
- print('>%d f(%s) = %.5f' % (t, x, score))
- return [x, score]
注意:為了提高可讀性,我們有意使用列表和命令式編碼樣式,而不是矢量化操作。隨意將實(shí)現(xiàn)改編為帶有NumPy數(shù)組的矢量化實(shí)現(xiàn),以實(shí)現(xiàn)更好的性能。
然后,我們可以定義我們的超參數(shù)并調(diào)用adam()函數(shù)來優(yōu)化我們的測試目標(biāo)函數(shù)。
在這種情況下,我們將使用算法的60次迭代,初始步長為0.02,beta1和beta2值分別為0.8和0.999。經(jīng)過一些反復(fù)試驗(yàn)后,發(fā)現(xiàn)了這些超參數(shù)值。
- # seed the pseudo random number generator
- seed(1)
- # define range for input
- bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
- # define the total iterations
- n_iter = 60
- # steps size
- alpha = 0.02
- # factor for average gradient
- beta1 = 0.8
- # factor for average squared gradient
- beta2 = 0.999
- # perform the gradient descent search with adam
- best, score = adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2)
- print('Done!')
- print('f(%s) = %f' % (best, score))
綜合所有這些,下面列出了使用Adam進(jìn)行梯度下降優(yōu)化的完整示例。
- # gradient descent optimization with adam for a two-dimensional test function
- from math import sqrt
- from numpy import asarray
- from numpy.random import rand
- from numpy.random import seed
- # objective function
- def objective(x, y):
- return x**2.0 + y**2.0
- # derivative of objective function
- def derivative(x, y):
- return asarray([x * 2.0, y * 2.0])
- # gradient descent algorithm with adam
- def adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2, eps=1e-8):
- # generate an initial point
- x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
- score = objective(x[0], x[1])
- # initialize first and second moments
- m = [0.0 for _ in range(bounds.shape[0])]
- v = [0.0 for _ in range(bounds.shape[0])]
- # run the gradient descent updates
- for t in range(n_iter):
- # calculate gradient g(t)
- g = derivative(x[0], x[1])
- # build a solution one variable at a time
- for i in range(x.shape[0]):
- # m(t) = beta1 * m(t-1) + (1 - beta1) * g(t)
- m[i] = beta1 * m[i] + (1.0 - beta1) * g[i]
- # v(t) = beta2 * v(t-1) + (1 - beta2) * g(t)^2
- v[i] = beta2 * v[i] + (1.0 - beta2) * g[i]**2
- # mhat(t) = m(t) / (1 - beta1(t))
- mmhat = m[i] / (1.0 - beta1**(t+1))
- # vhat(t) = v(t) / (1 - beta2(t))
- vvhat = v[i] / (1.0 - beta2**(t+1))
- # x(t) = x(t-1) - alpha * mhat(t) / (sqrt(vhat(t)) + eps)
- x[i] = x[i] - alpha * mhat / (sqrt(vhat) + eps)
- # evaluate candidate point
- score = objective(x[0], x[1])
- # report progress
- print('>%d f(%s) = %.5f' % (t, x, score))
- return [x, score]
- # seed the pseudo random number generator
- seed(1)
- # define range for input
- bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
- # define the total iterations
- n_iter = 60
- # steps size
- alpha = 0.02
- # factor for average gradient
- beta1 = 0.8
- # factor for average squared gradient
- beta2 = 0.999
- # perform the gradient descent search with adam
- best, score = adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2)
- print('Done!')
- print('f(%s) = %f' % (best, score))
運(yùn)行示例將Adam優(yōu)化算法應(yīng)用于我們的測試問題,并報(bào)告算法每次迭代的搜索性能。
注意:由于算法或評估程序的隨機(jī)性,或者數(shù)值精度的差異,您的結(jié)果可能會(huì)有所不同。考慮運(yùn)行該示例幾次并比較平均結(jié)果。
在這種情況下,我們可以看到在搜索53次迭代后找到了接近最佳的解決方案,輸入值接近0.0和0.0,評估為0.0。
- >50 f([-0.00056912 -0.00321961]) = 0.00001
- >51 f([-0.00052452 -0.00286514]) = 0.00001
- >52 f([-0.00043908 -0.00251304]) = 0.00001
- >53 f([-0.0003283 -0.00217044]) = 0.00000
- >54 f([-0.00020731 -0.00184302]) = 0.00000
- >55 f([-8.95352320e-05 -1.53514076e-03]) = 0.00000
- >56 f([ 1.43050285e-05 -1.25002847e-03]) = 0.00000
- >57 f([ 9.67123406e-05 -9.89850279e-04]) = 0.00000
- >58 f([ 0.00015359 -0.00075587]) = 0.00000
- >59 f([ 0.00018407 -0.00054858]) = 0.00000
- Done!
- f([ 0.00018407 -0.00054858]) = 0.000000
Adam可視化
我們可以在域的輪廓圖上繪制Adam搜索的進(jìn)度。這可以為算法迭代過程中的搜索進(jìn)度提供直觀的認(rèn)識(shí)。我們必須更新adam()函數(shù)以維護(hù)在搜索過程中找到的所有解決方案的列表,然后在搜索結(jié)束時(shí)返回此列表。下面列出了具有這些更改的功能的更新版本。
- # gradient descent algorithm with adam
- def adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2, eps=1e-8):
- solutions = list()
- # generate an initial point
- x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
- score = objective(x[0], x[1])
- # initialize first and second moments
- m = [0.0 for _ in range(bounds.shape[0])]
- v = [0.0 for _ in range(bounds.shape[0])]
- # run the gradient descent updates
- for t in range(n_iter):
- # calculate gradient g(t)
- g = derivative(x[0], x[1])
- # build a solution one variable at a time
- for i in range(bounds.shape[0]):
- # m(t) = beta1 * m(t-1) + (1 - beta1) * g(t)
- m[i] = beta1 * m[i] + (1.0 - beta1) * g[i]
- # v(t) = beta2 * v(t-1) + (1 - beta2) * g(t)^2
- v[i] = beta2 * v[i] + (1.0 - beta2) * g[i]**2
- # mhat(t) = m(t) / (1 - beta1(t))
- mmhat = m[i] / (1.0 - beta1**(t+1))
- # vhat(t) = v(t) / (1 - beta2(t))
- vvhat = v[i] / (1.0 - beta2**(t+1))
- # x(t) = x(t-1) - alpha * mhat(t) / (sqrt(vhat(t)) + ep)
- x[i] = x[i] - alpha * mhat / (sqrt(vhat) + eps)
- # evaluate candidate point
- score = objective(x[0], x[1])
- # keep track of solutions
- solutions.append(x.copy())
- # report progress
- print('>%d f(%s) = %.5f' % (t, x, score))
- return solutions
然后,我們可以像以前一樣執(zhí)行搜索,這一次將檢索解決方案列表,而不是最佳的最終解決方案。
- # seed the pseudo random number generator
- seed(1)
- # define range for input
- bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
- # define the total iterations
- n_iter = 60
- # steps size
- alpha = 0.02
- # factor for average gradient
- beta1 = 0.8
- # factor for average squared gradient
- beta2 = 0.999
- # perform the gradient descent search with adam
- solutions = adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2)
然后,我們可以像以前一樣創(chuàng)建目標(biāo)函數(shù)的輪廓圖。
- # sample input range uniformly at 0.1 increments
- xaxis = arange(bounds[0,0], bounds[0,1], 0.1)
- yaxis = arange(bounds[1,0], bounds[1,1], 0.1)
- # create a mesh from the axis
- x, y = meshgrid(xaxis, yaxis)
- # compute targets
- results = objective(x, y)
- # create a filled contour plot with 50 levels and jet color scheme
- pyplot.contourf(x, y, results, levels=50, cmap='jet')
最后,我們可以將在搜索過程中找到的每個(gè)解決方案繪制成一條由一條線連接的白點(diǎn)。
- # plot the sample as black circles
- solutions = asarray(solutions)
- pyplot.plot(solutions[:, 0], solutions[:, 1], '.-', color='w')
綜上所述,下面列出了對測試問題執(zhí)行Adam優(yōu)化并將結(jié)果繪制在輪廓圖上的完整示例。
- # example of plotting the adam search on a contour plot of the test function
- from math import sqrt
- from numpy import asarray
- from numpy import arange
- from numpy.random import rand
- from numpy.random import seed
- from numpy import meshgrid
- from matplotlib import pyplot
- from mpl_toolkits.mplot3d import Axes3D
- # objective function
- def objective(x, y):
- return x**2.0 + y**2.0
- # derivative of objective function
- def derivative(x, y):
- return asarray([x * 2.0, y * 2.0])
- # gradient descent algorithm with adam
- def adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2, eps=1e-8):
- solutions = list()
- # generate an initial point
- x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
- score = objective(x[0], x[1])
- # initialize first and second moments
- m = [0.0 for _ in range(bounds.shape[0])]
- v = [0.0 for _ in range(bounds.shape[0])]
- # run the gradient descent updates
- for t in range(n_iter):
- # calculate gradient g(t)
- g = derivative(x[0], x[1])
- # build a solution one variable at a time
- for i in range(bounds.shape[0]):
- # m(t) = beta1 * m(t-1) + (1 - beta1) * g(t)
- m[i] = beta1 * m[i] + (1.0 - beta1) * g[i]
- # v(t) = beta2 * v(t-1) + (1 - beta2) * g(t)^2
- v[i] = beta2 * v[i] + (1.0 - beta2) * g[i]**2
- # mhat(t) = m(t) / (1 - beta1(t))
- mmhat = m[i] / (1.0 - beta1**(t+1))
- # vhat(t) = v(t) / (1 - beta2(t))
- vvhat = v[i] / (1.0 - beta2**(t+1))
- # x(t) = x(t-1) - alpha * mhat(t) / (sqrt(vhat(t)) + ep)
- x[i] = x[i] - alpha * mhat / (sqrt(vhat) + eps)
- # evaluate candidate point
- score = objective(x[0], x[1])
- # keep track of solutions
- solutions.append(x.copy())
- # report progress
- print('>%d f(%s) = %.5f' % (t, x, score))
- return solutions
- # seed the pseudo random number generator
- seed(1)
- # define range for input
- bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
- # define the total iterations
- n_iter = 60
- # steps size
- alpha = 0.02
- # factor for average gradient
- beta1 = 0.8
- # factor for average squared gradient
- beta2 = 0.999
- # perform the gradient descent search with adam
- solutions = adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2)
- # sample input range uniformly at 0.1 increments
- xaxis = arange(bounds[0,0], bounds[0,1], 0.1)
- yaxis = arange(bounds[1,0], bounds[1,1], 0.1)
- # create a mesh from the axis
- x, y = meshgrid(xaxis, yaxis)
- # compute targets
- results = objective(x, y)
- # create a filled contour plot with 50 levels and jet color scheme
- pyplot.contourf(x, y, results, levels=50, cmap='jet')
- # plot the sample as black circles
- solutions = asarray(solutions)
- pyplot.plot(solutions[:, 0], solutions[:, 1], '.-', color='w')
- # show the plot
- pyplot.show()
運(yùn)行示例將像以前一樣執(zhí)行搜索,但是在這種情況下,將創(chuàng)建目標(biāo)函數(shù)的輪廓圖。
在這種情況下,我們可以看到在搜索過程中找到的每個(gè)解決方案都顯示一個(gè)白點(diǎn),從最優(yōu)點(diǎn)開始,逐漸靠近圖中心的最優(yōu)點(diǎn)。




























