snn.model¶
snn ships three model types with identical compile → fit → evaluate → predict
APIs. Choose the one that fits your architecture:
|
|
|
|
|---|---|---|---|
Linear pipeline |
✅ |
✅ |
✅ |
Skip connections |
✅ via |
✅ |
✅ |
Multiple inputs/outputs |
❌ |
✅ |
✅ |
Auto backward |
✅ |
❌ |
✅ |
Sequential¶
- class snn.model.Sequential(layers=None)[source]¶
Bases:
objectSequential 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.Layersubclass.- 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", …Instance —
Adam(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", …Instance —
HuberLoss(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_decayon 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 toBinaryCrossentropyandCategoricalCrossentropywhen constructed from a string or dict. Has no effect when you pass aLossinstance 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 whenvalidation_datais 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
- set_weights(weights)[source]¶
Restore weights from the format returned by
get_weights().
- save_weights(path)[source]¶
Save all trainable weights to a NumPy
.npzfile.- Parameters:
path (str) – Filename prefix (the
.npzextension is added automatically).
- load_weights(path)[source]¶
Load weights from a
.npzfile written bysave_weights().- Parameters:
path (str) – Path to the
.npzfile (with or without extension).
Model (subclassable)¶
- class snn.model.Model[source]¶
Bases:
objectSubclassable model class — define
call()andbackward()yourself.All
Layerinstances 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 toSequential.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
- 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.
- 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.
- load_weights(path)[source]¶
Load weights from a
.npzfile written bysave_weights().
GraphModel (functional API)¶
- class snn.model.GraphModel(inputs, outputs)[source]¶
Bases:
objectFunctional-API model: build arbitrary computation graphs with skip connections, multi-input layers, and shared weights.
Build a graph by connecting layers to
Inputnodes, then pass the terminal node(s) toGraphModel. 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:
- 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.
- 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.
Input¶
- class snn.model.Input(shape)[source]¶
Bases:
_TensorEntry-point node for the
GraphModelfunctional 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)