Project 40: Neural Net From Scratch Lite 🧠¶

Difficulty: 🔴 Advanced

Run cells top to bottom. Each step builds on the previous one and saves real files under outputs/neural_net_from_scratch_lite/.

← Back to index

Description¶

Train a tiny 2-layer neural net on a moon-shaped dataset with only NumPy — then plot the decision boundary.

Libraries & Modules¶

  • numpy
  • matplotlib

Python Concepts You'll Practice¶

Forward/backprop basics, loss curves, decision boundaries

🎛️ Parameter Variations¶

  • HIDDEN layer size
  • LR, EPOCHS
  • SEED
In [1]:
# Install dependencies for this project (safe to re-run)
import sys
!{sys.executable} -m pip install pillow numpy matplotlib wordcloud qrcode[pil] python-barcode fpdf2 jinja2 folium plotly pandas scipy ipywidgets -q
Skipping pip install (already installed)
In [2]:
# Interactive Jupyter setup — run this cell first
%matplotlib inline

from pathlib import Path
from IPython.display import display, Image as IPImage, HTML, Markdown, Audio, IFrame

OUTPUT_DIR = Path("outputs") / "neural_net_from_scratch_lite"
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
print(f"✅ Outputs folder: {OUTPUT_DIR.resolve()}")
✅ Outputs folder: C:\Users\Hansel Yan\Projects\CodeItAll\outputs\neural_net_from_scratch_lite

Step 1 — Make moons + init network¶

In [3]:
import numpy as np

# 🎛️ TWEAK THESE
N = 200
HIDDEN = 16
LR = 0.1
EPOCHS = 400
SEED = 0

rng = np.random.default_rng(SEED)

def make_moons(n, noise=0.15):
    n2 = n // 2
    t = np.linspace(0, np.pi, n2)
    x1 = np.c_[np.cos(t), np.sin(t)]
    x2 = np.c_[1 - np.cos(t), 0.5 - np.sin(t)]
    x = np.vstack([x1, x2])
    x += noise * rng.normal(size=x.shape)
    y = np.array([0] * n2 + [1] * n2)
    idx = rng.permutation(n)
    return x[idx], y[idx].reshape(-1, 1)

X, y = make_moons(N)
W1 = rng.normal(scale=0.8, size=(2, HIDDEN))
b1 = np.zeros((1, HIDDEN))
W2 = rng.normal(scale=0.8, size=(HIDDEN, 1))
b2 = np.zeros((1, 1))
print("Dataset", X.shape, "labels", y.mean())
Dataset (200, 2) labels 0.5

Step 2 — Train + plot boundary¶

In [4]:
import matplotlib.pyplot as plt

def sigmoid(z):
    return 1 / (1 + np.exp(-np.clip(z, -30, 30)))

losses = []
for epoch in range(EPOCHS):
    z1 = X @ W1 + b1
    a1 = np.tanh(z1)
    z2 = a1 @ W2 + b2
    a2 = sigmoid(z2)
    loss = -np.mean(y * np.log(a2 + 1e-9) + (1 - y) * np.log(1 - a2 + 1e-9))
    losses.append(loss)
    dz2 = (a2 - y) / len(X)
    dW2 = a1.T @ dz2
    db2 = dz2.sum(axis=0, keepdims=True)
    dz1 = (dz2 @ W2.T) * (1 - a1 ** 2)
    dW1 = X.T @ dz1
    db1 = dz1.sum(axis=0, keepdims=True)
    W1 -= LR * dW1; b1 -= LR * db1
    W2 -= LR * dW2; b2 -= LR * db2

xx, yy = np.meshgrid(np.linspace(X[:,0].min()-0.4, X[:,0].max()+0.4, 180),
                     np.linspace(X[:,1].min()-0.4, X[:,1].max()+0.4, 180))
grid = np.c_[xx.ravel(), yy.ravel()]
zz = sigmoid(np.tanh(grid @ W1 + b1) @ W2 + b2).reshape(xx.shape)

fig, axes = plt.subplots(1, 2, figsize=(10, 4))
axes[0].plot(losses, color="#111")
axes[0].set_title("Loss")
axes[1].contourf(xx, yy, zz, levels=20, cmap="RdGy")
axes[1].scatter(X[:,0], X[:,1], c=y.ravel(), cmap="RdGy", edgecolor="k", s=18)
axes[1].set_title("Decision boundary")
fig.suptitle("Neural Net From Scratch Lite — CodeItAll")
fig.tight_layout()
out = OUTPUT_DIR / "tiny_net.png"
fig.savefig(out, dpi=150)
plt.show()
print(f"Final loss {losses[-1]:.4f} → {out}")
No description has been provided for this image
Final loss 0.2478 → outputs\neural_net_from_scratch_lite\tiny_net.png

✅ Project complete¶

Check your output folder: outputs/neural_net_from_scratch_lite/

← Back to index