snn.model

snn ships three model types with identical compile fit evaluate predict APIs. Choose the one that fits your architecture:

Sequential

Model

GraphModel

Linear pipeline

Skip connections

✅ via Residual

Multiple inputs/outputs

Auto backward

Sequential

class snn.model.Sequential(layers=None)[source]

Bases: object

Sequential model — a linear stack of layers.

Build, compile, then train:

>>> model = Sequential([
...     Dense(64, activation='relu'),
...     Dropout(0.3),
...     Dense(10, activation='softmax'),
... ])
>>> model.compile(
...     optimizer='adam',
...     loss='categorical_crossentropy',
...     metrics=['accuracy'],
... )
>>> history = model.fit(X_train, y_train, epochs=20, batch_size=32,
...                     validation_data=(X_val, y_val))
>>> y_pred = model.predict(X_test)
>>> results = model.evaluate(X_test, y_test)
add(layer)[source]

Append a layer to the model.

Parameters:

layer (Layer) – Any snn.layers.base.Layer subclass.

Returns:

Allows chaining: model.add(Dense(64)).add(Dense(10)).

Return type:

self

compile(optimizer='adam', loss='mse', metrics=None, *, learning_rate=None, weight_decay=None, clip_norm=None, from_logits=False, verbose=False)[source]

Configure the model for training.

Parameters:
  • optimizer (str, Optimizer, or dict) –

    Weight-update rule. Three forms accepted:

    • String shorthand"adam", "sgd", "adamw", …

    • InstanceAdam(learning_rate=3e-4)

    • Config dict{"name": "adam", "learning_rate": 1e-3} Any key recognised by the optimizer constructor can appear in the dict; the "name" (or "class") key selects the optimizer class.

  • loss (str, Loss, or dict) –

    Objective function. Three forms accepted:

    • String shorthand"mse", "categorical_crossentropy", …

    • InstanceHuberLoss(delta=2.0)

    • Config dict{"name": "huber", "delta": 2.0}

  • metrics (str or list, optional) – Metric(s) tracked per epoch. Accepts a single string or a list of strings / callables.

  • learning_rate (float, optional) – Shortcut to override the optimizer’s learning rate without constructing an instance manually. Applied after the optimizer is built from optimizer.

  • weight_decay (float, optional) – Shortcut to set weight_decay on the optimizer (if supported).

  • clip_norm (float, optional) – Clip the global gradient L2 norm to this value before each optimizer step. Equivalent to using Trainer(model, clip_grad_norm=clip_norm) but available directly on the model.

  • from_logits (bool) – If True, the loss receives raw (unnormalised) scores. Passed automatically to BinaryCrossentropy and CategoricalCrossentropy when constructed from a string or dict. Has no effect when you pass a Loss instance directly.

  • verbose (bool) – If True, prints a compile-summary table showing the chosen optimizer, loss, metrics, and any extra settings.

Return type:

self

Examples

Minimal:

>>> model.compile("adam", "mse")

Learning-rate shortcut (no need to import Adam):

>>> model.compile("adam", "categorical_crossentropy",
...               metrics=["accuracy"], learning_rate=3e-4)

Dict config — all hyperparameters in one place:

>>> model.compile(
...     optimizer={"name": "adamw", "learning_rate": 1e-3,
...                "weight_decay": 1e-4},
...     loss={"name": "huber", "delta": 2.0},
...     metrics=["r2"],
... )

With gradient clipping (no Trainer needed):

>>> model.compile("adam", "mse", clip_norm=1.0)

Logit input to loss (last Dense has no activation):

>>> model.compile("adam", "categorical_crossentropy",
...               from_logits=True)
get_compile_config()[source]

Return a plain-dict snapshot of the current compile settings.

Useful for logging, serialising experiments, or re-compiling a new model with the same settings.

Returns:

Keys: "optimizer", "loss", "metrics", "learning_rate", "weight_decay", "clip_norm".

Return type:

dict

fit(x, y, epochs=10, batch_size=32, validation_data=None, shuffle=True, verbose=1, callbacks=None)[source]

Train the model.

