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.
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-aisz 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"))| Argument | Meaning |
|---|---|
2, 8 | 8 neurons, each taking 2 inputs (the hidden layer) |
"relu" | Activation — adds the non-linearity XOR requires |
8, 1 | 1 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 0Step 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 beforeload() 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 devNext steps
- Swap in
Conv2D+MaxPool2Dfor image data. - Use
DataLoader+fit_dlfor 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.