Elliot Sones.Machine Learning, from scratch

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
The multi-layer perceptron: 784 → 256 → 128 → 10
The multi-layer perceptron: 784 → 256 → 128 → 10
MNIST: 28×28 grayscale digits, 60k train / 10k test
MNIST: 28×28 grayscale digits, 60k train / 10k test

Build an MLP from scratch (pure NumPy, no ML frameworks) to predict hand-drawn digits 0-9, and deploy it to production.

Demo walkthrough video

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)
Loss plateau: train vs dev loss with a late-epoch plateau
Loss plateau: train vs dev loss with a late-epoch plateau
Loss wobble: zoomed view of oscillating loss after the plateau
Loss wobble: zoomed view of oscillating loss after the plateau
Common confusions: the classic mistakes (4→9, 3→5, 7→1)
Common confusions: the classic mistakes (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.