梯度下降是非常常用的優(yōu)化算法。作為機(jī)器學(xué)習(xí)的基礎(chǔ)知識(shí),這是一個(gè)必須要掌握的算法。借助本文,讓我們來一起詳細(xì)了解一下這個(gè)算法。
創(chuàng)新互聯(lián)是專業(yè)的四方臺(tái)網(wǎng)站建設(shè)公司,四方臺(tái)接單;提供成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、外貿(mào)網(wǎng)站建設(shè),網(wǎng)頁設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行四方臺(tái)網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!
前言
本文的代碼可以到我的Github上獲?。?/p>
本文的算法示例通過Python語言實(shí)現(xiàn),在實(shí)現(xiàn)中使用到了numpy和matplotlib。如果你不熟悉這兩個(gè)工具,請自行在網(wǎng)上搜索教程。
關(guān)于優(yōu)化
大多數(shù)學(xué)習(xí)算法都涉及某種形式的優(yōu)化。優(yōu)化指的是改變x以最小化或者最大化某個(gè)函數(shù)的任務(wù)。
我們通常以最小化指代大多數(shù)最優(yōu)化問題。最大化可經(jīng)由最小化來實(shí)現(xiàn)。
我們把要最小化或最大化的函數(shù)成為目標(biāo)函數(shù)(objective function)或準(zhǔn)則(criterion)。
我們通常使用一個(gè)上標(biāo)*表示最小化或最大化函數(shù)的x值,記做這樣:
[x^* = arg; min; f(x)]
優(yōu)化本身是一個(gè)非常大的話題。如果有興趣,可以通過《數(shù)值優(yōu)化》和《運(yùn)籌學(xué)》的書籍進(jìn)行學(xué)習(xí)。
模型與假設(shè)函數(shù)
所有的模型都是錯(cuò)誤的,但其中有些是有用的。– George Edward Pelham Box
模型是我們對要分析的數(shù)據(jù)的一種假設(shè),它是為解決某個(gè)具體問題從數(shù)據(jù)中學(xué)習(xí)到的,因此它是機(jī)器學(xué)習(xí)最核心的概念。
針對一個(gè)問題,通常有大量的模型可以選擇。
本文不會(huì)深入討論這方面的內(nèi)容,關(guān)于各種模型請參閱機(jī)器學(xué)習(xí)的相關(guān)書籍。本文僅以最簡單的線性模型為基礎(chǔ)來討論梯度下降算法。
這里我們先介紹一下在監(jiān)督學(xué)習(xí)(supervised learning)中常見的三個(gè)符號:
m,描述訓(xùn)練樣本的數(shù)量
x,描述輸入變量或特征
y,描述輸出變量或者叫目標(biāo)值
請注意,一個(gè)樣本可能有很多的特征,因此x和y通常是一個(gè)向量。不過在剛開始學(xué)習(xí)的時(shí)候,為了便于理解,你可以暫時(shí)理解為這就是一個(gè)具體的數(shù)值。
訓(xùn)練集會(huì)包含很多的樣本,我們用 表示其中第i個(gè)樣本。
x是數(shù)據(jù)樣本的特征,y是其目標(biāo)值。例如,在預(yù)測房價(jià)的模型中,x是房子的各種信息,例如:面積,樓層,位置等等,y是房子的價(jià)格。在圖像識(shí)別的任務(wù)中,x是圖形的所有像素點(diǎn)數(shù)據(jù),y是圖像中包含的目標(biāo)對象。
我們是希望尋找一個(gè)函數(shù),將x映射到y(tǒng),這個(gè)函數(shù)要足夠的好,以至于能夠預(yù)測對應(yīng)的y。由于歷史原因,這個(gè)函數(shù)叫做假設(shè)函數(shù)(hypothesis function)。
學(xué)習(xí)的過程如下圖所示。即:首先根據(jù)已有的數(shù)據(jù)(稱之為訓(xùn)練集)訓(xùn)練我們的算法模型,然后根據(jù)模型的假設(shè)函數(shù)來進(jìn)行新數(shù)據(jù)的預(yù)測。
線性模型(linear model)正如其名稱那樣:是希望通過一個(gè)直線的形式來描述模式。線性模型的假設(shè)函數(shù)如下所示:
[h_{\theta}(x) = \theta_{0} + \theta_{1} * x]
這個(gè)公式對于大家來說應(yīng)該都是非常簡單的。如果把它繪制出來,其實(shí)就是一條直線。
下圖是一個(gè)具體的例子,即: 的圖形:
在實(shí)際的機(jī)器學(xué)習(xí)工程中,你會(huì)擁有大量的數(shù)據(jù)。這些數(shù)據(jù)會(huì)來自于某個(gè)數(shù)據(jù)源。它們存儲(chǔ)在csv文件中,或者以其他的形式打包。
但是本文作為演示使用,我們通過一些簡單的代碼自動(dòng)生成了需要的數(shù)據(jù)。為了便于計(jì)算,演示的數(shù)據(jù)量也很小。
import numpy as np
max_x = 10
data_size = 10
theta_0 = 5
theta_1 = 2
def get_data:
x = np.linspace(1, max_x, data_size)
noise = np.random.normal(0, 0.2, len(x))
y = theta_0 + theta_1 * x + noise
return x, y
這段代碼很簡單,我們生成了x范圍是 [1, 10] 整數(shù)的10條數(shù)據(jù)。對應(yīng)的y是以線性模型的形式計(jì)算得到,其函數(shù)是:?,F(xiàn)實(shí)中的數(shù)據(jù)常常受到各種因素的干擾,所以對于y我們故意加上了一些高斯噪聲。因此最終的y值為比原先會(huì)有輕微的偏離。
最后我們的數(shù)據(jù)如下所示:
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
y = [6.66, 9.11, 11.08, 12.67, 15.12, 16.76, 18.75, 21.35, 22.77, 24.56]
我們可以把這10條數(shù)據(jù)繪制出來這樣就有一個(gè)直觀的了解了,如下圖所示:
雖然演示用的數(shù)據(jù)是我們通過公式計(jì)算得到的。但在實(shí)際的工程中,模型的參數(shù)是需要我們通過數(shù)據(jù)學(xué)習(xí)到的。所以下文我們假設(shè)我們不知道這里線性模式的兩個(gè)參數(shù)是什么,而是通過算法的形式求得。
最后再跟已知的參數(shù)進(jìn)行對比以驗(yàn)證我們的算法是否正確。
有了上面的數(shù)據(jù),我們可以嘗試畫一條直線來描述我們的模型。
例如,像下面這樣畫一條水平的直線:
很顯然,這條水平線離數(shù)據(jù)太遠(yuǎn)了,非常的不匹配。
那我們可以再畫一條斜線。
我們初次畫的斜線可能也不貼切,它可能像下面這樣:
最后我們通過不斷嘗試,找到了最終最合適的那條,如下所示:
梯度下降算法的計(jì)算過程,就和這種本能式的試探是類似的,它就是不停的迭代,一步步的接近最終的結(jié)果。
代價(jià)函數(shù)
上面我們嘗試了幾次通過一條直線來擬合(fitting)已有的數(shù)據(jù)。
二維平面上的一條直線可以通過兩個(gè)參數(shù)唯一的確定,兩個(gè)參數(shù)的確定也即模型的確定。那如何描述模型與數(shù)據(jù)的擬合程度呢?答案就是代價(jià)函數(shù)。
代價(jià)函數(shù)(cost function)描述了學(xué)習(xí)到的模型與實(shí)際結(jié)果的偏差程度。以上面的三幅圖為例,最后一幅圖中的紅線相比第一條水平的綠線,其偏離程度(代價(jià))應(yīng)該是更小的。
很顯然,我們希望我們的假設(shè)函數(shù)與數(shù)據(jù)盡可能的貼近,也就是說:希望代價(jià)函數(shù)的結(jié)果盡可能的小。這就涉及到結(jié)果的優(yōu)化,而梯度下降就是尋找最小值的方法之一。
代價(jià)函數(shù)也叫損失函數(shù)。
對于每一個(gè)樣本,假設(shè)函數(shù)會(huì)依據(jù)計(jì)算出一個(gè)估算值,我們常常用來表示。即 。
很自然的,我們會(huì)想到,通過下面這個(gè)公式來描述我們的模型與實(shí)際值的偏差程度:
[(h_\theta(x^i) - y^i)^2 = (\widehat{y}^{i} - y^i)^2 = (\theta_{0} + \theta_{1} * x^{i} - y^{i})^2]
請注意, 是實(shí)際數(shù)據(jù)的值, 是我們的模型的估算值。前者對應(yīng)了上圖中的離散點(diǎn)的y坐標(biāo),后者對應(yīng)了離散點(diǎn)在直線上投影點(diǎn)的y坐標(biāo)。
每一條數(shù)據(jù)都會(huì)存在一個(gè)偏差值,而代價(jià)函數(shù)就是對所有樣本的偏差求平均值,其計(jì)算公式如下所示:
[L(\theta) = \frac {1}{m} \sum_{i=1}^{m}(h_\theta(x^i) - y^i)^2 = \frac {1}{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} * x^{i} - y^{i})^2]
當(dāng)損失函數(shù)的結(jié)果越小,則意味著通過我們的假設(shè)函數(shù)估算出的結(jié)果與真實(shí)值越接近。這也就是為什么我們要最小化損失函數(shù)的原因。
不同的模型可能會(huì)用不同的損失函數(shù)。例如,logistic回歸的假設(shè)函數(shù)是這樣的:。其代價(jià)函數(shù)是這樣的:
借助上面這個(gè)公式,我們可以寫一個(gè)函數(shù)來實(shí)現(xiàn)代價(jià)函數(shù):
def cost_function(x, y, t0, t1):
cost_sum = 0
for i in range(len(x)):
cost_item = np.power(t0 + t1 * x[i] - y[i], 2)
cost_sum += cost_item
return cost_sum / len(x)
這個(gè)函數(shù)的代碼應(yīng)該不用多做解釋,它就是根據(jù)上面的完成計(jì)算。
我們可以嘗試選取不同的 和 組合來計(jì)算代價(jià)函數(shù)的值,然后將結(jié)果繪制出來:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
theta_0 = 5
theta_1 = 2
def draw_cost(x, y):
fig = plt.figure(figsize=(10, 8))
ax = fig.gca(projection='3d')
scatter_count = 100
radius = 1
t0_range = np.linspace(theta_0 - radius, theta_0 + radius, scatter_count)
t1_range = np.linspace(theta_1 - radius, theta_1 + radius, scatter_count)
cost = np.zeros((len(t0_range), len(t1_range)))
for a in range(len(t0_range)):
for b in range(len(t1_range)):
cost[a][b] = cost_function(x, y, t0_range[a], t1_range[b])
t0, t1 = np.meshgrid(t0_range, t1_range)
ax.set_xlabel('theta_0')
ax.set_ylabel('theta_1')
ax.plot_surface(t0, t1, cost, cmap=cm.hsv)
在這段代碼中,我們對 和 各自指定了一個(gè)范圍進(jìn)行100次的采樣,然后以不同的 組合對來計(jì)算代價(jià)函數(shù)的值。
如果我們將所有點(diǎn)的代價(jià)函數(shù)值繪制出來,其結(jié)果如下圖所示:
從這個(gè)圖形中我們可以看出,當(dāng) 越接近 [5, 2]時(shí)其結(jié)果(偏差)越小。相反,離得越遠(yuǎn),結(jié)果越大。
直觀解釋
從上面這幅圖中我們可以看出,代價(jià)函數(shù)在不同的位置結(jié)果大小不同。
從三維的角度來看,這就和地面的高低起伏一樣。最高的地方就好像是山頂。
而我們的目標(biāo)就是:從任意一點(diǎn)作為起點(diǎn),能夠快速尋找到一條路徑并以此到達(dá)圖形最低點(diǎn)(代價(jià)值最?。┑奈恢谩?/p>
而梯度下降的算法過程就和我們從山頂想要快速下山的做法是一樣的。
在生活中,我們很自然會(huì)想到沿著最陡峭的路往下行是下山速度最快的。如下面這幅圖所示:
針對這幅圖,細(xì)心的讀者可能很快就會(huì)有很多的疑問,例如:
對于一個(gè)函數(shù),怎么確定下行的方向?
每一步該往前走多遠(yuǎn)?
有沒有可能停留在半山腰的平臺(tái)上?
這些問題也就是本文接下來要討論的內(nèi)容。
算法描述
梯度下降算法最開始的一點(diǎn)就是需要確定下降的方向,即:梯度。
我們常常用 來表示梯度。
對于一個(gè)二維空間的曲線來說,梯度就是其切線的方向。如下圖所示:
而對于更高維空間的函數(shù)來說,梯度由所有變量的偏導(dǎo)數(shù)決定。
其表達(dá)式如下所示:
[\nabla f({\theta}) = ( \frac{\partial f({\theta})}{\partial \theta_1} , \frac{\partial f({\theta})}{\partial \theta_2} , ... , \frac{\partial f({\theta})}{\partial \theta_n} )]
在機(jī)器學(xué)習(xí)中,我們主要是用梯度下降算法來最小化代價(jià)函數(shù),記做:
[\theta ^* = arg min L(\theta)]
其中,L是代價(jià)函數(shù),是參數(shù)。
梯度下降算法的主體邏輯很簡單,就是沿著梯度的方向一直下降,直到參數(shù)收斂為止。
記做:
[\theta ^{k + 1}_i = \theta^{k}_i - \lambda \nabla f(\theta^{k})]
這里的下標(biāo)i表示第i個(gè)參數(shù)。 上標(biāo)k指的是第k步的計(jì)算結(jié)果,而非k次方。在能夠理解的基礎(chǔ)上,下文的公式中將省略上標(biāo)k。
這里有幾點(diǎn)需要說明:
收斂是指函數(shù)的變化率很小。具體選擇多少合適需要根據(jù)具體的項(xiàng)目來確定。在演示項(xiàng)目中我們可以選擇0.01或者0.001這樣的值。不同的值將影響算法的迭代次數(shù),因?yàn)樵谔荻认陆档淖詈?,我們?huì)越來越接近平坦的地方,這個(gè)時(shí)候函數(shù)的變化率也越來越小。如果選擇一個(gè)很小的值,將可能導(dǎo)致算法迭代次數(shù)暴增。
公式中的 稱作步長,也稱作學(xué)習(xí)率(learning rate)。它決定了每一步往前走多遠(yuǎn),關(guān)于這個(gè)值我們會(huì)在下文中詳細(xì)講解。你可以暫時(shí)人為它是一個(gè)類似0.01或0.001的固定值。
在具體的項(xiàng)目,我們不會(huì)讓算法無休止的運(yùn)行下去,所以通常會(huì)設(shè)置一個(gè)迭代次數(shù)的最大上限。
線性回歸的梯度下降
有了上面的知識(shí),我們可以回到線性模型代價(jià)函數(shù)的梯度下降算法實(shí)現(xiàn)了。
首先,根據(jù)代價(jià)函數(shù)我們可以得到梯度向量如下:
[\nabla f({\theta}) = (\frac{\partial L(\theta)}{ \partial\theta_{0}}, \frac{ \partial L(\theta)}{ \partial\theta_{1}}) = (\frac {2}{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} * x^{i} - y^{i}) , \frac {2}{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} * x^{i} - y^{i}) x^{i})]
接著,將每個(gè)偏導(dǎo)數(shù)帶入迭代的公式中,得到:
[\theta_{0} := \theta_{0} - \lambda \frac{\partial L(\theta_{0})}{ \partial\theta_{0}} = \theta_{0} - \frac {2 \lambda }{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} * x^{i} - y^{i}) \ \theta_{1} := \theta_{1} - \lambda \frac{\partial L(\theta_{1})}{ \partial\theta_{1}} = \theta_{1} - \frac {2 \lambda }{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} * x^{i} - y^{i}) x^{i}]
由此就可以通過代碼實(shí)現(xiàn)我們的梯度下降算法了,算法邏輯并不復(fù)雜:
learning_rate = 0.01
def gradient_descent(x, y):
t0 = 10
t1 = 10
delta = 0.001
for times in range(1000):
sum1 = 0
sum2 = 0
for i in range(len(x)):
sum1 += (t0 + t1 * x[i] - y[i])
sum2 += (t0 + t1 * x[i] - y[i]) * x[i]
t0_ = t0 - 2 * learning_rate * sum1 / len(x)
t1_ = t1 - 2 * learning_rate * sum2 / len(x)
print('Times: {}, gradient: [{}, {}]'.format(times, t0_, t1_))
if (abs(t0 - t0_) delta and abs(t1 - t1_) delta):
print('Gradient descent finish')
return t0_, t1_
t0 = t0_
t1 = t1_
print('Gradient descent too many times')
return t0, t1
這段代碼說明如下:
我們隨機(jī)選擇了 都為10作為起點(diǎn)
設(shè)置最多迭代1000次
收斂的范圍設(shè)為0.001
學(xué)習(xí)步長設(shè)為0.01
如果我們將算法迭代過程中求得的線性模式繪制出來,可以得到下面這幅動(dòng)態(tài)圖:
最后算法得到的結(jié)果如下:
Times: 657, gradient: [5.196562662718697, 1.952931052920264]
Times: 658, gradient: [5.195558390180733, 1.9530753071808193]
Times: 659, gradient: [5.194558335124868, 1.9532189556399233]
Times: 660, gradient: [5.193562479839619, 1.9533620008416623]
Gradient descent finish
從輸出中可以看出,算法迭代了660次就收斂了。這時(shí)的結(jié)果[5.193562479839619, 1.9533620008416623],這已經(jīng)比較接近目標(biāo)值 [5, 2]了。如果需要更高的精度,可以將delta的值調(diào)的更小,當(dāng)然,此時(shí)會(huì)需要更多的迭代次數(shù)。
高維擴(kuò)展
雖然我們舉的例子是二維的,但是對于更高維的情況也是類似的。同樣是根據(jù)迭代的公式進(jìn)行運(yùn)算即可:
[\theta_{i} = \theta_{i} - \lambda \frac {\partial L(\theta)}{\partial \theta_i} = \theta_{i} - \frac{2\lambda}{m} \sum_{i=1}^{m}(h_\theta(x^{k})-y^k)x_i^k]
這里的下標(biāo)i表示第i個(gè)參數(shù),上標(biāo)k表示第k個(gè)數(shù)據(jù)。
梯度下降家族BGD
在上面的內(nèi)容中我們看到,算法的每一次迭代都需要把所有樣本進(jìn)行遍歷處理。這種做法稱為之Batch Gradient Descent,簡稱BGD。作為演示示例只有10條數(shù)據(jù),這是沒有問題的。
但在實(shí)際的項(xiàng)目中,數(shù)據(jù)集的數(shù)量可能是幾百萬幾千萬條,這時(shí)候每一步迭代的計(jì)算量就會(huì)非常的大了。
于是就有了下面兩個(gè)變種。
SGD
Stochastic Gradient Descent,簡稱SGD,這種算法是每次從樣本集中僅僅選擇一個(gè)樣本來進(jìn)行計(jì)算。很顯然,這樣做算法在每一步的計(jì)算量一下就少了很多。
其算法公式如下:
[\theta_{i} = \theta_{i} - \lambda \frac {\partial L(\theta)}{\partial \theta_i} = \theta_{i} - \lambda(h_\theta(x^k)-y^k)x_i^k]
當(dāng)然,減少算法計(jì)算量也是有代價(jià)的,那就是:算法結(jié)果會(huì)強(qiáng)依賴于隨機(jī)取到的數(shù)據(jù)情況,這可能會(huì)導(dǎo)致算法的最終結(jié)果不太令人滿意。
MBGD
以上兩種做法其實(shí)是兩個(gè)極端,一個(gè)是每次用到了所有數(shù)據(jù),另一個(gè)是每次只用一個(gè)數(shù)據(jù)。
我們自然就會(huì)想到兩者取其中的方法:每次選擇一小部分?jǐn)?shù)據(jù)進(jìn)行迭代。這樣既避免了數(shù)據(jù)集過大導(dǎo)致每次迭代計(jì)算量過大的問題,也避免了單個(gè)數(shù)據(jù)對算法的影響。
這種算法稱之為Mini-batch Gradient Descent,簡稱MBGD。
其算法公式如下:
[\theta_{i} = \theta_{i} - \lambda \frac {\partial L(\theta)}{\partial \theta_i} = \theta_{i} - \frac{2\lambda}{m} \sum_{i=a}^{a + b}(h_\theta(x^k)-y^k)x_i^k]
當(dāng)然,我們可以認(rèn)為SGD是Mini-batch為1的特例。
針對上面提到的算法變種,該如何選擇呢?
下面是Andrew Ng給出的建議:
如果樣本數(shù)量較?。ɡ缧∮诘扔?000),選擇BGD即可。
如果樣本數(shù)量很大,選擇 來進(jìn)行MBGD,例如:64,128,256,512。
下表是 Optimization for Deep Learning 中對三種算法的對比
方法準(zhǔn)確性更新速度內(nèi)存占用在線學(xué)習(xí)BGD好慢高否SGD好(with annealing)快低是MBGD好中等中等是
算法優(yōu)化
式7是算法的基本形式,在這個(gè)基礎(chǔ)上有很多人進(jìn)行了更多的研究。接下來我們介紹幾種梯度下降算法的優(yōu)化方法。
Momentum
Momentum是動(dòng)量的意思。這個(gè)算法的思想就是借助了動(dòng)力學(xué)的模型:每次算法的迭代會(huì)使用到上一次的速度作為依據(jù)。
算法的公式如下:
[v^t = \gamma v^{t - 1} + \lambda \nabla f(\theta) \ \theta = \theta - v_t]
對比式7可以看出,這個(gè)算法的主要區(qū)別就是引入了,并且,每個(gè)時(shí)刻的受前一個(gè)時(shí)刻的影響。
從形式上看,動(dòng)量算法引入了變量 v 充當(dāng)速度角色——它代表參數(shù)在參數(shù)空間移動(dòng)的方向和速率。速度被設(shè)為負(fù)梯度的指數(shù)衰減平均。名稱動(dòng)量來自物理類比,根據(jù)牛頓運(yùn)動(dòng)定律,負(fù)梯度是移動(dòng)參數(shù)空間中粒子的力。動(dòng)量在物理學(xué)上定義為質(zhì)量乘以速度。在動(dòng)量學(xué)習(xí)算法中,我們假設(shè)是單位質(zhì)量,因此速度向量 v 也可以看作是粒子的動(dòng)量。
對于可以取值0,而是一個(gè)常量,設(shè)為0.9是一個(gè)比較好的選擇。
下圖是momentum算法的效果對比:
對原來的算法稍加修改就可以增加動(dòng)量效果:
def gradient_descent_with_momentum(x, y):
t0 = 10
t1 = 10
delta = 0.001
v0 = 0
v1 = 0
gamma = 0.9
for times in range(1000):
sum1 = 0
sum2 = 0
for i in range(len(x)):
sum1 += (t0 + t1 * x[i] - y[i])
sum2 += (t0 + t1 * x[i] - y[i]) * x[i]
v0 = gamma * v0 + 2 * learning_rate * sum1 / len(x)
v1 = gamma * v1 + 2 * learning_rate * sum2 / len(x)
t0_ = t0 - v0
t1_ = t1 - v1
print('Times: {}, gradient: [{}, {}]'.format(times, t0_, t1_))
if (abs(t0 - t0_) delta and abs(t1 - t1_) delta):
print('Gradient descent finish')
return t0_, t1_
t0 = t0_
t1 = t1_
print('Gradient descent too many times')
return t0, t1
以下是該算法的輸出:
Times: 125, gradient: [4.955453758569991, 2.000005017897775]
Times: 126, gradient: [4.955309381126545, 1.9956928964532015]
Times: 127, gradient: [4.9542964317327005, 1.9855674828684156]
Times: 128, gradient: [4.9536358220657, 1.9781180992510465]
Times: 129, gradient: [4.95412496254411, 1.9788858350530971]
Gradient descent finish
從結(jié)果可以看出,改進(jìn)的算法只用了129次迭代就收斂了。速度比原來660次快了很多。
同樣的,我們可以把算法計(jì)算的過程做成動(dòng)態(tài)圖:
對比原始的算法過程可以看出,改進(jìn)算法最大的區(qū)別是:在尋找目標(biāo)值時(shí)會(huì)在最終結(jié)果上下跳動(dòng),但是越往后跳動(dòng)的幅度越小,這也就是動(dòng)量所產(chǎn)生的效果。
Learning Rate 優(yōu)化
至此,你可能還是好奇該如何設(shè)定學(xué)習(xí)率的值。
事實(shí)上,這個(gè)值的選取需要一定的經(jīng)驗(yàn)或者反復(fù)嘗試才能確定。
《深度學(xué)習(xí)》一書中是這樣描述的:“與其說是科學(xué),這更像是一門藝術(shù),我們應(yīng)該謹(jǐn)慎地參考關(guān)于這個(gè)問題的大部分指導(dǎo)。”。
關(guān)鍵在于,這個(gè)值的選取不能過大也不能過小。
如果這個(gè)值過小,會(huì)導(dǎo)致每一次迭代的步長很小,其結(jié)果就是算法需要迭代非常多的次數(shù)。
那么,如果這個(gè)值過大會(huì)怎么樣呢?其結(jié)果就是:算法可能在結(jié)果的周圍來回震蕩,卻落不到目標(biāo)的點(diǎn)上。下面這幅圖描述了這個(gè)現(xiàn)象:
事實(shí)上,學(xué)習(xí)率的取值未必一定要是一個(gè)常數(shù),關(guān)于這個(gè)值的設(shè)定有很多的研究。
下面是比較常見的一些改進(jìn)算法。
AdaGrad
AdaGrad是Adaptive Gradient的簡寫,該算法會(huì)為每個(gè)參數(shù)設(shè)定不同的學(xué)習(xí)率。它使用歷史梯度的平方和作為基礎(chǔ)來進(jìn)行計(jì)算。
其算法公式如下:
[\theta_i = \theta_i - \frac{\lambda}{\sqrt{G_t + \epsilon}} \nabla f(\theta)]
對比式7,這里的改動(dòng)就在于分號下面的根號。
根號中有兩個(gè)符號,第二個(gè)符號比較好理解,它就是為了避免除0而人為引入的一個(gè)很小的常數(shù),例如可以設(shè)為:0.001。
第一個(gè)符號的表達(dá)式展開如下:
[G_t = \sum_{i = 1}^{t} \nabla f(\theta){i}\nabla f(\theta){i}^{T}]
這個(gè)值其實(shí)是歷史中每次梯度的平方的累加和。
AdaGrad算法能夠在訓(xùn)練中自動(dòng)的對learning rate進(jìn)行調(diào)整,對于出現(xiàn)頻率較低參數(shù)采用較大的學(xué)習(xí)率;相反,對于出現(xiàn)頻率較高的參數(shù)采用較小的學(xué)習(xí)率。因此,Adagrad非常適合處理稀疏數(shù)據(jù)。
但該算法的缺點(diǎn)是它可能導(dǎo)致學(xué)習(xí)率非常小以至于算法收斂非常的慢。
關(guān)于這個(gè)算法的直觀解釋可以看李宏毅教授的視頻課程:ML Lecture 3-1: Gradient Descent。
RMSProp
RMS是Root Mean Square的簡寫。RMSProp是AI教父Geoff Hinton提出的一種自適應(yīng)學(xué)習(xí)率方法。AdaGrad會(huì)累加之前所有的梯度平方,而RMSProp僅僅是計(jì)算對應(yīng)的平均值,因此可緩解Adagrad算法學(xué)習(xí)率下降較快的問題。
該算法的公式如下:
[E[\nabla f(\theta_{i})^2]^{t} = \gamma E[\nabla f(\theta_{i})^2]^{t - 1} + (1-\gamma)(\nabla f(\theta_{i})^{t})^{2} \ \theta_i = \theta_i - \frac{\lambda}{\sqrt{E[g^2]^{t+1} + \epsilon}} \nabla f(\theta_{i})]
類似的,是為了避免除0而引入。 是衰退參數(shù),通常設(shè)為0.9。
這里的 是t時(shí)刻梯度平方的平均值。
Adam
Adam是Adaptive Moment Estimation的簡寫。它利用梯度的一階矩估計(jì)和二階矩估計(jì)動(dòng)態(tài)調(diào)整每個(gè)參數(shù)的學(xué)習(xí)率。
Adam的優(yōu)點(diǎn)主要在于經(jīng)過偏置校正后,每一次迭代學(xué)習(xí)率都有個(gè)確定范圍,使得參數(shù)比較平穩(wěn)。
該算法公式如下:
[m^{t} = \beta_{1} m^{t-1} + (1-\beta_{1}) \nabla f(\theta) \ v^{t} = \beta_{2} v^{t-1} + (1-\beta_{2}) \nabla f(\theta)^2 \ \widehat{m}^{t} = \frac{m^{t}}{1 - \beta^{t}_1} \ \widehat{v}^{t} = \frac{v^{t}}{1 - \beta^{t}_2} \ \theta = \theta - \frac{\lambda}{\sqrt{\widehat{v}^{t}} + \epsilon}\widehat{m}^{t}]
,分別是對梯度的一階矩估計(jì)和二階矩估計(jì)。, 是對,的校正,這樣可以近似為對期望的無偏估計(jì)。
Adam算法的提出者建議 默認(rèn)值為0.9,默認(rèn)值為0.999,默認(rèn)值為 。
在實(shí)際應(yīng)用中 ,Adam較為常用,它可以比較快地得到一個(gè)預(yù)估結(jié)果。
優(yōu)化小結(jié)
這里我們列舉了幾種優(yōu)化算法。它們很難說哪種最好,不同的算法適合于不同的場景。在實(shí)際的工程中,可能需要逐個(gè)嘗試一下才能確定選擇哪一個(gè),這個(gè)過程也是目前現(xiàn)階段AI項(xiàng)目要經(jīng)歷的工序之一。
實(shí)際上,該方面的研究遠(yuǎn)不止于此,如果有興趣,可以繼續(xù)閱讀 《Sebastian Ruder: An overview of gradient descent optimization algorithms》 這篇論文或者 Optimization for Deep Learning 這個(gè)Slides進(jìn)行更多的研究。
由于篇幅所限,這里不再繼續(xù)展開了。
算法限制
梯度下降算法存在一定的限制。首先,它要求函數(shù)必須是可微分的,對于不可微的函數(shù),無法使用這種方法。
除此之外,在某些情況下,使用梯度下降算法在接近極值點(diǎn)的時(shí)候可能收斂速度很慢,或者產(chǎn)生Z字形的震蕩。這一點(diǎn)需要通過調(diào)整學(xué)習(xí)率來回避。
另外,梯度下降還會(huì)遇到下面兩類問題。
局部最小值
局部最小值(Local Minima)指的是,我們找到的最小值僅僅是一個(gè)區(qū)域內(nèi)的最小值,而并非全局的。由于算法的起點(diǎn)是隨意取的,以下面這個(gè)圖形為例,我們很容易落到局部最小值的點(diǎn)里面。
這就是好像你從上頂往下走,你第一次走到的平臺(tái)未必是山腳,它有可能只是半山腰的一個(gè)平臺(tái)的而已。
算法的起點(diǎn)決定了算法收斂的速度以及是否會(huì)落到局部最小值上。
壞消息是,目前似乎沒有特別好的方法來確定選取那個(gè)點(diǎn)作為起點(diǎn)是比較好的,這就有一點(diǎn)看運(yùn)氣的成分了。多次嘗試不同的隨機(jī)點(diǎn)或許是一個(gè)比較好的方法,這也就是為什么做算法的優(yōu)化這項(xiàng)工作是特別消耗時(shí)間的了。
但好消息是:
對于凸函數(shù)或者凹函數(shù)來說,不存在局部極值的問題。其局部極值一定是全局極值。
最近的一些研究表明,某些局部極值并沒有想象中的那么糟糕,它們已經(jīng)非常的接近全局極值所帶來的結(jié)果了。
鞍點(diǎn)
除了Local Minima,在梯度下降的過程中,還有可能遇到另外一種情況,即:鞍點(diǎn)(Saddle Point)。鞍點(diǎn)指的是我們找到點(diǎn)某個(gè)點(diǎn)確實(shí)是梯度為0,但它卻不是函數(shù)的極值,它的周圍既有比它小的值,也有比它大的值。這就好像馬鞍一樣。
如下圖所示:
多類隨機(jī)函數(shù)表現(xiàn)出以下性質(zhì):在低維空間中,局部極值很普遍。但在高維空間中,局部極值比較少見,而鞍點(diǎn)則很常見。
不過對于鞍點(diǎn),可以通過數(shù)學(xué)方法Hessian矩陣來確定。關(guān)于這點(diǎn),這里就不再展開了,有興趣的讀者可以以這里提供的幾個(gè)鏈接繼續(xù)探索。
參考資料與推薦讀物
Wikipeida: Gradient descent
Sebastian Ruder: An overview of gradient descent optimization algorithms
吳恩達(dá):機(jī)器學(xué)習(xí)
吳恩達(dá):深度學(xué)習(xí)
Peter Flach:機(jī)器學(xué)習(xí)
李宏毅 - ML Lecture 3-1: Gradient Descent
PDF: 李宏毅 - Gradient Descent
Intro to optimization in deep learning: Gradient Descent
Intro to optimization in deep learning: Momentum, RMSProp and Adam
Stochastic Gradient Descent – Mini-batch and more
劉建平Pinard - 梯度下降(Gradient Descent)小結(jié)
多元函數(shù)的偏導(dǎo)數(shù)、方向?qū)?shù)、梯度以及微分之間的關(guān)系思考
[Machine Learning] 梯度下降法的三種形式BGD、SGD以及MBGD
作者:阿Paul
快速實(shí)戰(zhàn)入門 人工智能—快速實(shí)戰(zhàn)入門【搶先看】 章節(jié)1:機(jī)器學(xué)習(xí)本質(zhì)到底做什么
章節(jié)2:線性回歸算法知識(shí)鋪墊
章節(jié)3:線性回歸算法深入剖析
章節(jié)4:環(huán)境安裝配置以及線性回歸算法實(shí)現(xiàn)
章節(jié)5:IDE的使用及利用sklearn模塊使用
章節(jié)6:優(yōu)化算法梯度下降法深入剖析
章節(jié)7:代碼實(shí)戰(zhàn)梯度下降法
章節(jié)8:提高模型的推廣能力以及代碼實(shí)戰(zhàn)
章節(jié)9:人工智能中的歸一化
章節(jié)10:多項(xiàng)式回歸算法
章節(jié)11:邏輯回歸算法詳解
章節(jié)12:代碼實(shí)戰(zhàn)邏輯回歸
章節(jié)13:代碼實(shí)戰(zhàn)水泥強(qiáng)度預(yù)測案例
章節(jié)14:代碼實(shí)戰(zhàn)保險(xiǎn)醫(yī)療花費(fèi)預(yù)測案例
章節(jié)15:代碼實(shí)戰(zhàn)音樂分類器案例
章節(jié)16:詳解邏輯回歸多分類與Softmax
章節(jié)17:模型的評估指標(biāo)詳解
章節(jié)18:模型評估代碼實(shí)戰(zhàn)
第一階段 Python語言基礎(chǔ)與使用 章節(jié)1:數(shù)學(xué)基礎(chǔ)補(bǔ)充
章節(jié)2:機(jī)器學(xué)習(xí)計(jì)算基礎(chǔ)庫
章節(jié)3:機(jī)器學(xué)習(xí)Python基礎(chǔ)
第二階段 機(jī)器學(xué)習(xí)算法與案例實(shí)戰(zhàn) 章節(jié)1:多元線性回歸
章節(jié)2:梯度下降法
章節(jié)3:邏輯回歸
章節(jié)4:模型評估與選擇
章節(jié)5:SVM
章節(jié)6:聚類
章節(jié)7:決策樹
章節(jié)8:集成學(xué)習(xí)和隨機(jī)森林
章節(jié)9:關(guān)聯(lián)規(guī)則挖掘
第三階段 機(jī)器學(xué)習(xí)算法與案例實(shí)戰(zhàn) 章節(jié)1:訓(xùn)練模型各種優(yōu)化算法
章節(jié)2:Adaboost 和 GBDT
章節(jié)3:XGBoost
章節(jié)4:貝葉斯分類器
章節(jié)5:最大熵模型與 EM 算法
章節(jié)6:主成分分析
章節(jié)7:隱含馬爾科夫模型
章節(jié)8:條件隨機(jī)場
章節(jié)9:主題模型
章節(jié)10:詞向量 Word2Vec
第四階段 深度學(xué)習(xí)原理與框架 章節(jié)1:神經(jīng)網(wǎng)絡(luò)與多層感知機(jī)
章節(jié)2:TensorFlow
章節(jié)3:訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)
章節(jié)4:卷積神經(jīng)網(wǎng)絡(luò)
章節(jié)5:實(shí)現(xiàn)經(jīng)典卷積神經(jīng)網(wǎng)絡(luò)
章節(jié)6:循環(huán)神經(jīng)網(wǎng)絡(luò)
章節(jié)7:強(qiáng)化學(xué)習(xí)
第五階段 人工智能項(xiàng)目實(shí)戰(zhàn) 章節(jié)1:面對海量數(shù)據(jù)挖掘
章節(jié)2:實(shí)時(shí)個(gè)性化推薦系統(tǒng)
章節(jié)3:自然語言基礎(chǔ)
章節(jié)4:聊天機(jī)器人
章節(jié)5:Keras
rbf神經(jīng)網(wǎng)絡(luò)有多種學(xué)習(xí)策略,首先選取中心,可以隨機(jī)選,也可采用K均值聚類,然后學(xué)習(xí)權(quán)值,可采用偽逆法(涉及矩陣的奇異值分解),也可以采用最小均方誤差法,或者進(jìn)化算法,上述方法中心是固定的,也可采用梯度下降法同時(shí)學(xué)習(xí)中心、寬度、權(quán)值,這個(gè)比較復(fù)雜。具體參考《神經(jīng)網(wǎng)絡(luò)原理》。
你用Java寫可以參考Weka,其完全開源,不過我沒有看過源碼,不知其用何種學(xué)習(xí)策略。最近用C++寫了一個(gè)簡單的rbf,即固定中心、最小均方誤差法學(xué)習(xí)權(quán)值,但我發(fā)現(xiàn)采用K均值聚類選中心跟隨機(jī)選沒有什么區(qū)別,不知二者有何區(qū)別?自己寫偽逆法對于我來說基本不可能,及其復(fù)雜,我看到過某人寫了個(gè)天書般的程序,一個(gè)函數(shù)500行。
希望對你有幫助,如果你有新發(fā)現(xiàn),歡迎與我探討,國內(nèi)估計(jì)沒多少人真正自己寫過RBF,都用MATLAB代入了事。
Python實(shí)現(xiàn)簡單多線程任務(wù)隊(duì)列
最近我在用梯度下降算法繪制神經(jīng)網(wǎng)絡(luò)的數(shù)據(jù)時(shí),遇到了一些算法性能的問題。梯度下降算法的代碼如下(偽代碼):
defgradient_descent(): # the gradient descent code plotly.write(X, Y)
一般來說,當(dāng)網(wǎng)絡(luò)請求 plot.ly 繪圖時(shí)會(huì)阻塞等待返回,于是也會(huì)影響到其他的梯度下降函數(shù)的執(zhí)行速度。
一種解決辦法是每調(diào)用一次 plotly.write 函數(shù)就開啟一個(gè)新的線程,但是這種方法感覺不是很好。 我不想用一個(gè)像 cerely(一種分布式任務(wù)隊(duì)列)一樣大而全的任務(wù)隊(duì)列框架,因?yàn)榭蚣軐τ谖业倪@點(diǎn)需求來說太重了,并且我的繪圖也并不需要 redis 來持久化數(shù)據(jù)。
那用什么辦法解決呢?我在 python 中寫了一個(gè)很小的任務(wù)隊(duì)列,它可以在一個(gè)單獨(dú)的線程中調(diào)用 plotly.write函數(shù)。下面是程序代碼。
fromthreadingimportThreadimportQueueimporttime classTaskQueue(Queue.Queue):
首先我們繼承 Queue.Queue 類。從 Queue.Queue 類可以繼承 get 和 put 方法,以及隊(duì)列的行為。
def__init__(self, num_workers=1): Queue.Queue.__init__(self) self.num_workers=num_workers self.start_workers()
初始化的時(shí)候,我們可以不用考慮工作線程的數(shù)量。
defadd_task(self, task,*args,**kwargs): args=argsor() kwargs=kwargsor{} self.put((task, args, kwargs))
我們把 task, args, kwargs 以元組的形式存儲(chǔ)在隊(duì)列中。*args 可以傳遞數(shù)量不等的參數(shù),**kwargs 可以傳遞命名參數(shù)。
defstart_workers(self): foriinrange(self.num_workers): t=Thread(target=self.worker) t.daemon=True t.start()
我們?yōu)槊總€(gè) worker 創(chuàng)建一個(gè)線程,然后在后臺(tái)刪除。
下面是 worker 函數(shù)的代碼:
defworker(self): whileTrue: tupl=self.get() item, args, kwargs=self.get() item(*args,**kwargs) self.task_done()
worker 函數(shù)獲取隊(duì)列頂端的任務(wù),并根據(jù)輸入?yún)?shù)運(yùn)行,除此之外,沒有其他的功能。下面是隊(duì)列的代碼:
我們可以通過下面的代碼測試:
defblokkah(*args,**kwargs): time.sleep(5) print“Blokkah mofo!” q=TaskQueue(num_workers=5) foriteminrange(1): q.add_task(blokkah) q.join()# wait for all the tasks to finish. print“Alldone!”
Blokkah 是我們要做的任務(wù)名稱。隊(duì)列已經(jīng)緩存在內(nèi)存中,并且沒有執(zhí)行很多任務(wù)。下面的步驟是把主隊(duì)列當(dāng)做單獨(dú)的進(jìn)程來運(yùn)行,這樣主程序退出以及執(zhí)行數(shù)據(jù)庫持久化時(shí),隊(duì)列任務(wù)不會(huì)停止運(yùn)行。但是這個(gè)例子很好地展示了如何從一個(gè)很簡單的小任務(wù)寫成像工作隊(duì)列這樣復(fù)雜的程序。
defgradient_descent(): # the gradient descent code queue.add_task(plotly.write, x=X, y=Y)
修改之后,我的梯度下降算法工作效率似乎更高了。如果你很感興趣的話,可以參考下面的代碼。fromthreadingimportThreadimportQueueimporttime classTaskQueue(Queue.Queue): def__init__(self, num_workers=1):Queue.Queue.__init__(self)self.num_workers=num_workersself.start_workers() defadd_task(self, task,*args,**kwargs):args=argsor()kwargs=kwargsor{}self.put((task, args, kwargs)) defstart_workers(self):foriinrange(self.num_workers):t=Thread(target=self.worker)t.daemon=Truet.start() defworker(self):whileTrue:tupl=self.get()item, args, kwargs=self.get()item(*args,**kwargs)self.task_done() deftests():defblokkah(*args,**kwargs):time.sleep(5)print"Blokkah mofo!" q=TaskQueue(num_workers=5) foriteminrange(10):q.add_task(blokkah) q.join()# block until all tasks are doneprint"All done!" if__name__=="__main__":tests()
文章題目:梯度下降法java代碼 梯度下降法代碼實(shí)現(xiàn)
本文網(wǎng)址:http://www.rwnh.cn/article24/ddosije.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機(jī)網(wǎng)站建設(shè)、建站公司、網(wǎng)站維護(hù)、網(wǎng)站排名、關(guān)鍵詞優(yōu)化、電子商務(wù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)