Skip to content
 
 

Repository files navigation

NeuralNetwork

A dependency-free neural network library written from scratch in C — including convolutional networks — that trains a ~99.3%-accurate MNIST digit classifier and beats classical baselines on time-series forecasting, with no libraries, no frameworks, just C99 and the standard math library.

The engine implements the pieces that make a network actually work: dense and convolutional / max-pooling layers, configurable activations (sigmoid / tanh / ReLU + softmax), MSE and cross-entropy losses, SGD-with-momentum and Adam, mini-batch training, principled weight initialisation (Xavier / He), and L2 weight decay. The convolutional backprop is verified against finite-difference gradients, and everything is tested, benchmarked, and CI-checked on Linux, macOS and Windows.

CI codecov Language Style License Docs

Benchmarks

Every number here is produced by code in this repo and checked by CI — no hand-waving.

MNIST handwritten digits (classification)

Model Test accuracy
CNN + data augmentation (conv8 → pool → conv16 → pool → dense128 → 10) ~99.3%
CNN (no augmentation) ~99.1%
MLP (784-128-10, ReLU + softmax, Adam) ~97.9%

All pure C99, no dependencies. The MLP trains in ~110 s; the CNN reaches 99.1% in ~10 epochs and 99.3% with --augment (random pixel shifts), using a learning-rate decay schedule.

scripts/get_mnist.sh                                 # download the dataset (~11 MB)
make mnist     && ./build/mnist_train                # the MLP
make mnist-cnn && ./build/mnist_cnn --augment 2      # the CNN, with augmentation
./build/mnist_cnn --save digits.cnn                  # save the trained model
./build/mnist_cnn --load digits.cnn                  # reload and evaluate, no retraining

CI trains fast subsets of both every push and fails if accuracy drops below its floor, so these numbers can't silently rot. The CNN's backprop is verified against finite-difference gradients (cnn_gradient_check) in the test suite.

CIFAR-10 (colour images)

The same convolutional engine on 3-channel 32x32 colour photos (a much harder problem than digits) — conv16 → pool → conv32 → pool → dense128 → 10:

Metric Value
Test accuracy ~72% (12 epochs, augmentation)
Input 3x32x32 RGB
scripts/get_cifar10.sh
make cifar-cnn && ./build/cifar_cnn --augment 1

Multi-channel convolution is the same code path as MNIST — only the input shape changes.

Fashion-MNIST (generalization)

The same loader and trainer, pointed at a different and harder dataset with zero code changes, reaches ~88.6% — proof the engine isn't overfit to one benchmark:

scripts/get_fashion_mnist.sh
./build/mnist_train --train-images data/fashion/train-images-idx3-ubyte \
  --train-labels data/fashion/train-labels-idx1-ubyte \
  --test-images data/fashion/t10k-images-idx3-ubyte \
  --test-labels data/fashion/t10k-labels-idx1-ubyte

Sunspot time-series (regression)

Forecasting annual sunspot activity from the prior 30 years (evaluation 1955–1979). The network beats both a naive baseline and a linear autoregression:

Model Eval RMSE
Neural net (open-loop) 0.129
Linear AR baseline 0.141
Naive / persistence 0.198

Sunspot forecast: network vs. actual

Features

  • Layers: fully-connected, plus 2D convolution, max-pooling and flatten (CNNs)
  • Activations: sigmoid, tanh, ReLU (hidden) + softmax (classification output)
  • Losses: mean squared error (regression) and cross-entropy (classification)
  • Optimizers: SGD with momentum, and Adam
  • Training: online or mini-batch; early stopping; L2 weight decay; LR decay; dropout; data augmentation
  • Models: save / load trained CNNs to disk (cnn_save / cnn_load)
  • Init: Xavier/Glorot (sigmoid/tanh), He (ReLU)
  • Reproducible: self-contained PCG32 RNG (no global rand()); fixed seed → fixed result
  • Portable: C99, builds clean on GCC / Clang / MSVC with -Wall -Wextra -Wpedantic
  • Parallel (optional): OpenMP multi-threads the CNN conv/dense layers (-DNEURALNET_ENABLE_OPENMP=ON); gradients stay bit-identical to the serial build
  • Embeddable: the engine never calls exit(); errors are returned

