Ismail Ben Alla

MyTorcha

A Deep Learning Framework From Scratch

Deep learning · Mar 2024 · Completed

A deep learning framework rebuilt from first principles: autograd to LSTMs and attention, with every gradient checked.

Built withPythonNumPypytestPyTorch

MyTorcha — overview

Overview

Frameworks like PyTorch make training a network one call, which also hides how it works. MyTorcha removes that layer: every gradient is code I wrote and a test that checks it, with NumPy as the only dependency.

It started as a scalar autograd engine, where each number is a node in a computation graph. That version is kept as a teaching chapter. It is also why the framework grew a second engine: one 784-to-10 layer builds 31,427 scalar nodes per image. The Tensor engine applies the same chain rule to whole arrays, which makes convolutional, recurrent and attention models practical on a CPU.

Rebuilding it meant testing the original first. Finite-difference checks exposed real bugs in v1: a log derivative off by a factor of about 3, nodes that compared equal by value and were silently merged during backpropagation, and optimizer state shared across every weight. Each one is now a regression test.

The question

Do I understand deep learning well enough to rebuild a framework, from autograd to LSTMs and attention, whose gradients match PyTorch's?

Key features

How it works

  1. Every op records its inputs

    An operation computes its output array and stores a function that maps the output's gradient to one gradient per input.

  2. backward() walks the graph

    An iterative topological sort orders the graph; gradients flow in reverse and are summed back over broadcast dimensions.

  3. Layers are Modules

    A Module finds its Parameters by walking its attributes, so a CNN or LSTM is plain Python composed from tested pieces.

  4. Checked twice

    Each backward rule is compared with finite differences in float64, and each layer's forward and backward with PyTorch's.

Examples

Input

cnn = nn.Sequential(nn.Conv2d(1, 8, 3, padding=1), nn.BatchNorm2d(8), nn.ReLU(), nn.MaxPool2d(2), ..., nn.Linear(784, 10)); mt.Trainer(cnn, nn.CrossEntropyLoss(), optim.Adam(cnn.parameters(), lr=2e-3)).fit(train_loader, epochs=3, val_loader=val_loader)

Output

Validation accuracy 94.35%, 96.70%, 97.10% over 3 epochs (9,146 parameters; an MLP with 101,770 parameters reaches 93.65%).

examples/03_digits_mlp_vs_cnn.py on 10k training and 2k validation images from the Kaggle digits CSV.

Input

model = CharLSTM(vocab); 300 Adam steps on a short text

Output

step 0: 'the dbveclgk stmap,sed d ahylb...' -> step 100: 'the chain rule walks that memory backwards.'

examples/05_char_lstm.py: an Embedding + LSTM language model trained from scratch.

Tech stack

What I learned

Limitations