Dragon Arrow written by Tatsuya Nakaji, all rights reserved animated-dragon-image-0164

ゼロから作る Deep Learning で使用する微分ファイル

updated on 2019-08-18

イメージ

深層学習の微分関数

書籍ではch01,ch02,ch03...フォルダと同じ階層にcommonフォルダを作成し、そのフォルダにgradient.pyとして置いてある

書籍のgithubhttps://github.com/oreilly-japan/deep-learning-from-scratch/blob/master/common/gradient.py

各関数が必要な時はimportするだけで利用できるようにする。


import numpy as np


def _numerical_gradient_1d(f, x):
    h = 1e-4 # 0.0001
    grad = np.zeros_like(x)
    
    for idx in range(x.size):
        tmp_val = x[idx]
        x[idx] = float(tmp_val) + h
        fxh1 = f(x) # f(x+h)
        
        x[idx] = tmp_val - h 
        fxh2 = f(x) # f(x-h)
        grad[idx] = (fxh1 - fxh2) / (2*h)
        
        x[idx] = tmp_val # 値を元に戻す
        
    return grad




def numerical_gradient_2d(f, X):
    if X.ndim == 1:
        return _numerical_gradient_1d(f, X)
    else:
        grad = np.zeros_like(X)
        
        for idx, x in enumerate(X):
            grad[idx] = _numerical_gradient_1d(f, x)
        
        return grad


# 中心差分で微分
def numerical_gradient(f, x):
    h = 1e-4 # 0.0001
    grad = np.zeros_like(x)
    
    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index
        tmp_val = x[idx]
        x[idx] = float(tmp_val) + h
        fxh1 = f(x) # f(x+h)
        
        x[idx] = tmp_val - h 
        fxh2 = f(x) # f(x-h)
        grad[idx] = (fxh1 - fxh2) / (2*h)
        
        x[idx] = tmp_val # 値を元に戻す
        it.iternext()
        
    return grad


_numerical_gradient_1d は画像一枚が渡ってきたときの微分

x = array([0.1, 0.1, 0.9, 0.1, 0.6, 0. , 0.9, 0.7, 0. , 0.7]) みたいな状況


_numerical_gradient_2d は画像がバッチ処理(100枚とか)で渡ってきたときの微分

x = array([[0.9, 0.9, 0.2, 0. , 0.2, 0.3, 0.8, 0.2, 0.1, 0.1],

       [0.3, 0.1, 0. , 0.2, 0.4, 0.1, 0.5, 0.6, 0.5, 0.7],

       ...

       [0.2, 0.7, 0.5, 0.4, 0.7, 0.8, 0.5, 0.3, 0.4, 0. ]])  みたいな状況


numerical_gradient はxの次元数に場合分けを使わない便利な書き方

役割は _numerical_gradient_2d と同じ

補足:【Python】numpy.nditer()という関数

numpy.nditer — NumPy v1.12 Manual

オプションのop_flags=['readwrite']配下の説明参照

https://teratail.com/questions/79756

何するの、これ? と思ったけど、使ってみた。まずはコードから。

np_array = np.random.randn(2, 3)
print(np_array)

nditer = np.nditer(np_array, flags=['multi_index'])
while not nditer.finished:
    print(nditer.multi_index)
    print(np_array[nditer.multi_index])
    nditer.iternext()

結果

# 最初のprint()
[[-1.74892591 -0.59628881  0.05522772]
 [ 1.31665726  1.22965398 -0.41140946]]

# whileループ
(0, 0)
-1.7489259098
(0, 1)
-0.596288812217
(0, 2)
0.0552277235215
(1, 0)
1.31665726393
(1, 1)
1.229653983
(1, 2)
-0.411409464808

なるほどー。何が便利かわからん。。。と思ったけど、numpyの次元が増えた時、2重ループとか使わず全パターン繰り返される

画像っぽい値で。2行2列3の値(3次元)

np_array = np.random.randn(2, 2, 3)
print(np_array)

nditer = np.nditer(np_array, flags=['multi_index'])
while not nditer.finished:
    print(nditer.multi_index)
    print(np_array[nditer.multi_index])
    nditer.iternext()
[[[ 0.40570373  0.29383617  0.19770627]
  [-0.35118724  0.64944819 -0.85610483]]

 [[-0.99811347 -0.3842173  -0.40674939]
  [-0.28392354  1.00559893 -0.36640248]]]
(0, 0, 0)
0.405703731115
(0, 0, 1)
0.293836169251
(0, 0, 2)
0.197706273648
(0, 1, 0)
-0.351187235594
(0, 1, 1)
0.649448185325
(0, 1, 2)
-0.856104830751
(1, 0, 0)
-0.998113470526
(1, 0, 1)
-0.384217298371
(1, 0, 2)
-0.406749392256
(1, 1, 0)
-0.283923537977
(1, 1, 1)
1.0055989324
(1, 1, 2)
-0.366402477684

すばらしい!!!