aether-ml 0.1.1__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.
- aether_ml-0.1.1/LICENSE +21 -0
- aether_ml-0.1.1/PKG-INFO +307 -0
- aether_ml-0.1.1/README.md +273 -0
- aether_ml-0.1.1/aether/__init__.py +44 -0
- aether_ml-0.1.1/aether/__init__.pyi +69 -0
- aether_ml-0.1.1/aether/_utils/__init__.py +12 -0
- aether_ml-0.1.1/aether/_utils/__init__.pyi +14 -0
- aether_ml-0.1.1/aether/_utils/_lazy.py +52 -0
- aether_ml-0.1.1/aether/_utils/null_objects.py +91 -0
- aether_ml-0.1.1/aether/_utils/progress.py +596 -0
- aether_ml-0.1.1/aether/base.py +83 -0
- aether_ml-0.1.1/aether/config.py +257 -0
- aether_ml-0.1.1/aether/custom_kernels/__init__.py +0 -0
- aether_ml-0.1.1/aether/custom_kernels/adam_kernel.py +121 -0
- aether_ml-0.1.1/aether/custom_kernels/batchnorm_kernel.py +186 -0
- aether_ml-0.1.1/aether/custom_kernels/conv_kernel.py +932 -0
- aether_ml-0.1.1/aether/custom_kernels/dropout_kernel.py +118 -0
- aether_ml-0.1.1/aether/custom_kernels/launch_math.py +40 -0
- aether_ml-0.1.1/aether/custom_kernels/loss_kernel.py +34 -0
- aether_ml-0.1.1/aether/custom_kernels/pooling_kernel.py +403 -0
- aether_ml-0.1.1/aether/layers/__init__.py +18 -0
- aether_ml-0.1.1/aether/layers/__init__.pyi +21 -0
- aether_ml-0.1.1/aether/layers/activations.py +105 -0
- aether_ml-0.1.1/aether/layers/conv.py +385 -0
- aether_ml-0.1.1/aether/layers/dropout.py +186 -0
- aether_ml-0.1.1/aether/layers/linear.py +161 -0
- aether_ml-0.1.1/aether/layers/normalization.py +338 -0
- aether_ml-0.1.1/aether/layers/pooling.py +580 -0
- aether_ml-0.1.1/aether/losses/__init__.py +8 -0
- aether_ml-0.1.1/aether/losses/__init__.pyi +10 -0
- aether_ml-0.1.1/aether/losses/categorical_crossentropy.py +229 -0
- aether_ml-0.1.1/aether/metrics/__init__.py +9 -0
- aether_ml-0.1.1/aether/metrics/__init__.pyi +8 -0
- aether_ml-0.1.1/aether/metrics/accuracy.py +61 -0
- aether_ml-0.1.1/aether/model.py +989 -0
- aether_ml-0.1.1/aether/optimizers/__init__.py +9 -0
- aether_ml-0.1.1/aether/optimizers/__init__.pyi +8 -0
- aether_ml-0.1.1/aether/optimizers/adam.py +289 -0
- aether_ml-0.1.1/aether/preprocessing/__init__.py +13 -0
- aether_ml-0.1.1/aether/preprocessing/__init__.pyi +16 -0
- aether_ml-0.1.1/aether/preprocessing/_utils.py +73 -0
- aether_ml-0.1.1/aether/preprocessing/transforms.py +564 -0
- aether_ml-0.1.1/aether/py.typed +0 -0
- aether_ml-0.1.1/aether_ml.egg-info/PKG-INFO +307 -0
- aether_ml-0.1.1/aether_ml.egg-info/SOURCES.txt +49 -0
- aether_ml-0.1.1/aether_ml.egg-info/dependency_links.txt +1 -0
- aether_ml-0.1.1/aether_ml.egg-info/requires.txt +18 -0
- aether_ml-0.1.1/aether_ml.egg-info/top_level.txt +1 -0
- aether_ml-0.1.1/pyproject.toml +50 -0
- aether_ml-0.1.1/setup.cfg +4 -0
- aether_ml-0.1.1/tests/test_base_case.py +454 -0
aether_ml-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AlexanderSoftCode
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
aether_ml-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aether-ml
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A ground-up deep learning framework featuring vectorized neural network and CNN layers, dual NumPy/CuPy backends, and CUDA/ROCm GPU acceleration.
|
|
5
|
+
Author: Alexander
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/AlexanderSoftCode/Aether-ML
|
|
8
|
+
Project-URL: Issues, https://github.com/AlexanderSoftCode/Aether-ML/issues
|
|
9
|
+
Keywords: deep-learning,gpu,cuda,rocm,cupy,neural-networks
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
16
|
+
Classifier: Environment :: GPU
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numpy>=2.0.0
|
|
21
|
+
Requires-Dist: safetensors>=0.8.0
|
|
22
|
+
Provides-Extra: gpu-nvidia
|
|
23
|
+
Requires-Dist: cupy-cuda13x; extra == "gpu-nvidia"
|
|
24
|
+
Provides-Extra: gpu-nvidia-12
|
|
25
|
+
Requires-Dist: cupy-cuda12x; extra == "gpu-nvidia-12"
|
|
26
|
+
Provides-Extra: gpu-nvidia-13
|
|
27
|
+
Requires-Dist: cupy-cuda13x; extra == "gpu-nvidia-13"
|
|
28
|
+
Provides-Extra: notebooks
|
|
29
|
+
Requires-Dist: jupyterlab; extra == "notebooks"
|
|
30
|
+
Requires-Dist: matplotlib; extra == "notebooks"
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: aether-ml[notebooks]; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# Aether-ML
|
|
36
|
+
|
|
37
|
+
A deep learning framework built from the ground up based on two numerical libraries **NumPy** and **CuPy**, with hand written GPU kernels for both **CUDA** and **ROCm**.
|
|
38
|
+
|
|
39
|
+
[](https://github.com/AlexanderSoftCode/Aether-ML/actions/workflows/tests.yml)
|
|
40
|
+
[](https://www.python.org/)
|
|
41
|
+
[](https://rocm.docs.amd.com/)
|
|
42
|
+
[](https://developer.nvidia.com/cuda-zone)
|
|
43
|
+
[](https://opensource.org/licenses/MIT)
|
|
44
|
+
|
|
45
|
+
## Why Aether-ML?
|
|
46
|
+
|
|
47
|
+
Aether-ML implements forward and backward propagation, convolutional and fully connected networks, normalization, pooling, and losses without building directly off any existing deep learning libraries. *NumPy* provides powerful `ndarray` datatypes that vectorize operations; *CuPy* ports this existing logic to the GPU, giving the extra functionality of custom GPU kernels speeding up an already embarrassingly parallel task.
|
|
48
|
+
|
|
49
|
+
The GPU path is **NOT** a thin wrapper over vectorized array operations, and it does not rely on an `xp` alias for a unified layer pass. Instead, nearly all components each run as hand-written kernels compiled from source templates targeting both **NVIDIA** and **AMD** hardware. To combat CPU overhead, all fronts including device backend, kernel-variant selection, and layer building are all resolved ahead of time at model construction, and never re-evaluated on every call.
|
|
50
|
+
|
|
51
|
+
Aether-ML is an intentionally ground-up project born knowing nothing about machine-learning nor GPU kernel programming. However, rather than promote a weak mental model of machine learning and how deep learning frameworks work, the goal was to implement how deep learning runtimes work under the hood. With this approach, all system level decisions, hardware dispatch, and memory footprints had to be resolved, building real knowledge about architecting and maintaining real, production-style software.
|
|
52
|
+
|
|
53
|
+

|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Key Features
|
|
58
|
+
|
|
59
|
+
**Layers** — `Conv2d`, `Dense`, `BatchNorm`, `MaxPool2d`, `AvgPool2d`, `GlobalAvgPool`, `Dropout`, `SpatialDropout`, `ReLU`, `LeakyReLU`, `Softmax`, with He initialization (He et al.) and Philox counter-based PRNG for dropout masks (Salmon et al.)
|
|
60
|
+
|
|
61
|
+
**Kernels** — Hand-written CUDA and HIP kernels for every compute layer, magic-number integer division (Granlund & Montgomery) for on-device index decomposition, and numerical stability guards on optimizer moment denominators, standard scaling, and loss terms
|
|
62
|
+
|
|
63
|
+
**Training** — AdamW with decoupled weight decay (Loshchilov & Hutter), L1 and L2 regularization executed inside the optimizer kernel, label smoothing within categorical cross entropy (Guo et al.), batch shuffling, in-training validation, and throttled progress reporting, and mixed precision via `set_precision`, which casts most layers while forcing FP32 in the optimizer, Softmax, and BatchNorm for numerical stability.
|
|
64
|
+
|
|
65
|
+
**Serialization** — `.aether` bundles pairing a JSON architecture manifest with `safetensors` weights, with no pickle anywhere in the load path, so an untrusted checkpoint cannot execute code. Preprocessor state is captured in the manifest, so a loaded model normalizes its own inputs
|
|
66
|
+
|
|
67
|
+
**Preprocessing** — `Compose` pipelines with `Rescale`, `StandardScaler`, and `ToTensor`
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
Aether-ML requires python 3.12 or newer. It depends on NumPy and `safetensors` and runs on the CPU out of the box. For GPU accelerated workloads via CuPy, separate wheels per vendor and toolkit version, it's recommended to follow their installation steps.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/AlexanderSoftCode/Aether-ML.git
|
|
77
|
+
cd Aether-ML
|
|
78
|
+
|
|
79
|
+
python -m venv .venv
|
|
80
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
81
|
+
|
|
82
|
+
# Install editable package with serialization support
|
|
83
|
+
pip install -e .
|
|
84
|
+
|
|
85
|
+
# For NVIDIA, matching your installed CUDA toolkit; choose one wheel
|
|
86
|
+
pip install cupy-cuda12x
|
|
87
|
+
pip install cupy-cuda13x # Best for Turing and newer
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**AMD ROCm**: Pre-built CuPy wheels depend on a specific ROCm driver version (e.g. `pip install cupy-rocm-7-0`). Refer to the [CuPy ROCm installation guide](https://docs.cupy.dev/en/latest/install.html#using-cupy-on-amd-gpu-experimental) for supported driver targets. For users with the ROCm 7.14.x and a GPU that supports said version, the repository includes a VS Code dev container under `.devcontainer/rocm-gfx1201/` for the AMD configuration.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Quickstart
|
|
95
|
+
|
|
96
|
+

|
|
97
|
+
|
|
98
|
+
A complete model lifecycle, from construction to a saved model.
|
|
99
|
+
<details>
|
|
100
|
+
<summary>Click to view a complete three-block CNN</summary>
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
import aether as ae
|
|
104
|
+
import numpy as np
|
|
105
|
+
import cupy as cp
|
|
106
|
+
|
|
107
|
+
TARGET_DEVICE="cupy"
|
|
108
|
+
feature_pipeline = ae.Compose([
|
|
109
|
+
ae.ToTensor(dtype='float32', target_device=TARGET_DEVICE),
|
|
110
|
+
ae.Rescale(factor=1.0 / 255.0),
|
|
111
|
+
ae.StandardScaler()
|
|
112
|
+
])
|
|
113
|
+
# Setup
|
|
114
|
+
model = ae.Model()
|
|
115
|
+
model.manual_seed(seed=42) #applies for all compute and PRNG based layers
|
|
116
|
+
|
|
117
|
+
model.add(ae.Conv2d(3, 32, (3, 3), (1, 1), padding="same"))
|
|
118
|
+
model.add(ae.BatchNorm(epsilon=1e-5, momentum=0.9))
|
|
119
|
+
model.add(ae.ReLU())
|
|
120
|
+
model.add(ae.MaxPool2d((2, 2), (2, 2), padding="valid"))
|
|
121
|
+
|
|
122
|
+
model.add(ae.Conv2d(32, 64, (3, 3), (1, 1), padding="same"))
|
|
123
|
+
model.add(ae.BatchNorm(epsilon=1e-5, momentum=0.9))
|
|
124
|
+
model.add(ae.LeakyReLU())
|
|
125
|
+
model.add(ae.MaxPool2d((2, 2), (2, 2), padding="valid"))
|
|
126
|
+
model.add(ae.SpatialDropout(rate=0.1))
|
|
127
|
+
|
|
128
|
+
model.add(ae.Conv2d(64, 128, (3, 3), (1, 1), padding="same"))
|
|
129
|
+
model.add(ae.BatchNorm(epsilon=1e-5, momentum=0.9))
|
|
130
|
+
model.add(ae.ReLU())
|
|
131
|
+
model.add(ae.MaxPool2d((2, 2), (2, 2), padding="valid"))
|
|
132
|
+
model.add(ae.SpatialDropout(rate=0.1))
|
|
133
|
+
|
|
134
|
+
model.add(ae.GlobalAvgPool())
|
|
135
|
+
model.add(ae.Dense(128, 128, l2=2e-5))
|
|
136
|
+
model.add(ae.Dropout(rate=0.075))
|
|
137
|
+
|
|
138
|
+
model.add(ae.Dense(128, 10))
|
|
139
|
+
|
|
140
|
+
model.configure(
|
|
141
|
+
loss=ae.SoftmaxCategoricalCrossEntropy(label_smoothing=0.05),
|
|
142
|
+
optimizer=ae.AdamW(lr=0.001, decay=1e-4, weight_decay=0.01),
|
|
143
|
+
accuracy=ae.CategoricalAccuracy(),
|
|
144
|
+
preprocessor=feature_pipeline # Optional, see examples/cifar10/
|
|
145
|
+
)
|
|
146
|
+
model.to(TARGET_DEVICE)
|
|
147
|
+
model.finalize(input_shape=X_train.shape[1:])
|
|
148
|
+
|
|
149
|
+
model.train(X=X_train,
|
|
150
|
+
y=y_train,
|
|
151
|
+
epochs=25,
|
|
152
|
+
batch_size=128,
|
|
153
|
+
shuffle=True,
|
|
154
|
+
print_every=100,
|
|
155
|
+
validation_data=(X_test, y_test)
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
model.evaluate(X=X_test, y=y_test, batch_size=128)
|
|
159
|
+
model.save(filepath="saved_models/quickstart.aether")
|
|
160
|
+
|
|
161
|
+
# --- In a separate evaluation / deployment script ---
|
|
162
|
+
model = ae.Model.load("saved_models/quickstart.aether")
|
|
163
|
+
predictions = model.predict(X_raw)
|
|
164
|
+
```
|
|
165
|
+
</details>
|
|
166
|
+
|
|
167
|
+
The accuracy here should converge to **75-77% test accuracy** on CIFAR-10 across 25 epochs. For end to end dataset pipelines, mixed-precision configurations, routing model to a fused kernel path, and proper saving and loading functionality, see the complete scripts inside [`examples/cifar10/`](examples/cifar10/)
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Architecture
|
|
172
|
+
|
|
173
|
+
Four decisions shape most of the codebase.
|
|
174
|
+
|
|
175
|
+
**Resolution happens once, not per call.** `model.to()` triggers `_compile_for_device` on every component, swapping method pointers to backend-specific implementations and selecting kernel variants once, ahead of time. The cost is that layers have two lifecycle phases; an uncompiled construction phase `layer.build()` and a compiled execution phase.
|
|
176
|
+
|
|
177
|
+
One exception is deliberate. The `training` flag stays a plain runtime boolean, because resolving it ahead of time would double the bound variants in every layer to save tens of nanoseconds per layer.
|
|
178
|
+
|
|
179
|
+
<picture>
|
|
180
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/Architecture_dispatch_dark.png">
|
|
181
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/Architecture_dispatch.png">
|
|
182
|
+
<img alt="Architecture dispatch" src="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/Architecture_dispatch.png">
|
|
183
|
+
</picture>
|
|
184
|
+
|
|
185
|
+
**One kernel source, two vendors.** `cupy.RawKernel`s in the framework are generated from a shared template, with vendor substitution maps for CUDA and HIP supplying the divergent pieces: intrinsic names, launch geometry, matrix-core APIs. This does not affect kernel performance, but adds a extra step during compilation, and effects readability.
|
|
186
|
+
|
|
187
|
+
**Kernels compile per shape, not per call.** Convolution kernels are cached on shape metadata, with filter size and stride baked in as compile-time constants rather than passed as arguments, letting the compiler unroll inner loops and fold index arithmetic. The cost is a compile on first encounter of each new shape, and a cache that grows with shape diversity, which is acceptable when shapes stay stable across thousands of training steps.
|
|
188
|
+
|
|
189
|
+
**Optional components are objects, not `None`.** The `Accuracy`, `Optimizer`, `Preprocessor`, and `TrainingProgress` components all include an optional `Null` object variant. This removes `if x is not None` from the training loop, further reducing CPU overhead during the model lifecycle.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Benchmarks
|
|
194
|
+
|
|
195
|
+
All figures below come from the same CNN, the same CIFAR-10 batches, the same seed, and the same hardware (AMD RX 9070 XT, Intel Core Ultra 265K). Only the dispatch path differs.
|
|
196
|
+
|
|
197
|
+
### Convergence
|
|
198
|
+
Four paths are tested; NumPy on CPU, legacy pre-refactor GPU implementation, vectorized CuPy without custom kernels, and the fused kernel path.
|
|
199
|
+
|
|
200
|
+
<picture>
|
|
201
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_convergence-dark.png">
|
|
202
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_convergence.png">
|
|
203
|
+
<img alt="Backend convergence" src="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_convergence.png">
|
|
204
|
+
</picture>
|
|
205
|
+
|
|
206
|
+
### Throughput
|
|
207
|
+
|
|
208
|
+
Per training step: **3.17 ms** on the fused kernel path vs **52.6 ms** on vectorized CuPy on the same GPU, with **326 ms** for NumPy on CPU. With the GPU paths, the kernel work shows a **17x speedup against identical hardware running array operations**.
|
|
209
|
+
|
|
210
|
+
<picture>
|
|
211
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_throughput-dark.png">
|
|
212
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_throughput.png">
|
|
213
|
+
<img alt="Backend throughput" src="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_throughput.png">
|
|
214
|
+
</picture>
|
|
215
|
+
|
|
216
|
+
### Transient VRAM
|
|
217
|
+
|
|
218
|
+
Transient VRAM footprint over two training steps. Unfused CuPy dispatches allocate independent temporary buffers in VRAM for intermediate steps, resulting in transient spikes up to **180 MiB**. Custom fused kernels eliminate the need to create temporary buffers by executing multiple actions in the same kernel within registers and shared memory, reducing peak transient VRAM by **5.6×** (32.0 MiB peak).
|
|
219
|
+
|
|
220
|
+
<picture>
|
|
221
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_vram-dark.png">
|
|
222
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_vram.png">
|
|
223
|
+
<img alt="Transient VRAM usage" src="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_vram.png">
|
|
224
|
+
</picture>
|
|
225
|
+
|
|
226
|
+
**On the legacy series.** "Legacy CuPy" contains a `float64` type promotion bug that was found during the rewrite. GPUs often have very few or no FP64 ALUs, which explains the slowdown. The pathway is still included as an honest before-and-after, and not as a competitive baseline.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Testing
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
python3 -m unittest discover tests
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
A single line tests **both** the **NumPy** and **CuPy** backends. Some of the coverage is included below:
|
|
237
|
+
|
|
238
|
+
- Numerical gradient checks on every compute layer
|
|
239
|
+
- Unit tests for losses, optimizers, preprocessing transforms, and the test harness itself
|
|
240
|
+
- Integration tests covering full `Model` lifecycles including `Compose` pipelines and save/load round trips
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Limitations and Scope
|
|
245
|
+
|
|
246
|
+
- **No NCHW input shapes.** Tight coupling between the **NHWC** layout and the kernels was required for maximum throughput. The alternative would've been to support both tensor shapes, and find workarounds for the non-contiguous array views that **NCHW** brings.
|
|
247
|
+
- **No autograd.** Every backward pass is written by hand. This is not an omission, and outside the scope of this project.
|
|
248
|
+
- **Single GPU.** No data or model parallelism.
|
|
249
|
+
- **No learning rate scheduling**, early stopping, or checkpointing.
|
|
250
|
+
- **No gradient clipping.** The numerical guards in the optimizer and loss protect against division by zero; they do not bound gradient magnitude.
|
|
251
|
+
- **Two accuracy metrics**: categorical and regression.
|
|
252
|
+
- **Fused kernel selection is automatic.** When CuPy is present, the fused path is used; there is no runtime switch to force the vectorized path. A workaround can still be made, but a user-facing place is not made.
|
|
253
|
+
- **CI covers the CPU path only.** GitHub Actions runners have no GPU, so the CuPy and kernel tests are skipped there and verified locally.
|
|
254
|
+
- **Only two tested configurations.** The listed AMD hardware, and a **RTX 3050 Mobile**.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Future Work
|
|
259
|
+
|
|
260
|
+
* [ ] **Transformer Block** — Implementation of a general transformer block and **ViT** transformer block into the framework, with the fallback vectorized path as well as a fused kernel path.
|
|
261
|
+
* [ ] **Fused Forward Pass Epilogue Support** — Currently, layers such as *ReLU* and *LeakyReLU* are not fused together at the end of the compute layers epilogue (e.g. *Conv2d*, *Dense*), combining the two layers into a single kernel pass promotes faster forward passes.
|
|
262
|
+
* [ ] **Custom Linear Kernel** — Support for a simple GEMM kernel utilizing matrix/tensor cores from the AMD/NVIDIA GPUs, plan is to fuse the dot product as well as the bias add here, and have it compatible with both dense and transformer block.
|
|
263
|
+
* [ ] **Data augmentation pipeline** — passed into `configure`, applied per mini-batch (Gaussian blur, flips, rotations, inversion)
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Origins
|
|
268
|
+
|
|
269
|
+
This project began as an implementation of *Neural Networks from Scratch in Python* by Harrison Kinsley and Daniel Kukieła, and has since diverged considerably from it. Three breaks are worth naming.
|
|
270
|
+
|
|
271
|
+
**Data flow.** The book mutates a `self.output` attribute on each layer. Aether-ML returns its outputs instead, which saves VRAM in layers such as `Dense` and `BatchNorm`, while enabling construction-time dispatch binding.
|
|
272
|
+
|
|
273
|
+
**Backends.** The book is NumPy only. Aether-ML dispatches to hand-written kernels across two GPU vendors from a shared template.
|
|
274
|
+
|
|
275
|
+
**Scope.** Convolution, pooling, and GPU execution do not appear in the book. Those are original implementations here, along with the kernel caching, vendor templating, and layer build phase that support them.
|
|
276
|
+
|
|
277
|
+
The pre-refactor implementations are preserved under `examples/legacy/` for anyone who wants to see the first era of this project.
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## References
|
|
282
|
+
|
|
283
|
+
* **NNFS Book**: Kinsley, H., & Kukieła, D. (2020). *Neural Networks from Scratch in Python*.
|
|
284
|
+
|
|
285
|
+
* **Adam Optimizer**: Kingma, D. P., & Ba, J. (2014). *Adam: A Method for Stochastic Optimization*. arXiv preprint arXiv:1412.6980.
|
|
286
|
+
|
|
287
|
+
* **AdamW Optimizer**: Loshchilov, I., & Hutter, F. (2017). *Decoupled Weight Decay Regularization*. arXiv preprint arXiv:1711.05101.
|
|
288
|
+
|
|
289
|
+
* **Batch Normalization**: Ioffe, S., & Szegedy, C. (2015). *Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift*. In Proceedings of the 32nd International Conference on Machine Learning (ICML) (pp. 448-456).
|
|
290
|
+
|
|
291
|
+
* **He Initialization**: He, K., Zhang, X., Ren, S., & Sun, J. (2015). *Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification*. In Proceedings of the IEEE International Conference on Computer Vision (ICCV) (pp. 1026-1034).
|
|
292
|
+
|
|
293
|
+
* **Label Smoothing**: Guo, C., Pleiss, G., Sun, Y., & Weinberger, K. Q. (2017). *On Calibration of Modern Neural Networks*. In Proceedings of the 34th International Conference on Machine Learning (ICML) (pp. 1321-1330).
|
|
294
|
+
|
|
295
|
+
* **Philox PRNG**: Salmon, J. K., Moraes, M. A., Dror, R. O., & Shaw, D. E. (2011). *Parallel Random Numbers: As Easy as 1, 2, 3*. In Proceedings of International Conference for High Performance Computing, Networking, Storage and Analysis (SC) (pp. 1-12).
|
|
296
|
+
|
|
297
|
+
* **Integer Division via Multiplication**: Granlund, T., & Montgomery, P. L. (1994). *Division by Invariant Integers Using Multiplication*. In Proceedings of the ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI) (pp. 61-72).
|
|
298
|
+
|
|
299
|
+
* **CIFAR-10 Dataset**: Krizhevsky, A. (2009). *Learning Multiple Layers of Features from Tiny Images* (Tech. Rep. TR-2009). Department of Computer Science, University of Toronto.
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## License
|
|
304
|
+
|
|
305
|
+
MIT License ([LICENSE](LICENSE))
|
|
306
|
+
|
|
307
|
+
The implementation preserved under `examples/legacy/` descends from the MIT-licensed Python code accompanying *Neural Networks from Scratch in Python* and carries its own [LICENSE](examples/legacy/LICENSE) retaining the original copyright notice.
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# Aether-ML
|
|
2
|
+
|
|
3
|
+
A deep learning framework built from the ground up based on two numerical libraries **NumPy** and **CuPy**, with hand written GPU kernels for both **CUDA** and **ROCm**.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/AlexanderSoftCode/Aether-ML/actions/workflows/tests.yml)
|
|
6
|
+
[](https://www.python.org/)
|
|
7
|
+
[](https://rocm.docs.amd.com/)
|
|
8
|
+
[](https://developer.nvidia.com/cuda-zone)
|
|
9
|
+
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
|
|
11
|
+
## Why Aether-ML?
|
|
12
|
+
|
|
13
|
+
Aether-ML implements forward and backward propagation, convolutional and fully connected networks, normalization, pooling, and losses without building directly off any existing deep learning libraries. *NumPy* provides powerful `ndarray` datatypes that vectorize operations; *CuPy* ports this existing logic to the GPU, giving the extra functionality of custom GPU kernels speeding up an already embarrassingly parallel task.
|
|
14
|
+
|
|
15
|
+
The GPU path is **NOT** a thin wrapper over vectorized array operations, and it does not rely on an `xp` alias for a unified layer pass. Instead, nearly all components each run as hand-written kernels compiled from source templates targeting both **NVIDIA** and **AMD** hardware. To combat CPU overhead, all fronts including device backend, kernel-variant selection, and layer building are all resolved ahead of time at model construction, and never re-evaluated on every call.
|
|
16
|
+
|
|
17
|
+
Aether-ML is an intentionally ground-up project born knowing nothing about machine-learning nor GPU kernel programming. However, rather than promote a weak mental model of machine learning and how deep learning frameworks work, the goal was to implement how deep learning runtimes work under the hood. With this approach, all system level decisions, hardware dispatch, and memory footprints had to be resolved, building real knowledge about architecting and maintaining real, production-style software.
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Key Features
|
|
24
|
+
|
|
25
|
+
**Layers** — `Conv2d`, `Dense`, `BatchNorm`, `MaxPool2d`, `AvgPool2d`, `GlobalAvgPool`, `Dropout`, `SpatialDropout`, `ReLU`, `LeakyReLU`, `Softmax`, with He initialization (He et al.) and Philox counter-based PRNG for dropout masks (Salmon et al.)
|
|
26
|
+
|
|
27
|
+
**Kernels** — Hand-written CUDA and HIP kernels for every compute layer, magic-number integer division (Granlund & Montgomery) for on-device index decomposition, and numerical stability guards on optimizer moment denominators, standard scaling, and loss terms
|
|
28
|
+
|
|
29
|
+
**Training** — AdamW with decoupled weight decay (Loshchilov & Hutter), L1 and L2 regularization executed inside the optimizer kernel, label smoothing within categorical cross entropy (Guo et al.), batch shuffling, in-training validation, and throttled progress reporting, and mixed precision via `set_precision`, which casts most layers while forcing FP32 in the optimizer, Softmax, and BatchNorm for numerical stability.
|
|
30
|
+
|
|
31
|
+
**Serialization** — `.aether` bundles pairing a JSON architecture manifest with `safetensors` weights, with no pickle anywhere in the load path, so an untrusted checkpoint cannot execute code. Preprocessor state is captured in the manifest, so a loaded model normalizes its own inputs
|
|
32
|
+
|
|
33
|
+
**Preprocessing** — `Compose` pipelines with `Rescale`, `StandardScaler`, and `ToTensor`
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
Aether-ML requires python 3.12 or newer. It depends on NumPy and `safetensors` and runs on the CPU out of the box. For GPU accelerated workloads via CuPy, separate wheels per vendor and toolkit version, it's recommended to follow their installation steps.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
git clone https://github.com/AlexanderSoftCode/Aether-ML.git
|
|
43
|
+
cd Aether-ML
|
|
44
|
+
|
|
45
|
+
python -m venv .venv
|
|
46
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
47
|
+
|
|
48
|
+
# Install editable package with serialization support
|
|
49
|
+
pip install -e .
|
|
50
|
+
|
|
51
|
+
# For NVIDIA, matching your installed CUDA toolkit; choose one wheel
|
|
52
|
+
pip install cupy-cuda12x
|
|
53
|
+
pip install cupy-cuda13x # Best for Turing and newer
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**AMD ROCm**: Pre-built CuPy wheels depend on a specific ROCm driver version (e.g. `pip install cupy-rocm-7-0`). Refer to the [CuPy ROCm installation guide](https://docs.cupy.dev/en/latest/install.html#using-cupy-on-amd-gpu-experimental) for supported driver targets. For users with the ROCm 7.14.x and a GPU that supports said version, the repository includes a VS Code dev container under `.devcontainer/rocm-gfx1201/` for the AMD configuration.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Quickstart
|
|
61
|
+
|
|
62
|
+

|
|
63
|
+
|
|
64
|
+
A complete model lifecycle, from construction to a saved model.
|
|
65
|
+
<details>
|
|
66
|
+
<summary>Click to view a complete three-block CNN</summary>
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
import aether as ae
|
|
70
|
+
import numpy as np
|
|
71
|
+
import cupy as cp
|
|
72
|
+
|
|
73
|
+
TARGET_DEVICE="cupy"
|
|
74
|
+
feature_pipeline = ae.Compose([
|
|
75
|
+
ae.ToTensor(dtype='float32', target_device=TARGET_DEVICE),
|
|
76
|
+
ae.Rescale(factor=1.0 / 255.0),
|
|
77
|
+
ae.StandardScaler()
|
|
78
|
+
])
|
|
79
|
+
# Setup
|
|
80
|
+
model = ae.Model()
|
|
81
|
+
model.manual_seed(seed=42) #applies for all compute and PRNG based layers
|
|
82
|
+
|
|
83
|
+
model.add(ae.Conv2d(3, 32, (3, 3), (1, 1), padding="same"))
|
|
84
|
+
model.add(ae.BatchNorm(epsilon=1e-5, momentum=0.9))
|
|
85
|
+
model.add(ae.ReLU())
|
|
86
|
+
model.add(ae.MaxPool2d((2, 2), (2, 2), padding="valid"))
|
|
87
|
+
|
|
88
|
+
model.add(ae.Conv2d(32, 64, (3, 3), (1, 1), padding="same"))
|
|
89
|
+
model.add(ae.BatchNorm(epsilon=1e-5, momentum=0.9))
|
|
90
|
+
model.add(ae.LeakyReLU())
|
|
91
|
+
model.add(ae.MaxPool2d((2, 2), (2, 2), padding="valid"))
|
|
92
|
+
model.add(ae.SpatialDropout(rate=0.1))
|
|
93
|
+
|
|
94
|
+
model.add(ae.Conv2d(64, 128, (3, 3), (1, 1), padding="same"))
|
|
95
|
+
model.add(ae.BatchNorm(epsilon=1e-5, momentum=0.9))
|
|
96
|
+
model.add(ae.ReLU())
|
|
97
|
+
model.add(ae.MaxPool2d((2, 2), (2, 2), padding="valid"))
|
|
98
|
+
model.add(ae.SpatialDropout(rate=0.1))
|
|
99
|
+
|
|
100
|
+
model.add(ae.GlobalAvgPool())
|
|
101
|
+
model.add(ae.Dense(128, 128, l2=2e-5))
|
|
102
|
+
model.add(ae.Dropout(rate=0.075))
|
|
103
|
+
|
|
104
|
+
model.add(ae.Dense(128, 10))
|
|
105
|
+
|
|
106
|
+
model.configure(
|
|
107
|
+
loss=ae.SoftmaxCategoricalCrossEntropy(label_smoothing=0.05),
|
|
108
|
+
optimizer=ae.AdamW(lr=0.001, decay=1e-4, weight_decay=0.01),
|
|
109
|
+
accuracy=ae.CategoricalAccuracy(),
|
|
110
|
+
preprocessor=feature_pipeline # Optional, see examples/cifar10/
|
|
111
|
+
)
|
|
112
|
+
model.to(TARGET_DEVICE)
|
|
113
|
+
model.finalize(input_shape=X_train.shape[1:])
|
|
114
|
+
|
|
115
|
+
model.train(X=X_train,
|
|
116
|
+
y=y_train,
|
|
117
|
+
epochs=25,
|
|
118
|
+
batch_size=128,
|
|
119
|
+
shuffle=True,
|
|
120
|
+
print_every=100,
|
|
121
|
+
validation_data=(X_test, y_test)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
model.evaluate(X=X_test, y=y_test, batch_size=128)
|
|
125
|
+
model.save(filepath="saved_models/quickstart.aether")
|
|
126
|
+
|
|
127
|
+
# --- In a separate evaluation / deployment script ---
|
|
128
|
+
model = ae.Model.load("saved_models/quickstart.aether")
|
|
129
|
+
predictions = model.predict(X_raw)
|
|
130
|
+
```
|
|
131
|
+
</details>
|
|
132
|
+
|
|
133
|
+
The accuracy here should converge to **75-77% test accuracy** on CIFAR-10 across 25 epochs. For end to end dataset pipelines, mixed-precision configurations, routing model to a fused kernel path, and proper saving and loading functionality, see the complete scripts inside [`examples/cifar10/`](examples/cifar10/)
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Architecture
|
|
138
|
+
|
|
139
|
+
Four decisions shape most of the codebase.
|
|
140
|
+
|
|
141
|
+
**Resolution happens once, not per call.** `model.to()` triggers `_compile_for_device` on every component, swapping method pointers to backend-specific implementations and selecting kernel variants once, ahead of time. The cost is that layers have two lifecycle phases; an uncompiled construction phase `layer.build()` and a compiled execution phase.
|
|
142
|
+
|
|
143
|
+
One exception is deliberate. The `training` flag stays a plain runtime boolean, because resolving it ahead of time would double the bound variants in every layer to save tens of nanoseconds per layer.
|
|
144
|
+
|
|
145
|
+
<picture>
|
|
146
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/Architecture_dispatch_dark.png">
|
|
147
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/Architecture_dispatch.png">
|
|
148
|
+
<img alt="Architecture dispatch" src="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/Architecture_dispatch.png">
|
|
149
|
+
</picture>
|
|
150
|
+
|
|
151
|
+
**One kernel source, two vendors.** `cupy.RawKernel`s in the framework are generated from a shared template, with vendor substitution maps for CUDA and HIP supplying the divergent pieces: intrinsic names, launch geometry, matrix-core APIs. This does not affect kernel performance, but adds a extra step during compilation, and effects readability.
|
|
152
|
+
|
|
153
|
+
**Kernels compile per shape, not per call.** Convolution kernels are cached on shape metadata, with filter size and stride baked in as compile-time constants rather than passed as arguments, letting the compiler unroll inner loops and fold index arithmetic. The cost is a compile on first encounter of each new shape, and a cache that grows with shape diversity, which is acceptable when shapes stay stable across thousands of training steps.
|
|
154
|
+
|
|
155
|
+
**Optional components are objects, not `None`.** The `Accuracy`, `Optimizer`, `Preprocessor`, and `TrainingProgress` components all include an optional `Null` object variant. This removes `if x is not None` from the training loop, further reducing CPU overhead during the model lifecycle.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Benchmarks
|
|
160
|
+
|
|
161
|
+
All figures below come from the same CNN, the same CIFAR-10 batches, the same seed, and the same hardware (AMD RX 9070 XT, Intel Core Ultra 265K). Only the dispatch path differs.
|
|
162
|
+
|
|
163
|
+
### Convergence
|
|
164
|
+
Four paths are tested; NumPy on CPU, legacy pre-refactor GPU implementation, vectorized CuPy without custom kernels, and the fused kernel path.
|
|
165
|
+
|
|
166
|
+
<picture>
|
|
167
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_convergence-dark.png">
|
|
168
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_convergence.png">
|
|
169
|
+
<img alt="Backend convergence" src="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_convergence.png">
|
|
170
|
+
</picture>
|
|
171
|
+
|
|
172
|
+
### Throughput
|
|
173
|
+
|
|
174
|
+
Per training step: **3.17 ms** on the fused kernel path vs **52.6 ms** on vectorized CuPy on the same GPU, with **326 ms** for NumPy on CPU. With the GPU paths, the kernel work shows a **17x speedup against identical hardware running array operations**.
|
|
175
|
+
|
|
176
|
+
<picture>
|
|
177
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_throughput-dark.png">
|
|
178
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_throughput.png">
|
|
179
|
+
<img alt="Backend throughput" src="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_throughput.png">
|
|
180
|
+
</picture>
|
|
181
|
+
|
|
182
|
+
### Transient VRAM
|
|
183
|
+
|
|
184
|
+
Transient VRAM footprint over two training steps. Unfused CuPy dispatches allocate independent temporary buffers in VRAM for intermediate steps, resulting in transient spikes up to **180 MiB**. Custom fused kernels eliminate the need to create temporary buffers by executing multiple actions in the same kernel within registers and shared memory, reducing peak transient VRAM by **5.6×** (32.0 MiB peak).
|
|
185
|
+
|
|
186
|
+
<picture>
|
|
187
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_vram-dark.png">
|
|
188
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_vram.png">
|
|
189
|
+
<img alt="Transient VRAM usage" src="https://raw.githubusercontent.com/AlexanderSoftCode/Aether-ML/main/notebooks/assets/benchmark_vram.png">
|
|
190
|
+
</picture>
|
|
191
|
+
|
|
192
|
+
**On the legacy series.** "Legacy CuPy" contains a `float64` type promotion bug that was found during the rewrite. GPUs often have very few or no FP64 ALUs, which explains the slowdown. The pathway is still included as an honest before-and-after, and not as a competitive baseline.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Testing
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
python3 -m unittest discover tests
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
A single line tests **both** the **NumPy** and **CuPy** backends. Some of the coverage is included below:
|
|
203
|
+
|
|
204
|
+
- Numerical gradient checks on every compute layer
|
|
205
|
+
- Unit tests for losses, optimizers, preprocessing transforms, and the test harness itself
|
|
206
|
+
- Integration tests covering full `Model` lifecycles including `Compose` pipelines and save/load round trips
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Limitations and Scope
|
|
211
|
+
|
|
212
|
+
- **No NCHW input shapes.** Tight coupling between the **NHWC** layout and the kernels was required for maximum throughput. The alternative would've been to support both tensor shapes, and find workarounds for the non-contiguous array views that **NCHW** brings.
|
|
213
|
+
- **No autograd.** Every backward pass is written by hand. This is not an omission, and outside the scope of this project.
|
|
214
|
+
- **Single GPU.** No data or model parallelism.
|
|
215
|
+
- **No learning rate scheduling**, early stopping, or checkpointing.
|
|
216
|
+
- **No gradient clipping.** The numerical guards in the optimizer and loss protect against division by zero; they do not bound gradient magnitude.
|
|
217
|
+
- **Two accuracy metrics**: categorical and regression.
|
|
218
|
+
- **Fused kernel selection is automatic.** When CuPy is present, the fused path is used; there is no runtime switch to force the vectorized path. A workaround can still be made, but a user-facing place is not made.
|
|
219
|
+
- **CI covers the CPU path only.** GitHub Actions runners have no GPU, so the CuPy and kernel tests are skipped there and verified locally.
|
|
220
|
+
- **Only two tested configurations.** The listed AMD hardware, and a **RTX 3050 Mobile**.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Future Work
|
|
225
|
+
|
|
226
|
+
* [ ] **Transformer Block** — Implementation of a general transformer block and **ViT** transformer block into the framework, with the fallback vectorized path as well as a fused kernel path.
|
|
227
|
+
* [ ] **Fused Forward Pass Epilogue Support** — Currently, layers such as *ReLU* and *LeakyReLU* are not fused together at the end of the compute layers epilogue (e.g. *Conv2d*, *Dense*), combining the two layers into a single kernel pass promotes faster forward passes.
|
|
228
|
+
* [ ] **Custom Linear Kernel** — Support for a simple GEMM kernel utilizing matrix/tensor cores from the AMD/NVIDIA GPUs, plan is to fuse the dot product as well as the bias add here, and have it compatible with both dense and transformer block.
|
|
229
|
+
* [ ] **Data augmentation pipeline** — passed into `configure`, applied per mini-batch (Gaussian blur, flips, rotations, inversion)
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Origins
|
|
234
|
+
|
|
235
|
+
This project began as an implementation of *Neural Networks from Scratch in Python* by Harrison Kinsley and Daniel Kukieła, and has since diverged considerably from it. Three breaks are worth naming.
|
|
236
|
+
|
|
237
|
+
**Data flow.** The book mutates a `self.output` attribute on each layer. Aether-ML returns its outputs instead, which saves VRAM in layers such as `Dense` and `BatchNorm`, while enabling construction-time dispatch binding.
|
|
238
|
+
|
|
239
|
+
**Backends.** The book is NumPy only. Aether-ML dispatches to hand-written kernels across two GPU vendors from a shared template.
|
|
240
|
+
|
|
241
|
+
**Scope.** Convolution, pooling, and GPU execution do not appear in the book. Those are original implementations here, along with the kernel caching, vendor templating, and layer build phase that support them.
|
|
242
|
+
|
|
243
|
+
The pre-refactor implementations are preserved under `examples/legacy/` for anyone who wants to see the first era of this project.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## References
|
|
248
|
+
|
|
249
|
+
* **NNFS Book**: Kinsley, H., & Kukieła, D. (2020). *Neural Networks from Scratch in Python*.
|
|
250
|
+
|
|
251
|
+
* **Adam Optimizer**: Kingma, D. P., & Ba, J. (2014). *Adam: A Method for Stochastic Optimization*. arXiv preprint arXiv:1412.6980.
|
|
252
|
+
|
|
253
|
+
* **AdamW Optimizer**: Loshchilov, I., & Hutter, F. (2017). *Decoupled Weight Decay Regularization*. arXiv preprint arXiv:1711.05101.
|
|
254
|
+
|
|
255
|
+
* **Batch Normalization**: Ioffe, S., & Szegedy, C. (2015). *Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift*. In Proceedings of the 32nd International Conference on Machine Learning (ICML) (pp. 448-456).
|
|
256
|
+
|
|
257
|
+
* **He Initialization**: He, K., Zhang, X., Ren, S., & Sun, J. (2015). *Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification*. In Proceedings of the IEEE International Conference on Computer Vision (ICCV) (pp. 1026-1034).
|
|
258
|
+
|
|
259
|
+
* **Label Smoothing**: Guo, C., Pleiss, G., Sun, Y., & Weinberger, K. Q. (2017). *On Calibration of Modern Neural Networks*. In Proceedings of the 34th International Conference on Machine Learning (ICML) (pp. 1321-1330).
|
|
260
|
+
|
|
261
|
+
* **Philox PRNG**: Salmon, J. K., Moraes, M. A., Dror, R. O., & Shaw, D. E. (2011). *Parallel Random Numbers: As Easy as 1, 2, 3*. In Proceedings of International Conference for High Performance Computing, Networking, Storage and Analysis (SC) (pp. 1-12).
|
|
262
|
+
|
|
263
|
+
* **Integer Division via Multiplication**: Granlund, T., & Montgomery, P. L. (1994). *Division by Invariant Integers Using Multiplication*. In Proceedings of the ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI) (pp. 61-72).
|
|
264
|
+
|
|
265
|
+
* **CIFAR-10 Dataset**: Krizhevsky, A. (2009). *Learning Multiple Layers of Features from Tiny Images* (Tech. Rep. TR-2009). Department of Computer Science, University of Toronto.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## License
|
|
270
|
+
|
|
271
|
+
MIT License ([LICENSE](LICENSE))
|
|
272
|
+
|
|
273
|
+
The implementation preserved under `examples/legacy/` descends from the MIT-licensed Python code accompanying *Neural Networks from Scratch in Python* and carries its own [LICENSE](examples/legacy/LICENSE) retaining the original copyright notice.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from importlib.metadata import PackageNotFoundError, version as _dist_version
|
|
2
|
+
|
|
3
|
+
from aether._utils._lazy import install_lazy_attrs
|
|
4
|
+
from aether.layers import _LAYER_MODULES
|
|
5
|
+
from aether.losses import _LOSS_MODULES
|
|
6
|
+
from aether.metrics import _ACC_MODULES
|
|
7
|
+
from aether.optimizers import _OPTIMIZER_MODULES
|
|
8
|
+
from aether.preprocessing import _PREPROCESSING_MODULES
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
__version__ = _dist_version("aether-ml")
|
|
12
|
+
except PackageNotFoundError:
|
|
13
|
+
# Running from a source checkout that was never pip-installed. Keep the
|
|
14
|
+
# attribute defined so `ae.__version__` never falls through to the lazy
|
|
15
|
+
# __getattr__ and raises a misleading AttributeError.
|
|
16
|
+
__version__ = "0.0.0.dev0"
|
|
17
|
+
|
|
18
|
+
_MODEL_MODULES = {
|
|
19
|
+
"Model": "aether.model",
|
|
20
|
+
}
|
|
21
|
+
_TOP_LEVEL_MODULES = {
|
|
22
|
+
**_MODEL_MODULES,
|
|
23
|
+
**_LAYER_MODULES,
|
|
24
|
+
**_LOSS_MODULES,
|
|
25
|
+
**_ACC_MODULES,
|
|
26
|
+
**_OPTIMIZER_MODULES,
|
|
27
|
+
**_PREPROCESSING_MODULES
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
install_lazy_attrs(globals(), _TOP_LEVEL_MODULES)
|
|
31
|
+
|
|
32
|
+
# Clean up private setup variables so ae.<tab> remains clean
|
|
33
|
+
del (
|
|
34
|
+
install_lazy_attrs,
|
|
35
|
+
_dist_version,
|
|
36
|
+
PackageNotFoundError,
|
|
37
|
+
_LAYER_MODULES,
|
|
38
|
+
_LOSS_MODULES,
|
|
39
|
+
_ACC_MODULES,
|
|
40
|
+
_OPTIMIZER_MODULES,
|
|
41
|
+
_MODEL_MODULES,
|
|
42
|
+
_PREPROCESSING_MODULES,
|
|
43
|
+
_TOP_LEVEL_MODULES,
|
|
44
|
+
)
|