02a1 自製梯度下降法 - cccbook/py2gpt GitHub Wiki
import math
import numpy as np
from numpy.linalg import norm
# 函數 f 對變數 k 的偏微分: df / dk
def df(f, p, k, step=0.01):
p1 = p.copy()
p1[k] = p[k]+step
return (f(p1) - f(p)) / step
# 函數 f 在點 p 上的梯度
def grad(f, p, step=0.01):
gp = p.copy()
for k in range(len(p)):
gp[k] = df(f, p, k, step)
return gp
# 使用梯度下降法尋找函數最低點
def gradientDescendent(f, p0, step=0.01, max_loops=100000, dump_period=1000):
p = p0.copy()
fp0 = f(p)
for i in range(max_loops):
fp = f(p)
gp = grad(f, p) # 計算梯度 gp
glen = norm(gp) # norm = 梯度的長度 (步伐大小)
if i%dump_period == 0:
print('{:05d}:f(p)={:.3f} p={:s} gp={:s} glen={:.5f}'.format(i, fp, str(p), str(gp), glen))
if glen < 0.00001: # or fp0 < fp: # 如果步伐已經很小了,或者 f(p) 變大了,那麼就停止吧!
break
gstep = np.multiply(gp, -1*step) # gstep = 逆梯度方向的一小步
p += gstep # 向 gstep 方向走一小步
fp0 = fp
print('{:05d}:f(p)={:.3f} p={:s} gp={:s} glen={:.5f}'.format(i, fp, str(p), str(gp), glen))
return p # 傳回最低點!ChatGPT:
這是一個使用梯度下降法(gradient descent)尋找函數最低點的程式。梯度下降法是一種常用的優化演算法,適用於凸函數或者局部凸函數的最小值優化。它的主要思路是從當前位置開始,以當前位置負梯度的方向走一小步,進行多次迭代,直到達到停止條件為止。
這個程式主要分為三個部分:
-
函數的偏微分
函數 f 對變數 k 的偏微分是指在變數 k 上微小增加一點,對應函數 f 的微小變化。在程式中,df 函數接收一個函數 f、一個點 p、一個變數 k,以及一個步長 step。它首先複製點 p 到 p1,然後在 p1 上將變數 k 增加 step,計算 f(p1) 和 f(p) 的差值,除以步長 step 得到 f 對 k 的偏微分值。
-
函數的梯度
函數 f 在一個點 p 上的梯度是指在這個點上,函數在每個變數方向上的偏微分值組成的向量。在程式中,grad 函數接收一個函數 f、一個點 p,以及一個步長 step。它首先複製點 p 到 gp,然後遍歷每個變數 k,計算 f 對 k 的偏微分值,將其組成一個向量返回。
-
梯度下降法
使用梯度下降法尋找函數最低點的過程是由 gradientDescendent 函數實現的。它接收一個函數 f、一個起始點 p0,以及一些其他的參數,例如步長 step、最大迭代次數 max_loops、以及 dump_period。dump_period 參數控制著每隔多少迭代就輸出一次當前狀態。在這個函數中,我們首先複製起始點 p0 到 p,然後計算當前位置的函數值 fp0。接著進入一個循環,進行多次迭代。每次迭代中,我們計算當前位置的函數值 fp、梯度 gp,以及 glen,若 glen不會太小,就往逆梯度方向走一步。反覆這個過程,直到梯度長度 glen 太小才離開,這時應該已經找到某個區域最小值了。
import gd as gd
def f(p):
[x, y, z] = p
return (x-1)**2+(y-2)**2+(z-3)**2
p = [0.0, 0.0, 0.0]
gd.gradientDescendent(f, p)ChatGPT:
這個主程式使用了之前所定義的 gd 模組,目的是使用梯度下降法來尋找函數 f(p) 的最低點。
函數 f(p) 的形式是
接著,我們將起始點 p 設為 [0.0, 0.0, 0.0],也就是變數
接下來,我們呼叫 gd.gradientDescendent 函數來進行梯度下降法的最佳化。這個函數接受 4 個參數:
- f: 要最佳化的函數
- p0: 函數的起始點
- step: 步伐大小
- max_loops: 迭代的最大次數
在這個主程式中,我們只傳入了前兩個參數 f 和 p0,而 step 和 max_loops 則使用了預設值,分別為 0.01 和 100000。
最後,我們會得到最低點的位置,以 p 的形式傳回。我們可以看到程式在運行的過程中,會每 1000 次迭代就輸出一次目前的狀態,包括當前的 f(p)、p、gp(梯度)和 glen(梯度的長度)。最終程式會輸出最低點的位置和對應的 f(p) 值。