@@ -31,6 +31,12 @@ def __call__(self, x: pt.TensorLike) -> pt.TensorVariable: ...
3131class LinearLayer (UnaryLayerOp ):
3232 __props__ = ("n_in" , "n_out" , "bias" )
3333
34+ def build_inner_graph (self , X , W , b = None ):
35+ res = X @ W
36+ if self .bias :
37+ res = res + b
38+ return [res ]
39+
3440
3541class Linear (Layer ):
3642 def __init__ (self , name : str | None , n_in : int , n_out : int , bias : bool = True ):
@@ -49,20 +55,15 @@ def __init__(self, name: str | None, n_in: int, n_out: int, bias: bool = True):
4955 def __call__ (self , X : pt .TensorLike ) -> pt .TensorVariable :
5056 X = pt .as_tensor (X )
5157
52- init_st_shape = shape_to_str (X .type .shape )
53-
54- res = X @ self .W
5558 inputs = [X , self .W ]
5659 if self .bias :
57- res += self .b
5860 inputs .append (self .b )
5961
60- final_st_shape = shape_to_str (res .type .shape )
62+ input_shape = shape_to_str (X .type .shape )
63+ output_shape = shape_to_str ((X @ self .W ).type .shape )
6164
6265 ofg = LinearLayer (
63- inputs = inputs ,
64- outputs = [res ],
65- name = f"{ self .name } [{ init_st_shape } -> { final_st_shape } ]" ,
66+ name = f"{ self .name } [{ input_shape } -> { output_shape } ]" ,
6667 n_in = self .n_in ,
6768 n_out = self .n_out ,
6869 bias = self .bias ,
@@ -76,6 +77,9 @@ def __call__(self, X: pt.TensorLike) -> pt.TensorVariable:
7677class EmbeddingLayer (UnaryLayerOp ):
7778 __props__ = ("n_embeddings" , "n_features" )
7879
80+ def build_inner_graph (self , ids , W ):
81+ return [W [ids ]]
82+
7983
8084class Embedding (Layer ):
8185 r"""
@@ -107,8 +111,6 @@ def __call__(self, ids: pt.TensorLike) -> pt.TensorVariable:
107111 ids = pt .as_tensor (ids )
108112
109113 out = EmbeddingLayer (
110- inputs = [ids , self .W ],
111- outputs = [self .W [ids ]],
112114 name = self .name ,
113115 n_embeddings = self .n_embeddings ,
114116 n_features = self .n_features ,
@@ -121,6 +123,9 @@ def __call__(self, ids: pt.TensorLike) -> pt.TensorVariable:
121123class DropoutLayer (UnaryLayerOp ):
122124 __props__ = ("p" ,)
123125
126+ def build_inner_graph (self , X , mask ):
127+ return [pt .where (mask , ift = X / (1 - self .p ), iff = 0 )]
128+
124129
125130class Dropout (Layer ):
126131 def __init__ (self , name : str | None = None , p : float = 0.5 , random_state : Any | None = None ):
@@ -137,8 +142,6 @@ def __call__(self, X: pt.TensorLike) -> pt.TensorVariable:
137142 mask = mask .astype (config .floatX )
138143
139144 X_masked = DropoutLayer (
140- inputs = [X , mask ],
141- outputs = [pt .where (mask , ift = X / (1 - p ), iff = 0 )],
142145 name = f"{ self .name } [p = { self .p } ]" ,
143146 p = self .p ,
144147 )(X , mask )
@@ -147,20 +150,52 @@ def __call__(self, X: pt.TensorLike) -> pt.TensorVariable:
147150 return X_masked
148151
149152
153+ def _batch_normalize (X , epsilon ):
154+ mu = X .mean (axis = 0 )
155+ sigma_sq = X .var (axis = 0 )
156+ return (X - mu ) / pt .sqrt (sigma_sq + epsilon ), mu , sigma_sq
157+
158+
150159class BatchNormLayer (LayerOp ):
151160 __props__ = ("n_in" , "epsilon" , "momentum" , "affine" )
152161
153162 def update_map (self ):
163+ # Outputs 1 and 2 (the new running mean and variance) update inputs 3 and 4 (the old ones).
154164 return {1 : 3 , 2 : 4 }
155165
166+ def build_inner_graph (self , X , * rest ):
167+ if self .affine :
168+ loc , scale , running_mean , running_var = rest
169+ else :
170+ running_mean , running_var = rest
171+
172+ X_normalized , mu , sigma_sq = _batch_normalize (X , self .epsilon )
173+ X_rescaled = X_normalized * scale + loc if self .affine else X_normalized
174+
175+ new_running_mean = self .momentum * mu + (1 - self .momentum ) * running_mean
176+ new_running_var = self .momentum * sigma_sq + (1 - self .momentum ) * running_var
177+
178+ return [X_rescaled , new_running_mean , new_running_var ]
179+
156180
157181class NoRunningStatsBatchNormLayer (UnaryLayerOp ):
158182 __props__ = ("n_in" , "epsilon" , "momentum" , "affine" )
159183
184+ def build_inner_graph (self , X , * rest ):
185+ X_normalized , _ , _ = _batch_normalize (X , self .epsilon )
186+ if self .affine :
187+ loc , scale = rest
188+ return [X_normalized * scale + loc ]
189+ return [X_normalized ]
190+
160191
161192class PredictionBatchNormLayer (UnaryLayerOp ):
162193 __props__ = ("n_in" , "epsilon" , "momentum" , "affine" )
163194
195+ def build_inner_graph (self , X , loc , scale , running_mean , running_var ):
196+ res = (X - running_mean ) / pt .sqrt (running_var + self .epsilon )
197+ return [loc + res * scale ]
198+
164199
165200class BatchNorm2D (Layer ):
166201 r"""
@@ -261,26 +296,14 @@ def __call__(self, X: pt.TensorLike) -> pt.TensorVariable:
261296
262297 self ._initialize_params (X )
263298
264- mu = X .mean (axis = 0 )
265- sigma_sq = X .var (axis = 0 )
266-
267- X_normalized = (X - mu ) / pt .sqrt (sigma_sq + self .epsilon )
268-
269299 if self .affine :
270300 assert self .scale is not None and self .loc is not None
271- X_rescaled = X_normalized * self .scale + self .loc
272301 inputs .extend ([self .loc , self .scale ])
273- else :
274- X_rescaled = X_normalized
275302
276303 if self .track_running_stats :
277304 assert self .running_mean is not None and self .running_var is not None
278- new_running_mean = self .momentum * mu + (1 - self .momentum ) * self .running_mean
279- new_running_var = self .momentum * sigma_sq + (1 - self .momentum ) * self .running_var
280305
281306 batch_norm_op : LayerOp = BatchNormLayer (
282- inputs = [* inputs , self .running_mean , self .running_var ],
283- outputs = [X_rescaled , new_running_mean , new_running_var ],
284307 name = self .name ,
285308 n_in = self .n_in ,
286309 epsilon = self .epsilon ,
@@ -294,8 +317,6 @@ def __call__(self, X: pt.TensorLike) -> pt.TensorVariable:
294317
295318 else :
296319 batch_norm_op = NoRunningStatsBatchNormLayer (
297- inputs = inputs ,
298- outputs = [X_rescaled ],
299320 name = self .name ,
300321 n_in = self .n_in ,
301322 epsilon = self .epsilon ,
@@ -315,6 +336,18 @@ def __call__(self, X: pt.TensorLike) -> pt.TensorVariable:
315336class LayerNormLayer (UnaryLayerOp ):
316337 __props__ = ("n_in" , "epsilon" , "affine" )
317338
339+ def build_inner_graph (self , X , * rest ):
340+ mu = X .mean (axis = - 1 , keepdims = True )
341+ # Biased variance (ddof=0), matching torch.nn.LayerNorm; pretrained weights assume it. Do
342+ # not "correct" to the unbiased estimator -- it would diverge from every pretrained model.
343+ sigma_sq = X .var (axis = - 1 , keepdims = True )
344+ X_normalized = (X - mu ) / pt .sqrt (sigma_sq + self .epsilon )
345+
346+ if self .affine :
347+ scale , loc = rest
348+ return [X_normalized * scale + loc ]
349+ return [X_normalized ]
350+
318351
319352class LayerNorm (Layer ):
320353 r"""
@@ -380,23 +413,12 @@ def __call__(self, X: pt.TensorLike) -> pt.TensorVariable:
380413 X = pt .as_tensor (X )
381414 self ._initialize_params (X )
382415
383- mu = X .mean (axis = - 1 , keepdims = True )
384- # Biased variance (ddof=0), matching torch.nn.LayerNorm; pretrained weights assume it. Do
385- # not "correct" to the unbiased estimator -- it would diverge from every pretrained model.
386- sigma_sq = X .var (axis = - 1 , keepdims = True )
387- X_normalized = (X - mu ) / pt .sqrt (sigma_sq + self .epsilon )
388-
389416 inputs = [X ]
390417 if self .affine :
391418 assert self .scale is not None and self .loc is not None
392- X_rescaled = X_normalized * self .scale + self .loc
393419 inputs .extend ([self .scale , self .loc ])
394- else :
395- X_rescaled = X_normalized
396420
397421 X_transformed = LayerNormLayer (
398- inputs = inputs ,
399- outputs = [X_rescaled ],
400422 name = self .name ,
401423 n_in = self .n_in ,
402424 epsilon = self .epsilon ,
0 commit comments