勾配法で2次関数の最大値・最小値を求める

勾配法を利用して、1次関数の最大値・最小値を探索し図示、またイテレーションの推移をグラフ化する。このプログラムは、ニューラルネットワークを学んでいる最中に作ったプログラムであり、あえてライブラリを使っていない部分がある。

```python
#勾配法を使って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次関数の場合、
<img width="331" alt="スクリーンショット 2019-07-19 07.14.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280072/dc098d64-1323-e781-402a-7118676916c2.png">
となり、始点と最大値が図示される。また、イテレーションの収束過程は、
<img width="331" alt="スクリーンショット 2019-07-19 07.25.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280072/81c9df65-812b-4369-0f9a-186b8f5b1fb5.png">
となり、収束していることが分かる。最小値の場合も、

```python
# 最小値：-4.99995103475578
```
となり、
<img width="331" alt="スクリーンショット 2019-07-19 07.33.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280072/12a3f9cc-94da-2807-ece3-8cc493f292de.png"> <img width="331" alt="スクリーンショット 2019-07-19 07.42.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280072/91cfb240-535d-85be-9958-d47abc4435eb.png">
最小値が図示される。
では、3次関数の場合だと、

```python
# 最大値：10.900894327379042
```
<img width="331" alt="スクリーンショット 2019-07-19 07.47.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280072/13b2fe1f-6599-4e19-f2f3-73c3fb59dc86.png"> <img width="331" alt="スクリーンショット 2019-07-19 07.47.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280072/fca7ca37-d3c3-d4f3-8e3a-e140c86d87d7.png">
きちんと動作している。

参考文献
オライリー・ジャパン　ゼロから作るDeepLearning　斎藤康毅 著