pytensor-ml 0.0.1__tar.gz → 0.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/PKG-INFO +1 -1
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/pyproject.toml +4 -1
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/pytensor_ml/__init__.py +3 -0
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/pytensor_ml/_version.py +3 -3
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/pytensor_ml/activations.py +7 -0
- pytensor_ml-0.0.3/pytensor_ml/layers.py +236 -0
- pytensor_ml-0.0.3/pytensor_ml/model.py +161 -0
- pytensor_ml-0.0.3/pytensor_ml/optimizers.py +387 -0
- pytensor_ml-0.0.3/pytensor_ml/pytensorf.py +115 -0
- pytensor_ml-0.0.3/pytensor_ml/rewriting/__init__.py +0 -0
- pytensor_ml-0.0.3/pytensor_ml/rewriting/layers.py +85 -0
- pytensor_ml-0.0.3/pytensor_ml/util.py +49 -0
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/pytensor_ml.egg-info/PKG-INFO +1 -1
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/pytensor_ml.egg-info/SOURCES.txt +5 -1
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/pytensor_ml.egg-info/requires.txt +1 -0
- pytensor_ml-0.0.1/pytensor_ml/layers.py +0 -53
- pytensor_ml-0.0.1/pytensor_ml/model.py +0 -102
- pytensor_ml-0.0.1/pytensor_ml/optimizers.py +0 -199
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/LICENSE +0 -0
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/README.md +0 -0
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/pytensor_ml/loss.py +0 -0
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/pytensor_ml.egg-info/dependency_links.txt +0 -0
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/pytensor_ml.egg-info/top_level.txt +0 -0
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/setup.cfg +0 -0
- {pytensor_ml-0.0.1 → pytensor_ml-0.0.3}/setup.py +0 -0
|
@@ -33,7 +33,8 @@ keywords = [
|
|
|
33
33
|
|
|
34
34
|
dependencies = [
|
|
35
35
|
"pytensor",
|
|
36
|
-
"numpy"
|
|
36
|
+
"numpy",
|
|
37
|
+
"pymc"
|
|
37
38
|
]
|
|
38
39
|
|
|
39
40
|
[project.optional-dependencies]
|
|
@@ -96,6 +97,8 @@ ignore = [
|
|
|
96
97
|
"D415",
|
|
97
98
|
"D417",
|
|
98
99
|
]
|
|
100
|
+
fixable = ["ALL"]
|
|
101
|
+
|
|
99
102
|
|
|
100
103
|
[tool.ruff.lint.isort]
|
|
101
104
|
lines-between-types = 1
|
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
import sys
|
|
3
3
|
|
|
4
4
|
from pytensor_ml._version import get_versions
|
|
5
|
+
from pytensor_ml.pytensorf import function
|
|
5
6
|
|
|
6
7
|
_log = logging.getLogger(__name__)
|
|
7
8
|
|
|
@@ -13,3 +14,5 @@ if not logging.root.handlers:
|
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
__version__ = get_versions()["version"]
|
|
17
|
+
|
|
18
|
+
__all__ = ["function"]
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "
|
|
11
|
+
"date": "2025-06-18T22:13:49+0200",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "0.0.
|
|
14
|
+
"full-revisionid": "56994236bd76d029096eb5ad6ca3767a893bb4fa",
|
|
15
|
+
"version": "0.0.3"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -37,6 +37,13 @@ class Sigmoid(Activation):
|
|
|
37
37
|
return out
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
class SoftPlus(Activation):
|
|
41
|
+
def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
|
|
42
|
+
out = pt.softplus(x)
|
|
43
|
+
out.name = "SoftPlus"
|
|
44
|
+
return out
|
|
45
|
+
|
|
46
|
+
|
|
40
47
|
class Softmax(Activation):
|
|
41
48
|
def __init__(self, axis: int = -1):
|
|
42
49
|
self.axis = axis
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pytensor.tensor as pt
|
|
7
|
+
import pytensor.tensor.random as ptr
|
|
8
|
+
|
|
9
|
+
from pytensor import config
|
|
10
|
+
from pytensor.compile.sharedvalue import shared
|
|
11
|
+
|
|
12
|
+
from pytensor_ml.pytensorf import LayerOp
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def shape_to_str(shape):
|
|
16
|
+
inner = ",".join([str(st_dim) if st_dim is not None else "?" for st_dim in shape])
|
|
17
|
+
return f"({inner})"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Layer(ABC):
|
|
21
|
+
def __call__(self, x: pt.TensorLike) -> pt.TensorVariable: ...
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LinearLayer(LayerOp):
|
|
25
|
+
__props__ = ("n_in", "n_out", "bias")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Linear(Layer):
|
|
29
|
+
def __init__(self, name: str | None, n_in: int, n_out: int, bias: bool = True):
|
|
30
|
+
self.name = name if name else "Linear"
|
|
31
|
+
self.n_in = n_in
|
|
32
|
+
self.n_out = n_out
|
|
33
|
+
self.bias = bias
|
|
34
|
+
|
|
35
|
+
self.W = pt.tensor(f"{self.name}_W", shape=(n_in, self.n_out))
|
|
36
|
+
|
|
37
|
+
if self.bias:
|
|
38
|
+
self.b = pt.tensor(f"{self.name}_b", shape=(self.n_out,))
|
|
39
|
+
|
|
40
|
+
def __call__(self, X: pt.TensorLike) -> pt.TensorLike:
|
|
41
|
+
X = pt.as_tensor(X)
|
|
42
|
+
|
|
43
|
+
init_st_shape = shape_to_str(X.type.shape)
|
|
44
|
+
|
|
45
|
+
res = X @ self.W
|
|
46
|
+
inputs = [X, self.W]
|
|
47
|
+
if self.bias:
|
|
48
|
+
res += self.b
|
|
49
|
+
inputs.append(self.b)
|
|
50
|
+
|
|
51
|
+
final_st_shape = shape_to_str(res.type.shape)
|
|
52
|
+
|
|
53
|
+
ofg = LinearLayer(
|
|
54
|
+
inputs=inputs,
|
|
55
|
+
outputs=[res],
|
|
56
|
+
name=f"{self.name}[{init_st_shape} -> {final_st_shape}]",
|
|
57
|
+
n_in=self.n_in,
|
|
58
|
+
n_out=self.n_out,
|
|
59
|
+
bias=self.bias,
|
|
60
|
+
)
|
|
61
|
+
out = ofg(*inputs)
|
|
62
|
+
out.name = f"{self.name}_output"
|
|
63
|
+
|
|
64
|
+
return out
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class DropoutLayer(LayerOp):
|
|
68
|
+
__props__ = ("p",)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Dropout(Layer):
|
|
72
|
+
def __init__(self, name: str | None = None, p: float = 0.5, random_state: Any | None = None):
|
|
73
|
+
if p < 0.0 or p > 1.0:
|
|
74
|
+
raise ValueError(f"Dropout probability has to be between 0 and 1, but got {p}")
|
|
75
|
+
self.name = name if name else "Dropout"
|
|
76
|
+
self.p = p
|
|
77
|
+
self.rng = shared(np.random.default_rng(random_state))
|
|
78
|
+
|
|
79
|
+
def __call__(self, X: pt.TensorLike) -> pt.TensorLike:
|
|
80
|
+
X = pt.as_tensor(X)
|
|
81
|
+
p = pt.as_tensor(self.p, dtype=config.floatX)
|
|
82
|
+
new_rng, mask = ptr.bernoulli(p=1 - p, size=X.shape, rng=self.rng).owner.outputs
|
|
83
|
+
mask = mask.astype(config.floatX)
|
|
84
|
+
|
|
85
|
+
X_masked = DropoutLayer(
|
|
86
|
+
inputs=[X, mask],
|
|
87
|
+
outputs=[pt.where(mask, ift=X / (1 - p), iff=0)],
|
|
88
|
+
name=f"{self.name}[p = {self.p}]",
|
|
89
|
+
p=self.p,
|
|
90
|
+
)(X, mask)
|
|
91
|
+
X_masked.name = f"{self.name}_output"
|
|
92
|
+
|
|
93
|
+
return X_masked
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class BatchNormLayer(LayerOp):
|
|
97
|
+
__props__ = ("n_in", "epsilon", "momentum", "affine")
|
|
98
|
+
|
|
99
|
+
def update_map(self):
|
|
100
|
+
return {1: 3, 2: 4}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class NoRunningStatsBatchNormLayer(LayerOp):
|
|
104
|
+
__props__ = ("n_in", "epsilon", "momentum", "affine")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class PredictionBatchNormLayer(LayerOp):
|
|
108
|
+
__props__ = ("n_in", "epsilon", "momentum", "affine")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class BatchNorm2D(Layer):
|
|
112
|
+
def __init__(
|
|
113
|
+
self,
|
|
114
|
+
name: str | None = None,
|
|
115
|
+
n_in: int | None = None,
|
|
116
|
+
epsilon: float = 1e-5,
|
|
117
|
+
momentum: float = 0.1,
|
|
118
|
+
affine: bool = True,
|
|
119
|
+
track_running_stats: bool = True,
|
|
120
|
+
):
|
|
121
|
+
self.name = name if name else "BatchNorm"
|
|
122
|
+
self.n_in = n_in
|
|
123
|
+
self.epsilon = epsilon
|
|
124
|
+
self.momentum = momentum
|
|
125
|
+
self.affine = affine
|
|
126
|
+
self.track_running_stats = track_running_stats
|
|
127
|
+
|
|
128
|
+
self.loc = None
|
|
129
|
+
self.scale = None
|
|
130
|
+
|
|
131
|
+
self.running_mean = None
|
|
132
|
+
self.running_var = None
|
|
133
|
+
|
|
134
|
+
self.initialized = False
|
|
135
|
+
self._initialize_params(None)
|
|
136
|
+
|
|
137
|
+
def _initialize_params(self, X: pt.TensorLike | None):
|
|
138
|
+
if self.initialized:
|
|
139
|
+
return
|
|
140
|
+
|
|
141
|
+
if self.n_in is None and X is None:
|
|
142
|
+
self.scale = None
|
|
143
|
+
self.loc = None
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
if X is not None:
|
|
147
|
+
n_in = X.type.shape[-1]
|
|
148
|
+
else:
|
|
149
|
+
n_in = self.n_in
|
|
150
|
+
|
|
151
|
+
if self.affine:
|
|
152
|
+
self.scale = pt.tensor(f"{self.name}_scale", shape=(n_in,))
|
|
153
|
+
self.loc = pt.tensor(f"{self.name}_loc", shape=(n_in,))
|
|
154
|
+
self.initialized = True
|
|
155
|
+
|
|
156
|
+
else:
|
|
157
|
+
self.initialized = True
|
|
158
|
+
|
|
159
|
+
def __call__(self, X: pt.TensorLike) -> pt.TensorLike:
|
|
160
|
+
X = pt.as_tensor(X)
|
|
161
|
+
inputs = [X]
|
|
162
|
+
|
|
163
|
+
self._initialize_params(X)
|
|
164
|
+
|
|
165
|
+
mu = X.mean(axis=0)
|
|
166
|
+
sigma_sq = X.var(axis=0)
|
|
167
|
+
|
|
168
|
+
X_normalized = (X - mu) / pt.sqrt(sigma_sq + self.epsilon)
|
|
169
|
+
|
|
170
|
+
if self.affine:
|
|
171
|
+
X_rescaled = X_normalized * self.scale + self.loc
|
|
172
|
+
inputs.extend([self.loc, self.scale])
|
|
173
|
+
|
|
174
|
+
else:
|
|
175
|
+
X_rescaled = X_normalized
|
|
176
|
+
|
|
177
|
+
if self.track_running_stats:
|
|
178
|
+
self.running_mean = mu.type(name=f"{self.name}_running_mean")
|
|
179
|
+
self.running_var = sigma_sq.type(name=f"{self.name}_running_var")
|
|
180
|
+
|
|
181
|
+
new_running_mean = self.momentum * mu + (1 - self.momentum) * self.running_mean
|
|
182
|
+
new_running_var = self.momentum * sigma_sq + (1 - self.momentum) * self.running_var
|
|
183
|
+
|
|
184
|
+
batch_norm_op = BatchNormLayer(
|
|
185
|
+
inputs=[*inputs, self.running_mean, self.running_var],
|
|
186
|
+
outputs=[X_rescaled, new_running_mean, new_running_var],
|
|
187
|
+
name=f"{self.name}",
|
|
188
|
+
n_in=self.n_in,
|
|
189
|
+
epsilon=self.epsilon,
|
|
190
|
+
momentum=self.momentum,
|
|
191
|
+
affine=self.affine,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
X_transformed, self.new_running_mean, self.new_running_var = batch_norm_op(
|
|
195
|
+
*inputs, self.running_mean, self.running_var
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
else:
|
|
199
|
+
batch_norm_op = NoRunningStatsBatchNormLayer(
|
|
200
|
+
inputs=inputs,
|
|
201
|
+
outputs=[X_rescaled],
|
|
202
|
+
name=f"{self.name}",
|
|
203
|
+
n_in=self.n_in,
|
|
204
|
+
epsilon=self.epsilon,
|
|
205
|
+
momentum=self.momentum,
|
|
206
|
+
affine=self.affine,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
X_transformed = batch_norm_op(*inputs)
|
|
210
|
+
|
|
211
|
+
X_transformed.name = f"{self.name}_output"
|
|
212
|
+
|
|
213
|
+
return X_transformed
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def Input(name: str, shape: tuple[int]) -> pt.TensorLike:
|
|
217
|
+
if not all(isinstance(dim, int) for dim in shape):
|
|
218
|
+
raise ValueError("All dimensions must be integers")
|
|
219
|
+
|
|
220
|
+
return pt.tensor(name=name, shape=shape)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def Sequential(*layers: Callable) -> Callable:
|
|
224
|
+
def forward(x: pt.TensorLike) -> pt.TensorLike:
|
|
225
|
+
for layer in layers:
|
|
226
|
+
x = layer(x)
|
|
227
|
+
return x
|
|
228
|
+
|
|
229
|
+
return forward
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
Squeeze = pt.squeeze
|
|
233
|
+
Concatenate = pt.concatenate
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
__all__ = ["BatchNorm2D", "Concatenate", "Dropout", "Input", "Linear", "Sequential", "Squeeze"]
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from collections.abc import Generator
|
|
2
|
+
from functools import partial
|
|
3
|
+
from typing import Any, Literal, cast
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from pytensor import config
|
|
8
|
+
from pytensor.compile.sharedvalue import SharedVariable
|
|
9
|
+
from pytensor.graph import graph_inputs
|
|
10
|
+
from pytensor.graph.basic import Constant, ancestors
|
|
11
|
+
from pytensor.printing import debugprint
|
|
12
|
+
from pytensor.tensor import TensorVariable
|
|
13
|
+
|
|
14
|
+
from pytensor_ml.pytensorf import LayerOp, function, rewrite_for_prediction
|
|
15
|
+
|
|
16
|
+
InitializationSchemes = Literal["zeros", "xavier_uniform", "xavier_normal"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def required_graph_inputs(tensor: TensorVariable) -> Generator[TensorVariable, None, None]:
|
|
20
|
+
return (
|
|
21
|
+
cast(TensorVariable, var)
|
|
22
|
+
for var in graph_inputs([tensor])
|
|
23
|
+
if not isinstance(var, Constant | SharedVariable)
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Model:
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
X: TensorVariable,
|
|
31
|
+
y: TensorVariable,
|
|
32
|
+
random_state: Any | None = None,
|
|
33
|
+
compile_kwargs: dict | None = None,
|
|
34
|
+
):
|
|
35
|
+
self.X = X
|
|
36
|
+
self.y = y
|
|
37
|
+
|
|
38
|
+
self.rng = np.random.default_rng(random_state)
|
|
39
|
+
|
|
40
|
+
self._compile_kwargs = compile_kwargs if compile_kwargs else {}
|
|
41
|
+
self._weight_values: list[np.ndarray[float]] | None = None
|
|
42
|
+
self._non_trainable_values: list[np.ndarray[float]] | None = None
|
|
43
|
+
self._predict_fn = None
|
|
44
|
+
|
|
45
|
+
def initialize_weights(
|
|
46
|
+
self,
|
|
47
|
+
scheme: InitializationSchemes = "xavier_normal",
|
|
48
|
+
random_seed: int | str | np.random.Generator | None = None,
|
|
49
|
+
):
|
|
50
|
+
self._weight_values = initialize_weights(self, scheme, random_seed)
|
|
51
|
+
|
|
52
|
+
def initialize_non_trainable_values(self) -> list[np.ndarray]:
|
|
53
|
+
if self.updates[0]:
|
|
54
|
+
values = [
|
|
55
|
+
self.rng.uniform(0, 1, param.type.shape).astype(param.type.dtype)
|
|
56
|
+
for param in self.updates[0]
|
|
57
|
+
]
|
|
58
|
+
self._non_trainable_values = values
|
|
59
|
+
else:
|
|
60
|
+
self._non_trainable_values = []
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def weights(self) -> list[TensorVariable]:
|
|
64
|
+
not_trainable = {self.X}
|
|
65
|
+
for ancestor in ancestors([self.y]):
|
|
66
|
+
node = ancestor.owner
|
|
67
|
+
if node is not None and isinstance(node.op, LayerOp):
|
|
68
|
+
for input_idx in node.op.update_map().values():
|
|
69
|
+
not_trainable.add(node.inputs[input_idx])
|
|
70
|
+
|
|
71
|
+
return [var for var in required_graph_inputs(self.y) if var not in not_trainable]
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def updates(self) -> tuple[list[TensorVariable], list[TensorVariable]]:
|
|
75
|
+
updates = {}
|
|
76
|
+
for ancestor in ancestors([self.y]):
|
|
77
|
+
node = ancestor.owner
|
|
78
|
+
if node is not None and isinstance(node.op, LayerOp):
|
|
79
|
+
for output_idx, input_idx in node.op.update_map().items():
|
|
80
|
+
new_value = node.outputs[output_idx]
|
|
81
|
+
old_value = node.inputs[input_idx]
|
|
82
|
+
assert old_value.type.dtype == new_value.type.dtype
|
|
83
|
+
updates[old_value] = new_value
|
|
84
|
+
|
|
85
|
+
if updates:
|
|
86
|
+
return list(updates.keys()), list(updates.values())
|
|
87
|
+
|
|
88
|
+
return [], []
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def weight_values(self) -> list[np.ndarray[float]]:
|
|
92
|
+
if self._weight_values is None:
|
|
93
|
+
self.initialize_weights()
|
|
94
|
+
return self._weight_values
|
|
95
|
+
|
|
96
|
+
@weight_values.setter
|
|
97
|
+
def weight_values(self, values: list[np.ndarray[float]]):
|
|
98
|
+
for i, new_value in enumerate(values):
|
|
99
|
+
self._weight_values[i][:] = new_value
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def non_trainable_values(self) -> list[np.ndarray[float]]:
|
|
103
|
+
if self._non_trainable_values is None:
|
|
104
|
+
self.initialize_non_trainable_values()
|
|
105
|
+
return self._non_trainable_values
|
|
106
|
+
|
|
107
|
+
@non_trainable_values.setter
|
|
108
|
+
def non_trainable_values(self, values: list[np.ndarray[float]]):
|
|
109
|
+
for i, new_value in enumerate(values):
|
|
110
|
+
self._non_trainable_values[i][:] = new_value
|
|
111
|
+
|
|
112
|
+
def predict(self, X_values: np.ndarray) -> np.ndarray:
|
|
113
|
+
y_pred = rewrite_for_prediction(self.y)
|
|
114
|
+
if self._predict_fn is None:
|
|
115
|
+
f = function(
|
|
116
|
+
[*self.weights, *self.updates[0], self.X],
|
|
117
|
+
y_pred,
|
|
118
|
+
**self._compile_kwargs,
|
|
119
|
+
)
|
|
120
|
+
self._predict_fn = partial(f, *self.weight_values, *self.non_trainable_values)
|
|
121
|
+
|
|
122
|
+
return cast(np.ndarray, self._predict_fn(X_values))
|
|
123
|
+
|
|
124
|
+
def __str__(self):
|
|
125
|
+
return debugprint(self.y, file="str")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _zero_init(shape: tuple[int], *args) -> np.ndarray:
|
|
129
|
+
return np.zeros(shape, dtype=config.floatX)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _xavier_uniform_init(shape: tuple[int], dtype, rng: np.random.Generator) -> np.ndarray:
|
|
133
|
+
scale = np.sqrt(6.0 / np.sum([x for x in shape if x is not None]))
|
|
134
|
+
return rng.uniform(-scale, scale, size=shape).astype(dtype)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _xavier_normal_init(shape: tuple[int], dtype, rng: np.random.Generator) -> np.ndarray:
|
|
138
|
+
scale = np.sqrt(2.0 / np.sum([x for x in shape if x is not None]))
|
|
139
|
+
return rng.normal(0, scale, size=shape).astype(dtype)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
initialization_factory = {
|
|
143
|
+
"zeros": _zero_init,
|
|
144
|
+
"xavier_uniform": _xavier_uniform_init,
|
|
145
|
+
"xavier_normal": _xavier_normal_init,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def initialize_weights(
|
|
150
|
+
model, scheme: InitializationSchemes, random_seed: int | str | np.random.Generator | None
|
|
151
|
+
):
|
|
152
|
+
rng = np.random.default_rng(random_seed)
|
|
153
|
+
|
|
154
|
+
initial_values = []
|
|
155
|
+
for var in model.weights:
|
|
156
|
+
shape = var.type.shape
|
|
157
|
+
dtype = var.type.dtype
|
|
158
|
+
f_initialize = initialization_factory[scheme]
|
|
159
|
+
initial_values.append(f_initialize(shape, dtype, rng))
|
|
160
|
+
|
|
161
|
+
return initial_values
|