▼ 下記ページを理解していること。
[Python] [10] 損失関数と数値微分の実装サンプル
[Python] [11] 偏微分と勾配の実装サンプル
[Python] [12] 勾配降下法の実装サンプル
▼ Python3.6、NumPyがインストールされていること。
このページでは、venvの仮想環境(Python3.6)上にNumPyをインストールした環境で、Python対話モード(Pythonインタプリタ)にて実装サンプルを記載している。
※ Python対話モード、NumPyについては下記を参考。
Python対話モード:[Python] 対話モード (インタプリタ) の使用方法
NumPy:[Python] [NumPy] インストールとnumpy.ndarrayの使用方法
$ python
>>> import numpy as np
>>>
>>> def softmax(x):
... if x.ndim == 2: # 次元が2の場合
... x = x.T # xの転置行列を x に設定
... x = x - np.max(x, axis=0) # x から xの最大値(列単位)を減算した結果を x に設定
... y = np.exp(x) / np.sum(np.exp(x), axis=0) # 「eのx乗」÷「eのx乗」の最大値(列単位)の合計を y に設定
... return y.T # Tの転置行列を返す
... # 次元が 2 でない場合
... x = x - np.max(x) # オーバーフロー対策
... return np.exp(x) / np.sum(np.exp(x)) # 「eのx乗」÷「eのx乗」の合計を返す
...
>>>
>>> # 上記対話モードの続き
>>> def cross_entropy_error(y, t):
... if y.ndim == 1: # 次元が1の場合
... t = t.reshape(1, t.size) # t の列数を t のsizeに変更する(形状を変形)
... y = y.reshape(1, y.size) # yの列数を y のsizeに変更する(形状を変形)
...
... # 教師データがone-hot-vectorの場合、正解ラベルのインデックスに変換
... if t.size == y.size:
... t = t.argmax(axis=1) # t の最大要素(行単位)インデックスを設定
...
... batch_size = y.shape[0] # y の初次元要素数を設定
... return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size # 上記URL(※)を参考。
...
>>>
>>> # 上記対話モードの続き
>>> def numerical_gradient(f, x):
... h = 1e-4 # 0.0001
... grad = np.zeros_like(x) # xと同じ形状の配列で値がすべて 0
...
... # ↓↓↓ xの行列すべての要素を1ネストでループできるよう nditer で it を定義している
... # ※ op_flags=['readwrite'] は、ループ内でx[idx]に書き込めるよう書き込みもできるようにしている
... it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
...
... while not it.finished: # it でxの全要素をループ
... idx = it.multi_index
... tmp_val = x[idx]
... x[idx] = float(tmp_val) + h # ループの該当要素を浮動小数点に変換
... fxh1 = f(x) # f(x + h)の算出 … (A)
...
... x[idx] = tmp_val - h
... fxh2 = f(x) # f(x-h) # f(x - h)の算出 … (B)
... grad[idx] = (fxh1 - fxh2) / (2*h)
...
... x[idx] = tmp_val # 値を元に戻す
... it.iternext()
...
... return grad
...
>>>
>>> # 上記対話モードの続き
>>> import sys, os
>>>
>>> class simpleNet:
... def __init__(self):
... # 重みパラメータ:2x3行列の標準正規分布(ガウス分布)でランダムな数値を出力する
... self.W = np.random.randn(2,3)
...
... def predict(self, x):
... return np.dot(x, self.W) # x と self.W の積を返す
...
... def loss(self, x, t):
... z = self.predict(x)
... y = softmax(z)
... loss = cross_entropy_error(y, t)
...
... return loss
...
>>>
>>> # 上記対話モードの続き
>>> net = simpleNet()
>>> print(net.W) # 重みパラメータ
[[ 0.49236891 -1.2239298 -1.13722119]
[ 0.05405365 -0.79897152 -0.08066587]]
>>>
>>> # ↑↑↑ 上記対話モードの続き
>>> x = np.array([0.6, 0.9])
>>> p = net.predict(x)
>>> print(p)
[ 1.05414809 0.63071653 1.1328074 ]
>>> np.argmax(p) # 最大値のインデックス
2
>>>
>>> # 上記対話モードの続き
>>> t = np.array([0, 0, 1]) # 正解ラベル
>>> net.loss(x, t)
0.92806853663411326
>>>
>>> # 上記対話モードの続き
>>> def f(W):
... return net.loss(x, t)
...
>>> dW = numerical_gradient(f, net.W)
>>> print(dW)
[[ 0.21924763 0.14356247 -0.36281009 ]
[ 0.32887144 0.2153437 -0.544211514]]
>>>