🧠AI / Deep Learning★ FeaturedYear: 2026Active development

LucilleAI

A machine-learning framework written in Rust that keeps tensors on the GPU and records every operation for training

LucilleAI cover
RustCUDAcuBLASC++NVTXGradlePython (PyO3 bindings)

A machine-learning framework written from scratch in Rust, with a CUDA backend, that trains a real model.

What it is

I wanted to understand what actually happens between a tensor operation and a gradient update, so I built the whole stack: tensor library, autodiff, a neural-network layer set, an optimizer, a tokenizer, and a training loop. Then I wrote a model on top of it and watched its loss go down.

That is the honest framing. The value is not “this competes with PyTorch” — it does not, and I am not claiming it does. The value is that every layer is one I can open and read, and that I hit the specific problems that frameworks paper over.

What it does

  • Tensors live on the GPU, and automatic differentiation keeps a recording of every operation so gradients can be replayed backwards.
  • cuBLAS runs on its own CUDA stream while my code runs on another. The single most valuable thing in this project: matmuls overlap with the kernels around them, so the GPU is never idle waiting for work I have not handed it yet.
  • The framework did not crash — it produced wrong numbers. A trace revealed a “ghost NaN” that only appeared on Turing-generation hardware, where tf32 silently rounds tensor-core inputs. That single bug cost more time than everything else in the project combined, and the fix is one that only someone reading the generated code would ever find.
  • The tokenizer had a reproducibility bug. It counted tokens into a floating-point accumulator that cannot reorder additions, so the same text produced a different vocabulary size on different runs. u64 fixed it.
  • A real model trained on real text: 3.7 million parameters, 128-token context, loss driven down from initialisation, and a tokenizer that round-trips Persian text without mangling it.

The numbers

81,200 lines — 76,700 Rust, 4,500 CUDA
24 hand-written CUDA kernels
32 differentiable operations
3.7 M model parameters
396 pretraining steps, loss 0.0486
99.2% accuracy on a held-out suite (656 of 661)

Why it’s worth a look

Two reasons.

The first is the stream bridge. Overlapping library work with your own work is a real technique and it is genuinely hard to get right — you have to own memory lifetimes, because an allocation that is still in use by the other stream cannot be reused. The second is the NaN. I did not find it by reading code; I found it by dumping per-kernel traces and looking at where a finite number became not-a-number. It only happened on Turing, only under specific tensor-core paths, and every error-based check in the world would have passed it.

Current state

  • AMP was off in both runs. No mixed-precision training was actually exercised.
  • The project’s own evaluation flags itself as overfit, and it should. A 99% figure from a self-authored benchmark suite means the benchmark was too easy, not that the model is excellent.
  • The model is small — 3.7 million parameters is a demonstration, not a language model.

None of that makes it less interesting. It makes it what it is: a complete, readable, from-scratch ML stack where every number on this page came from my own code and my own runs.

More projects