前提
python 3.11.2
構造
. |── dezero │ ├── __init__.py │ ├── core.py │ ├── core_simple.py │ ├── functions.py │ └── utils.py ├── dots │ ├── hoge.dot │ └── sample.png ├── steps │ ├── goldstein.png │ ├── my_sin.png │ ├── step23.py │ ├── step27.py │ ├── step28.py │ ├── step29.py │ ├── step34.py │ ├── step35.py │ ├── step38.py │ ├── step39.py │ ├── step40.py │ └── tanh.png
% python step40.py variable([11 12 13]) Traceback (most recent call last): File "/Users/*/tree/steps/step40.py", line 13, in <module> y.backward() File "/Users/*/tree/steps/../dezero/core.py", line 67, in backward gxs = f.backward(*gys) ^^^^^^^^^^^^^^^^ File "/Users/*/tree/steps/../dezero/core.py", line 182, in backward gx0 = dezero.functions.sum_to(gx0, self.x0_shape) ^^^^^^^^^^^^^^^^ AttributeError: module 'dezero' has no attribute 'functions'. Did you mean: 'Function'?
該当のソースコード
core.py
1import dezero 2import numpy as np 3import weakref 4import contextlib 5 6######################################################################## 7 8class Config: 9 enable_backprop = True 10 11######################################################################## 12 13#微分値をVariableインスタンスにすることで逆伝播の計算のつながりも記憶されるため高階微分も自動で求めることが可能になる。 14 15 16class Variable: 17 def __init__(self, data, name=None): 18 if data is not None: 19 if not isinstance(data, np.ndarray): 20 raise TypeError("{} is not supported".format(type(data))) 21 22 self.data = data 23 self.name = name 24 self.grad = None 25 self.creater = None 26 self.generation = 0 27 28 def __len__(self): 29 return len(self.data) 30 31 #__repr__()はprint()で出力される文字列をカスタマイズするもの 32 def __repr__(self): 33 if self.data is None: 34 return 'variable(None)' 35 p = str(self.data).replace('\n','\n' + ' ' * 9) 36 return 'variable(' + p + ')' 37 38 39 40 def set_creater(self, func): 41 self.creater = func 42 self.generation = func.generation + 1 43 44 def backward(self, retain_flag=False, create_graph=False): 45 if self.grad is None: 46 self.grad = Variable(np.ones_like(self.data)) 47 48 funcs = [] 49 seen_set = set() 50 51 def add_func(f): 52 if f not in seen_set: 53 funcs.append(f) 54 seen_set.add(f) 55 funcs.sort(key=lambda x: x.generation) 56 57 add_func(self.creater) 58 59 60 while funcs: 61 #pop()は”最後尾”の要素を取り出す 62 f = funcs.pop() 63 gys = [output().grad for output in f.outputs] 64 65 #using_configは逆伝播するかどうかのもので、ここでは逆伝播の計算の中で逆伝播を求めるかどうかのflagになっている。 66 with using_config('enable_backprop', create_graph): 67 gxs = f.backward(*gys) 68 if not isinstance(gxs, tuple): 69 gxs = (gxs,) 70 71 for x, gx in zip(f.inputs, gxs): 72 #同じ変数を使って計算するとき用 73 if x.grad is None: 74 x.grad = gx 75 else: 76 x.grad = gx + x.grad 77 78 if x.creater is not None: 79 add_func(x.creater) 80 81 #機械学習において必要なのは末端の変数の微分値だから中間はいらない 82 #だから何らかの関数に出力されている変数の微分値だけ消している 83 if not retain_flag: 84 for y in f.outputs: 85 y().grad = None 86 87 88 def cleargrad(self): 89 self.grad = None 90 91 def reshape(self, *shape): 92 #一次元配列だった場合第二引数は数字だけでいい 93 if len(shape) == 1 and isinstance(shape[0], (tuple, list)): 94 shape = shape[0] 95 return dezero.functions.reshape(self, shape) 96 97 def sum(self, axis=None, keepdims=False): 98 return dezero.functions.sum(self, axis, keepdims) 99 100 @property 101 def shape(self): 102 return self.data.shape 103 @property 104 def ndim(self): 105 return self.data.ndim 106 @property 107 def size(self): 108 return self.data.size 109 @property 110 def dtype(self): 111 return self.data.dtype 112 @property 113 def T(self): 114 return dezero.functions.transpose(self) 115 __array_priority__ = 200 116 117######################################################################## 118 119class Function: 120 # *をつけると各々の引数がタプルとして渡される 121 def __call__(self, *inputs): 122 inputs = [as_variable(x) for x in inputs] 123 xs = [x.data for x in inputs] 124 ys = self.forward(*xs) 125 if not isinstance(ys, tuple): 126 ys = (ys,) 127 outputs = [Variable(as_array(y)) for y in ys] 128 129 if Config.enable_backprop: 130 #推論時の順伝播のみの時には不必要 131 self.generation = max([x.generation for x in inputs]) 132 133 for output in outputs: 134 output.set_creater(self) 135 136 self.inputs = inputs 137 #Funtion classと出力のVariable classで循環参照が起きてしまっていたことの解消 138 self.outputs = [weakref.ref(output) for output in outputs] 139 140 141 return outputs if len(outputs) > 1 else outputs[0] 142 143 def forward(self, xs): 144 raise NotImplementedError() 145 def backward(self, gys): 146 raise NotImplementedError() 147################################################################ 148#core.pyでの修正点:今まではgradインスタンスはndarrayインスタンスを参照にしていたが、それをVariableインスタンス 149#に変えるため、各々のbackwardメソッドの入力値は ~.data から ~ にする 150 151 152class Square(Function): 153 def forward(self, x): 154 y = x ** 2 155 return y 156 157 def backward(self, gy): 158 ###二乗の計算の入力値は常に一つだが元々inputがリストの形になっているから[0]が必要 159 x = self.inputs 160 gx = 2 * x * gy 161 return gx 162 163class Exp(Function): 164 def forward(self, x): 165 y = np.exp(x) 166 return y 167 168 def backward(self, gy): 169 x = self.inputs 170 gx = np.exp(x) * gy 171 return gx 172 173class Add(Function): 174 def forward(self, x0, x1): 175 self.x0_shape, self.x1_shape = x0.shape, x1.shape 176 y = x0 + x1 177 return(y,) 178 179 def backward(self,gy): 180 gx0, gx1 = gy, gy 181 if self.x0_shape != self.x1_shape: 182 gx0 = dezero.functions.sum_to(gx0, self.x0_shape) 183 gx1 = dezero.functions.sum_to(gx1, self.x1_shape) 184 return gx0, gx1 185 186class Mul(Function): 187 def forward(self, x0, x1): 188 self.x0_shape, self.x1_shape = x0.shape, x1.shape 189 y = x0 * x1 190 return y 191 192 def backward(self,gy): 193 x0, x1 = self.inputs 194 gx0 = gy * x1 195 gx1 = gy * x0 196 if self.x0_shape != self.x1_shape: 197 gx0 = dezero.functions.sum_to(gx0, self.x0_shape) 198 gx1 = dezeto.unctions.sum_to(gx1, self.x1_shape) 199 return gx0, gx1 200 201class Neg(Function): 202 def forward(self, x): 203 return -x 204 205 def backward(self, gy): 206 return -gy 207 208class Sub(Function): 209 def forward(self, x0, x1): 210 self.x0_shape, self.x1_shape = x0.shape, x1.shape 211 y = x0 - x1 212 return y 213 214 def backward(self, gy): 215 gx0, gx1 = gy, -gy 216 if self.x0_shape != self.x1_shape: 217 gx0 = dezero.functions.sum_to(gx0, self.x0_shape) 218 gx1 = dezero.functions.sum_to(gx1, self.x1_shape) 219 return gx0, gx1 220 221class Div(Function): 222 def forward(self, x0, x1): 223 self.x0_shape, self.x1_shape = x0.shape, x1.shape 224 x1 = as_array(x1) 225 y = x0 / x1 226 return y 227 228 def backward(self, gy): 229 x0, x1 = self.inputs 230 gx0, gx1 = gy / x1 , -gy * x0 / x1**2 231 if self.x0_shape != self.x1_shape: 232 gx0 = dezero.functions.sum_to(gx0, self.x0_shape) 233 gx1 = dezero.functions.sum_to(gx1, self.x1_shape) 234 return gx0, gx1 235 236class Pow(Function): 237 def __init__(self, c): 238 self.c = c 239 240 def forward(self, x): 241 y = x ** self.c 242 return y 243 244 def backward(self, gy): 245 x, = self.inputs 246 c = self.c 247 return c * x ** (c-1) * gy 248 249 250# class Sum(Function): 251# def forward(self, x): 252# self.x_shape = x.shape 253# y = x.sum() 254# return y 255# def backward(self, gy): 256# gx = broadcast_to(gy, self.x_shape) 257# return gx 258# def sum(x): 259# return Sum()(x) 260######################################################################## 261 262def square(x): 263 return Square()(x) 264 265def exp(x): 266 f = Exp() 267 return f(x) 268 269def mul(x0, x1): 270 x1 = as_array(x1) 271 return Mul()(x0, x1) 272 273def add(x0, x1): 274 x1 = as_array(x1) 275 return Add()(x0, x1) 276 277def neg(x): 278 return Neg()(x) 279 280def sub(x0, x1): 281 x1 = as_array(x1) 282 return Sub()(x0, x1) 283 284def rsub(x0, x1): 285 x1 = as_array(x1) 286 return Sub()(x1, x0) 287 288def div(x0, x1): 289 x1 = as_array(x1) 290 return Div()(x0, x1) 291 292def rdiv(x0,x1): 293 x1 = as_array(x1) 294 return Div()(x1, x0) 295 296def pow(x, c): 297 c = as_array(c) 298 return Pow(c)(x) 299 300def no_grad(): 301 return using_config('enable_backprop', False) 302 303######################################################################## 304 305省略
functions.py
1if '__file__' in globals(): 2 import os, sys 3 sys.path.append(os.path.join(os.path.dirname(__file__),'..')) 4 5 6 7 8class BroadcastTo(Function): 9 def __init__(self, shape): 10 self.shape = shape 11 12 def forward(self, x): 13 self.x_shape = x.shape 14 y = np.broadcast_to(x, self.shape) 15 return y 16 def backward(self, gy): 17 gx = sum_to(gy, self.x_shape) 18 return gx 19 20def broadcast_to(x, shape): 21 if x.shape == shape: 22 return as_variable(x) 23 return BroadcastTo(shape)(x) 24 25 26from dezero import utils 27 28class SumTo(Function): 29 def __init__(self, shape): 30 self.shape = shape 31 def forward(self, x): 32 self.x_shape = x.shape 33 y = utils.sum_to(x, self.shape) 34 return y 35 def backward(self, gy): 36 gx = broadcast_to(gy, self.x_shape) 37 return y 38 39def sum_to(x, shape): 40 if x.shape == shape: 41 return as_variable(x) 42 return SumTo(shape)(x) 43 44class Sum(Function): 45 def __init__(self, axis, keepdims): 46 self.axis = axis 47 self.keepdims = keepdims 48 def forward(self, x): 49 self.x_shape = x.shape 50 y = x.sum(axis=self.axis, keepdims=self.keepdims) 51 return y 52 def backward(self, gy): 53 gy = utils.reshape_sum_backward(gy, self.x_shape, self.axis, self.keepdims) 54 gx = broadcast_to(gy, self.x_shape) 55 return gx 56 57def sum(x, axis=None, keepdims=False): 58 return Sum(axis, keepdims)(x)
step40.py
1if '__file__' in globals(): 2 import os, sys 3 sys.path.append(os.path.join(os.path.dirname(__file__),'..')) 4 5import numpy as np 6from dezero import Variable 7 8x0 = Variable(np.array([1,2,3])) 9x1 = Variable(np.array([10])) 10y = x0 + x1 11print(y) 12 13y.backward() 14print(x1.grad)
試したこと
書籍に沿ってコードは書いていたので間違いはなく、同じような状況に陥っている人は居なそうでした。
ファイルが入っているディレクトリをimportすることには違和感を感じたのですが、書籍では特に触れずに実行できた居たようなのでそこも疑問です。
> 書籍に沿ってコードは書いていた
書籍名も質問に追記した方がいいと思いますよ
https://github.com/oreilly-japan/deep-learning-from-scratch-3
のコードですかね
実行しようとしてるのが、書籍
https://github.com/oreilly-japan/deep-learning-from-scratch-3
のコードならですが、
https://github.com/oreilly-japan/deep-learning-from-scratch-3/blob/master/dezero/core.py
や
https://github.com/oreilly-japan/deep-learning-from-scratch-3/blob/master/dezero/functions.py
の、質問のコードに抜粋されてるところを見ると、質問のコードと違いがあります
それ以外のファイルも含めて、
https://github.com/oreilly-japan/deep-learning-from-scratch-3
から落としたものを全部そのまま使って、質問者さんの環境で「python step40.py」を実行したら、どうなるのでしょうか?
その場合でも、質問のエラー
> AttributeError: module 'dezero' has no attribute 'functions'. Did you mean: 'Function'?
は出るのでしょうか?
ちなみに、google colabで
!git clone https://github.com/oreilly-japan/deep-learning-from-scratch-3.git
%cd /content/deep-learning-from-scratch-3/steps/
!python step40.py
を実行したら、質問のエラーは出ずに実行でき、結果が
variable([11 12 13])
variable([3])
と表示されました
dezero/__init__.py はどうなってますか?
dezero.functions モジュールが import されていないためのエラーではないでしょうか。
すみません __init__.py に書き加えるのを忘れていました、、
素人質問で恐縮なのですが、__init__.pyでimportする以外に選択肢はあるのでしょうか、
いまいち__init__.pyが何をしているのかわかっていません
例えば、core.pyの先頭にimport dezero.functions とするのも同じなように思えてしまいます。(おそらく違うのですが)、
素人質問で申し訳ありません、
__init__.py はパッケージをimportしたときに実行されます。pythonではパッケージをimportしたときにサブモジュールまではimportされないので、同時にサブモジュールもimportしておきたいときは __init__.py でimport をするようにします。
これはパッケージの使われ方をどう設計するかによる部分かと思います。
__init__.py は別として、最低でも、そのサブモジュールを利用するファイル (今回なら core.py) で import を明記しておくべきとは思います。

あなたの回答
tips
プレビュー