勾配法を利用して、1次関数の最大値・最小値を探索し図示、またイテレーションの推移をグラフ化する。このプログラムは、ニューラルネットワークを学んでいる最中に作ったプログラムであり、あえてライブラリを使っていない部分がある。
#勾配法を使って1変数関数の最大値・最小値を探索、イテレーションの推移をグラフ化
import numpy as np
import matplotlib.pyplot as plt
def function_3(x):
return -x**2 + 5
def function_4(x):
return -x**3 - 4*x**2 + 4*x + 10
# 数値微分
def numerical_diff(f, x):
h = 1e-4 # 0.0001
diff = (f(x+h)-f(x-h)) / (2*h)
return diff
#勾配法で最小値、最大値を求める
def grad_descent_1(f, init_x, lr, steps):
x = init_x
grads = numerical_diff(f, x)
if grads >= 0:
for _ in range(steps):
grads = numerical_diff(f, x)
x -= lr*grads
print('最小値:')
return f(x)
elif grads < 0:
for _ in range(steps):
grads = numerical_diff(f, x)
x += lr*grads
print('最大値:')
return f(x)
# グラフ用
def grad_descent_2(f, init_x, lr, steps):
lr = lr
steps = steps
x = init_x
grads = numerical_diff(f, x)
if grads >= 0:
while True:
grads = numerical_diff(f, x)
x -= lr*grads
yield x
elif grads < 0:
while True:
grads = numerical_diff(f, x)
x += lr*grads
yield x
#関数の概形と現在位置、最小値を表示
def graph(f, init_x, x):
init_y = f(init_x)
y = f(x) #関数の設定
plt.figure()
def start_point(init_x):
plt.plot(init_x, init_y, 'o', label="start", color='blue')
start_point(init_x) #始点を表示
plt.plot(x, y, label="x**2 + 5") #グラフを表示
plt.plot(linear_grad, 'o', label="min") #最小点
plt.legend() # 凡例をグラフにプロット
plt.grid(which='both', axis = "both", linewidth =1)
plt.show()
#最大値・最小値に収束していく様子をグラフ化
def visualize(f):
grad = grad_descent_2(f, init_x=init_x, lr=lr, steps=steps)
x = range(steps)
y = [next(grad) for j in x] #gradsを配列に格納
fig = plt.figure()
ax = plt.axes()
ax.set_xlabel("steps")
ax.set_ylabel("x")
plt.title("勾配法による収束過程")
plt.plot(x, y)
plt.show()
x = np.arange(-4, 6, 0.1)
func = function_3
init_x = 3.0
lr = 0.01
steps = 300
linear_grad = grad_descent_1(func, init_x=init_x, lr=lr, steps=steps)
print(linear_grad)
graph(func, init_x=init_x, x=x)
visualize(func)
# 最大値:4.99995103475578
2次関数の場合、
となり、始点と最大値が図示される。また、イテレーションの収束過程は、
となり、収束していることが分かる。最小値の場合も、
# 最小値:-4.99995103475578
# 最大値:10.900894327379042
参考文献
オライリー・ジャパン ゼロから作るDeepLearning 斎藤康毅 著