Guides menu
Guide

Build a neural network

A step-by-step tutorial: train a small neural network that learns the XOR function using serez-ai. By the end you will have built, trained, evaluated, and saved a model — entirely in Serez Code.

What you'll learn: building a Sequential model, choosing layers and activations, training with an optimizer, making predictions, and saving/loading weights.

Step 1 — Set up the project

Create a project folder and install the library:

mkdir xor-net
cd xor-net
sz init --y
sz install serez-ai

sz init --y scaffolds a serez.json with a dev script already wired to sz index.sz— so you'll run the project with sz run dev rather than calling the file directly. That keeps the run command stable as the project grows.

Now create index.sz, importing the library and seeding the RNG so results are reproducible:

import "serez-ai"

Random.seed(42)

Step 2 — Prepare the data

XOR is the classic non-linear problem: the output is 1 only when the two inputs differ. A single linear layer can't solve it — you need a hidden layer.

// 4 training examples — every combination of two bits
let X = [[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]]
let y = [[0.0],      [1.0],      [1.0],      [0.0]]

Step 3 — Build the model

Stack layers with Sequential. Two inputs → a hidden layer of 8 ReLU units → one sigmoid output (a probability between 0 and 1):

let model = new Sequential()
model.add(new Dense(2, 8, "relu"))
model.add(new Dense(8, 1, "sigmoid"))
ArgumentMeaning
2, 88 neurons, each taking 2 inputs (the hidden layer)
"relu"Activation — adds the non-linearity XOR requires
8, 11 output neuron taking the 8 hidden activations
"sigmoid"Squashes the output into a 0–1 probability

Step 4 — Train

Pick a loss function and an optimizer, then call fit_opt. Binary cross-entropy (BCE) is the right loss for a 0/1 target; Adam is a solid default optimizer.

let loss = new BCE()
let opt  = new Adam(0.05, 0.9, 0.999)

// fit_opt(X, y, loss_fn, epochs, optimizer) → array of per-epoch losses
let history = model.fit_opt(X, y, loss, 2000, opt)

out "First loss: {history[0]}"
out "Final loss: {history[history.length() - 1]}"

The final loss should be far smaller than the first — that's the network learning.

Step 5 — Predict

Run the four inputs back through the trained model:

let preds = model.forward(X)

out "XOR(0,0) ~ {preds[0][0]}"   // → near 0
out "XOR(0,1) ~ {preds[1][0]}"   // → near 1
out "XOR(1,0) ~ {preds[2][0]}"   // → near 1
out "XOR(1,1) ~ {preds[3][0]}"   // → near 0

Step 6 — Save and load

Persist the trained weights so you don't have to retrain. The model is saved as a plain text file you can ship alongside your app:

model.save("xor.weights")

// Later — or in another script — rebuild the same architecture and load:
let restored = new Sequential()
restored.add(new Dense(2, 8, "relu"))
restored.add(new Dense(8, 1, "sigmoid"))
restored.load("xor.weights")

out restored.forward([[1.0, 0.0]])[0][0]   // same prediction as before
Note: load() expects the exact same layer architecture as when you saved. Build the layers first, then load.

Run it

Use the dev script that sz init created:

sz run dev

Next steps

  • Swap in Conv2D + MaxPool2D for image data.
  • Use DataLoader + fit_dl for mini-batch training on larger datasets.
  • See the serez-ai reference for the full layer and optimizer list.
  • Put it to work: turn this trained model into a live JSON API with Serve a Model over HTTP.
  • Ready for language models and agents? Continue with the GPT agent tutorial.