Skip to content
← All projects

NumPy Neural Network from Scratch

98.07% on MNIST · SGD, Momentum & Adam compared
  • Python
  • NumPy
  • Deep Learning

What I implemented by hand

Forward pass, backpropagation, Xavier initialisation, and SGD / Momentum / Adam optimizers written by hand in NumPy. No autograd, no ML framework. Same 784 → 128 → 10 architecture as my earlier C++ network.

How I evaluated it

Same 784 → 128 → 10 network, same weight seeds, 100 epochs, batch size 32, MNIST's standard 60,000 / 10,000 train/test split. SGD and Momentum (β = 0.9) at learning rate 0.01, Adam at 0.001. One run per optimizer; reproducible with python run_comparison.py.

Results

At 100 epochs all three finish between 97.90% and 98.07% — too close for a single run to rank them. The real difference is speed: Adam passes 97% by epoch 2 and classical momentum by epoch 4, while plain SGD takes until epoch 26.

  • SGD
  • Momentum
  • Adam

Test accuracy vs epoch (floor 90%)

90%92%94%96%98%100%120406080100Epoch

Hover or focus the chart and use the arrow keys to inspect an epoch.

Training loss vs epoch (log scale)

1e01e-21e-41e-61e-81e-10120406080100Epoch

Hover or focus the chart and use the arrow keys to inspect an epoch.

View data
EpochSGD accMomentum accAdam accSGD lossMomentum lossAdam loss
190.49%94.87%95.93%0.64380.30480.2601
1095.33%97.81%97.79%0.16540.03100.0157
2096.68%97.88%97.85%0.10570.00820.0057
3097.22%98.07%98.07%0.07700.00320.0040
4097.39%98.04%97.72%0.05970.00190.0022
5097.66%98.07%98.03%0.04770.00130.0016
6097.77%98.08%97.95%0.03929.82e-40.0030
7097.85%98.07%97.78%0.03287.89e-40.0031
8097.85%98.04%97.79%0.02776.55e-40.0017
9097.88%98.08%97.79%0.02355.56e-40.0019
10097.90%98.07%97.91%0.02024.83e-40.0011
SGD — final test accuracy (epoch 100)97.90%
Momentum — final test accuracy (epoch 100)98.07%
Adam — final test accuracy (epoch 100)97.91%
SGD — first epoch ≥ 97% test accuracy26
Momentum — first epoch ≥ 97% test accuracy4
Adam — first epoch ≥ 97% test accuracy2
SGD — test accuracy at epoch 593.83%
Momentum — test accuracy at epoch 597.44%
Adam — test accuracy at epoch 597.42%

What broke

  • My first 'momentum' was the EMA form, v = βv + (1−β)g. At the same learning rate that's just SGD with extra smoothing, and its curve sat exactly on top of SGD's. Switching to classical momentum (v = βv + g) took it from epoch 26 to epoch 4 to reach 97%. A unit test now checks the velocity sequence (g, 1.9g, 2.71g), and it fails on the old code.
  • The logged training loss was the last mini-batch's, not the epoch average, so the curve was mostly noise. It's now the batch-size-weighted mean over each epoch.