Build

You need a C99 compiler. Either build system works.

CMake (recommended)

cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failure   # unit + regression + classification tests
./build/neuralnet                             # the sunspot forecaster
./build/mnist_train                           # the MNIST classifier (after get_mnist.sh)

Options: -DNEURALNET_WARNINGS_AS_ERRORS=ON, -DNEURALNET_ENABLE_SANITIZERS=ON, -DNEURALNET_ENABLE_COVERAGE=ON, -DNEURALNET_BUILD_EXAMPLES=OFF.

Make

make        # build/neuralnet
make mnist  # build/mnist_train
make test   # build and run all tests

Usage

MNIST classifier

scripts/get_mnist.sh                          # fetch data into data/mnist/
./build/mnist_train                           # train (defaults: 128 hidden, Adam, 10 epochs)
./build/mnist_train --hidden 256 --epochs 15  # bigger net, longer
./build/mnist_train --save digits.weights     # save the trained model
./build/mnist_train --help

Sunspot forecaster

./neuralnet                                   # train on the built-in data
./neuralnet --config config/default.cfg       # settings from a file
./neuralnet --data my.csv --layers 30,20,1 --activation relu
./neuralnet --predict predictions.csv         # write predictions to CSV
./neuralnet --save model.weights              # save / --load to reuse
./neuralnet --help

See ./neuralnet --help for the full flag list (learning rate, momentum, seed, split, etc.).

Project layout

src/
  network.{c,h}   engine: layers, activations, losses, optimizers, mini-batch
  rng.{c,h}       reproducible PCG32 generator
  util.{c,h}      validated, locale-independent numeric parsing
  dataset.{c,h}   time-series loading + normalisation
  baseline.{c,h}  naive + linear-AR reference forecasters
  config.{c,h}    CLI flags + config-file parsing + validation
  forecast.{c,h}  training loop, early stopping, evaluation, metrics
  cnn.{c,h}       convolutional engine: conv/pool/flatten/dense/softmax + Adam
  mnist.{c,h}     IDX-format MNIST loader
  main.c          sunspot forecaster CLI
examples/
  mnist.c         MLP MNIST trainer    mnist_cnn.c   convolutional MNIST trainer
tests/
  test_network.c  engine units (forward, activations, XOR, weight I/O, RNG)
  test_classify.c softmax + cross-entropy + Adam + mini-batch (4-quadrant task)
  test_forecast.c golden regression: the net must beat the naive baseline
  test_cnn.c      finite-difference gradient check of conv/pool/dense backprop
scripts/
  get_mnist.sh    download MNIST    plot.py   matplotlib visualisation

How it works (the math)

Each non-input neuron computes an activation of the weighted sum of the previous layer's outputs plus a bias. Training minimises the loss by gradient descent: the output error term is act'(out)·(target−out) for MSE, or simply (out−target) for softmax + cross-entropy; hidden errors are back-propagated through the downstream weights. SGD applies Δw = eta·grad + alpha·Δw_prev; Adam keeps per-weight first/second moment estimates for an adaptive step. Weights start from an activation-matched scheme (Xavier or He), and inputs are scaled into the activation's responsive range.

Diagram

Further reading: https://en.wikipedia.org/wiki/Backpropagation

Documentation

Browse the generated API reference at https://bikebrainz.github.io/NeuralNetwork/ (built from source with Doxygen on every push to main).

Contributing

See CONTRIBUTING.md. CI enforces builds (no warnings), tests, sanitizers, Valgrind, clang-tidy, cppcheck, clang-format, coverage, and the MNIST / Fashion-MNIST / CIFAR-10 accuracy floors.

Authors

Originally written by Dean Urschel; reworked into a configurable, tested, benchmarked, cross-platform library by Gavin Chase.

License

MIT

About

This program is a customizable multi layer back propagation neural network "from scratch".

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages