pytensor-ml 0.0.1__tar.gz → 0.0.2__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.
Files changed (23) hide show
  1. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/PKG-INFO +1 -1
  2. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/pyproject.toml +4 -1
  3. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/pytensor_ml/__init__.py +3 -0
  4. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/pytensor_ml/_version.py +3 -3
  5. pytensor_ml-0.0.2/pytensor_ml/layers.py +103 -0
  6. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/pytensor_ml/model.py +11 -5
  7. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/pytensor_ml/optimizers.py +45 -10
  8. pytensor_ml-0.0.2/pytensor_ml/pytensorf.py +95 -0
  9. pytensor_ml-0.0.2/pytensor_ml/rewriting/__init__.py +0 -0
  10. pytensor_ml-0.0.2/pytensor_ml/rewriting/layers.py +37 -0
  11. pytensor_ml-0.0.2/pytensor_ml/util.py +49 -0
  12. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/pytensor_ml.egg-info/PKG-INFO +1 -1
  13. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/pytensor_ml.egg-info/SOURCES.txt +5 -1
  14. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/pytensor_ml.egg-info/requires.txt +1 -0
  15. pytensor_ml-0.0.1/pytensor_ml/layers.py +0 -53
  16. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/LICENSE +0 -0
  17. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/README.md +0 -0
  18. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/pytensor_ml/activations.py +0 -0
  19. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/pytensor_ml/loss.py +0 -0
  20. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/pytensor_ml.egg-info/dependency_links.txt +0 -0
  21. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/pytensor_ml.egg-info/top_level.txt +0 -0
  22. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/setup.cfg +0 -0
  23. {pytensor_ml-0.0.1 → pytensor_ml-0.0.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytensor_ml
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: A(nother) deep learning library, built on top of Pytensor
5
5
  Author: Ricardo Vieira
6
6
  Author-email: Jesse Grabowski <jessegrabowski@gmail.com>
@@ -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": "2024-12-30T10:40:07+0800",
11
+ "date": "2025-01-26T00:24:12+0800",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "599c7aacb627660af38618e6095d8762a8d49a0a",
15
- "version": "0.0.1"
14
+ "full-revisionid": "75eda5beff184dd32a4fe21444ec961a27c8eca3",
15
+ "version": "0.0.2"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -0,0 +1,103 @@
1
+ from abc import ABC
2
+ from collections.abc import Callable
3
+
4
+ import numpy as np
5
+ import pytensor.tensor as pt
6
+ import pytensor.tensor.random as ptr
7
+
8
+ from pytensor import config
9
+ from pytensor.compile.builders import OpFromGraph
10
+ from pytensor.compile.sharedvalue import shared
11
+
12
+
13
+ def shape_to_str(shape):
14
+ inner = ",".join([str(st_dim) if st_dim is not None else "?" for st_dim in shape])
15
+ return f"({inner})"
16
+
17
+
18
+ class Layer(ABC):
19
+ def __call__(self, x: pt.TensorLike) -> pt.TensorLike: ...
20
+
21
+
22
+ class LinearLayer(OpFromGraph): ...
23
+
24
+
25
+ class Linear(Layer):
26
+ __props__ = ("name", "n_in", "n_out")
27
+
28
+ def __init__(self, name: str | None, n_in: int, n_out: int):
29
+ self.name = name if name else "Linear"
30
+ self.n_in = n_in
31
+ self.n_out = n_out
32
+
33
+ self.W = pt.tensor(f"{self.name}_W", shape=(n_in, self.n_out))
34
+ self.b = pt.tensor(f"{self.name}_b", shape=(self.n_out,))
35
+
36
+ def __call__(self, X: pt.TensorLike) -> pt.TensorLike:
37
+ X = pt.as_tensor(X)
38
+
39
+ init_st_shape = shape_to_str(X.type.shape)
40
+
41
+ res = X @ self.W + self.b
42
+
43
+ final_st_shape = shape_to_str(res.type.shape)
44
+
45
+ ofg = LinearLayer(
46
+ inputs=[X, self.W, self.b],
47
+ outputs=[res],
48
+ inline=True,
49
+ name=f"{self.name}[{init_st_shape} -> {final_st_shape}]",
50
+ )
51
+ out = ofg(X, self.W, self.b)
52
+ out.name = f"{self.name}_output"
53
+
54
+ return out
55
+
56
+
57
+ class DropoutLayer(OpFromGraph): ...
58
+
59
+
60
+ class Dropout(Layer):
61
+ __props__ = ("name", "p")
62
+
63
+ def __init__(self, name: str | None, p: float = 0.5):
64
+ if p < 0.0 or p > 1.0:
65
+ raise ValueError(f"Dropout probability has to be between 0 and 1, but got {p}")
66
+ self.name = name if name else "Dropout"
67
+ self.p = p
68
+ self.rng = shared(np.random.default_rng())
69
+
70
+ def __call__(self, X: pt.TensorLike) -> pt.TensorLike:
71
+ X = pt.as_tensor(X)
72
+ new_rng, mask = ptr.bernoulli(p=1 - self.p, size=X.shape, rng=self.rng).owner.outputs
73
+ mask = mask.astype(config.floatX)
74
+
75
+ X_masked = DropoutLayer(
76
+ inputs=[X, mask], outputs=[pt.where(mask, ift=X / (1 - self.p), iff=0)], inline=True
77
+ )(X, mask)
78
+ X_masked.name = f"{self.name}[p = {self.p}]"
79
+
80
+ return X_masked
81
+
82
+
83
+ def Input(name: str, shape: tuple[int]) -> pt.TensorLike:
84
+ if not all(isinstance(dim, int) for dim in shape):
85
+ raise ValueError("All dimensions must be integers")
86
+
87
+ return pt.tensor(name=name, shape=shape)
88
+
89
+
90
+ def Sequential(*layers: Callable) -> Callable:
91
+ def forward(x: pt.TensorLike) -> pt.TensorLike:
92
+ for layer in layers:
93
+ x = layer(x)
94
+ return x
95
+
96
+ return forward
97
+
98
+
99
+ Squeeze = pt.squeeze
100
+ Concatenate = pt.concatenate
101
+
102
+
103
+ __all__ = ["Concatenate", "Input", "Linear", "Sequential", "Squeeze"]
@@ -5,13 +5,14 @@ from typing import Literal, cast
5
5
  import numpy as np
6
6
 
7
7
  from pytensor import config
8
- from pytensor.compile.function import function
9
8
  from pytensor.compile.sharedvalue import SharedVariable
10
9
  from pytensor.graph import graph_inputs
11
10
  from pytensor.graph.basic import Constant
12
11
  from pytensor.printing import debugprint
13
12
  from pytensor.tensor import TensorVariable
14
13
 
14
+ from pytensor_ml.pytensorf import function, rewrite_for_prediction
15
+
15
16
  InitializationSchemes = Literal["zeros", "xavier_uniform", "xavier_normal"]
16
17
 
17
18
 
@@ -24,14 +25,15 @@ def required_graph_inputs(tensor: TensorVariable) -> Generator[TensorVariable, N
24
25
 
25
26
 
26
27
  class Model:
27
- def __init__(self, X: TensorVariable, y: TensorVariable):
28
+ def __init__(self, X: TensorVariable, y: TensorVariable, compile_kwargs: dict | None = None):
28
29
  self.X = X
29
30
  self.y = y
30
31
 
32
+ self._compile_kwargs = compile_kwargs if compile_kwargs else {}
31
33
  self._weight_values: list[np.ndarray[float]] | None = None
32
34
  self._predict_fn = None
33
35
 
34
- def initalize_weights(
36
+ def initialize_weights(
35
37
  self,
36
38
  scheme: InitializationSchemes = "xavier_normal",
37
39
  random_seed: int | str | np.random.Generator | None = None,
@@ -55,10 +57,14 @@ class Model:
55
57
 
56
58
  def predict(self, X_values: np.ndarray) -> np.ndarray:
57
59
  if self._predict_fn is None:
58
- f = function([*self.weights, self.X], self.y)
60
+ f = function(
61
+ [*self.weights, self.X],
62
+ rewrite_for_prediction(self.y),
63
+ **self._compile_kwargs,
64
+ )
59
65
  self._predict_fn = partial(f, *self.weight_values)
60
66
 
61
- return self._predict_fn(X_values)
67
+ return cast(np.ndarray, self._predict_fn(X_values))
62
68
 
63
69
  def __str__(self):
64
70
  return debugprint(self.y, file="str")
@@ -4,12 +4,12 @@ from collections.abc import Callable, Sequence
4
4
  import numpy as np
5
5
 
6
6
  from pytensor import config
7
- from pytensor.compile.function import function
8
7
  from pytensor.compile.function.types import Function
9
8
  from pytensor.gradient import grad
10
9
  from pytensor.tensor import TensorLike, TensorVariable, sqrt, tensor
11
10
 
12
11
  from pytensor_ml.model import Model
12
+ from pytensor_ml.pytensorf import function, rewrite_pregrad
13
13
 
14
14
 
15
15
  class Optimizer(ABC):
@@ -19,6 +19,7 @@ class Optimizer(ABC):
19
19
  loss_fn: Callable,
20
20
  ndim_out: int = 1,
21
21
  optimizer_weights: list[TensorVariable] | None = None,
22
+ compile_kwargs: dict | None = None,
22
23
  ):
23
24
  self.model = model
24
25
  self.loss_fn = loss_fn
@@ -26,7 +27,7 @@ class Optimizer(ABC):
26
27
  self.optimizer_weights = optimizer_weights if optimizer_weights is not None else []
27
28
  self._optimizer_weights_values = self._initialize_weights()
28
29
 
29
- self.update_fn = self.build_update_fn()
30
+ self.update_fn = self.build_update_fn(compile_kwargs)
30
31
 
31
32
  @abstractmethod
32
33
  def update_parameters(self, params: Sequence[TensorVariable], loss: TensorVariable): ...
@@ -51,7 +52,7 @@ class Optimizer(ABC):
51
52
  n_params = len(self.model.weights)
52
53
  return all_weights[:n_params], all_weights[n_params:]
53
54
 
54
- def build_update_fn(self) -> Function:
55
+ def build_update_fn(self, compile_kwargs) -> Function:
55
56
  """
56
57
  Compile a function to update model weights
57
58
 
@@ -59,12 +60,17 @@ class Optimizer(ABC):
59
60
  values. By default, the functions also returns the loss value. To return additional diagnostics, this method
60
61
  should be overloaded.
61
62
 
63
+ Parameters
64
+ ----------
65
+ compile_kwargs: dict
66
+ Keyword arguments passed to pytensor.function
67
+
62
68
  Returns
63
69
  -------
64
70
  update_fn: Function
65
71
  A function that updates the model weights given a batch of data, targets, and current weights.
66
72
  """
67
-
73
+ compile_kwargs = compile_kwargs if compile_kwargs else {}
68
74
  x, y_hat = self.model.X, self.model.y
69
75
 
70
76
  label_slice = (slice(None),) * self.ndim_out + (0,) * (y_hat.ndim - self.ndim_out)
@@ -74,9 +80,16 @@ class Optimizer(ABC):
74
80
  optimizer_weights = self.optimizer_weights
75
81
 
76
82
  loss = self.loss_fn(y, y_hat)
83
+ loss = rewrite_pregrad(loss)
77
84
 
78
85
  all_weights = self.update_parameters(weights + optimizer_weights, loss)
79
- fn = function([x, y, *weights, *optimizer_weights], [*all_weights, loss])
86
+ fn = function(
87
+ [x, y, *weights, *optimizer_weights],
88
+ [*all_weights, loss],
89
+ **compile_kwargs,
90
+ )
91
+ fn.trust_input = True
92
+
80
93
  return fn
81
94
 
82
95
  def step(self, x_values, y_values) -> np.ndarray:
@@ -90,7 +103,6 @@ class Optimizer(ABC):
90
103
  *new_weights, loss_values = self.update_fn(
91
104
  x_values, y_values, *self.model.weight_values, *self.optimizer_weights_values
92
105
  )
93
- self.update_fn.trust_input = True
94
106
 
95
107
  self.model.weight_values, self.optimizer_weights_values = self._split_weights(new_weights)
96
108
 
@@ -98,9 +110,17 @@ class Optimizer(ABC):
98
110
 
99
111
 
100
112
  class SGD(Optimizer):
101
- def __init__(self, model, loss_fn, *, ndim_out: int = 1, learning_rate: TensorLike = 0.01):
113
+ def __init__(
114
+ self,
115
+ model,
116
+ loss_fn,
117
+ *,
118
+ ndim_out: int = 1,
119
+ learning_rate: TensorLike = 0.01,
120
+ compile_kwargs: dict | None = None,
121
+ ):
102
122
  self.learning_rate = learning_rate
103
- super().__init__(model, loss_fn, ndim_out=ndim_out)
123
+ super().__init__(model, loss_fn, ndim_out=ndim_out, compile_kwargs=compile_kwargs)
104
124
 
105
125
  def update_parameters(
106
126
  self, params: list[TensorVariable], loss: TensorVariable
@@ -122,17 +142,25 @@ class ADAGrad(Optimizer):
122
142
  ndim_out: int = 1,
123
143
  learning_rate: TensorLike = 0.01,
124
144
  epsilon: TensorLike = 1e-8,
145
+ compile_kwargs: dict | None = None,
125
146
  ):
126
147
  self.learning_rate = learning_rate
127
148
  self.epsilon = epsilon
128
149
  g2_weights = [param.type() for param in model.weights]
129
150
 
130
- super().__init__(model, loss_fn, ndim_out=ndim_out, optimizer_weights=g2_weights)
151
+ super().__init__(
152
+ model,
153
+ loss_fn,
154
+ ndim_out=ndim_out,
155
+ optimizer_weights=g2_weights,
156
+ compile_kwargs=compile_kwargs,
157
+ )
131
158
 
132
159
  def update_parameters(
133
160
  self, weights: list[TensorVariable], loss: TensorVariable
134
161
  ) -> list[TensorVariable]:
135
162
  weights, optimizer_weights = self._split_weights(weights)
163
+
136
164
  grads = grad(loss, weights)
137
165
 
138
166
  new_weights = []
@@ -158,6 +186,7 @@ class Adam(Optimizer):
158
186
  beta1: TensorLike = 0.9,
159
187
  beta2: TensorLike = 0.999,
160
188
  epsilon: TensorLike = 1e-8,
189
+ compile_kwargs: dict | None = None,
161
190
  ):
162
191
  self.learning_rate = learning_rate
163
192
  self.beta1 = beta1
@@ -169,7 +198,13 @@ class Adam(Optimizer):
169
198
  t = tensor("t", shape=(1,), dtype=config.floatX)
170
199
 
171
200
  optimizer_weights = m_weights + v_weights + [t]
172
- super().__init__(model, loss_fn, ndim_out=ndim_out, optimizer_weights=optimizer_weights)
201
+ super().__init__(
202
+ model,
203
+ loss_fn,
204
+ ndim_out=ndim_out,
205
+ optimizer_weights=optimizer_weights,
206
+ compile_kwargs=compile_kwargs,
207
+ )
173
208
 
174
209
  def update_parameters(
175
210
  self, weights: list[TensorVariable], loss: TensorVariable
@@ -0,0 +1,95 @@
1
+ from typing import cast
2
+
3
+ import pytensor
4
+
5
+ from pymc.pytensorf import SeedSequenceSeed, collect_default_updates, reseed_rngs
6
+ from pytensor import Mode
7
+ from pytensor.compile import Function, SharedVariable, get_mode
8
+ from pytensor.graph import FunctionGraph, RewriteDatabaseQuery, rewrite_graph
9
+ from pytensor.tensor.variable import Variable
10
+
11
+ from pytensor_ml.rewriting.layers import predict_db
12
+
13
+
14
+ def atleast_list(x):
15
+ if not isinstance(x, list | tuple):
16
+ return [x]
17
+ return x
18
+
19
+
20
+ def function(
21
+ inputs,
22
+ outputs,
23
+ random_seed: SeedSequenceSeed = None,
24
+ mode=None,
25
+ **kwargs,
26
+ ) -> Function:
27
+ """
28
+ Compile a Pytensor function, including specialized rewrites.
29
+
30
+ Parameters
31
+ ----------
32
+ inputs: list of TensorVariables, optional
33
+ Inputs of the compiled PyTensor function
34
+ outputs: list of TensorVariables, optional
35
+ Outputs of the compiled PyTensor function
36
+ random_seed: int, array-like of int or SeedSequence, optional
37
+ Seed used to override any RandomState/Generator shared variables in the graph.
38
+ If not specified, the value of original shared variables will still be overwritten.
39
+ mode: optional
40
+ PyTensor mode used to compile the function
41
+
42
+ Returns
43
+ -------
44
+ pytensor_function: Function
45
+ Compiled function
46
+ """
47
+ rng_updates = collect_default_updates(
48
+ inputs=[inp.variable if isinstance(inp, pytensor.In) else inp for inp in inputs],
49
+ outputs=[
50
+ out.variable if isinstance(out, pytensor.Out) else out for out in atleast_list(outputs)
51
+ ],
52
+ )
53
+
54
+ if rng_updates:
55
+ rngs = cast(list[SharedVariable], list(rng_updates))
56
+ reseed_rngs(rngs, random_seed)
57
+
58
+ mode = get_mode(mode)
59
+ opt_qry = mode.provided_optimizer.including("random_make_inplace")
60
+ mode = Mode(linker=mode.linker, optimizer=opt_qry)
61
+ pytensor_function = pytensor.function(
62
+ inputs,
63
+ outputs,
64
+ updates={**rng_updates, **kwargs.pop("updates", {})},
65
+ mode=mode,
66
+ **kwargs,
67
+ )
68
+ return pytensor_function
69
+
70
+
71
+ def rewrite_pregrad(graph):
72
+ """Apply simplifying or stabilizing rewrites to graph that are safe to use pre-grad."""
73
+ return rewrite_graph(graph, include=("canonicalize", "stabilize"))
74
+
75
+
76
+ def rewrite_for_prediction(graph):
77
+ """Apply rewrites to specialize a graph for forward passes (e.g. removing Dropout layers)"""
78
+ if isinstance(graph, FunctionGraph):
79
+ fgraph = graph
80
+ else:
81
+ outputs = [graph] if isinstance(graph, Variable) else graph
82
+ fgraph = FunctionGraph(outputs=outputs, clone=False)
83
+
84
+ rewriter = predict_db.query(RewriteDatabaseQuery(include=["basic"]))
85
+ rewriter.rewrite(fgraph)
86
+
87
+ if isinstance(graph, FunctionGraph):
88
+ return fgraph
89
+ if isinstance(graph, Variable):
90
+ return fgraph.outputs[0]
91
+
92
+ return fgraph.outputs
93
+
94
+
95
+ __all__ = ["function"]
File without changes
@@ -0,0 +1,37 @@
1
+ from pytensor.graph.basic import Apply
2
+ from pytensor.graph.fg import FunctionGraph
3
+ from pytensor.graph.rewriting.basic import node_rewriter
4
+ from pytensor.graph.rewriting.db import EquilibriumDB
5
+ from pytensor.tensor.variable import Variable
6
+
7
+ from pytensor_ml.layers import DropoutLayer
8
+
9
+ predict_db = EquilibriumDB()
10
+
11
+
12
+ @node_rewriter([DropoutLayer])
13
+ def remove_dropout_for_prediction(fgraph: FunctionGraph, node: Apply) -> list[Variable] | None:
14
+ """
15
+ Set dropout probability to zero for all dropout layers.
16
+
17
+ Parameters
18
+ ----------
19
+ fgraph: FunctionGraph
20
+ Graph being rewritten
21
+ node: Node
22
+ Node being rewritten
23
+
24
+ Returns
25
+ -------
26
+ X: Variable
27
+ The input to the dropout layer, removing the dropout from the graph
28
+ """
29
+ X, rng = node.inputs
30
+ return [X]
31
+
32
+
33
+ predict_db.register(
34
+ "remove_dropout_for_prediction",
35
+ remove_dropout_for_prediction,
36
+ "basic",
37
+ )
@@ -0,0 +1,49 @@
1
+ import numpy as np
2
+
3
+ from pytensor import config
4
+
5
+
6
+ class DataLoader:
7
+ def __init__(self, X, y, batch_size=64, dtype=None):
8
+ if dtype is None:
9
+ dtype = config.floatX
10
+ self.rng = np.random.default_rng()
11
+
12
+ self.X = X.astype(dtype)
13
+ self.y = y.astype(dtype)
14
+ self.n = X.shape[0]
15
+
16
+ self.batch_size = batch_size
17
+ self.cursor = 0
18
+ self.indices = np.arange(len(X)).astype("int32")
19
+ self.reset()
20
+
21
+ def shuffle(self):
22
+ self.rng.shuffle(self.indices)
23
+
24
+ def move_cursor(self):
25
+ start, stop = self.cursor, self.cursor + self.batch_size
26
+
27
+ has_remainder, stop = divmod(stop, self.n)
28
+
29
+ idx = slice(start, None if has_remainder > 0 else stop)
30
+ indices = self.indices[idx]
31
+
32
+ if has_remainder > 0:
33
+ excess = self.batch_size - indices.shape[0]
34
+ self.shuffle()
35
+ self.epoch += 1
36
+ indices = np.r_[indices, self.indices[:excess]]
37
+
38
+ self.cursor = (self.cursor + self.batch_size) % self.n
39
+
40
+ return indices
41
+
42
+ def reset(self):
43
+ self.cursor = 0
44
+ self.epoch = 0
45
+ self.shuffle()
46
+
47
+ def __call__(self):
48
+ idx = self.move_cursor()
49
+ return self.X[idx], self.y[idx]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytensor-ml
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: A(nother) deep learning library, built on top of Pytensor
5
5
  Author: Ricardo Vieira
6
6
  Author-email: Jesse Grabowski <jessegrabowski@gmail.com>
@@ -9,8 +9,12 @@ pytensor_ml/layers.py
9
9
  pytensor_ml/loss.py
10
10
  pytensor_ml/model.py
11
11
  pytensor_ml/optimizers.py
12
+ pytensor_ml/pytensorf.py
13
+ pytensor_ml/util.py
12
14
  pytensor_ml.egg-info/PKG-INFO
13
15
  pytensor_ml.egg-info/SOURCES.txt
14
16
  pytensor_ml.egg-info/dependency_links.txt
15
17
  pytensor_ml.egg-info/requires.txt
16
- pytensor_ml.egg-info/top_level.txt
18
+ pytensor_ml.egg-info/top_level.txt
19
+ pytensor_ml/rewriting/__init__.py
20
+ pytensor_ml/rewriting/layers.py
@@ -1,5 +1,6 @@
1
1
  pytensor
2
2
  numpy
3
+ pymc
3
4
 
4
5
  [dev]
5
6
  pre-commit
@@ -1,53 +0,0 @@
1
- from abc import ABC
2
- from collections.abc import Callable
3
-
4
- from pytensor.tensor import TensorLike, as_tensor, concatenate, squeeze, tensor
5
-
6
-
7
- class Layer(ABC):
8
- def __call__(self, x: TensorLike) -> TensorLike: ...
9
-
10
-
11
- class Linear(Layer):
12
- __props__ = ("name", "n_out")
13
-
14
- def __init__(self, name: str | None, n_in: int, n_out: int):
15
- self.name = name
16
- self.n_in = n_in
17
- self.n_out = n_out
18
-
19
- self.W = tensor(f"{self.name}_W", shape=(n_in, self.n_out))
20
- self.b = tensor(f"{self.name}_b", shape=(self.n_out,))
21
-
22
- def __call__(self, X: TensorLike) -> TensorLike:
23
- X = as_tensor(X)
24
-
25
- init_st_shape = [st_dim if st_dim is not None else "?" for st_dim in X.type.shape]
26
- X = X @ self.W + self.b
27
- final_st_shape = [st_dim if st_dim is not None else "?" for st_dim in X.type.shape]
28
- X.name = f"{self.__class__.__name__}[{init_st_shape} -> {final_st_shape}]"
29
-
30
- return X
31
-
32
-
33
- def Input(name: str, shape: tuple[int]) -> TensorLike:
34
- if not all(isinstance(dim, int) for dim in shape):
35
- raise ValueError("All dimensions must be integers")
36
-
37
- return tensor(name=name, shape=shape)
38
-
39
-
40
- def Sequential(*layers: Callable) -> Callable:
41
- def forward(x: TensorLike) -> TensorLike:
42
- for layer in layers:
43
- x = layer(x)
44
- return x
45
-
46
- return forward
47
-
48
-
49
- Squeeze = squeeze
50
- Concatenate = concatenate
51
-
52
-
53
- __all__ = ["Concatenate", "Input", "Linear", "Sequential", "Squeeze"]
File without changes
File without changes
File without changes
File without changes