Python DeepLearningに再挑戦 9 ニューラルネットワークの学習 その3 数値微分
概要
Python DeepLearningに再挑戦 9 ニューラルネットワークの学習 その3 数値微分
参考書籍
ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装
- 作者: 斎藤康毅
- 出版社/メーカー: オライリージャパン
- 発売日: 2016/09/24
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (6件) を見る
数値微分
- df(x)/dx は、f(x)のxについての微分(xに対するf(x)の変化の度合い)
- lim(h->0)は、hを限りなく0に近づける「小さな変化」という意味。
pythonの実装例(悪い見本)
def numerical_diff(f, x): h = 10e-50 return (f(x+h) - f(x) /h)
- これだと、丸め誤差(rounding error)となり、10e-50は、0.0 で表示されてしまう。そのため、1e-4を用いることが改善ポイントの一つ。
- そうすると結果に誤差が出てしまう。その誤差を小さくするための方法が、f(x+h) - f(x-h) というようにxを中心として差分を計算する中心差分。
- x + h と xの差分は、前方差分。
上記を踏まえて修正した実装が以下の通り。
def numerical_diff(f,x): h = 1e-4 return (f(x+h)-f(x-h))/2*h
数値微分の例
y = 0.01x**2 + 0.1x
import numpy as np import matplotlib.pylab as plt x = np.arange(0.0, 20.0, 0.1) y = function_1(x) plt.xlabel("x") plt.ylabel("f(x)") plt.plot(x,y) plt.show() numerical_diff(function_1,5) # 0.1999.. numerical_diff(function_1,10) # 0.29999..
普通に微分で解くと、0.2、0.3になると思うので、だいたい合っていることがわかる。
これをmatplotlibで表示させてみる。
def tangent_line(f, x): d = numerical_diff(f, x) print(d) y = f(x) - d*x return lambda t: d*t + y tf = tangent_line(function_1, 5) y2 = tf(x) plt.plot(x, y) plt.plot(x, y2) plt.show()
ほとんど接線みたいになってる!
偏微分
pythonの実装は以下の通り。
def function_2(x): return x[0]**2 + x[1]**2
例題1:x0=3, x1=4の時のx0に対する偏微分を求めよ。 例題2:x0=3, x1=4の時のx1に対する偏微分を求めよ。
def function_tmp1(x0): return x0*x0 + 4.0**2.0 def numerical_diff(f,x): h = 1e-4 return (f(x+h)-f(x-h))/(2*h) numerical_diff(function_tmp1, 3.0) >>>6.00000000000378 def function_tmp2(x1): return 3.0**2.0 + x1*x1 numerical_diff(function_tmp2, 4.0) >>> 7.999999999999119
偏微分を求める場合は、以下の手順が1つの方法。
勾配
x0,x1をまとめて計算する。 x0=3, x1=4の時の、(x0,x1)の両方をまとめて、以下の図のようにベクトルとして計算する。 偏微分をベクトルとしてまとめたものを勾配(gradient)という。
def numerical_gradient(f,x): h = 1e-4 #0.0001 grad = np.zeros_like(x) # xと同じ形状かつ、全て要素が0の配列を生成 for idx in range(x.size): tmp_val = x[idx] #1回目は、x[0], 2回目は、x[1] # f(x+h)の計算 x[idx] = tmp_val + h #1回目は、x[0], 2回目は、x[1] fxh1 = f(x) # 偏微分関数function2にxを引数として渡している。fxh1は、その解 # f(x-h)の計算 x[idx] = tmp_val - h #1回目は、x[0], 2回目は、x[1] fxh2 = f(x) # 偏微分関数function2にxを引数として渡している。fxh2は、その解 grad[idx] = (fxh1 - fxh2) /(2*h) #1回目は、grad[0], 2回目は、grad[1] x[idx] = tmp_val # 値を元に戻す。(これは次の計算のために必要なため) return grad numerical_gradient(function_2, np.array([3.0, 4.0])) >>> array([ 6., 8.]) numerical_gradient(function_2, np.array([0.0, 2.0])) >>> array([ 0., 4.])
- 結論的には、勾配が示す方向は、各場所に置いて、関数の値を最も減らす方向。勾配は各地点に置いて低くなる方向を指す。
あーもうちょっと数学的にはちゃんと理解できないなぁ。でも多分偏微分は、損失関数の誤差を小さくするのに役に立つんやろうな。