ゼロから作るDeep Learning 3 ステップ21~ステップ24 まとめ
hirohirohirohiros.hatenablog.com
ステップ21
演算子の優先度
左項がndarrayインスタンスで右項が自作インスタンスだった場合,先に左項のndarrayインスタンスの__add__ メゾットが呼ばれます.しかし,ndarrayインスタンスの__add__メゾットは右項が自作インスタンスの場合に対応しておらず,エラーを吐きます.よって,ndarrayインスタンスでも対応している自作インスタンスの__radd__メゾットが呼ばれるようにしたいです.そのために,演算子の優先度を設定します.そのためには,自作インスタンスの属性に,__array_priority__を追加し,適切な整数値に設定すれば良いです.
class Variavle: __array_priority__ = 200
本書では200を設定していましたが,どのような値を設定するのが適切なのでしょうか?ndarrayインスタンスより自作インスタンスの方が優先されて呼ばれれば良いので,__array_priority__値をndarrayインスタンスより高くすればよいことが分かります.
ndarrayインスタンスの__array_priority__を確認すると,
z = np.array(1) print(z.__array_priority__) >>0.0
0.0となります.よって0より大きい値ならよいことが分かりました.
本書では整数値を設定するように書いてありましたが,ndarrayインスタンスが0.0となっているように,小数点を設定しても問題無いようです.実際Variableインスタンスの__array_priority__を0.1としても正しく実行されました.
ステップ23
モジュール,パッケージ,ライブラリ
ステップ23では今まで書いてきたコードをパッケージとして纏めます.その際にpythonで使われる3つの単語の意味についてまとめます.
- モジュール
Pythonのファイルのこと.特に,他のPythonのプログラムからインポートして利用されることを想定して作られたPythonファイルのこと.
- パッケージ
複数のモジュールをまとめたもの.複数のモジュールが入ったディレクトリのことを指す.
- ライブラリ
複数のパッケージをまとめたもの.一つ以上のパッケージから構成される.パッケージを指してライブラリと呼ばれることもある.
今回のDezeroライブラリならこのようになると思われます.
dezero <----------------- パッケージ(ライブラリ) |── __init__.py <-------- モジュール |── core_simple.py <-- モジュール |── ... |── utils.py <---------- モジュール
ステップ24
複雑な関数の微分
このステップでは第2ステージのまとめとして,複雑な関数を実装し,微分を求めます.本書で選ばれなかった関数についてもいくつか実装しようと思います.
matplotlibでの3次元関数のプロットは以下のコードを使うと簡単にできます.
import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(projection='3d') x = y = np.arange(-3, 3, 0.01) X, Y = np.meshgrid(x, y) z = X**2 + Y**2 #プロットしたい関数 ax.plot_surface(X,Y,z, cmap='terrain') plt.show()
- Sphere関数
Sphere関数はz=x^2+y^2という数式で表されます.グラフの形はこんな感じです.
Dezeroで(x, y) = (1, 1)の微分を求めるとこうなります.
import numpy as np from dezero import Variable def sphere(x, y): z = x**2 + y**2 return z x = Variable(np.array(1.0)) y = Variable(np.array(1.0)) z = sphere(x, y) z.backward() print(x.grad, y.grad) >>2.0 2.0
正しく2.0, 2.0と微分されていることが分かります.
- matyas関数
Sphere関数はz=0.26(x^2+y^2)-0.48xyという数式で表されます.グラフの形はこんな感じです.
Dezeroで表現し,微分するとこうなります(一部省略).
def matyas(x, y): z = 0.26*(x**2 + y**2) - 0.48*x*y return z z = matyas(x, y) z.backward() print(x.grad, y.grad) >>0.040000000000000036 0.040000000000000036
複雑な数式も演算子をオーバードライブさせることでシンプルに書くことが出来ています.
- Rosenbrock関数
Rosenbrock関数はz=100(y - x^2)^2 + (1-x)^2という数式で表されます.グラフの形はこんな感じです.
Dezeroで微分すると
def Rosenbrock(x, y): z = 100*(y - x**2)**2 + (1 - x)**2 return z z = Rosenbrock(x, y) z.backward() print(x.grad, y.grad) >>-0.0 0.0
- Beale関数
Beale関数はz=(1.5-x+xy)^2+(2.25-x+xy^2)^2+(2.625-x+xy^2)-2 という数式で表されます.グラフの形はこんな感じです.
Dezeroで微分すると
def Beale(x, y): z = (1.5 - x + x*y**2)**2 + (2.25 - x + x*y**2)**2 + (2.625 - x + x*y**2)**2 return z z = Beale(x, y) z.backward() print(x.grad, y.grad) >>0.0 25.5
Define-and-RunとDefine-by-Run
DezeroはDefine-by-Runで書かれています.これはユーザーが通常の計算を行ったとき,その裏側で計算グラフのためのリンクが作られ,計算グラフが定義されるような仕組みのことです.これだと,普通のPythonで書くような感覚でコードを書くことが出来るので,学習コストが低く,デバックもしやすいメリットがあります.2015年にChainerが提唱し,PyTorchやver2.0以降のTensorflowも採用しています.
対してDefine-and-Runは計算グラフをあらかじめ定義し,計算を行う仕組みのことです.そのドメイン固有の言語を使い,計算グラフを定義するので,最適化が行われ高速に動くというメリットがあります.さらに,計算グラフを定義するコンパイルのような仕組みを持つため,Pythonを介さず別の実行形式に変換されることで,Pythonから独立し高いパフォーマンスが得られるというメリットもあります.分散学習もDefine-and-Runの方が有利になるそうです.ver2.0以前のTensorflowが採用しています.