Multi-Layer Perceptron.
97%+Part 1 of 4 · MNIST digit classification (0-9)
This is where the series starts: a network built in pure NumPy. Forward pass, backprop, Adam, weight init, regularization, all written by hand. Backprop stops being magic around the third time you derive it.
The model below is the exact one from the repo, running live. Draw a digit and it will tell you what it sees.
Goal
Accurately predict hand-drawn digits in production
Dataset
MNIST 28×28 grayscale images (60k train / 10k test)
Architecture
784 → 256 → 128 → 10 with ReLU, Adam, He init, L2 regularization
Result
97%+ test accuracy
Try it live
Draw a digit from 0 to 9 · the actual model, running right here in your browser
loading weights · 0.9 mb
Prediction
·
Model input · 28×28
0
1
2
3
4
5
6
7
8
9
How I built it


Build an MLP from scratch (pure NumPy, no ML frameworks) to predict hand-drawn digits 0-9, and deploy it to production.
Process
Iteration 1: a simple single-layer MLP
- Architecture: 784 → 10 (single linear layer) with softmax, full-batch gradient descent
- Result: 92.6% dev accuracy, 91-92% test accuracy
- Issues: capacity too low, loss plateau at 0.28, confusions on similar shapes (4/9, 3/5, 7/1)



Iteration 2: deeper architecture + better training
- Architecture: 784 → 256 → 128 → 10 with ReLU activations
- Training: mini-batch Adam (batch 128), He initialization, L2 regularization (5e-4), 15 epochs
- Result: 99.8% train, 97.1% dev, 97.2% test accuracy
Production deployment
- Interactive app (
python app.py) for drawing and classifying digits - Added diagnostics: shows the exact 28×28 tensor fed to the network, stroke density, center offset, area ratio
- Found production input was too different from training data, so I adjusted the preprocessing
- Passed heavy stress testing (see the demo video above)
Everything, from data ingestion to UI, runs with pure NumPy. No high-level ML frameworks, yet the model delivers 97%+ accuracy and production-grade UX.