はじめに
この記事では、ゼロから作るDeepLearning➂フレームワーク編で躓いたところをまとめていきたいと思います。
本書はかなりわかりやすく丁寧に書かれてはおります。
しかし、ほんの一部省略が発生してしまい、コードを動かしている人であれば、誰もが止まってしまうのではないかと思いまとめることにしました。
私の読書スピードに合わせて順次更新予定となります。
問題
1. ステップ32 高階微分(実装編)
この章で書籍初となるあとは読者に任せたが発生します。
具体的には、core.py
と__init__.py
になります。
修正はAdd
とMul
については書かれていますが、そのほかについては同じように書いてくださいとのことでした。しかし、ここで同じように書いても動かなかったり、同じようにってどういうこと?の人もいるかと思いますのでコードを載せておきます。
import weakref
import numpy as np
import contextlib
# =============================================================================
# Config
# =============================================================================
class Config:
enable_backprop = True
@contextlib.contextmanager
def using_config(name, value):
old_value = getattr(Config, name)
setattr(Config, name, value)
try:
yield
finally:
setattr(Config, name, old_value)
def no_grad():
return using_config('enable_backprop', False)
# =============================================================================
# Variable / Function
# =============================================================================
class Variable:
__array_priority__ = 200
def __init__(self, data, name=None):
if data is not None:
if not isinstance(data, np.ndarray):
raise TypeError('{} is not supported'.format(type(data)))
self.data = data
self.name = name
self.grad = None
self.creator = None
self.generation = 0
@property
def shape(self):
return self.data.shape
@property
def ndim(self):
return self.data.ndim
@property
def size(self):
return self.data.size
@property
def dtype(self):
return self.data.dtype
def __len__(self):
return len(self.data)
def __repr__(self):
if self.data is None:
return 'variable(None)'
p = str(self.data).replace('\n', '\n' + ' ' * 9)
return 'variable(' + p + ')'
def set_creator(self, func):
self.creator = func
self.generation = func.generation + 1
def cleargrad(self):
self.grad = None
def backward(self, retain_grad=False, create_graph=False):
if self.grad is None:
self.grad = np.ones_like(self.data)
funcs = []
seen_set = set()
def add_func(f):
if f not in seen_set:
funcs.append(f)
seen_set.add(f)
funcs.sort(key=lambda x: x.generation)
add_func(self.creator)
while funcs:
f = funcs.pop()
gys = [output().grad for output in f.outputs] # output is weakref
with using_config('enable_backprop', create_graph):
gxs = f.backward(*gys)
if not isinstance(gxs, tuple):
gxs = (gxs,)
for x, gx in zip(f.inputs, gxs):
if x.grad is None:
x.grad = gx
else:
x.grad = x.grad + gx
if x.creator is not None:
add_func(x.creator)
if not retain_grad:
for y in f.outputs:
y().grad = None # y is weakref
def as_variable(obj):
if isinstance(obj, Variable):
return obj
return Variable(obj)
def as_array(x):
if np.isscalar(x):
return np.array(x)
return x
class Function:
def __call__(self, *inputs):
inputs = [as_variable(x) for x in inputs]
xs = [x.data for x in inputs]
ys = self.forward(*xs)
if not isinstance(ys, tuple):
ys = (ys,)
outputs = [Variable(as_array(y)) for y in ys]
if Config.enable_backprop:
self.generation = max([x.generation for x in inputs])
for output in outputs:
output.set_creator(self)
self.inputs = inputs
self.outputs = [weakref.ref(output) for output in outputs]
return outputs if len(outputs) > 1 else outputs[0]
def forward(self, xs):
raise NotImplementedError()
def backward(self, gys):
raise NotImplementedError()
# =============================================================================
# 四則演算 / 演算子のオーバーロード
# =============================================================================
class Add(Function):
def forward(self, x0, x1):
y = x0 + x1
return y
def backward(self, gy):
return gy, gy
def add(x0, x1):
x1 = as_array(x1)
return Add()(x0, x1)
class Mul(Function):
def forward(self, x0, x1):
y = x0 * x1
return y
def backward(self, gy):
x0, x1 = self.inputs
return gy * x1, gy * x0
def mul(x0, x1):
x1 = as_array(x1)
return Mul()(x0, x1)
class Neg(Function):
def forward(self, x):
return -x
def backward(self, gy):
return -gy
def neg(x):
return Neg()(x)
class Sub(Function):
def forward(self, x0, x1):
y = x0 - x1
return y
def backward(self, gy):
return gy, -gy
def sub(x0, x1):
x1 = as_array(x1)
return Sub()(x0, x1)
def rsub(x0, x1):
x1 = as_array(x1)
return Sub()(x1, x0)
class Div(Function):
def forward(self, x0, x1):
y = x0 / x1
return y
def backward(self, gy):
x0, x1 = self.inputs
gx0 = gy / x1
gx1 = gy * (-x0 / x1 ** 2)
return gx0, gx1
def div(x0, x1):
x1 = as_array(x1)
return Div()(x0, x1)
def rdiv(x0, x1):
x1 = as_array(x1)
return Div()(x1, x0)
class Pow(Function):
def __init__(self, c):
self.c = c
def forward(self, x):
y = x ** self.c
return y
def backward(self, gy):
x, = self.inputs
c = self.c
gx = c * x ** (c - 1) * gy
return gx
def pow(x, c):
return Pow(c)(x)
def setup_variable():
Variable.__add__ = add
Variable.__radd__ = add
Variable.__mul__ = mul
Variable.__rmul__ = mul
Variable.__neg__ = neg
Variable.__sub__ = sub
Variable.__rsub__ = rsub
Variable.__truediv__ = div
Variable.__rtruediv__ = rdiv
Variable.__pow__ = pow
# =============================================================================
# step23.pyからstep32.pyまではsimple_coreを利用
is_simple_core = False
# is_simple_core = True
# =============================================================================
if is_simple_core:
from dezero.core_simple import Variable
from dezero.core_simple import Function
from dezero.core_simple import using_config
from dezero.core_simple import no_grad
from dezero.core_simple import as_array
from dezero.core_simple import as_variable
from dezero.core_simple import setup_variable
else:
from dezero.core import Variable
from dezero.core import Function
from dezero.core import using_config
from dezero.core import no_grad
from dezero.core import as_array
from dezero.core import as_variable
from dezero.core import setup_variable
from dezero.core import Config
setup_variable()
__version__ = '0.0.13'
難しいところは、書籍でコードが省略されていること、そしてGitではこの先のすべてのコードが載ってしまっているので動かないということです。
とくにPOWに関しては、self.inputs
に右辺をしただけではエラーになり、x, = self.inputs
にする必要があります。
また、このファイルは今後内容が追加されていくため再度勉強しようと思った時にまた同じエラーに遭遇しそうなのでメモしておきました。
ここをコピペで済ませるとstep33でエラーに遭遇して戻ることになります。また、コードを動かすこともstep32ではないので、構成的にも微妙だなとは感じました。
ステップ38 Variableからreshapeを使う
基本的には書籍通りですが、functions.py
の先頭に以下を追加する必要があります。
from dezero.core import Function, Variable, as_variable, as_array
reshapeのなかでas_variableを利用しているためです。
続きについて
勉強中のため止まった際に更新します。
それとimport関連でしばしエラーになりますが、上の対応を真似て行えばできますので以降は省略したいと思います。
おわりに
この書籍はもうこれ以上分かりやすく説明するのは無理なのではないかというところまでかみ砕かれており今年読んだ中でもトップレベルでよい本だったのですが、やはり一部わかりづらいところはあるようなのでまとめてみることにしました。
誰かの参考になれば幸いです。