Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BeviaLLM - From-Scratch Mini LLM (NumPy, CPU)

This project is an educational, from-scratch implementation of a tiny GPT-like language model using pure Python + NumPy (no PyTorch, no JAX, no autograd libraries).

The goal is to deeply understand:

  • Token embeddings & positional embeddings
  • Causal self-attention
  • Transformer blocks (LayerNorm, MLP, residuals)
  • Softmax + cross-entropy
  • Backpropagation implemented manually
  • Optimizers (AdamW)
  • Text sampling

This is not meant to be fast or production-grade. It is meant to be readable and hackable.


Quick Start

TL;DR - Get a tiny LLM training on your MacBook CPU in 2 minutes.

1) Create training data

Create a data.txt file (must not be empty):

This is a small training corpus for my from-scratch LLM.
It learns characters and predicts the next one.
I want to understand transformers deeply.

(Optional) Enlarge the dataset so the model can actually learn patterns:

python - <<'PY'
t = open("data.txt","r",encoding="utf-8").read()
open("data.txt","w",encoding="utf-8").write((t + "\n") * 2000)
print("New size:", len(open("data.txt").read()))
PY

2) Install requirements

pip install numpy

(Recommended: use a virtual environment)

3) Run training

python main.py \
  --data data.txt \
  --ctx 64 \
  --dim 64 \
  --layers 1 \
  --mlp_hidden 128 \
  --batch 8 \
  --steps 2000 \
  --log_every 50 \
  --sample_every 500 \
  --sample_len 200

You should see:

  • Training loss decreasing
  • Periodic sampled text printed to the console

VS Code Setup (macOS)

This setup uses the existing .venv and ensures debug runs include the required --data arg.

1) Open the project

  • VS Code -> File -> Open...
  • Select: /Users/vincentbevia/PycharmProjects/BeviaLLM

2) Install extensions (recommended)

  • Python (Microsoft)
  • Pylance (Microsoft)

3) Select your interpreter

  • Cmd+Shift+P -> Python: Select Interpreter
  • Choose: .../BeviaLLM/.venv/bin/python

If it is not listed, click Enter interpreter path... and paste:

/Users/vincentbevia/PycharmProjects/BeviaLLM/.venv/bin/python

4) Create VS Code config files

Create a folder at the project root: .vscode/

A) .vscode/launch.json (Debug with args)

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "BeviaLLM: Train (debug)",
      "type": "python",
      "request": "launch",
      "program": "${workspaceFolder}/main.py",
      "console": "integratedTerminal",
      "justMyCode": true,
      "args": [
        "--data", "data.txt",
        "--ctx", "64",
        "--dim", "64",
        "--layers", "1",
        "--mlp_hidden", "128",
        "--batch", "8",
        "--steps", "2000",
        "--log_every", "50",
        "--sample_every", "500",
        "--sample_len", "200"
      ]
    }
  ]
}

B) .vscode/settings.json (Quality of life)

{
  "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
  "python.analysis.typeCheckingMode": "basic",
  "python.terminal.activateEnvironment": true,
  "editor.formatOnSave": true
}

5) Run it in VS Code (this worked reliably)

  • Run -> Start Debugging
  • Select the "BeviaLLM: Train (debug)" configuration if prompted

6) Make sure data.txt exists and is not empty

In the VS Code terminal (View -> Terminal):

ls -l data.txt
wc -c data.txt

If it is empty, add a little text or reuse the Quick Start snippet.

7) Run from the VS Code terminal (non-debug)

python main.py --data data.txt --ctx 64 --dim 64 --layers 1 --mlp_hidden 128 --batch 8 --steps 500 --log_every 50

8) Optional: add a one-key task

Create .vscode/tasks.json:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "BeviaLLM: train",
      "type": "shell",
      "command": "${workspaceFolder}/.venv/bin/python",
      "args": [
        "${workspaceFolder}/main.py",
        "--data", "data.txt",
        "--ctx", "64",
        "--dim", "64",
        "--layers", "1",
        "--mlp_hidden", "128",
        "--batch", "8",
        "--steps", "2000",
        "--log_every", "50",
        "--sample_every", "500",
        "--sample_len", "200"
      ],
      "problemMatcher": [],
      "group": { "kind": "build", "isDefault": true }
    }
  ]
}

Then run via Cmd+Shift+P -> Tasks: Run Task -> BeviaLLM: train.

Two common VS Code pitfalls (and fixes)

  • VS Code runs system Python, not .venv. Fix: Step 3 + python.defaultInterpreterPath in settings.json.
  • Working directory is wrong, so it cannot find data.txt. Fix: Keep data.txt in repo root and use the launch.json above.

Features

  • Character-level language model (no tokenizer required)
  • Causal self-attention (GPT-style)
  • Manual forward + backward pass for all layers
  • AdamW optimizer implemented from scratch
  • Text sampling with temperature
  • CPU-only (runs on MacBook / laptop)

Project Structure

The code is modularized into a clean package for readability:

BeviaLLM/
├── main.py              # CLI + training loop
├── data.txt             # Training text
├── bevialm/
│   ├── __init__.py      # Package exports
│   ├── utils.py         # set_seed, softmax, cross_entropy_loss, init_weight
│   ├── optimizer.py     # AdamW optimizer
│   ├── layers.py        # Embedding, Linear, LayerNorm, CausalSelfAttention, TransformerBlock
│   ├── model.py         # CharTransformerLM
│   └── data.py          # load_text, build_vocab, encode, get_batch, sample
└── .vscode/             # VS Code debug/task configs

Requirements

  • Python 3.10+
  • NumPy

Important Arguments

Argument Meaning
--data Path to training text file
--ctx Context length (sequence length)
--dim Model hidden dimension
--layers Number of transformer blocks
--mlp_hidden Hidden size of MLP inside block
--batch Batch size
--steps Training iterations
--log_every How often to print loss
--sample_every How often to sample text
--sample_len Length of generated sample

What You Will Learn

This project is designed so you can open the bevialm/ modules and trace gradients all the way through:

File What to study
bevialm/layers.py How attention really works (Q, K, V, masking)
bevialm/layers.py Why LayerNorm stabilizes training
bevialm/layers.py How residual connections affect gradient flow
bevialm/utils.py How softmax gradients behave
bevialm/optimizer.py Why optimizers like AdamW matter
bevialm/model.py How embeddings and the full forward/backward pass work
bevialm/data.py How text sampling with temperature works

Performance Notes

This is CPU-only and written in NumPy:

  • It is slow by design
  • Small models train in minutes
  • Larger configs will be very slow

Use small configs while learning.


Next Steps / Exercises

Once you are comfortable:

  • Implement multi-head attention (currently single-head in bevialm/layers.py)
  • Replace ReLU with GELU
  • Add dropout
  • Add learning rate warmup + cosine decay
  • Replace char-level model with BPE tokenizer
  • Add checkpoint saving/loading
  • Visualize attention maps
  • Add unit tests for each module

Disclaimer

This project is for learning and experimentation only. It is not optimized, not secure, and not intended for production use.


References

  • Vaswani et al., Attention Is All You Need
  • Andrej Karpathy - Let us build GPT from scratch
  • CS231n - Backpropagation fundamentals

Motivation

Understanding LLMs at this level makes you:

  • Better at debugging real models
  • More critical of black-box AI
  • Stronger at system design for AI-powered software

Have fun breaking it and rebuilding it.


About

This project is an *educational, from-scratch implementation of a tiny GPT-like language model* using *pure Python + NumPy* (no PyTorch, no JAX, no autograd libraries).

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages