Open source. Open to your next idea. Explore EveryO 0.2.0

INTERNALS / COMPLETE GUIDE v0.2.0 source snapshot

Architecture.

Understand EveryO package boundaries, tensor flow, backend dispatch, design decisions, and how to extend the framework.

READ WITH CONTEXT

Complete upstream referenceAll source sections are preserved below, with added explanations and local guide links.

Some snippets require earlier definitions or optional integrations. Start with the complete CPU quickstart for a runnable baseline.

Back to the quickstart →
READ THE FULL STORY

Source: EveryO 6c05a06. This page preserves the reviewed upstream guide and adds learning context. Version-specific headings and historical wording remain visible; boundary notes explain newer APIs separately.

BEFORE YOU BEGIN

Follow the whole stack.

Read the architecture from NumPy upward. Each layer gives the next one a smaller, clearer contract: storage, differentiable operations, modules, optimization, and training.

A training step across the layers

  1. Load a batch. DataLoader yields features and targets.
  2. Run the model. Module calls compose differentiable tensor operations.
  3. Measure the loss. The loss reduces prediction errors to a scalar.
  4. Propagate gradients. backward() traverses the recorded graph.
  5. Update parameters. SGD or Adam applies the gradients; History records the epoch results.
Two CUDA paths, two contracts

The standard Tensor architecture described below keeps NumPy buffers in host memory. The separate eo.cuda.to_device() resident-buffer API is inference-only and does not attach autograd nodes. See the device guide for that distinction.

One update without Trainer

This complete example makes the training contract explicit. The optimizer receives model parameters, the forward pass creates a scalar loss, backward() fills the gradients, and step() applies one update.

Python · reference snippet
import everyo as eo

features = eo.tensor([[1.0, 2.0]])
targets = eo.tensor([[1.0]])
model = eo.Sequential(eo.Linear(2, 1, seed=0))
optimizer = eo.SGD(model.parameters(), lr=0.01)
criterion = eo.MSELoss()

model.train()
optimizer.zero_grad()
predictions = model(features)
loss = criterion(predictions, targets)
loss.backward()
optimizer.step()

print(loss.item())  # loss from the forward pass, before the update

Call zero_grad() before each fresh update. Use model.eval() and eo.no_grad() when you want evaluation behavior without recording a new gradient graph. Trainer manages the standard epoch loop for you.

EveryO is layered so that each level depends only on the ones below it. You can read it bottom-up in an afternoon.

Reference / output
                everyo.cli            everyo.visualization
                     |                          |
        +------------+--------------------------+
        |
   everyo.training  (Trainer, callbacks, history, metrics)
        |
   everyo.optim     (SGD, Adam)          everyo.serialization  (.evo archives)
        |                                        |
   everyo.nn        (Module, layers, activations, losses)
        |
   everyo.core      (Tensor, operations, autograd, device, dtype)
        |
   everyo.backends  (numpy_backend, tensorflow_backend)  everyo.cuda
        |                                                    |
      NumPy                                     optional native extension

Layers

everyo.core

The foundation.

  • dtype.py — the small set of supported NumPy dtypes, with float32 as the default floating point type.
  • device.py — the Device abstraction. Buffers always live in host memory; the device selects where operations execute. Requesting CUDA without a working extension degrades to CPU with a warning rather than failing.
  • tensor.py — the Tensor type: data, device, gradient state, operators.
  • operations.py — every differentiable operation. Each one validates its inputs, computes the forward result (dispatching to CUDA when the tensor is on a CUDA device and a kernel exists) and records a graph node.
  • autograd.py — the reverse-mode engine: graph nodes, an iterative topological sort, gradient accumulation and the no_grad mode flag.
  • convolution.py — convolution and pooling. These live apart from operations.py because they need windowing helpers nothing else uses, and because the file would otherwise dwarf every other module. The forward pass is im2col, which turns a convolution into a single matrix multiplication; the backward pass is two more multiplications plus a scatter-add back into the padded image.

everyo.backends

operator_backend.py defines the common OperatorBackend contract and owns all device dispatch and fallback decisions. numpy_backend.py is the reference implementation of every kernel EveryO needs. tensorflow_backend.py is optional and used for cross-checking numerics, for a Keras reference model and for benchmarks. Importing the package never imports TensorFlow.

everyo.cuda

availability.py answers "can we run CUDA kernels right now?" without ever raising. interface.py mirrors the NumPy kernel signatures and exposes supports() plus unsupported_reason() so dispatch misses are observable and testable. Direct calls fall back to NumPy unless strict=True is passed. The kernels themselves live in the top-level cuda/ directory and are built separately.

everyo.nn

Module owns parameters and sub-modules, and can describe itself as a plain dictionary via get_config(). That description is what makes pickle-free serialisation possible. Layers, activations and losses all build on it.

everyo.optim

Optimizer holds the parameter list and per-parameter state keyed by object identity. SGD and Adam implement step().

everyo.data

Dataset is a two-method contract (__len__, __getitem__). DataLoader batches and shuffles it, with a fast path that slices whole arrays when the dataset is an ArrayDataset. Preprocessing follows a fit/transform split so that scaling statistics come from training data only.

everyo.training

Trainer runs the loop; Callback observers can stop training, checkpoint, log or reschedule the learning rate. History records one row per epoch and exports to JSON or CSV.

everyo.serialization

An .evo file is a ZIP archive holding manifest.json (metadata plus the architecture as JSON) and parameters.npz (saved and loaded with allow_pickle=False). Loading rebuilds the model by looking class names up in a registry, so opening an untrusted file cannot execute code.

Design decisions

Buffers stay in host memory. A CUDA device tag changes where kernels run, not where data lives. Transfers happen inside each kernel call. This costs bandwidth, and the benchmarks measure that cost honestly; in exchange the data model stays simple enough to read.

Images are NHWC. (batch, height, width, channels) is TensorFlow's native layout, and EveryO uses TensorFlow as its correctness reference — so convolution results can be compared against tf.nn.conv2d without transposing anything, which is exactly what the test suite does.

float32 by default. Python lists and scalars become float32; a NumPy array keeps its own precision, so float64 work stays float64. Gradient checks in the test suite use float64 for accuracy.

Gradients are recorded only when needed. A node is created only when gradient mode is on and at least one input requires gradients, so inference costs nothing extra.

Errors name the shapes. EveryOShapeError messages state what was attempted, which shapes were involved and what would have been valid.

Optional means optional. TensorFlow and CUDA are detected safely, report why they are unavailable, and never prevent the core from working.

Extending EveryO

To add a differentiable operation:

  1. Implement the forward pass in everyo/core/operations.py.
  2. Return the gradients for each input from the backward_fn closure.
  3. Add a finite-difference check to tests/test_autograd.py.

To add a layer:

  1. Subclass Module, decorate it with @register_module so it can be serialised, and implement forward.
  2. Implement get_config() if the constructor takes arguments.
  3. Export it from everyo/nn/__init__.py and the top-level everyo/__init__.py.
  4. Add tests, including a save/load round trip.

Original guide reproduced under the project’s MIT license. Upstream source remains authoritative.

View the reviewed source on GitHub ↗

Search documentation

Enter opens the first result · Tab moves through links · Esc closes