Neural network from scratch in pure Python + NumPy that recognises handwritten digits (MNIST), with an educational GUI: watch it learn, tweak it while it trains, look inside every layer.
Python
16
27 commits
updated Sep 26, 2026
Handwritten digit recognition in pure Python + NumPy โ watch the network learn, tweak it while it learns, and get your hands inside it.
Features โข Quick Start โข Tech Stack โข Terminal โข Experiments โข Development
A small neural network written from scratch in Python + NumPy (no PyTorch or TensorFlow) that learns to recognize the handwritten digits of the MNIST dataset, with an educational graphical interface to watch it learn, change its parameters while it learns and get your hands inside the trained network. The interface is available in English and Italian (the EN | IT selector at the top right, next to Pick).
nn_digits/neural_net/network.py).pipx install neural-network-digits, or as a zip with a double-click.How many photos to download (or the whole collection: all the 70,000 photos of MNIST, after a window that tells you how much space they take), what the dataset looks like before training (examples, photos per digit, "average digit", pixel values) and the project reset.

Loss curve (for every mini-batch and for every epoch), accuracy, strength of the corrections per layer with the inactive neurons, what every neuron of the first layer "looks for" and the gaussians of the weights (now compared with the start) and of the noise.

Accuracy on photos never seen before. The photos can be "damaged" (noise, rotation, stroke thickness) and you can set a confidence threshold: below it the network says "I don't know", and you see how many photos it still answers and how many of those it gets right. There are also the robustness curves and the wrong photos.
The big chart switches from the confusion matrix to the points map: every photo is a point on a plane (PCA or t-SNE, written in NumPy), layer by layer. You can see the digits separate, and when you hover a point with the mouse the photo and the answer of the network show up.

You draw a digit and see the neurons light up. Click a neuron to see weighted sum, output, bias and weights, and you can shift its bias, multiply its weights or switch it off. There are also global knobs (temperature, noise on the weights, pruning), and the effect on the test accuracy shows up right away. The edited network can be saved.

The math of a layer number by number, with colored cells like in the "LLM visualizers":
When you hover a cell, the connected ones light up and the math is explained.

A side panel (F2, or the Assistant button at the top right) that knows the program and sees what is happening in it:
It is not a language model, on purpose: the answers come from a small search engine (TF-IDF, written in NumPy
in nn_digits/assistant/search.py) over a glossary of neural networks and
the explanations of the controls, and the hints are simple rules
(nn_digits/assistant/rules.py). It needs nothing
to download, it answers in a moment and it never makes things up about the numbers it reads.

