文章和代碼都在這兒,騙 歡迎Star GitHub repo
簡介
梯度下降是機器學習中較為基本也比較常見的一類優(yōu)化算法的總稱儡遮。在這里,我假設你已經(jīng)知道了什么是 sigmoid 函數(shù),掌握求導時的鏈式法則,和一些基礎的矩陣乘法碉渡。
如果你對以上三點有疑問的話,可以參考老朋友 Wikipedia 和 Google 給出的意見 灾测。
度量錯誤
梯度下降是一種從“錯誤”中學習的算法爆价。你也許意識到我們需要找到一種度量預測的錯誤程度的方法(metric)。通常情況下我們會選擇均方誤差(MSE)媳搪,但也有一些其他的選擇,比如誤差平方和(SSR)或是平均絕對誤差(MAE)骤宣。本文中將統(tǒng)一使用均方誤差秦爆。
圖中的 y 以及 y_hat 的上角標 μ 指代的是訓練數(shù)據(jù)集中第 μ 個數(shù)據(jù),而非表示冪乘憔披。
Google 和 Wikipedia 永遠是最好的幫手等限,如果你想權衡使用 MSE 和 MAE 的利弊,我推薦你自己去探索一下芬膝。之前在 StackExchange 上看到過一個相關的問題望门,留在這里供你參考。一言以蔽之锰霜,MSE 對偏離實際值越多的預測值“懲罰”的越多(你想想筹误,都給誤差值平方了),有利于我們的模型更好的趨近最優(yōu)情況癣缅。關于 MSE 和 MAE 的介紹厨剪,也可以參考我之前的一篇文章。
批量梯度下降
這里需要插一句友存,梯度下降有很多不同的變種(稍后討論)祷膳,我們上面中給出的均方誤差公式和接下來的算法,是對應批量梯度下降法(Batch Gradient Descent屡立,簡稱BGD)的直晨,這是梯度下降法最原始的形式,它的具體思路是在更新每一參數(shù)時都使用所有的樣本來進行更新。我們將繼續(xù)使用 sigmoid 函數(shù)作為激活函數(shù)勇皇。
我們先給出從 Udacity 深度學習課程中截取的算法描述奕巍,我們進行逐步分析這個算法的實現(xiàn)。
預設:
這一部分沒有寫在上圖的算法中儒士,但是是不可缺少的的止。我們需要初始化一些參數(shù)。分別為訓練次數(shù) e着撩,學習率(learning rate)η 和預設權重(weight)w诅福。關于預設權重的設定,請參考下面這段話拖叙。
First, you'll need to initialize the weights. We want these to be small such that the input to the sigmoid is in the linear region near 0 and not squashed at the high and low ends. It's also important to initialize them randomly so that they all have different starting values and diverge, breaking symmetry. So, we'll initialize the weights from a normal distribution centered at 0. A good value for the scale is 1/√
?n where n is the number of input units. This keeps the input to the sigmoid low for increasing numbers of input units.
第一步將初始化變化的權重 Δw氓润,這一步實際上是便于我們稍后將算法轉為代碼。接下來第二步薯鳍,針對訓練數(shù)據(jù)集中的每一組數(shù)據(jù)我們執(zhí)行以下三個小步驟 (公式已略去咖气,參見上圖):
- 將數(shù)據(jù)集在神經(jīng)網(wǎng)絡中進行一次正向傳遞(可參考我的上篇博客或 Google),得到預測結果 y_hat
- 計算輸出層神經(jīng)元的誤差梯度(error gradient)δ
- 更新權重變化 Δw_i
在完成了一次對整個數(shù)據(jù)集的遍歷之后挖滤,我們將進行第三步崩溪,將Δw_i (權重變化值)和 w_i (預設的權重)相加,得到新的 w_i斩松。 這樣伶唯,我們便完成了一次對權重的更新。之后我們只需要重復 e 次第二惧盹、三步乳幸。
梯度下降的整個過程可以用下圖來進行理解。最開始我們預設的權重在最外側深紅色圓環(huán)上钧椰,經(jīng)過一次一次的迭代逐漸靠近中心的最優(yōu)點(optima)粹断。
接下來我們來一起構建一個 Python 完成的梯度下降算法。完整的數(shù)據(jù)和代碼可以在我的 GitHub Repo 找到嫡霞,這里就不貼出數(shù)據(jù)和準備數(shù)據(jù)的代碼了瓶埋。
哦對了,你可以試著更改預設部分提到的學習率和訓練次數(shù)秒际,看看它們會如何影響我們的訓練結果悬赏。
import numpy as np
from data_prep import features, targets, features_test, targets_test
def sigmoid(x):
"""Calculate sigmoid"""
return 1 / (1 + np.exp(-x))
np.random.seed(42)
n_records, n_features = features.shape
last_loss = None
# 預設權重
weights = np.random.normal(scale=1 / n_features ** .5, size=n_features)
# 設定循環(huán)次數(shù)和學習率
epochs = 1000
learnrate = 0.5
for e in range(epochs):
# 第一步,設定預設變化的權重為0
del_w = np.zeros(weights.shape)
# 遍歷全部數(shù)據(jù)集
for x, y in zip(features.values, targets):
# 正向傳遞計算y_hat
output = sigmoid(np.dot(weights, x))
# 計算誤差梯度
error = (y - output) * output * (1 - output)
# 更新權重變化
del_w += error * x
# 對預設權重的更新
weights += learnrate * del_w / n_records
# 打印出運算過程的一些數(shù)據(jù)
if e % (epochs / 10) == 0:
out = sigmoid(np.dot(features, weights))
loss = np.mean((out - targets) ** 2)
if last_loss and last_loss < loss:
print("Train loss: ", loss, " WARNING - Loss Increasing")
else:
print("Train loss: ", loss)
last_loss = loss
# 驗證我們的算法娄徊,在測試數(shù)據(jù)上進行測試
tes_out = sigmoid(np.dot(features_test, weights))
predictions = tes_out > 0.5
accuracy = np.mean(predictions == targets_test)
print("Prediction accuracy: {:.3f}".format(accuracy))
如果你對第三步中更新預設權重的部分有疑惑闽颇,這里有一行代碼需要你額外注意,
# 對預設權重的更新
weights += learnrate * del_w / n_records
由于我們是將所有訓練數(shù)據(jù)都遍歷了一遍之后得到的變化權重 del_w
寄锐, 所以需要將它除以訓練數(shù)據(jù)集的數(shù)量兵多。這也是和我們即將提到的另一種算法有差異的部分尖啡。
其他梯度下降方法
可以從參考資料的第一篇文章中得知,除了批量梯度下降法(Batch Gradient Descent剩膘,簡稱BGD)外衅斩,還有隨機梯度下降法(Stochastic Gradient Descent,簡稱SGD)以及更進一步的小批量梯度下降法(Mini-batch Gradient Descent怠褐,簡稱MBGD)畏梆。這幾種不同方法的優(yōu)劣對比可以單開一篇文章來探討(或者參見參考資料第一篇),這里只通過介紹性的知識進行簡單總結奈懒。
隨機梯度下降法和批量梯度下降法不同的是奠涌,在后者的訓練過程中,每一次的訓練都需要遍歷全部的訓練數(shù)據(jù)集磷杏,這種算法的確可以保證達到全局最優(yōu)解(global optimal)溜畅。然而,如果我們的數(shù)據(jù)量較大极祸,或者訓練數(shù)據(jù)的維度較高(特征數(shù)量多)的時候慈格,巨大的計算量會極大的拖慢我們模型的訓練速度。所以這里提出一種改進的算法——隨機梯度下降法遥金。唯一一點和批量梯度下降法不同的是浴捆,我們每次選取一個訓練數(shù)據(jù),計算誤差梯度后汰规,直接在預設權重上進行更新汤功。這樣就避免了遍歷全部數(shù)據(jù)后再求平均變化權重的計算過程。極大的減少了計算量溜哮,對訓練速度有著明顯的提高。美中不足的是色解,這種算法只能達到一個和全局最優(yōu)解極為接近的數(shù)值茂嗓,而且不利于并行實現(xiàn)。
下面給出隨機梯度下降法的實現(xiàn)
import numpy as np
from data_prep import features, targets, features_test, targets_test
def sigmoid(x):
"""Calculate sigmoid"""
return 1 / (1 + np.exp(-x))
np.random.seed(42)
n_records, n_features = features.shape
last_loss = None
# 預設權重
weights = np.random.normal(scale=1 / n_features ** .5, size=n_features)
# 設定學習率
learnrate = 0.5
# 遍歷全部數(shù)據(jù)集
for x, y in zip(features.values, targets):
# 正向傳遞計算y_hat
output = sigmoid(np.dot(weights, x))
# 計算誤差梯度
error = (y - output) * output * (1 - output)
# 更新預設權重
weights += error * x
# 驗證我們的算法科阎,在測試數(shù)據(jù)上進行測試
tes_out = sigmoid(np.dot(features_test, weights))
predictions = tes_out > 0.5
accuracy = np.mean(predictions == targets_test)
print("Prediction accuracy: {:.3f}".format(accuracy))
可以看到述吸,除了少去了整體循環(huán)的過程和更新權重的部分有變化,其余的地方并沒有太多改動過锣笨。
小批量梯度下降法(MBGD)蝌矛,是一種結合了以上兩種梯度下降法的新想法。其思路非常簡單错英,在 BGD 方法中入撒,每次循環(huán)都將遍歷整個數(shù)據(jù)集,而在 SGD 方法中椭岩,沒有額外循環(huán)茅逮,只遍歷每個數(shù)據(jù)一次即可璃赡。MBGD 則保留了 BGD 中循環(huán)的思路,但每次循環(huán)中并不會遍歷全部數(shù)據(jù)献雅,而是有選擇的隨機選取少量數(shù)據(jù)碉考。具體的思路可以參考 Google。
BGD | SGD | MBGD | |
---|---|---|---|
全局最優(yōu) | 是 | 近似 | 比SGD更接近最優(yōu) |
訓練速度 | 很慢 | 很快 | 比SGD稍慢 |
這塊有一點我記得不是非常清楚了挺身,希望有明白的朋友指點一下侯谁。忘記在哪本書里看到過SGD也可以達到全局最優(yōu)(也可能我記錯了)。
參考資料
- All formulas are generated by HostMath
- Some figures are taken from the Udacity deep learning course
- 梯度下降法的三種形式BGD章钾、SGD以及MBGD
- The Gradient Descent - Wikipedia
- Neural Networks Tutorial Slides by Andrew Moore