二次元での勾配降下法を実装してみます。
今回は以下のような関数について考えます。
$$f(x,y) = -(x^2\sin{y}*y^2\sin{x})$$
目次
関数の描画
matplotlibを使用して、二次元座標上にカラーマップを使って関数を描画します。
import numpy as np
import matplotlib.pyplot as plt
import sympy as sym
def f(x,y):
x,y = np.meshgrid(x,y)
return-(x**2*np.sin(y)*y**2*np.sin(x))
x = np.linspace(-1.5,3,201)
y = np.linspace(-1.5,3,201)
Z = f(x,y)
fig = plt.figure()
plt.imshow(Z,extent=[x[0],x[-1],y[0],y[-1]],vmin=-5,vmax=5,origin='lower')
fig.savefig("a.png",dpi =500)

勾配の最小化
勾配が小さくなる方向にlcminを進めます。
import numpy as np
import matplotlib.pyplot as plt
import sympy as sp
def f(x,y):
x,y = np.meshgrid(x,y)
return-(x**2*np.sin(y)*y**2*np.sin(x))
x = np.linspace(-1.5,3,201)
y = np.linspace(-1.5,3,201)
Z = f(x,y)
fig = plt.figure()
sx,sy = sp.symbols('sx,sy')
sZ = -(sx**2*sp.sin(sy)*sy**2*sp.sin(sx))
df_x = sp.lambdify( (sx,sy),sp.diff(sZ,sx),'sympy' )
df_y = sp.lambdify( (sx,sy),sp.diff(sZ,sy),'sympy' )
#パーシャルxについて、x,y = (1,1)の勾配
print(df_x(1,1).evalf())
#パーシャルyについて、x,y = (1,1)の勾配
print(df_y(1,2).evalf())
#init local min
#ランダムな値を2つ生成
lc_min = np.random.rand(2)*4.5-1.5
init_pt = lc_min[:]
learning_rate = .01
epochs = 1000
#追跡軌道
traj = np.zeros((epochs,2))
for i in range(epochs):
grad_i = np.array([ df_x(lc_min[0],lc_min[1]).evalf(),
df_y(lc_min[0],lc_min[1]).evalf()])
#勾配が0になる方向に進んでいく
lc_min = lc_min - learning_rate*grad_i
traj[i,:] = lc_min
plt.imshow(Z,extent=[x[0],x[-1],y[0],y[-1]],vmin=-5,vmax=5,origin='lower')
plt.plot(init_pt[0],init_pt[1],'bs')
plt.plot(lc_min[0],lc_min[1],'ro')
plt.plot(traj[:,0],traj[:,1],'r')
plt.legend(['init_pt','lc_min'])
plt.colorbar()
fig.savefig("a.png",dpi =500)