You need Python 3.9 or newer. There are two ways to install the program.
pipx install neural-network-digits # or: pip install neural-network-digits
neural-network-digits # opens the interface
pipx puts the program in an environment of its own and the neural-network-digits
command in the PATH. On Linux Tkinter must be installed first (see the table below). The photos, the model and
the settings go in the folder of your user: %APPDATA%\neural-network-digits on Windows,
~/Library/Application Support/neural-network-digits on macOS and ~/.local/share/neural-network-digits
on Linux.
Download the zip of the latest version from the Releases page and extract it.
Windows: double-click start.bat. The first time it installs the libraries by itself (numpy, pillow,
matplotlib), then it opens the interface.
Linux and macOS: open a terminal in the extracted folder and run ./start.sh. The first time it
creates a virtual environment in .venv and installs the libraries there (the Python of the system is not
touched), then it opens the interface. It needs Tkinter and venv: if one is missing, start.sh tells you
what to install.
| System | Once, before the first start |
|---|---|
| Ubuntu / Debian | sudo apt install python3-venv python3-tk |
| Fedora | sudo dnf install python3-tkinter |
| Arch | sudo pacman -S tk |
| macOS | Python from python.org (Tkinter included; the Python that comes with macOS has a Tkinter that is too old) |
Tested on Windows 11, Ubuntu 24.04, Debian 12, Fedora 44 and Arch Linux; on macOS the automatic tests
and start.sh run at every push (CI).
There are no photos yet:
Every time the interface opens, the program asks GitHub whether a new version is out (you can turn this off in the Info tab). If there is one, it shows the changes and offers:
data/ folder (photos, model, settings) is not touched, and if something goes wrong
the old files are put back.From the terminal it is start.bat update (on Linux and macOS ./start.sh update). If you downloaded the project
with git clone, update with git pull instead. If you installed it with pip, the program tells you when a new
version is out and you update it with pipx upgrade neural-network-digits (or pip install -U neural-network-digits).
| Component | Technology |
|---|---|
| Neural network | Python / NumPy โ written from scratch, no ML framework |
| Interface | Tkinter |
| Charts | matplotlib (loss, gaussians, confusion matrix, PCA / t-SNE map) |
| Images | Pillow |
| Dataset | MNIST (downloaded by the program from the Data tab, not in the repository) |
| Languages | English / Italian (nn_digits/i18n.py + nn_digits/locales/it.json) |
| Tests | pytest (headless on Linux with xvfb) |
| Package | PyPI, built with hatchling |
| CI/CD | GitHub Actions โ tests on Windows, Linux and macOS, automatic releases (GitHub and PyPI) from tags |
The same steps, without the interface (on Linux and macOS: ./start.sh instead of start.bat; installed
with pip: neural-network-digits):
start.bat download --per-digit 100 1. downloads the photos (with --all the whole collection)
start.bat explore 2. pre-training: charts about the dataset
start.bat train --epochs 60 --lr 0.05 3. trains (also --noise --dropout --activation tanh ...)
start.bat evaluate --noise 0.3 4. tests on the test photos (also --rotation --thickness --map t-SNE)
start.bat draw 5. only drawing board and lab
start.bat reset [--all] deletes model and charts (with --all the photos too)
start.bat update checks whether there is a new version and installs it
start.bat --version shows the installed version
start.bat train --help shows all the options. The charts are saved in data/charts/ (installed with pip,
in the charts folder of the data).
784 pixels โ 64 neurons โ 32 neurons โ 10 outputs (one per digit)
git clone https://github.com/dev-luigi/neural-network-digits.git
cd neural-network-digits
git checkout develop
pip install -r requirements.txt -r requirements-dev.txt
python -m pytest
python start.py
On Linux and macOS let ./start.sh create the .venv with the libraries, then use its Python:
.venv/bin/python -m pip install -r requirements-dev.txt and .venv/bin/python -m pytest.
The tests check:
data/ is not touched, dangerous zips are refused
and the old files go back in place if something goes wrong, also coming from the versions before nn_digits/;Versions follow semantic versioning MAJOR.MINOR.PATCH:
On the develop branch:
python tools/release.py prepare 1.1.0 # changes the version and opens the paragraph in CHANGELOG.md
# ...write the changes in CHANGELOG.md, commit, push and merge develop into main with a pull request...
Then, from the main branch:
python tools/release.py publish # tests, tag v1.1.0 and push: the CI/CD does the rest
The CI/CD publishes the release on GitHub with the zip, then the package on PyPI: that last step waits for your approval (Actions tab, the run of the release, Review deployments). PyPI trusts this repository's workflow (Trusted Publishing), so there is no password or token to keep.
neural-network-digits/
โโโ start.bat # double-click = graphical interface (Windows)
โโโ start.sh # ./start.sh = graphical interface (Linux, macOS), in its own .venv
โโโ start.py # starts the zip and git copies: interface or a step from the terminal
โโโ project.py # only for the updates from version 1.1.1 and older (the real one is in nn_digits/)
โโโ requirements.txt # the libraries: numpy, pillow, matplotlib (requirements-dev.txt: pytest)
โโโ pyproject.toml # the package for PyPI and the settings of the tests
โโโ CHANGELOG.md # the changes of every version
โโโ LICENSE # MIT
โโโ nn_digits/ # the program: the package that pip installs
โ โโโ cli.py # the terminal commands (start.py and the neural-network-digits command start here)
โ โโโ project.py # name, version, author and addresses of the program
โ โโโ updater.py # check and install of new versions from GitHub
โ โโโ i18n.py # languages: tr() gives every text in the chosen language
โ โโโ locales/it.json # the Italian translations
โ โโโ neural_net/ # the "brain", without windows
โ โ โโโ network.py # forward, softmax, loss, backpropagation (read this first!)
โ โ โโโ data.py # photos: download, loading, preparation, noise, alterations
โ โ โโโ training.py # the training loop, one epoch at a time, and the tests on the test photos
โ โ โโโ charts.py # all the charts (loss, gaussians, confusion, points map, softmax...)
โ โ โโโ storage.py # where the files are saved (data/ or the folder of the user), and the reset
โ โโโ assistant/ # the assistant, without windows
โ โ โโโ brain.py # how it answers a question, looking at the state of the program
โ โ โโโ knowledge.py # the glossary, what every tab shows, the experiments to try
โ โ โโโ rules.py # the hints: the rules that notice the common mistakes
โ โ โโโ search.py # the small search engine (TF-IDF with NumPy)
โ โโโ gui/ # the window (Tkinter + matplotlib)
โ โโโ window.py # the window with the 5 tabs and the Info tab
โ โโโ tab_*.py # one tab per file (tab_info.py also offers the updates)
โ โโโ drawing_board.py # the drawing board and the diagram of the network (tab 4)
โ โโโ lab.py # the changes to the trained network (tab 4)
โ โโโ assistant.py # the assistant panel (F2)
โ โโโ pick.py # Pick (F1): click a control to have it explained
โ โโโ app_state.py # the state of the program, in a dictionary for the assistant
โ โโโ base.py # colors and pieces of interface reused by all the tabs
โโโ docs/ # the screenshots of this README
โโโ tests/ # the automatic tests (pytest)
โโโ tools/ # release.py publishes a new version, check_package.py checks the package for PyPI
โโโ .github/workflows/ # the CI/CD: tests at every push, release (GitHub and PyPI) at every tag
โโโ data/ # created by the program: photos, model, charts, settings (excluded from git)
The logic (nn_digits/neural_net/) does not depend on the interface: both the tabs and the terminal commands
use it.
The photos come from the MNIST dataset by Yann LeCun, Corinna Cortes and Christopher J.C. Burges, distributed under the CC BY-SA 3.0 license. The program downloads them from the public copy used by Keras/TensorFlow and does not include them in the repository.
Questions, ideas and what you did with the program go in the Discussions; bugs in the issues. If the project is useful to you, a star helps other people find it, and you can support it with GitHub Sponsors.
The code is distributed under the MIT license.
Luigi Tanzillo โ luigitanzillo.it ยท github.com/dev-luigi
27 commits
Python
99.0%
Neural network from scratch in pure Python + NumPy that recognises handwritten digits (MNIST), with an educational GUI: watch it learn, tweak it while it trains, look inside every layer.
Python
16
27 commits
updated Sep 26, 2026
Handwritten digit recognition in pure Python + NumPy โ watch the network learn, tweak it while it learns, and get your hands inside it.
Features โข Quick Start โข Tech Stack โข Terminal โข Experiments โข Development
A small neural network written from scratch in Python + NumPy (no PyTorch or TensorFlow) that learns to recognize the handwritten digits of the MNIST dataset, with an educational graphical interface to watch it learn, change its parameters while it learns and get your hands inside the trained network. The interface is available in English and Italian (the EN | IT selector at the top right, next to Pick).
nn_digits/neural_net/network.py).pipx install neural-network-digits, or as a zip with a double-click.How many photos to download (or the whole collection: all the 70,000 photos of MNIST, after a window that tells you how much space they take), what the dataset looks like before training (examples, photos per digit, "average digit", pixel values) and the project reset.

