Download README.md from gmmeyer/pythonlm: direct link, hf CLI and curl.
- Browser
- Download file 4.33 kB
-
https://huggingface.co/gmmeyer/pythonlm/resolve/main/README.md
- Command line
-
hf download hf://gmmeyer/pythonlm/README.md
-
curl -L -o README.md https://huggingface.co/gmmeyer/pythonlm/resolve/main/README.md
license: mit
library_name: onnx
tags:
- python
- interpreter
- program-execution
- from-scratch
- onnx
pythonlm
A 26M-parameter transformer, trained from scratch, that executes CPython 3.14 bytecode one instruction at a time. You give it a Python script; CPython compiles it, and the model runs the bytecode: given the listing and the state of the virtual machine (instruction pointer, value stack, variables), it writes the state after one more instruction plus anything that instruction printed. A small harness validates each step's structure and streams the printed text. Nothing in the loop computes values for it.
Code, data pipeline and harness: https://github.com/gmmeyer/pythonlm
What it runs
A growing subset of Python, refused up front when a script steps outside it:
- Phase A: integers (to ±999), variables,
+ - * // %,print() - Phase B: comparisons,
and/or/not,if/elif/else,while,foroverrange(),break,continue - Phase C: strings: concatenation, repetition, f-strings,
str(),len(), indexing,print()with several arguments,sepandend
Model
- Decoder-only, 8 layers × 512, 8 heads, SwiGLU (1408), pre-norm RMSNorm, RoPE on explicit positions, QK-norm, no biases; 26.1M parameters
- 354-token vocabulary: opcodes, instruction-address digits, integers as sign
plus digits (least significant first), one token per string character,
anonymized variable names (
N0–N31), structure - Context 2048: the listing (≤1,408 tokens) plus one step
//and%steps write a scratch section first (the quotient, most significant digit first, and for%the divisor × quotient), which the harness ignores- Trained from random init on one RTX 5090, ~36B tokens of synthetic execution traces over phases A → B → C. Traces come from a reference interpreter checked against real CPython at every instruction.
Evaluation (this checkpoint)
This checkpoint is the average of the weights of two consecutive training runs (c5, and c6 which continued it on more string-comparison drills): c6 fixed some of c5's failures and broke others, and their average beats both.
Exact stdout match against CPython, end to end through the harness:
| Set | Scripts | Match |
|---|---|---|
| Hand-written, phase A | 200 | 99.5% |
| Hand-written, phase B | 200 | 99.5% |
| Hand-written, phase C | 200 | 93.5% |
| Generated held-out, phase C | 2,000 | 99.55% |
Teacher-forced per-step error on generated held-out traces: phase A 4.5e-5,
B 5.2e-5, C 2.0e-4 (most of C's comes from one script family comparing
characters with <=; those wrong steps rarely change the output). The
hand-written scripts (written to look like code people write, never in
training) are the harder and more honest number.
Files
pythonlm.onnx: ONNX, weights stored in fp16 and computed in fp32 (53 MB); used by the browser demo. Inputsids,pos, and the KV cache in two parts (prefix_k/vfor the listing,step_k/vfor the current step), token-major[tokens, layers, heads, 64]; outputslogitsand this call'sk,v. Matches the PyTorch weights on every argmax of 30,447 hand-written output tokens.pythonlm.json: shapes, the vocabulary, its hash, the ONNX file's sha256, and self-test cases (PyTorch's argmax on fixed steps) for checking a runtime.pythonlm.pt: PyTorch weights and config (bcvm.train.model.StepModel).
Usage
The model is only useful inside the harness, which compiles the script, refuses unsupported code, encodes states, constrains decoding to well-formed states and validates each step:
git clone https://github.com/gmmeyer/pythonlm && cd pythonlm
uv run --extra train bcvm run my_script.py --ckpt pythonlm.pt
In the browser, web/ in the repository runs the same harness under Pyodide
and this ONNX file on ONNX Runtime Web.
Limitations
- Only the subset above; everything else is refused before it runs.
- It can be wrong. The validator catches malformed or structurally impossible steps (wrong stack depth, a name that changed without being assigned), not wrong values, so a wrong sum or a miscopied string goes through.
- Integers beyond ±999 and strings beyond 48 characters are out of distribution.
License
MIT, the same as the code.