Parameters:
  • x (ndarray) – Input data of shape (N, ...).

  • y (ndarray) – Target data of shape (N, ...).

  • epochs (int) – Number of full passes over the dataset.

  • batch_size (int) – Number of samples per gradient update.

  • validation_data (tuple, optional) – (X_val, y_val) evaluated at the end of each epoch.

  • shuffle (bool) – Whether to shuffle the training data each epoch.

  • verbose (int) – 0 = silent, 1 = one line per epoch.

  • callbacks (list, optional) – Callables invoked at the end of each epoch as callback(epoch, history).

Returns:

Training history. Keys are "loss", each metric name, and "val_*" equivalents when validation_data is provided.

Return type:

dict

predict(x, batch_size=None)[source]

Run inference on x.

Parameters:
  • x (ndarray)

  • batch_size (int, optional) – If given, runs inference in mini-batches (saves memory for large datasets).

Returns:

Predictions of shape (N, output_units).

Return type:

ndarray

evaluate(x, y, batch_size=None, verbose=1)[source]

Compute loss and metrics on (x, y).

Parameters:
  • x (ndarray)

  • y (ndarray)

  • batch_size (int, optional)

  • verbose (int)

Returns:

{"loss": float, metric_name: float, …}

Return type:

dict

summary()[source]

Print a table of layers, output shapes, and parameter counts.

get_weights()[source]

Return a list of per-layer weight dicts (copies).

set_weights(weights)[source]

Restore weights from the format returned by get_weights().

save_weights(path)[source]

Save all trainable weights to a NumPy .npz file.

Parameters:

path (str) – Filename prefix (the .npz extension is added automatically).

load_weights(path)[source]

Load weights from a .npz file written by save_weights().

Parameters:

path (str) – Path to the .npz file (with or without extension).

Model (subclassable)

class snn.model.Model[source]

Bases: object

Subclassable model class — define call() and backward() yourself.

All Layer instances assigned as attributes (or stored in lists/tuples of attributes) are auto-discovered for parameter collection and optimizer updates. The compile / fit / predict / evaluate API is identical to Sequential.

Examples

Basic usage (functionally identical to Sequential):

class MyNet(Model):
    def __init__(self):
        super().__init__()
        self.fc1 = Dense(128, activation='relu')
        self.bn   = BatchNormalization()
        self.fc2  = Dense(10, activation='softmax')

    def call(self, x, training=False):
        x = self.fc1(x, training=training)
        x = self.bn(x, training=training)
        return self.fc2(x, training=training)

    def backward(self, grad):
        grad = self.fc2.backward(grad)
        grad = self.bn.backward(grad)
        return self.fc1.backward(grad)

model = MyNet()
model.compile('adam', 'categorical_crossentropy', metrics=['accuracy'])
model.fit(X_train, y_train, epochs=20)

Residual block (skip connections):

class ResBlock(Model):
    def __init__(self, units):
        super().__init__()
        self.fc1 = Dense(units, activation='relu')
        self.fc2 = Dense(units)                 # no activation here
        self.proj = Dense(units)                # shortcut projection

    def call(self, x, training=False):
        shortcut = self.proj(x, training=training)
        h = self.fc1(x, training=training)
        h = self.fc2(h, training=training)
        self._pre_act = h + shortcut
        return np.maximum(0, self._pre_act)     # ReLU after residual

    def backward(self, grad):
        d = grad * (self._pre_act > 0)          # ReLU gate
        d_proj = self.proj.backward(d)
        d = self.fc2.backward(d)
        d = self.fc1.backward(d)
        return d + d_proj
call(x, training=False)[source]

Forward pass. Override in subclasses.

backward(grad)[source]

Backward pass. Override in subclasses.

compile(optimizer='adam', loss='mse', metrics=None, *, learning_rate=None, weight_decay=None, clip_norm=None, from_logits=False, verbose=False)[source]

Configure the model — identical interface to Sequential.

Parameters:
  • optimizer (str, Optimizer, or dict)

  • loss (str, Loss, or dict)

  • metrics (str or list, optional)

  • learning_rate (float, optional)

  • weight_decay (float, optional)

  • clip_norm (float, optional)

  • from_logits (bool)

  • verbose (bool)