Loss curve (for every mini-batch and for every epoch), accuracy, strength of the corrections per layer with the inactive neurons, what every neuron of the first layer "looks for" and the gaussians of the weights (now compared with the start) and of the noise.

Accuracy on photos never seen before. The photos can be "damaged" (noise, rotation, stroke thickness) and you can set a confidence threshold: below it the network says "I don't know", and you see how many photos it still answers and how many of those it gets right. There are also the robustness curves and the wrong photos.
The big chart switches from the confusion matrix to the points map: every photo is a point on a plane (PCA or t-SNE, written in NumPy), layer by layer. You can see the digits separate, and when you hover a point with the mouse the photo and the answer of the network show up.

You draw a digit and see the neurons light up. Click a neuron to see weighted sum, output, bias and weights, and you can shift its bias, multiply its weights or switch it off. There are also global knobs (temperature, noise on the weights, pruning), and the effect on the test accuracy shows up right away. The edited network can be saved.

The math of a layer number by number, with colored cells like in the "LLM visualizers":
When you hover a cell, the connected ones light up and the math is explained.

A side panel (F2, or the Assistant button at the top right) that knows the program and sees what is happening in it:
It is not a language model, on purpose: the answers come from a small search engine (TF-IDF, written in NumPy
in nn_digits/assistant/search.py) over a glossary of neural networks and
the explanations of the controls, and the hints are simple rules
(nn_digits/assistant/rules.py). It needs nothing
to download, it answers in a moment and it never makes things up about the numbers it reads.

