最佳化-梯度下降法(簡單版)

2021-03-01
Project

前言

寫完了牛頓法 ,來寫梯度下降法。
擬牛頓法有點深奧,應該暫時不會寫。


主體

想法類似於牛頓法,但是少了Hessian矩陣的計算:

迭代公式:
$x_{k+1}=x_k - \alpha \nabla f(x_k)$
同樣,這邊$x$是一堆變量$a, b, c …$。

$\nabla f(x_k)$為梯度,可以利用對各變數偏微分得到,例如:
$\nabla f(x, y) = \frac{\partial f}{\partial x}(x, y)i + \frac{\partial f}{\partial y}(x, y)j$

而$\alpha$則是一個調整係數,一般都是在區間(0, 1]裡,具體什麼用途,必須配合等下的例子才能說明。


code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import numpy as np
import matplotlib.pyplot as plt

f = lambda x, y: np.sqrt(x ** 2 + y ** 2 / 3)

def dfdx(x0, y0):
h = 1e-6
return (f(x0 + h, y0) - f(x0, y0)) / h

def dfdy(x0, y0):
h = 1e-6
return (f(x0, y0 + h) - f(x0, y0)) / h

# 梯度矩陣
def grad(x, y):
return np.array([dfdx(x, y), dfdy(x, y)])

X = np.linspace(-5, 10, 256)
Y = np.linspace(-5, 10, 256)

X, Y = np.meshgrid(X, Y)
Z = f(X, Y)
plt.xlabel("x")
plt.ylabel("y")
C = plt.contour(X, Y, Z)

# 調整係數
a = .8

# 起始點
preP = np.array([8.3, 7.6])
nowP = preP - grad(preP[0], preP[1]) * a
plt.plot((preP[0], nowP[0]), (preP[1], nowP[1]), 'bo-')

# 迭代
for i in range(50):
preP = nowP
nowP = preP - grad(preP[0], preP[1]) * a
plt.plot((preP[0], nowP[0]), (preP[1], nowP[1]), 'ro-')

print(nowP)
print(f(nowP[0], nowP[1]))

測試

$\sqrt{x^2 + \frac{y^2}{3}}$, 起始值=(8.3, 7.6), $\alpha = 1$, 迭代次數 16
收斂值 $(x,y) = (-0.00243513, -0.10515567), f(x, y) = 0.06076047183997639$

$xe^{-x^2 - y^2}$, 起始值=(0.3, 0.6), $\alpha = 0.7$, 迭代次數 4
收斂值 $(x,y) = (-0.7073123, 0.11798947), f(x, y) = -0.42295258860276885$

$(3x - 2)^2(y - 5)^2 - 1$, 起始值=(-4, 0), $\alpha = 0.1$, 迭代次數 1
收斂值 無法收斂
註解: 無法收斂的原因在下面會講

$(3x - 2)^2(y - 5)^2 - 1$, 起始值=(-4, 0), $\alpha = 0.001$, 迭代次數 100
收斂值 $(x,y) = (0.65374851, 3.42158651), f(x, y) = -0.9962581603349078$

$(2x + 5)^2(213y - 0.65)^2 - 5$, 起始值=(0, 0), $\alpha = 0.1$, 迭代次數 4
收斂值 無法收斂


觀察結論

以上面的結果與其餘未放上來的資料來說,這個版本的梯度下降法不那麼實用。
當我們的函數值在 某點梯度極大 ,我們的迭代點將會因為這個梯度”衝過頭”,更糟糕的是,衝過頭後的點梯度可能會更大(類似遊樂園的海盜船,但是停不下來),導致 無法收斂 的情況,上面雖然只放了兩個例子,但是非常容易構造此種函數。

因為這樣子,所以才需要引入 調整係數 ,但是又出現了另一個缺點,收斂太慢。
調整係數低確實可以提高收斂的機率,但是以此為代價,迭代次數亦會跟著成長。

衝過頭以致無法收斂的例子

將調整係數調低後收斂

當然這些問題其實可以透過一系列手段解決,而這些手段留在以後再說。


梯度下降法與牛頓法比較

!藍色為牛頓法
!紅色為梯度下降法

$(2x + 5)^2(213y - 0.65)^2 - 5$, 起始值=(0, 0), 迭代次數 6

$(3x - 2)^2(y - 5)^2 - 1$, 起始值=(0, 0), 迭代次數 30
註解: 牛頓法在第5次就收斂了

$xe^{-x^2 - y^2}$, 起始值=(0.3, 0.6), 迭代次數 10

$xe^{-x^2 - y^2}$, 起始值=(1, 0.2), 迭代次數 10

$\sqrt{x^2 + \frac{y^2}{3}}$, 起始值=(8.3, 7.6), 迭代次數 10

可以知道牛頓法的收斂速度比梯度下降要快上許多,可以形象化理解為,梯度下降就是找周圍最陡峭、與等高線垂直的方向走,而牛頓法不只考慮最陡峭,同時考慮是否會變平緩或陡峭。

感覺梯度下降法如果調整係數低一點,穩定度會比牛頓法好上許多。但是收斂步驟增多又變成一大問題。
牛頓法收斂快,但是不穩定,尤其是Hessian逆矩陣對於運算效率是一種負擔。
兩者各有其問題、優點,或許可以在 擬牛頓法變形梯度下降 找到更好的最佳化方法?


Reference

https://zh.wikipedia.org/zh-tw/%E7%89%9B%E9%A1%BF%E6%B3%95
https://en.wikipedia.org/wiki/Newton's_method_in_optimization
https://en.wikipedia.org/wiki/Gradient
https://en.wikipedia.org/wiki/Hessian_matrix
https://zhuanlan.zhihu.com/p/46536960
https://zhuanlan.zhihu.com/p/37524275
https://www.zhihu.com/question/19723347