数値微分(Numerical Differention

#数値微分(Numerical Differentiation)

Deep Learningの学習機能の実装方法として解説の良く出てくる数値微分について学んだ事を、学習メモとして記載します。
解析的な手法で導関数（Derivative）を求める事が困難な場合、数値微分と言う手法が用いられ、近似による微分計算が行わます。
数値微分では、2点近似と3点近似（中心差分）が利用されます。

コラム
解析的手法としては、ニュートンフラッツ法とが代表的な方法で、これのより誤差の無い正確な微分が可能です。

#2点近似

```math

f^{'}(x_{0})=\frac{f(x_{0}+h)-f(x_{0})}{h} -(1)

```

```py3


#Pythonでコーディングすると以下の通り
def nemerical_diff (f,x):
    h = 1e-4
    return (f(x+h)-f(x))/h
```

#3点近似(中心差分)

```math

f^{'}(x_{0})=\frac{f(x_{0}+h)-f(x_{0}-h)}{2\times h
}

```
```py3


#Pythonでコーディングすると以下の通り
def nemerical_diff (f,x):
    h = 1e-4
    return (f(x+h)-f(x-h))/2*h
```

上記の数値微分の式は、以下に説明するテイラー展開（Taylor Expansion）により導かれています。


#テイラー展開（Taylor Expansion）:
関数を多項式の形に展開する方法。
実際に関数をテイラー展開すると以下の様になります。
以下は、$x_{0}$のまわりでのテイラー展開となります。

```math

f(x)= f(x_{0})+f^{'}(x_{0})(x-x_{0})+\frac{1}{2!}f^{''}(x_{0})(x-x_{0})^{2}+\frac{1}{3!}f^{'''}(x_{0})(x-x_{0})^{3} ・・・-(2)

```
上記のテイラー展開式は、$x_{0}$におけるこの関数の値、この関数のグラフの傾きの値、2 階微分の値、3 階微分の値・・・を表しています。
この式を公式化すると以下の通りとなります。

```math
f(x) = \sum_{k=0}^{\infty}\frac{1}{k!}f^{k}(x_{0})(x-x_{0})^{k} - (3)
```

以下は、テイラー展開で数値微分の式を導く流れを追いかけます。
先ず、テイラー展開式(2)で繰り返し出てくる$(x-x_{0})$をhに置き換えます。

```math

f(x)= f(x_{0})+f^{'}(x_{0})h+\frac{1}{2!}f^{''}(x_{0})h^{2}+\frac{1}{3!}f^{'''}(x_{0})h^{3} ・・・-(4)

```
尚、 このメモはDeep Learningの学習処理で損失関数の勾配を求める為に数値微分を利用する事にフォーカスしていますので、テイラー展開式を関数の傾斜の項までを利用します。（2階、3階微分の項は省略）

```math

f(x)= f(x_{0})+f^{'}(x_{0})h-(5)

```
$x-x_{0}=h$とし、傾斜の項（$f^{'}(x_{0})$）を左辺へ移すと、

```math

x-x_{0} = h \rightarrow x = x_{0}+h\\
f(x_{0}+h) = f(x_{0})+f^{'}(x_{0})h\\
f^{'}(x_{0})h =f(x_{0}+h)-f(x_{0})\\
f^{'}(x_{0}) =\frac{f(x_{0}+h)-f(x_{0})}{h}-(6)

```

以上で数値微分の式（６）が無事導かれました・

#更に数値微分を理解する。
下式は、f(x)のxについての微分を現し、Xに対するf(x)の変化の度合いを表している。

```math

\begin{align}
\frac{df(x)}{dx} = \lim_{h\to 0}\frac{f(x+h) - f(x)}{h}\\
\end{align}
```

```math

\frac{df(x)}{dx} = \lim_{h\to 0}\frac{f(x+h) - f(x-h)}{2\times h}

```
# Numerical Differentiation
```py3

import numpy as np
import matplotlib.pyplot as plt

def numerical_diff (f,x):
    h = 1e-4
    return (f(x+h)-f(x-h))/(2*h)

def function_1 (x):
    return 0.01*x**2 + 0.1*x

def tangent_line (f,x):
    d = numerical_diff (f,x)
    y = f(x) - d*x
    print (d)
    return lambda t: d*t + y 
#無名関数を戻り値とする事で、数値ではなく関数をreturn値として返す。
#d*t+yは、x0に於ける元の関数の接線を表す関数。

x = np.arange (0.0, 20.0, 0.1)
y = function_1(x)

plt.xlabel ("x")
plt.ylabel ("f(x)")

tf = tangent_line (function_1, 5)
y2 = tf(x)

plt.plot (x, y)
plt.plot (x, y2)
plt.show ()
```

#偏微分

関数

```math
f(x_{0},x_{1}) = x_{0}^{2} + x_{1}^{2}
```

```py3

def function_2 (x):
    return x[0]**2 + x[1]**2
```
例題1: $ x_{0}=3、x_{1}=4$で、$x_{0}$について偏微分
例題2: $ x_{0}=3、x_{1}=4$で、$x_{1}$について偏微分

```py3

def func0(x):
    return x[0]**2+4**2
def func1(x):
    return 3**2+x[1]**2

X = [3,4]
d0 = numerical_diff(func0,3)
d1 = numerical_diff(func1,4)

```

勾配とは、ある関数の変数全てに対して実施した偏微分をVectorとして纏めたものを言います。

#勾配計算のCoding例

```py3

def _numerical_gradient_no_batch(f,x):
    h = 1e-4
    grad  = np.zeros_like(x)
    for idx in tange (x.size):
        tmp_val = x[idx]
        x[idx] = float(tmp_val) + h
        fxh1 = f(x)

        x[idx] = tmp_val - h
        fxh2 = f(x)

        grad[idx] = (fxh1 - fxh2)/(2*h)
        x
        X[idx] = tmp_val
    return grad
```