pytensor-ml 0.0.4__tar.gz → 0.0.6__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.6/.github/workflows/mypy.yml +57 -0
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/.github/workflows/release.yml +1 -1
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/.pre-commit-config.yaml +2 -2
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/PKG-INFO +8 -6
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/conda_envs/pytensor_ml-gpu_jax.yml +1 -2
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/conda_envs/pytensor_ml.yml +8 -2
- pytensor_ml-0.0.6/examples/mnist_feed_forward.ipynb +198 -0
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/pyproject.toml +26 -6
- pytensor_ml-0.0.6/pytensor_ml/__init__.py +29 -0
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/pytensor_ml/_version.py +2 -2
- pytensor_ml-0.0.6/pytensor_ml/activations.py +225 -0
- pytensor_ml-0.0.6/pytensor_ml/base.py +56 -0
- pytensor_ml-0.0.6/pytensor_ml/checkpoint.py +115 -0
- pytensor_ml-0.0.6/pytensor_ml/dispatch/__init__.py +53 -0
- pytensor_ml-0.0.6/pytensor_ml/dispatch/jax/__init__.py +3 -0
- pytensor_ml-0.0.6/pytensor_ml/dispatch/jax/attention.py +25 -0
- pytensor_ml-0.0.6/pytensor_ml/dispatch/mlx/__init__.py +3 -0
- pytensor_ml-0.0.6/pytensor_ml/dispatch/mlx/attention.py +30 -0
- pytensor_ml-0.0.6/pytensor_ml/json_serialize.py +89 -0
- pytensor_ml-0.0.6/pytensor_ml/layers/__init__.py +40 -0
- pytensor_ml-0.0.6/pytensor_ml/layers/attention.py +252 -0
- pytensor_ml-0.0.6/pytensor_ml/layers/combinators.py +35 -0
- pytensor_ml-0.0.6/pytensor_ml/layers/dropout.py +40 -0
- pytensor_ml-0.0.6/pytensor_ml/layers/embedding.py +53 -0
- pytensor_ml-0.0.6/pytensor_ml/layers/linear.py +58 -0
- pytensor_ml-0.0.6/pytensor_ml/layers/norm.py +337 -0
- pytensor_ml-0.0.6/pytensor_ml/layers/transformer.py +173 -0
- pytensor_ml-0.0.6/pytensor_ml/loss.py +113 -0
- pytensor_ml-0.0.6/pytensor_ml/model.py +118 -0
- pytensor_ml-0.0.6/pytensor_ml/optim/__init__.py +59 -0
- pytensor_ml-0.0.6/pytensor_ml/optim/alias.py +233 -0
- pytensor_ml-0.0.6/pytensor_ml/optim/base.py +198 -0
- pytensor_ml-0.0.6/pytensor_ml/optim/clipping.py +64 -0
- pytensor_ml-0.0.6/pytensor_ml/optim/rules.py +586 -0
- pytensor_ml-0.0.6/pytensor_ml/optim/train.py +63 -0
- pytensor_ml-0.0.6/pytensor_ml/optim/transform.py +138 -0
- pytensor_ml-0.0.6/pytensor_ml/params.py +56 -0
- pytensor_ml-0.0.6/pytensor_ml/pretrained.py +256 -0
- pytensor_ml-0.0.6/pytensor_ml/pytensorf/__init__.py +29 -0
- pytensor_ml-0.0.6/pytensor_ml/pytensorf/collect.py +95 -0
- pytensor_ml-0.0.6/pytensor_ml/pytensorf/compile.py +109 -0
- pytensor_ml-0.0.6/pytensor_ml/pytensorf/rewrite.py +41 -0
- pytensor_ml-0.0.6/pytensor_ml/pytensorf/rng.py +173 -0
- pytensor_ml-0.0.6/pytensor_ml/rewriting/layers.py +85 -0
- pytensor_ml-0.0.6/pytensor_ml/serialize/__init__.py +2 -0
- pytensor_ml-0.0.6/pytensor_ml/serialize/base.py +282 -0
- pytensor_ml-0.0.6/pytensor_ml/serialize/blockwise.py +15 -0
- pytensor_ml-0.0.6/pytensor_ml/serialize/builders.py +48 -0
- pytensor_ml-0.0.6/pytensor_ml/serialize/elemwise.py +36 -0
- pytensor_ml-0.0.6/pytensor_ml/serialize/scalar.py +56 -0
- pytensor_ml-0.0.6/pytensor_ml/serialize/scan.py +41 -0
- pytensor_ml-0.0.6/pytensor_ml/state.py +118 -0
- pytensor_ml-0.0.6/pytensor_ml/util.py +75 -0
- pytensor_ml-0.0.6/scripts/run_mypy.py +12 -0
- pytensor_ml-0.0.6/tests/dispatch/jax/__init__.py +0 -0
- pytensor_ml-0.0.6/tests/dispatch/jax/test_attention.py +64 -0
- pytensor_ml-0.0.6/tests/dispatch/jax/test_basic.py +75 -0
- pytensor_ml-0.0.6/tests/dispatch/mlx/__init__.py +0 -0
- pytensor_ml-0.0.6/tests/dispatch/mlx/test_attention.py +65 -0
- pytensor_ml-0.0.6/tests/dispatch/mlx/test_basic.py +75 -0
- pytensor_ml-0.0.6/tests/optim/__init__.py +0 -0
- pytensor_ml-0.0.6/tests/optim/conftest.py +40 -0
- pytensor_ml-0.0.6/tests/optim/test_clipping.py +43 -0
- pytensor_ml-0.0.6/tests/optim/test_rules.py +439 -0
- pytensor_ml-0.0.6/tests/optim/test_training.py +158 -0
- pytensor_ml-0.0.6/tests/optim/test_transform.py +86 -0
- pytensor_ml-0.0.6/tests/rewriting/__init__.py +0 -0
- pytensor_ml-0.0.6/tests/rewriting/conftest.py +23 -0
- pytensor_ml-0.0.6/tests/rewriting/test_layers.py +52 -0
- pytensor_ml-0.0.6/tests/test_activations.py +143 -0
- pytensor_ml-0.0.6/tests/test_attention.py +243 -0
- pytensor_ml-0.0.6/tests/test_checkpoint.py +151 -0
- pytensor_ml-0.0.6/tests/test_collect.py +82 -0
- pytensor_ml-0.0.6/tests/test_layers.py +332 -0
- pytensor_ml-0.0.6/tests/test_loss.py +72 -0
- pytensor_ml-0.0.6/tests/test_model.py +102 -0
- pytensor_ml-0.0.6/tests/test_pretrained.py +142 -0
- pytensor_ml-0.0.6/tests/test_pytensorf.py +42 -0
- pytensor_ml-0.0.6/tests/test_serialize.py +262 -0
- pytensor_ml-0.0.6/tests/test_state.py +93 -0
- pytensor_ml-0.0.6/tests/test_transformer.py +142 -0
- pytensor_ml-0.0.6/tests/test_util.py +93 -0
- pytensor_ml-0.0.4/examples/mnist_feed_forward.ipynb +0 -359
- pytensor_ml-0.0.4/pytensor_ml/__init__.py +0 -16
- pytensor_ml-0.0.4/pytensor_ml/activations.py +0 -57
- pytensor_ml-0.0.4/pytensor_ml/layers.py +0 -238
- pytensor_ml-0.0.4/pytensor_ml/loss.py +0 -65
- pytensor_ml-0.0.4/pytensor_ml/model.py +0 -109
- pytensor_ml-0.0.4/pytensor_ml/optimizers.py +0 -365
- pytensor_ml-0.0.4/pytensor_ml/params.py +0 -226
- pytensor_ml-0.0.4/pytensor_ml/pytensorf.py +0 -115
- pytensor_ml-0.0.4/pytensor_ml/rewriting/layers.py +0 -85
- pytensor_ml-0.0.4/pytensor_ml/state.py +0 -303
- pytensor_ml-0.0.4/pytensor_ml/util.py +0 -49
- pytensor_ml-0.0.4/tests/rewriting/test_layers.py +0 -62
- pytensor_ml-0.0.4/tests/test_layers.py +0 -182
- pytensor_ml-0.0.4/tests/test_loss.py +0 -61
- pytensor_ml-0.0.4/tests/test_model.py +0 -101
- pytensor_ml-0.0.4/tests/test_optimizers.py +0 -172
- pytensor_ml-0.0.4/tests/test_params.py +0 -107
- pytensor_ml-0.0.4/tests/test_state.py +0 -138
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/.gitattributes +0 -0
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/.github/workflows/pre-commit.yml +0 -0
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/.github/workflows/run_tests.yml +0 -0
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/.gitignore +0 -0
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/LICENSE +0 -0
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/README.md +0 -0
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/pytensor_ml/rewriting/__init__.py +0 -0
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/tests/__init__.py +0 -0
- {pytensor_ml-0.0.4 → pytensor_ml-0.0.6}/tests/conftest.py +0 -0
- {pytensor_ml-0.0.4/tests/rewriting → pytensor_ml-0.0.6/tests/dispatch}/__init__.py +0 -0
- /pytensor_ml-0.0.4/tests/test_pytensorf.py → /pytensor_ml-0.0.6/tests/test_base.py +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: mypy
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
paths:
|
|
8
|
+
- ".github/workflows/mypy.yml"
|
|
9
|
+
- "pytensor_ml/**"
|
|
10
|
+
- "scripts/run_mypy.py"
|
|
11
|
+
- "pyproject.toml"
|
|
12
|
+
- "conda_envs/**"
|
|
13
|
+
|
|
14
|
+
# Cancels all previous workflow runs for pull requests that have not completed.
|
|
15
|
+
concurrency:
|
|
16
|
+
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
|
|
17
|
+
cancel-in-progress: true
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
mypy:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
|
|
23
|
+
defaults:
|
|
24
|
+
run:
|
|
25
|
+
shell: bash -l {0}
|
|
26
|
+
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
29
|
+
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
|
30
|
+
env:
|
|
31
|
+
CACHE_NUMBER: 0
|
|
32
|
+
with:
|
|
33
|
+
path: ~/conda_pkgs_dir
|
|
34
|
+
key: ${{ runner.os }}-py312-conda-${{ env.CACHE_NUMBER }}-${{
|
|
35
|
+
hashFiles('conda_envs/pytensor_ml.yml') }}
|
|
36
|
+
- uses: conda-incubator/setup-miniconda@8ee1f361103df19b6f8c8655fd3967a8ecb162d5 # v4.0.1
|
|
37
|
+
with:
|
|
38
|
+
miniforge-variant: Miniforge3
|
|
39
|
+
miniforge-version: latest
|
|
40
|
+
mamba-version: "*"
|
|
41
|
+
activate-environment: pytensor_ml
|
|
42
|
+
channel-priority: strict
|
|
43
|
+
environment-file: conda_envs/pytensor_ml.yml
|
|
44
|
+
python-version: 3.12
|
|
45
|
+
use-mamba: true
|
|
46
|
+
use-only-tar-bz2: false
|
|
47
|
+
|
|
48
|
+
- name: Install current branch
|
|
49
|
+
run: |
|
|
50
|
+
conda activate pytensor_ml
|
|
51
|
+
pip install -e .
|
|
52
|
+
python --version
|
|
53
|
+
|
|
54
|
+
- name: Run mypy
|
|
55
|
+
run: |
|
|
56
|
+
conda activate pytensor_ml
|
|
57
|
+
python ./scripts/run_mypy.py
|
|
@@ -30,7 +30,7 @@ jobs:
|
|
|
30
30
|
|
|
31
31
|
publish-package:
|
|
32
32
|
# Don't publish from forks
|
|
33
|
-
if: github.repository_owner == '
|
|
33
|
+
if: github.repository_owner == 'pymc-devs' && github.event_name == 'release' && github.event.action == 'published'
|
|
34
34
|
# Use the `release` GitHub environment to protect the Trusted Publishing (OIDC)
|
|
35
35
|
# workflow by requiring signoff from a maintainer.
|
|
36
36
|
environment: release
|
|
@@ -12,7 +12,7 @@ repos:
|
|
|
12
12
|
- id: trailing-whitespace
|
|
13
13
|
|
|
14
14
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
15
|
-
rev: v0.15.
|
|
15
|
+
rev: v0.15.17
|
|
16
16
|
hooks:
|
|
17
17
|
- id: ruff
|
|
18
18
|
args: [ --fix, --exit-non-zero-on-fix ]
|
|
@@ -50,7 +50,7 @@ repos:
|
|
|
50
50
|
conf.py)
|
|
51
51
|
entry: >
|
|
52
52
|
(?x)(arviz-devs.github.io|
|
|
53
|
-
pymc-
|
|
53
|
+
pymc-extras.readthedocs.io|
|
|
54
54
|
docs.pymc.io|
|
|
55
55
|
numpy.org/doc|
|
|
56
56
|
pymc-examples.readthedocs.io|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytensor_ml
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.6
|
|
4
4
|
Summary: A(nother) deep learning library, built on top of Pytensor
|
|
5
|
-
Project-URL: Homepage, https://github.com/
|
|
6
|
-
Project-URL: Source, https://github.com/
|
|
7
|
-
Project-URL: Tracker, https://github.com/
|
|
5
|
+
Project-URL: Homepage, https://github.com/pymc-devs/pytensor_ml
|
|
6
|
+
Project-URL: Source, https://github.com/pymc-devs/pytensor_ml
|
|
7
|
+
Project-URL: Tracker, https://github.com/pymc-devs/pytensor_ml/issues
|
|
8
8
|
Author: Ricardo Vieira
|
|
9
9
|
Author-email: Jesse Grabowski <jessegrabowski@gmail.com>
|
|
10
10
|
License: Apache License
|
|
@@ -224,9 +224,11 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
224
224
|
Classifier: Topic :: Scientific/Engineering
|
|
225
225
|
Requires-Python: >=3.12
|
|
226
226
|
Requires-Dist: numpy
|
|
227
|
-
Requires-Dist:
|
|
228
|
-
Requires-Dist:
|
|
227
|
+
Requires-Dist: pytensor<4.0.0,>=3.2.3
|
|
228
|
+
Requires-Dist: safetensors
|
|
229
229
|
Provides-Extra: dev
|
|
230
|
+
Requires-Dist: jax; extra == 'dev'
|
|
231
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
230
232
|
Requires-Dist: pre-commit; extra == 'dev'
|
|
231
233
|
Requires-Dist: pytest; extra == 'dev'
|
|
232
234
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
@@ -5,9 +5,9 @@ channels:
|
|
|
5
5
|
|
|
6
6
|
dependencies:
|
|
7
7
|
- python>=3.12
|
|
8
|
-
- pytensor>=3.
|
|
9
|
-
- pymc>=6.0.0,<7.0.0
|
|
8
|
+
- pytensor>=3.2.3,<4.0.0
|
|
10
9
|
- numpy
|
|
10
|
+
- safetensors
|
|
11
11
|
- scikit-learn
|
|
12
12
|
|
|
13
13
|
# For testing
|
|
@@ -28,3 +28,9 @@ dependencies:
|
|
|
28
28
|
|
|
29
29
|
# developer tools
|
|
30
30
|
- pre-commit
|
|
31
|
+
- mypy
|
|
32
|
+
|
|
33
|
+
# JAX backend for dispatch tests; via pip so Windows pulls CPU wheels instead of conda jaxlib
|
|
34
|
+
- pip
|
|
35
|
+
- pip:
|
|
36
|
+
- jax
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "code",
|
|
5
|
+
"execution_count": null,
|
|
6
|
+
"id": "15279e7f",
|
|
7
|
+
"metadata": {},
|
|
8
|
+
"outputs": [],
|
|
9
|
+
"source": [
|
|
10
|
+
"%load_ext autoreload\n",
|
|
11
|
+
"%autoreload 2"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"cell_type": "code",
|
|
16
|
+
"execution_count": null,
|
|
17
|
+
"id": "00915a61",
|
|
18
|
+
"metadata": {},
|
|
19
|
+
"outputs": [],
|
|
20
|
+
"source": [
|
|
21
|
+
"import pytensor\n",
|
|
22
|
+
"\n",
|
|
23
|
+
"floatX = \"float32\"\n",
|
|
24
|
+
"pytensor.config.floatX = floatX"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"cell_type": "code",
|
|
29
|
+
"execution_count": null,
|
|
30
|
+
"id": "fae24c05",
|
|
31
|
+
"metadata": {},
|
|
32
|
+
"outputs": [],
|
|
33
|
+
"source": [
|
|
34
|
+
"import numpy as np\n",
|
|
35
|
+
"\n",
|
|
36
|
+
"from sklearn.datasets import load_digits\n",
|
|
37
|
+
"from sklearn.preprocessing import MinMaxScaler, OneHotEncoder\n",
|
|
38
|
+
"\n",
|
|
39
|
+
"from pytensor_ml.activations import LeakyReLU\n",
|
|
40
|
+
"from pytensor_ml.layers import BatchNorm2D, Dropout, Linear, Sequential\n",
|
|
41
|
+
"from pytensor_ml.loss import CrossEntropy\n",
|
|
42
|
+
"from pytensor_ml.model import Model\n",
|
|
43
|
+
"from pytensor_ml.optim import adagrad\n",
|
|
44
|
+
"from pytensor_ml.util import DataLoader"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"cell_type": "code",
|
|
49
|
+
"execution_count": null,
|
|
50
|
+
"id": "65882b64",
|
|
51
|
+
"metadata": {},
|
|
52
|
+
"outputs": [],
|
|
53
|
+
"source": [
|
|
54
|
+
"X, y = load_digits(return_X_y=True)\n",
|
|
55
|
+
"y_onehot = OneHotEncoder().fit_transform(y[:, None]).toarray().astype(floatX)\n",
|
|
56
|
+
"X_normed = MinMaxScaler().fit_transform(X).astype(floatX)"
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"cell_type": "code",
|
|
61
|
+
"execution_count": null,
|
|
62
|
+
"id": "f81f9076",
|
|
63
|
+
"metadata": {},
|
|
64
|
+
"outputs": [],
|
|
65
|
+
"source": [
|
|
66
|
+
"# X_in = Input('X_in', shape=(64, ))\n",
|
|
67
|
+
"X_in = pytensor.tensor.tensor(\"X_in\", shape=(None, 64))"
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"cell_type": "code",
|
|
72
|
+
"execution_count": null,
|
|
73
|
+
"id": "41840fac",
|
|
74
|
+
"metadata": {},
|
|
75
|
+
"outputs": [],
|
|
76
|
+
"source": [
|
|
77
|
+
"prediction_network = Sequential(\n",
|
|
78
|
+
" Linear(\"Linear_1\", n_in=64, n_out=256),\n",
|
|
79
|
+
" BatchNorm2D(\"BatchNorm_1\"),\n",
|
|
80
|
+
" Dropout(\"Dropout_1\", p=0.5),\n",
|
|
81
|
+
" LeakyReLU(),\n",
|
|
82
|
+
" Linear(\"Linear_2\", n_in=256, n_out=128),\n",
|
|
83
|
+
" BatchNorm2D(\"BatchNorm_2\"),\n",
|
|
84
|
+
" Dropout(\"Dropout_2\", p=0.5),\n",
|
|
85
|
+
" LeakyReLU(),\n",
|
|
86
|
+
" Linear(\"Logits\", n_in=128, n_out=10),\n",
|
|
87
|
+
")\n",
|
|
88
|
+
"\n",
|
|
89
|
+
"y_hat = prediction_network(X_in)\n",
|
|
90
|
+
"model = Model(X_in, y_hat, compile_kwargs={\"mode\": \"NUMBA\"})\n",
|
|
91
|
+
"model"
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"cell_type": "code",
|
|
96
|
+
"execution_count": null,
|
|
97
|
+
"id": "3bfa0ddc",
|
|
98
|
+
"metadata": {},
|
|
99
|
+
"outputs": [],
|
|
100
|
+
"source": [
|
|
101
|
+
"loss_fn = CrossEntropy(expect_onehot_labels=True, expect_logits=True, reduction=\"mean\")"
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"cell_type": "code",
|
|
106
|
+
"execution_count": null,
|
|
107
|
+
"id": "290d3191",
|
|
108
|
+
"metadata": {},
|
|
109
|
+
"outputs": [],
|
|
110
|
+
"source": [
|
|
111
|
+
"# `model.compile_train` is the training companion to `model.predict`: it builds the loss against a target,\n",
|
|
112
|
+
"# differentiates it, folds in the BatchNorm running-statistic updates, and compiles a one-step training\n",
|
|
113
|
+
"# function (reusing the model's NUMBA compile settings).\n",
|
|
114
|
+
"step = model.compile_train(adagrad(learning_rate=1e-3), loss_fn, ndim_out=2)"
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"cell_type": "code",
|
|
119
|
+
"execution_count": null,
|
|
120
|
+
"id": "e77b697f",
|
|
121
|
+
"metadata": {},
|
|
122
|
+
"outputs": [],
|
|
123
|
+
"source": [
|
|
124
|
+
"model.initialize()"
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"cell_type": "code",
|
|
129
|
+
"execution_count": null,
|
|
130
|
+
"id": "dd6dede5",
|
|
131
|
+
"metadata": {},
|
|
132
|
+
"outputs": [],
|
|
133
|
+
"source": [
|
|
134
|
+
"from tqdm.notebook import tqdm\n",
|
|
135
|
+
"\n",
|
|
136
|
+
"loader = DataLoader(X_normed, y_onehot)\n",
|
|
137
|
+
"loss_history = []\n",
|
|
138
|
+
"n_epochs = 10_000\n",
|
|
139
|
+
"\n",
|
|
140
|
+
"for _ in tqdm(range(n_epochs)):\n",
|
|
141
|
+
" X_batch, y_batch = loader()\n",
|
|
142
|
+
" loss_value = step(X_batch, y_batch)\n",
|
|
143
|
+
" loss_history.append(loss_value)"
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"cell_type": "code",
|
|
148
|
+
"execution_count": null,
|
|
149
|
+
"id": "ab1ad6ef",
|
|
150
|
+
"metadata": {},
|
|
151
|
+
"outputs": [],
|
|
152
|
+
"source": [
|
|
153
|
+
"import matplotlib.pyplot as plt\n",
|
|
154
|
+
"\n",
|
|
155
|
+
"plt.plot(loss_history)"
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"cell_type": "code",
|
|
160
|
+
"execution_count": null,
|
|
161
|
+
"id": "fb38dd99",
|
|
162
|
+
"metadata": {},
|
|
163
|
+
"outputs": [],
|
|
164
|
+
"source": [
|
|
165
|
+
"from scipy.special import softmax\n",
|
|
166
|
+
"\n",
|
|
167
|
+
"# `model.predict` compiles a forward pass specialized for inference: Dropout is dropped and BatchNorm uses its\n",
|
|
168
|
+
"# running statistics.\n",
|
|
169
|
+
"y_hat_logits = model.predict(X_normed)\n",
|
|
170
|
+
"y_hat_probs = softmax(y_hat_logits, axis=-1)\n",
|
|
171
|
+
"y_pred = np.argmax(y_hat_probs, axis=-1)"
|
|
172
|
+
]
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"cell_type": "code",
|
|
176
|
+
"execution_count": null,
|
|
177
|
+
"id": "4393b9c3",
|
|
178
|
+
"metadata": {},
|
|
179
|
+
"outputs": [],
|
|
180
|
+
"source": [
|
|
181
|
+
"import seaborn as sns\n",
|
|
182
|
+
"\n",
|
|
183
|
+
"from sklearn.metrics import confusion_matrix\n",
|
|
184
|
+
"\n",
|
|
185
|
+
"sns.heatmap(confusion_matrix(y, y_pred), annot=True, fmt=\"0.0f\")"
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
"metadata": {
|
|
190
|
+
"kernelspec": {
|
|
191
|
+
"display_name": "Python 3 (ipykernel)",
|
|
192
|
+
"language": "python",
|
|
193
|
+
"name": "python3"
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"nbformat": 4,
|
|
197
|
+
"nbformat_minor": 5
|
|
198
|
+
}
|
|
@@ -34,22 +34,24 @@ keywords = [
|
|
|
34
34
|
]
|
|
35
35
|
|
|
36
36
|
dependencies = [
|
|
37
|
-
"pytensor>=3.
|
|
37
|
+
"pytensor>=3.2.3,<4.0.0",
|
|
38
38
|
"numpy",
|
|
39
|
-
"
|
|
39
|
+
"safetensors"
|
|
40
40
|
]
|
|
41
41
|
|
|
42
42
|
[project.urls]
|
|
43
|
-
Homepage = "https://github.com/
|
|
44
|
-
Source = "https://github.com/
|
|
45
|
-
Tracker = "https://github.com/
|
|
43
|
+
Homepage = "https://github.com/pymc-devs/pytensor_ml"
|
|
44
|
+
Source = "https://github.com/pymc-devs/pytensor_ml"
|
|
45
|
+
Tracker = "https://github.com/pymc-devs/pytensor_ml/issues"
|
|
46
46
|
|
|
47
47
|
[project.optional-dependencies]
|
|
48
48
|
dev = [
|
|
49
49
|
"pre-commit",
|
|
50
50
|
"pytest",
|
|
51
51
|
"pytest-cov",
|
|
52
|
-
"scikit-learn"
|
|
52
|
+
"scikit-learn",
|
|
53
|
+
"mypy",
|
|
54
|
+
"jax",
|
|
53
55
|
]
|
|
54
56
|
|
|
55
57
|
[tool.hatch.version]
|
|
@@ -109,6 +111,24 @@ ignore = [
|
|
|
109
111
|
]
|
|
110
112
|
fixable = ["ALL"]
|
|
111
113
|
|
|
114
|
+
[tool.ruff.lint.per-file-ignores]
|
|
115
|
+
# An __init__.py re-exports its package's API and imports submodules for their registration side effects.
|
|
116
|
+
"**/__init__.py" = ["F401"]
|
|
117
|
+
|
|
112
118
|
|
|
113
119
|
[tool.ruff.lint.isort]
|
|
114
120
|
lines-between-types = 1
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
[tool.mypy]
|
|
124
|
+
python_version = "3.12"
|
|
125
|
+
ignore_missing_imports = true
|
|
126
|
+
strict_equality = true
|
|
127
|
+
warn_redundant_casts = true
|
|
128
|
+
warn_unused_configs = true
|
|
129
|
+
warn_unused_ignores = true
|
|
130
|
+
# pytensor ops are typed to return Any, so returning their results from a function annotated with a
|
|
131
|
+
# concrete tensor type fires constantly without catching real bugs in our code.
|
|
132
|
+
warn_return_any = false
|
|
133
|
+
warn_unreachable = true
|
|
134
|
+
files = ["pytensor_ml"]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import pytensor_ml.dispatch # installs the lazy backend-dispatch registration hook
|
|
5
|
+
|
|
6
|
+
from pytensor_ml._version import __version__
|
|
7
|
+
from pytensor_ml.checkpoint import load_state, save_state
|
|
8
|
+
from pytensor_ml.pretrained import from_pretrained, load_network, save_network, save_pretrained
|
|
9
|
+
from pytensor_ml.pytensorf import function
|
|
10
|
+
|
|
11
|
+
_log = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
if not logging.root.handlers:
|
|
14
|
+
_log.setLevel(logging.INFO)
|
|
15
|
+
if len(_log.handlers) == 0:
|
|
16
|
+
handler = logging.StreamHandler(sys.stderr)
|
|
17
|
+
_log.addHandler(handler)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"__version__",
|
|
22
|
+
"from_pretrained",
|
|
23
|
+
"function",
|
|
24
|
+
"load_network",
|
|
25
|
+
"load_state",
|
|
26
|
+
"save_network",
|
|
27
|
+
"save_pretrained",
|
|
28
|
+
"save_state",
|
|
29
|
+
]
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
21
|
+
__version__ = version = '0.0.6'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 0, 6)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytensor.tensor as pt
|
|
3
|
+
|
|
4
|
+
from pytensor import config
|
|
5
|
+
|
|
6
|
+
from pytensor_ml.base import Layer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _constant_like(value: float, x: pt.TensorVariable) -> pt.TensorVariable:
|
|
10
|
+
"""
|
|
11
|
+
Wrap a scalar so that combining it with ``x`` cannot widen ``x``'s dtype.
|
|
12
|
+
|
|
13
|
+
PyTensor's autocaster types a bare Python float by value, so whether a literal widens its operand
|
|
14
|
+
depends on that value: against a float32 input ``0.5 * x`` stays float32, while ``0.01 * x``
|
|
15
|
+
promotes to float64. Pinning the constant to ``x``'s dtype removes the dependence.
|
|
16
|
+
"""
|
|
17
|
+
dtype = np.dtype(x.dtype)
|
|
18
|
+
# np.finfo maps complex64 -> float32, keeping complex inputs at their own precision.
|
|
19
|
+
dtype = np.finfo(dtype).dtype if np.issubdtype(dtype, np.inexact) else np.dtype(config.floatX)
|
|
20
|
+
return pt.constant(np.asarray(value, dtype=dtype))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Activation(Layer): ...
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ReLU(Activation):
|
|
27
|
+
r"""
|
|
28
|
+
Rectified Linear Unit.
|
|
29
|
+
|
|
30
|
+
Compute the positive part of the input:
|
|
31
|
+
|
|
32
|
+
.. math::
|
|
33
|
+
|
|
34
|
+
\mathrm{ReLU}(x) = \max(0, x).
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
|
|
38
|
+
out = pt.maximum(0, x)
|
|
39
|
+
out.name = "ReLU"
|
|
40
|
+
return out
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class LeakyReLU(Activation):
|
|
44
|
+
r"""
|
|
45
|
+
Leaky Rectified Linear Unit.
|
|
46
|
+
|
|
47
|
+
Replace ReLU's flat negative branch with a small negative slope, so negative inputs keep a
|
|
48
|
+
nonzero gradient:
|
|
49
|
+
|
|
50
|
+
.. math::
|
|
51
|
+
|
|
52
|
+
\mathrm{LeakyReLU}(x) = \begin{cases} x & x > 0 \\ \alpha x & x \le 0 \end{cases}
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
negative_slope : float, optional
|
|
57
|
+
The slope :math:`\alpha` applied to negative inputs. Default is 0.01.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(self, negative_slope: float = 0.01):
|
|
61
|
+
self.negative_slope = negative_slope
|
|
62
|
+
|
|
63
|
+
def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
|
|
64
|
+
x = pt.as_tensor(x)
|
|
65
|
+
out = pt.switch(x > 0, x, _constant_like(self.negative_slope, x) * x)
|
|
66
|
+
out.name = "LeakyReLU"
|
|
67
|
+
return out
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class Tanh(Activation):
|
|
71
|
+
r"""
|
|
72
|
+
Hyperbolic tangent.
|
|
73
|
+
|
|
74
|
+
Squash the input to :math:`(-1, 1)`:
|
|
75
|
+
|
|
76
|
+
.. math::
|
|
77
|
+
|
|
78
|
+
\tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
|
|
82
|
+
out = pt.tanh(x)
|
|
83
|
+
out.name = "TanH"
|
|
84
|
+
return out
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class Sigmoid(Activation):
|
|
88
|
+
r"""
|
|
89
|
+
Logistic sigmoid.
|
|
90
|
+
|
|
91
|
+
Squash the input to :math:`(0, 1)`:
|
|
92
|
+
|
|
93
|
+
.. math::
|
|
94
|
+
|
|
95
|
+
\sigma(x) = \frac{1}{1 + e^{-x}}.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
|
|
99
|
+
out = pt.sigmoid(x)
|
|
100
|
+
out.name = "Sigmoid"
|
|
101
|
+
return out
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class SoftPlus(Activation):
|
|
105
|
+
r"""
|
|
106
|
+
Softplus activation.
|
|
107
|
+
|
|
108
|
+
Compute a smooth approximation to ReLU:
|
|
109
|
+
|
|
110
|
+
.. math::
|
|
111
|
+
|
|
112
|
+
\mathrm{softplus}(x) = \log(1 + e^x).
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
|
|
116
|
+
out = pt.softplus(x)
|
|
117
|
+
out.name = "SoftPlus"
|
|
118
|
+
return out
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class GELU(Activation):
|
|
122
|
+
r"""
|
|
123
|
+
Gaussian Error Linear Unit.
|
|
124
|
+
|
|
125
|
+
Compute :math:`\mathrm{GELU}(x) = x \, \Phi(x)`, where :math:`\Phi` is the standard normal
|
|
126
|
+
cumulative distribution function:
|
|
127
|
+
|
|
128
|
+
.. math::
|
|
129
|
+
|
|
130
|
+
\mathrm{GELU}(x) = \frac{x}{2} \left(1 + \operatorname{erf}\!\left(\frac{x}{\sqrt{2}}\right)\right).
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
approximate : bool, optional
|
|
135
|
+
Use the tanh approximation
|
|
136
|
+
|
|
137
|
+
.. math::
|
|
138
|
+
|
|
139
|
+
\mathrm{GELU}(x) \approx \frac{x}{2}
|
|
140
|
+
\left(1 + \tanh\!\left[\sqrt{2/\pi}\,(x + 0.044715\,x^3)\right]\right)
|
|
141
|
+
|
|
142
|
+
This is the variant HuggingFace calls ``"gelu_new"`` / ``"gelu_pytorch_tanh"``, PyTorch exposes
|
|
143
|
+
as ``nn.GELU(approximate="tanh")``, and Flax as ``gelu(approximate=True)``; GPT-2 uses it. It is
|
|
144
|
+
cheaper to evaluate than the exact :math:`\operatorname{erf}` form. Default is True.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
def __init__(self, approximate: bool = True):
|
|
148
|
+
self.approximate = approximate
|
|
149
|
+
|
|
150
|
+
def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
|
|
151
|
+
x = pt.as_tensor(x)
|
|
152
|
+
half = _constant_like(0.5, x)
|
|
153
|
+
one = _constant_like(1.0, x)
|
|
154
|
+
if self.approximate:
|
|
155
|
+
sqrt_2_over_pi = _constant_like(np.sqrt(2.0 / np.pi), x)
|
|
156
|
+
cubic_coef = _constant_like(0.044715, x)
|
|
157
|
+
out = half * x * (one + pt.tanh(sqrt_2_over_pi * (x + cubic_coef * x**3)))
|
|
158
|
+
else:
|
|
159
|
+
sqrt2 = _constant_like(np.sqrt(2.0), x)
|
|
160
|
+
out = half * x * (one + pt.erf(x / sqrt2))
|
|
161
|
+
out.name = "GELU"
|
|
162
|
+
return out
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class Swish(Activation):
|
|
166
|
+
r"""
|
|
167
|
+
Swish activation, also known as SiLU.
|
|
168
|
+
|
|
169
|
+
Compute :math:`\mathrm{Swish}(x) = x \, \sigma(\beta x)`, where :math:`\sigma` is the logistic
|
|
170
|
+
sigmoid. With :math:`\beta = 1` this is the Sigmoid Linear Unit (PyTorch ``nn.SiLU``, HuggingFace
|
|
171
|
+
``"silu"`` / ``"swish"``).
|
|
172
|
+
|
|
173
|
+
Parameters
|
|
174
|
+
----------
|
|
175
|
+
beta : float, optional
|
|
176
|
+
Slope of the sigmoid gate. Larger :math:`\beta` sharpens the gate toward a ReLU; :math:`\beta
|
|
177
|
+
\to 0` collapses it toward the linear map :math:`x/2`. Default is 1.0.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
def __init__(self, beta: float = 1.0):
|
|
181
|
+
self.beta = beta
|
|
182
|
+
|
|
183
|
+
def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
|
|
184
|
+
x = pt.as_tensor(x)
|
|
185
|
+
out = x * pt.sigmoid(_constant_like(self.beta, x) * x)
|
|
186
|
+
out.name = "Swish"
|
|
187
|
+
return out
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class Softmax(Activation):
|
|
191
|
+
r"""
|
|
192
|
+
Softmax activation.
|
|
193
|
+
|
|
194
|
+
Normalize the input along one axis into a probability distribution:
|
|
195
|
+
|
|
196
|
+
.. math::
|
|
197
|
+
|
|
198
|
+
\mathrm{softmax}(x)_i = \frac{e^{x_i}}{\sum_j e^{x_j}}.
|
|
199
|
+
|
|
200
|
+
Parameters
|
|
201
|
+
----------
|
|
202
|
+
axis : int, optional
|
|
203
|
+
The axis along which the values sum to one. Default is -1.
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
def __init__(self, axis: int = -1):
|
|
207
|
+
self.axis = axis
|
|
208
|
+
|
|
209
|
+
def __call__(self, x: pt.TensorLike) -> pt.TensorVariable:
|
|
210
|
+
out = pt.special.softmax(x, axis=self.axis)
|
|
211
|
+
out.name = "Softmax"
|
|
212
|
+
return out
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
__all__ = [
|
|
216
|
+
"GELU",
|
|
217
|
+
"Activation",
|
|
218
|
+
"LeakyReLU",
|
|
219
|
+
"ReLU",
|
|
220
|
+
"Sigmoid",
|
|
221
|
+
"SoftPlus",
|
|
222
|
+
"Softmax",
|
|
223
|
+
"Swish",
|
|
224
|
+
"Tanh",
|
|
225
|
+
]
|