Aditya Sridhar

Model · Oct 2026 · runs in your browser

MLP in NumPy

The network drawn on the front page. Three weight matrices, ReLU, softmax, and a backward pass written out by hand with no autograd. Trained on MNIST on a laptop CPU; the weights below are the ones running in your browser.

Test accuracy
98.69%
Parameters
109,386
Weights
107 KB, int8
Training time
22 s, CPU

Try it

28×28 input

Waiting for a digit

  1. 0
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9

Inference is a plain JavaScript forward pass over the exported weights, no server and no ML runtime. Your drawing is cropped, scaled to fit 20×20 and centred by its centre of mass in a 28×28 frame, which is how the MNIST digits themselves were prepared.

Training run

Accuracytraintest
85.0%90.0%95.0%14710131620epoch 1 · train 84.8%epoch 2 · train 93.9%epoch 3 · train 95.0%epoch 4 · train 95.8%epoch 5 · train 96.3%epoch 6 · train 96.7%epoch 7 · train 96.9%epoch 8 · train 97.2%epoch 9 · train 97.3%epoch 10 · train 97.6%epoch 11 · train 97.8%epoch 12 · train 97.8%epoch 13 · train 98.0%epoch 14 · train 98.1%epoch 15 · train 98.3%epoch 16 · train 98.3%epoch 17 · train 98.4%epoch 18 · train 98.4%epoch 19 · train 98.5%epoch 20 · train 98.4%epoch 1 · test 95.3%epoch 2 · test 96.4%epoch 3 · test 97.0%epoch 4 · test 97.5%epoch 5 · test 97.4%epoch 6 · test 98.0%epoch 7 · test 98.0%epoch 8 · test 98.2%epoch 9 · test 98.2%epoch 10 · test 98.2%epoch 11 · test 98.4%epoch 12 · test 98.5%epoch 13 · test 98.4%epoch 14 · test 98.5%epoch 15 · test 98.5%epoch 16 · test 98.6%epoch 17 · test 98.6%epoch 18 · test 98.6%epoch 19 · test 98.7%epoch 20 · test 98.7%
Losstraintest
0.2000.40014710131620epoch 1 · train 0.483epoch 2 · train 0.203epoch 3 · train 0.163epoch 4 · train 0.140epoch 5 · train 0.121epoch 6 · train 0.109epoch 7 · train 0.101epoch 8 · train 0.091epoch 9 · train 0.085epoch 10 · train 0.078epoch 11 · train 0.072epoch 12 · train 0.070epoch 13 · train 0.064epoch 14 · train 0.062epoch 15 · train 0.056epoch 16 · train 0.055epoch 17 · train 0.052epoch 18 · train 0.053epoch 19 · train 0.051epoch 20 · train 0.052epoch 1 · test 0.156epoch 2 · test 0.119epoch 3 · test 0.100epoch 4 · test 0.084epoch 5 · test 0.078epoch 6 · test 0.071epoch 7 · test 0.065epoch 8 · test 0.058epoch 9 · test 0.059epoch 10 · test 0.059epoch 11 · test 0.049epoch 12 · test 0.051epoch 13 · test 0.049epoch 14 · test 0.046epoch 15 · test 0.046epoch 16 · test 0.045epoch 17 · test 0.045epoch 18 · test 0.044epoch 19 · test 0.044epoch 20 · test 0.043

Test accuracy sits above training accuracy for the whole run. That is not the model generalising better than it fits: training batches are randomly shifted by up to 2 pixels, the test set is not, so the two numbers are measured on different distributions.

Results

MetricValueSource
Test accuracy, 10,000 images98.69%training/mlp_numpy/log.csv, epoch 20
Test loss0.0435training/mlp_numpy/log.csv, epoch 20
Train accuracy (augmented)98.39%training/mlp_numpy/log.csv, epoch 20
Test accuracy after int8 export98.65%public/models/mlp/meta.json
Wall-clock training time22.3 straining/mlp_numpy/log.csv, seconds column

How it works

Forward. Each layer computes h = relu(W·x + b); the last one skips the ReLU and feeds a softmax. 784 inputs, hidden layers of 128 and 64, ten outputs: 109,386 parameters, initialised with He scaling so activations keep their size through the ReLUs.

Backward. Softmax and cross-entropy together have a clean gradient, p − onehot(y). From there each layer hands back dW = δᵀ·h, db = Σδ, and passes δ·W ⊙ [z > 0] to the layer before it. A unit test perturbs every parameter of a small network by ±10⁻⁶ and checks the analytic gradient against the finite difference.

Training. SGD with momentum 0.9, batch 128, learning rate 0.05 on a cosine decay to zero over 20 epochs, weight decay 0.00005 on the weights only, random shifts of up to 2 pixels. Seed 0; rerunning the script reproduces the log exactly.

Export. Weights are quantised to int8 with one scale per output neuron, which cuts the download to a quarter and costs 0.04 points of accuracy.

Reproduce

Hardware
12th Gen Intel(R) Core(TM) i5-12450H, CPU only, NumPy 1.26.4
Commit
dc58af1
Data
MNIST, 60,000 train / 10,000 test, downloaded and MD5-checked by the script

Check the backward pass

cd training/mlp_numpy && python -m pytest test_gradcheck.py

Train and export

python training/mlp_numpy/train.py