那條線是用公式算出來的;現在 Maya 想一步步走下山去找它。學習率決定她是走到,還是衝過頭。步子太大時會怎樣?
梯度下降就是一個迴圈:測量損失,求出讓損失增加的方向,然後朝反方向移動。這個想法用幾行 Python 就能實現,而學習率會讓它的成功與失敗都變得可見。 你還會加入動量,並看到特徵尺度為什麼會改變梯度。
先定義損失
損失把模型的錯誤變成一個數字。對於過原點的直線,均方誤差比較每個預測 w * x 與真實 y。只有先定義「更好」是什麼意思,模型才知道往哪裡改。
def loss(w, xs, ys):
return sum((w * x - y) ** 2 for x, y in zip(xs, ys)) / len(xs)
print(loss(1, [1, 2], [2, 4]))用一個數字描述目前模型
計算梯度
導數告訴你損失相對於 w 的斜率。梯度為負,說明增大 w 會降低損失,所以更新時減去梯度。係數 2 來自對平方項求導。
def gradient(w, xs, ys):
return 2 * sum((w * x - y) * x for x, y in zip(xs, ys)) / len(xs)
print(gradient(1, [1, 2], [2, 4]))損失上升最快的方向
走一步,再重複
學習率控制步子大小。每輪都在新的 w 上重新計算梯度,再更新一次。對於這條乾淨的資料線,估計值會逐漸靠近 2,而整個過程不需要提前知道答案。
def gradient(w, xs, ys):
return 2 * sum((w * x - y) * x for x, y in zip(xs, ys)) / len(xs)
xs = [1, 2]
ys = [2, 4]
w = 1.0
for _ in range(3):
w -= 0.1 * gradient(w, xs, ys)
print(round(w, 3))小步走向更好的配適
學習率會讓迴圈失控
學習率太小會浪費迭代;太大會衝過谷底甚至發散。始終檢查損失曲線,設定步數上限,並在更新量或改進量可以忽略時停止。
def gradient(w, xs, ys):
return 2 * sum((w * x - y) * x for x, y in zip(xs, ys)) / len(xs)
def train(learning_rate, xs, ys):
w = 1.0
for _ in range(5):
w -= learning_rate * gradient(w, xs, ys)
return w
xs = [1, 2]
ys = [2, 4]
print(round(train(0.1, xs, ys), 2))
print(abs(train(1.5, xs, ys)) > 1000)同一組資料,完全不同的結果
動量平滑重複方向
動量保留一個會累積過去梯度的速度。它可以沿著一致的方向走得更快,並抑制雜訊造成的來回擺動,但也可能在最小值附近過衝。動量是另一個需要驗證的超參數,不保證更快收斂。
def gradient(w, xs, ys):
return 2 * sum((w * x - y) * x for x, y in zip(xs, ys)) / len(xs)
xs = [1, 2]
ys = [2, 4]
w = 0.0
velocity = 0.0
for _ in range(10):
velocity = 0.9 * velocity - 0.1 * gradient(w, xs, ys)
w += velocity
print(round(w, 3))先累積方向,再邁步
特徵尺度會改變梯度
把輸入乘以十會大幅改變損失曲面和梯度。同一個學習率不再等價。應標準化特徵,或按尺度調整學習率,並在長時間訓練前檢查最初幾個損失值。
def gradient(w, xs, ys):
return 2 * sum((w * x - y) * x for x, y in zip(xs, ys)) / len(xs)
for factor in [1, 10]:
xs = [factor, 2 * factor]
ys = [2 * factor, 4 * factor]
print(factor, round(gradient(1.0, xs, ys), 1))同一個學習率並不具備尺度不變性
本課要點
- 損失把預測誤差變成最佳化目標。
- 梯度指向損失增加的方向,所以更新時相減。
- 學習率控制每次更新移動多遠。
- 學習率過大可能發散,過小則可能很慢。
- 記錄損失,並設定步數上限或收斂門檻值。
- 動量累積方向;特徵縮放會改變有效學習率。
常見錯誤
- 目標是最小化損失,卻把梯度加上去。
- 參數已經改變,卻繼續使用舊梯度。
- 只看最終參數,不看損失曲線。
- 改變特徵單位後繼續沿用原學習率,並假定收斂行為不變。
在同一組資料上以學習率 0.1 更新十次,列印最終權重,保留三位小數。
動手試試
改改變數、先預測輸出,再執行程式碼。第一次點擊「執行程式碼」時會下載 Python 執行時,可能需要幾秒鐘。
練習本課
12 道題,作答後立即看到解釋。