You need Python 3.9 or newer. There are two ways to install the program.
pipx install neural-network-digits # or: pip install neural-network-digits
neural-network-digits # opens the interface
pipx puts the program in an environment of its own and the neural-network-digits
command in the PATH. On Linux Tkinter must be installed first (see the table below). The photos, the model and
the settings go in the folder of your user: %APPDATA%\neural-network-digits on Windows,
~/Library/Application Support/neural-network-digits on macOS and ~/.local/share/neural-network-digits
on Linux.
Download the zip of the latest version from the Releases page and extract it.
Windows: double-click start.bat. The first time it installs the libraries by itself (numpy, pillow,
matplotlib), then it opens the interface.
Linux and macOS: open a terminal in the extracted folder and run ./start.sh. The first time it
creates a virtual environment in .venv and installs the libraries there (the Python of the system is not
touched), then it opens the interface. It needs Tkinter and venv: if one is missing, start.sh tells you
what to install.
| System | Once, before the first start |
|---|---|
| Ubuntu / Debian | sudo apt install python3-venv python3-tk |
| Fedora | sudo dnf install python3-tkinter |
| Arch | sudo pacman -S tk |
| macOS | Python from python.org (Tkinter included; the Python that comes with macOS has a Tkinter that is too old) |
Tested on Windows 11, Ubuntu 24.04, Debian 12, Fedora 44 and Arch Linux; on macOS the automatic tests
and start.sh run at every push (CI).
There are no photos yet:
Every time the interface opens, the program asks GitHub whether a new version is out (you can turn this off in the Info tab). If there is one, it shows the changes and offers:
data/ folder (photos, model, settings) is not touched, and if something goes wrong
the old files are put back.From the terminal it is start.bat update (on Linux and macOS ./start.sh update). If you downloaded the project
with git clone, update with git pull instead. If you installed it with pip, the program tells you when a new
version is out and you update it with pipx upgrade neural-network-digits (or pip install -U neural-network-digits).
| Component | Technology |
|---|---|
| Neural network | Python / NumPy โ written from scratch, no ML framework |
| Interface | Tkinter |
| Charts | matplotlib (loss, gaussians, confusion matrix, PCA / t-SNE map) |
| Images | Pillow |
| Dataset | MNIST (downloaded by the program from the Data tab, not in the repository) |
| Languages | English / Italian (nn_digits/i18n.py + nn_digits/locales/it.json) |
| Tests | pytest (headless on Linux with xvfb) |
| Package | PyPI, built with hatchling |
| CI/CD | GitHub Actions โ tests on Windows, Linux and macOS, automatic releases (GitHub and PyPI) from tags |
The same steps, without the interface (on Linux and macOS: ./start.sh instead of start.bat; installed
with pip: neural-network-digits):
start.bat download --per-digit 100 1. downloads the photos (with --all the whole collection)
start.bat explore 2. pre-training: charts about the dataset
start.bat train --epochs 60 --lr 0.05 3. trains (also --noise --dropout --activation tanh ...)
start.bat evaluate --noise 0.3 4. tests on the test photos (also --rotation --thickness --map t-SNE)
start.bat draw 5. only drawing board and lab
start.bat reset [--all] deletes model and charts (with --all the photos too)
start.bat update checks whether there is a new version and installs it
start.bat --version shows the installed version
start.bat train --help shows all the options. The charts are saved in data/charts/ (installed with pip,
in the charts folder of the data).
784 pixels โ 64 neurons โ 32 neurons โ 10 outputs (one per digit)
git clone https://github.com/dev-luigi/neural-network-digits.git
cd neural-network-digits
git checkout develop
pip install -r requirements.txt -r requirements-dev.txt
python -m pytest
python start.py
On Linux and macOS let ./start.sh create the .venv with the libraries, then use its Python:
.venv/bin/python -m pip install -r requirements-dev.txt and .venv/bin/python -m pytest.
The tests check:
data/ is not touched, dangerous zips are refused
and the old files go back in place if something goes wrong, also coming from the versions before nn_digits/;Versions follow semantic versioning MAJOR.MINOR.PATCH:
On the develop branch:
python tools/release.py prepare 1.1.0 # changes the version and opens the paragraph in CHANGELOG.md
# ...write the changes in CHANGELOG.md, commit, push and merge develop into main with a pull request...
Then, from the main branch:
python tools/release.py publish # tests, tag v1.1.0 and push: the CI/CD does the rest
The CI/CD publishes the release on GitHub with the zip, then the package on PyPI: that last step waits for your approval (Actions tab, the run of the release, Review deployments). PyPI trusts this repository's workflow (Trusted Publishing), so there is no password or token to keep.
neural-network-digits/
โโโ start.bat # double-click = graphical interface (Windows)
โโโ start.sh # ./start.sh = graphical interface (Linux, macOS), in its own .venv
โโโ start.py # starts the zip and git copies: interface or a step from the terminal
โโโ project.py # only for the updates from version 1.1.1 and older (the real one is in nn_digits/)
โโโ requirements.txt # the libraries: numpy, pillow, matplotlib (requirements-dev.txt: pytest)
โโโ pyproject.toml # the package for PyPI and the settings of the tests
โโโ CHANGELOG.md # the changes of every version
โโโ LICENSE # MIT
โโโ nn_digits/ # the program: the package that pip installs
โ โโโ cli.py # the terminal commands (start.py and the neural-network-digits command start here)
โ โโโ project.py # name, version, author and addresses of the program
โ โโโ updater.py # check and install of new versions from GitHub
โ โโโ i18n.py # languages: tr() gives every text in the chosen language
โ โโโ locales/it.json # the Italian translations
โ โโโ neural_net/ # the "brain", without windows
โ โ โโโ network.py # forward, softmax, loss, backpropagation (read this first!)
โ โ โโโ data.py # photos: download, loading, preparation, noise, alterations
โ โ โโโ training.py # the training loop, one epoch at a time, and the tests on the test photos
โ โ โโโ charts.py # all the charts (loss, gaussians, confusion, points map, softmax...)
โ โ โโโ storage.py # where the files are saved (data/ or the folder of the user), and the reset
โ โโโ assistant/ # the assistant, without windows
โ โ โโโ brain.py # how it answers a question, looking at the state of the program
โ โ โโโ knowledge.py # the glossary, what every tab shows, the experiments to try
โ โ โโโ rules.py # the hints: the rules that notice the common mistakes
โ โ โโโ search.py # the small search engine (TF-IDF with NumPy)
โ โโโ gui/ # the window (Tkinter + matplotlib)
โ โโโ window.py # the window with the 5 tabs and the Info tab
โ โโโ tab_*.py # one tab per file (tab_info.py also offers the updates)
โ โโโ drawing_board.py # the drawing board and the diagram of the network (tab 4)
โ โโโ lab.py # the changes to the trained network (tab 4)
โ โโโ assistant.py # the assistant panel (F2)
โ โโโ pick.py # Pick (F1): click a control to have it explained
โ โโโ app_state.py # the state of the program, in a dictionary for the assistant
โ โโโ base.py # colors and pieces of interface reused by all the tabs
โโโ docs/ # the screenshots of this README
โโโ tests/ # the automatic tests (pytest)
โโโ tools/ # release.py publishes a new version, check_package.py checks the package for PyPI
โโโ .github/workflows/ # the CI/CD: tests at every push, release (GitHub and PyPI) at every tag
โโโ data/ # created by the program: photos, model, charts, settings (excluded from git)
The logic (nn_digits/neural_net/) does not depend on the interface: both the tabs and the terminal commands
use it.
The photos come from the MNIST dataset by Yann LeCun, Corinna Cortes and Christopher J.C. Burges, distributed under the CC BY-SA 3.0 license. The program downloads them from the public copy used by Keras/TensorFlow and does not include them in the repository.
Questions, ideas and what you did with the program go in the Discussions; bugs in the issues. If the project is useful to you, a star helps other people find it, and you can support it with GitHub Sponsors.
The code is distributed under the MIT license.
Luigi Tanzillo โ luigitanzillo.it ยท github.com/dev-luigi
27 commits
Python
99.0%