fit(x, y, epochs=10, batch_size=32, validation_data=None, shuffle=True, verbose=1, callbacks=None)[source]

Train the model. Identical interface to Sequential.fit.

predict(x, batch_size=None)[source]

Run inference on x. Returns predictions as a NumPy array.

evaluate(x, y, batch_size=None, verbose=1)[source]

Compute loss and metrics on (x, y).

summary()[source]

Print all discovered Layer types and parameter counts.

save_weights(path)[source]

Save all trainable weights to a .npz file.

load_weights(path)[source]

Load weights from a .npz file written by save_weights().

get_compile_config()[source]

GraphModel (functional API)

class snn.model.GraphModel(inputs, outputs)[source]

Bases: object

Functional-API model: build arbitrary computation graphs with skip connections, multi-input layers, and shared weights.

Build a graph by connecting layers to Input nodes, then pass the terminal node(s) to GraphModel. The model traces the DAG, sorts it topologically, and handles forward / backward automatically.

Examples

Simple feedforward (equivalent to Sequential):

from snn.model import Input, GraphModel
from snn.layers import Dense

x   = Input(shape=(784,))
h   = Dense(256, activation='relu')(x)
h   = Dense(128, activation='relu')(h)
out = Dense(10, activation='softmax')(h)

model = GraphModel(inputs=x, outputs=out)
model.compile('adam', 'categorical_crossentropy', metrics=['accuracy'])
model.fit(X_train, y_train, epochs=20)

Residual / skip connection:

x      = Input(shape=(128,))
skip   = Dense(64)(x)                        # shortcut
h      = Dense(64, activation='relu')(x)
h      = Dense(64)(h)                        # same size as skip
merged = Add()([h, skip])                    # element-wise add
out    = Dense(10, activation='softmax')(merged)

model = GraphModel(inputs=x, outputs=out)

Multi-branch (text + metadata fusion):

text_in  = Input(shape=(300,))              # e.g. mean-pooled GloVe
meta_in  = Input(shape=(10,))               # numerical features

h_text   = Dense(128, 'relu')(text_in)
h_meta   = Dense(32, 'relu')(meta_in)
merged   = Concatenate()([h_text, h_meta])  # (batch, 160)
out      = Dense(5, 'softmax')(merged)

model = GraphModel(inputs=[text_in, meta_in], outputs=out)
model.fit([X_text, X_meta], y, epochs=10)
Parameters:
  • inputs (Input or list of Input) – Entry-point node(s).

  • outputs (_Tensor or list of _Tensor) – Terminal node(s) whose values are returned by predict.

compile(optimizer='adam', loss='mse', metrics=None, *, learning_rate=None, weight_decay=None, clip_norm=None, from_logits=False, verbose=False)[source]

Configure the model. Identical interface to Sequential.

Parameters:
  • optimizer (str, Optimizer, or dict)

  • loss (str, Loss, or dict)

  • metrics (str or list, optional)

  • learning_rate (float, optional)

  • weight_decay (float, optional)

  • clip_norm (float, optional)

  • from_logits (bool)

  • verbose (bool)

fit(x, y, epochs=10, batch_size=32, validation_data=None, shuffle=True, verbose=1, callbacks=None)[source]

Train the model. Identical interface to Sequential.fit.

predict(x, batch_size=None)[source]

Run inference on x.

evaluate(x, y, batch_size=None, verbose=1)[source]

Compute loss and metrics on (x, y).

summary()[source]

Print all graph nodes and parameter counts.

save_weights(path)[source]

Save all weights to a .npz file.

load_weights(path)[source]

Load weights from a .npz file.

Input

class snn.model.Input(shape)[source]

Bases: _Tensor

Entry-point node for the GraphModel functional API.

Parameters:

shape (tuple) – Shape of a single sample (without the batch dimension).

Examples

>>> x = Input(shape=(784,))
>>> h = Dense(128, activation='relu')(x)
>>> out = Dense(10, activation='softmax')(h)
>>> model = GraphModel(inputs=x, outputs=out)