diff --git a/.gitignore b/.gitignore index 3701490..48b7a00 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,20 @@ benchmark_results.json # Debug *.dSYM/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Logs +*.log +training_log*.csv + +# Packaging +*.egg-info/ +dist/ +build/ +.eggs/ diff --git a/README.md b/README.md index 28de9a7..05db292 100644 --- a/README.md +++ b/README.md @@ -1,410 +1,230 @@ -# Atomic-1Bit โš›๏ธ -> *High Intelligence, Low Compute.* +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Python 3.8+](https://img.shields.io/badge/Python-3.8%2B-blue.svg)](https://www.python.org/downloads/) +[![C++17](https://img.shields.io/badge/C%2B%2B-17-blue.svg)](https://isocpp.org/) +[![BitNet b1.58](https://img.shields.io/badge/Weights-1.58--bit%20Ternary-green.svg)](#how-it-works) -**Atomic-1Bit** is a bare-metal, ultra-lightweight inference engine for **BitNet b1.58** (1.58-bit ternary models). -It proves that you don't need FP16 matrix multiplication to run modern AI. The core engine runs on **INT8 addition and subtraction** only. +# Atomic-1Bit -## ๐ŸŽฏ What's New in v1.3 +**Run language models using only addition and subtraction.** Atomic-1Bit is a bare-metal inference engine for 1.58-bit ternary models (BitNet b1.58) that replaces floating-point matrix multiplication with integer add/sub operations, cutting model size by 62% and enabling deployment on devices as small as an ESP32. -- โœ… **Flagship 12.5M Model** (8 layers, 320-dim, 5 heads, 256 context) -- โœ… **Comprehensive Test Suite** (71 tests with pytest, 100% parity verification) -- โœ… **Evaluation Harness** (perplexity, repetition, diversity, coherence metrics) -- โœ… **SIMD Optimization** (2x speedup with NEON/AVX2, dual accumulator pattern) -- โœ… **KV-Cache Implementation** (autoregressive generation acceleration) -- โœ… **YAML Config System** (model presets, tokenizer abstraction) -- โœ… **ESP32 Deployment** (flash-streaming for 520KB SRAM) -- โœ… **Raspberry Pi Benchmark** (NEON SIMD comparison, thermal monitoring) -- โœ… **WebAssembly Port** (run models in the browser, no server required) -- โœ… **Complete Documentation** (805-line ARCHITECTURE.md, CONTRIBUTING.md) -- โœ… **CI/CD Pipeline** (GitHub Actions with performance regression detection) +## Why Atomic-1Bit? -## ๐Ÿ“Š Benchmarks & Results +Most LLM inference requires expensive GPU hardware and gigabytes of memory. Even "small" models assume you have a modern GPU or at least a fast CPU with plenty of RAM. -We successfully trained and deployed Atomic-1Bit models ranging from 1.3M to 12.5M parameters. All models verified for **bit-exact parity** between Python and C++ implementations. +Atomic-1Bit takes a different approach. By quantizing weights to just three values **{-1, 0, 1}**, we eliminate floating-point multiplication entirely: -### Key Achievements -- **Numerical Parity Verified**: CPU and Metal backends produce **bit-exact** matches to Python reference -- **Ultra-Low Model Size**: 62% size reduction vs FP16 baselines -- **Energy Efficiency**: 37-123x lower energy for matrix operations (add/sub only) -- **Memory Bandwidth**: 8x reduction from ternary weight compression -- **Cross-Platform**: CPU (NEON/AVX2), Metal (Apple Silicon), CUDA (NVIDIA), WebAssembly +``` +weight == 1 -> accumulator += input +weight == -1 -> accumulator -= input +weight == 0 -> skip (free sparsity) +``` + +The result is a full transformer that runs on **integer arithmetic only**. No CUDA required. No FP16. No matrix multiply units. Just `add` and `sub` instructions that work on any processor manufactured in the last 30 years. + +**This matters because:** +- A 1.33M parameter model drops from **5.3 MB to 2.0 MB** (-62%) +- The C++ runtime has **zero external dependencies** -- it's a single binary +- It runs on a **Raspberry Pi**, an **ESP32**, or a **2015 laptop** +- The Python training stack and C++ inference engine produce **bit-exact identical output** -### Model Variants +> This is experimental research software. It works, it's verified, and it's honest about what it is: a proof that useful AI inference doesn't require expensive hardware. -| Model | Params | Dim | Layers | Heads | Context | Target Device | -|-------|--------|-----|--------|-------|---------|---------------| -| **Stories Base** | 1.33M | 128 | 4 | 4 | 64 | Embedded/Testing | -| **Pocket** | 2.6M | 256 | 4 | 4 | 128 | ESP32, Browser | -| **Flagship** | 12.5M | 320 | 8 | 5 | 256 | Desktop, RPi | +--- -### Performance Comparison (Apple M-series CPU) +## Performance -**Context**: Sequence Length=128, Gen Tokens=50, Batch Size=1, Single Thread. +Benchmarked on Apple M-series, single thread, sequence length 128, 50 generated tokens: -| Metric | FP16 Baseline | Atomic-1Bit | Delta | -| :--- | :--- | :--- | :--- | +| Metric | FP16 Baseline | Atomic-1Bit | Improvement | +|:---|:---|:---|:---| | **Model Size** | 5.3 MB | **2.0 MB** | **-62%** | -| **Parameters** | 1.33M | 1.33M | 0% | -| **Precision** | Float16 | Ternary {-1, 0, 1} | - | -| **Speed (Python)** | ~826 TPS | ~130 TPS | -83% (Research Stack) | -| **Speed (C++ CPU)**| N/A | **~160-170 TPS** | **Portable Runtime** | -| **Speed (Metal)**| N/A | **~TBD TPS** | **Apple Silicon Optimized** | -| **Speed (CUDA)**| N/A | **~TBD TPS** | **NVIDIA GPU Optimized** | +| **Parameters** | 1.33M | 1.33M | Same | +| **Precision** | Float16 | Ternary {-1, 0, 1} | -- | +| **Throughput (C++)** | N/A | **~160-170 TPS** | Portable runtime | +| **Throughput (Python)** | ~826 TPS | ~130 TPS | Unoptimized | -**Visual Summary** +
+Visual benchmarks -![Performance Chart](assets/chart_model_size.png) -![Speed Chart](assets/chart_speed.png) +![Model Size Comparison](assets/chart_model_size.png) +![Speed Comparison](assets/chart_speed.png) ![Text Samples](assets/text_samples_comparison.png) -*Note: Benchmarks reflect single-core CPU performance. SIMD kernels optimized for memory bandwidth efficiency.* +
--- -## ๐Ÿš€ The Stack - -### 1. Research Stack (Python/PyTorch) -Located in `atomic_1bit/`. -- **Purpose**: Architecture design, training, and evaluation. -- **Components**: `BitLinear`, `AtomicTransformer`, `GistEncoder`. -- **Training**: TinyStories, Alpaca instruction tuning, with thermal safety monitoring. -- **Evaluation**: Comprehensive metrics (perplexity, repetition, diversity, coherence). - -### 2. Bare Metal Stack (C++) -Located in `embedded/` and `atomic_1bit/core/`. -- **Purpose**: Deployment on constrained devices and high-performance hardware. -- **Structure**: Modular backend architecture (`backends/`) supporting CPU, Metal, and CUDA. -- **Components**: `atomic_lib.h`, `cpu_kernel.cpp`, `metal_kernel.mm`, `cuda_kernel.cu`. -- **Optimization**: SIMD vectorization (NEON, AVX2), KV-cache, aligned memory layouts. - -### 3. Deployment Platforms -**ESP32** (`embedded/platforms/esp32/`) -- Flash-streaming for 520KB SRAM constraint -- PSRAM support for embeddings -- PlatformIO configuration - -**Raspberry Pi** (`benchmarks/platforms/rpi/`) -- NEON SIMD benchmark suite -- Thermal monitoring and TPS measurement -- Target: >10 TPS for real-time inference - -**WebAssembly** (`embedded/platforms/wasm/`) -- Browser-based inference (no server) -- Interactive demo with file picker -- ~7MB memory footprint - -### 4. Evaluation & Benchmarking -Located in `benchmarks/` and `atomic_1bit/evaluation/`. -- **Performance**: Reproducible FP16 vs Atomic-1Bit comparisons -- **Quality**: Perplexity, repetition rate, diversity metrics -- **Platform**: Cross-device benchmarks (desktop, RPi, ESP32) +## Quick Start ---- +### Prerequisites -## โšก Quick Start +- Python 3.8+ +- GCC/G++ or Clang (for C++ inference, C++17 support required) +- macOS (Apple Silicon recommended) or Linux + +### Install -### Prerequisites ```bash -# Install Python dependencies +git clone https://github.com/guirguispierre/Atomic-1Bit.git +cd Atomic-1Bit pip install -r requirements.txt - -# Or manually: -pip install torch tiktoken datasets numpy matplotlib psutil tqdm pyyaml - -# C++ compiler (GCC/Clang with C++17 support) -# For Metal: Xcode Command Line Tools -# For CUDA: NVIDIA CUDA Toolkit ``` -### 1. Run Tests -Verify the entire system with the comprehensive test suite: -```bash -# Run all 71 tests (includes parity verification) -pytest tests/ -v +### Verify the kernel works -# Run only parity tests -pytest tests/test_kernel_parity.py -v +This confirms the C++ ternary kernel matches the Python/NumPy reference exactly: -# Quick parity check +```bash python3 atomic_1bit/python/inference.py +# Expected: ">> SUCCESS: Kernel Output Matches Reference." ``` -### 2. Build C++ Kernel -```bash -cd atomic_1bit/core - -# CPU backend (default) -make - -# Metal backend (Apple Silicon) -make BACKEND=METAL - -# CUDA backend (NVIDIA) -make BACKEND=CUDA - -cd ../.. -``` +### Train a model -### 3. Train a Model ```bash -# Train base model on TinyStories +# Train on TinyStories dataset (~15k steps) python3 atomic_1bit/training/train.py - -# Train Pocket model (4096 vocab, embedded-optimized) -python3 atomic_1bit/training/train_pocket.py - -# Train flagship 12.5M instruct model -python3 atomic_1bit/training/train_instruct.py ``` -Training includes automatic thermal safety monitoring (auto-pause >80ยฐC, resume <70ยฐC). - -### 4. Evaluate Quality -```bash -# Run full evaluation suite -python3 atomic_1bit/evaluation/run_eval.py \ - --model weights/stories_final.pt \ - --output eval_results.json - -# Check perplexity only -python3 atomic_1bit/evaluation/perplexity.py weights/stories_final.pt -``` +### Export and run on bare metal -### 5. Export for Deployment ```bash -# Export to binary format for C++ runtime +# Export trained model to binary python3 atomic_1bit/utils/export_to_cpp.py \ --model weights/stories_final.pt \ --output embedded/atomic_model.bin \ --dim 256 --depth 6 --heads 4 --vocab_size 4096 --context_len 128 -# Compile and run +# Compile the C++ engine cd embedded g++ -O3 -std=c++17 atomic_runner.cpp -o runner -./runner --model atomic_model.bin --steps 100 --temp 0.7 --seed 42 -``` - -### 6. Deploy to Platforms - -**WebAssembly (Browser)** -```bash -cd embedded/platforms/wasm -make # Requires Emscripten SDK -make serve # Start HTTP server -# Open http://localhost:8080/index.html -``` -**ESP32 (Microcontroller)** -```bash -cd embedded/platforms/esp32 -# See README.md for PlatformIO setup -pio run -t upload +# Generate text +./runner --model atomic_model.bin --steps 100 --temp 0.7 --seed 42 --start_token 58 ``` -**Raspberry Pi (Benchmark)** -```bash -python3 benchmarks/platforms/rpi/benchmark_rpi.py \ - --model weights/pocket_final.pt \ - --bin embedded/atomic_model.bin \ - --steps 100 -``` +See [docs/COMMANDS.md](docs/COMMANDS.md) for the full command reference and [docs/INSTALL.md](docs/INSTALL.md) for detailed installation instructions. --- -## ๐Ÿง  Theory: "The Magic Kernel" - -The heart of Atomic-1Bit is `ternary_matmul`. Instead of expensive multiplication, we use: -```cpp -if (weight == 1) acc += input; // 1 add operation -if (weight == -1) acc -= input; // 1 sub operation -// if weight == 0, do nothing (Sparsity!) -``` - -**Why This Works:** -- **37-123x Energy Savings**: Add/sub consumes far less energy than multiply -- **8x Memory Bandwidth**: Ternary weights compress 4:1 vs INT8 -- **Hardware-Friendly**: SIMD vectorization for 64 elements per cycle -- **Gradient Flow**: Straight-Through Estimator (STE) enables backpropagation - -**Gist Tokens** (Thought Compression): -Pre-compute system prompts into a single vector. Inject at attention level with **zero inference cost**. +## How It Works -![Gist Flow](assets/diagram_gist_flow_1765658121862.png) +Atomic-1Bit implements a standard transformer architecture (embeddings, multi-head attention, feed-forward layers) with one critical difference: every linear layer uses **BitLinear** instead of `nn.Linear`. -For deep technical details, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). +During training, weights are quantized to {-1, 0, 1} using a straight-through estimator (STE), which lets gradients flow through the discrete quantization step. Activations are quantized to INT8. At inference time, the entire forward pass reduces to integer additions and subtractions. ---- - -## ๐Ÿ”ฅ Features +The project has three components: -### Thermal Safety -Long-running training includes automatic thermal protection: -- **Auto-Pause**: If system temp > 80ยฐC -- **Auto-Resume**: When temp < 70ยฐC -- **Safety Checkpoint**: Saves `*_thermal_safe.pt` before pausing +1. **Research stack** (`atomic_1bit/`) -- PyTorch training, evaluation, and model architecture. Train on TinyStories or Alpaca-cleaned datasets with thermal safety monitoring, gradient accumulation, and cosine scheduling. -*Note: On Apple Silicon, sensor access may require elevated permissions. The monitor gracefully disables if sensors are unavailable.* +2. **Bare-metal runtime** (`embedded/`) -- Standalone C++ inference engine with zero dependencies. Supports CPU, Metal (Apple Silicon), and CUDA backends through conditional compilation. Produces bit-exact output matching the Python reference. -### KV-Cache Optimization -Autoregressive generation with key-value caching: -```python -# Use cache for faster generation -output = model.generate(prompt, max_new_tokens=100, use_cache=True) -``` +3. **Gist tokens** -- Pre-computed "thought vectors" that compress a system prompt into a single embedding, injected into the attention stream at zero inference cost. -### YAML Configuration -Define model presets in `configs/`: -```yaml -model: - name: "atomic-12.5M" - dim: 320 - depth: 8 - heads: 5 - context_length: 256 - vocab_size: 50257 - -quantization: - weight_bits: 1.58 - activation_bits: 8 -``` +![Ternary Matmul Diagram](assets/diagram_ternary_matmul_1765658104682.png) -### Modular Tokenizers -Tokenizer abstraction for different vocabularies: -- `TiktokenWrapper`: GPT-2 (50257 vocab) -- `PocketTokenizer`: Frequency-filtered (4096 vocab) -- Extensible for custom tokenizers +For more details, see [docs/USAGE.md](docs/USAGE.md). --- -## ๐Ÿ“š Documentation +## Project Structure -- **[CLAUDE.md](CLAUDE.md)** - Project overview and command reference -- **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** - Deep dive into BitNet b1.58, STE, ATOM format, kernel design -- **[CONTRIBUTING.md](CONTRIBUTING.md)** - Development setup, testing, PR guidelines -- **[ROADMAP.md](ROADMAP.md)** - Version history and future plans +``` +atomic_1bit/ + model/ Transformer architecture (BitLinear, BitAttention) + nn/ Core layers (BitLinear with STE quantization) + training/ Training scripts (TinyStories, Alpaca, Pocket) + evaluation/ Quality metrics (perplexity, coherence, diversity) + python/ Python inference, chat interface, kernel wrapper + utils/ Export, gist generation, thermal monitoring + core/ C++ kernels (CPU, Metal, CUDA backends) + tokenizers/ Tokenizer abstraction layer + config.py YAML/JSON configuration system +embedded/ Standalone C++ runner + ESP32 port guide +configs/ Model presets (4K pocket to 12.5M flagship) +benchmarks/ Reproducible benchmark suite vs FP16 baselines +tests/ 67 pytest tests for correctness verification +scripts/ Plotting, evaluation, and reproduction scripts +docs/ Installation, usage, commands, benchmarking guides +examples/ Runnable example scripts +``` --- -## ๐Ÿงช Testing & CI/CD +## Model Configurations -### Test Suite (71 Tests) -```bash -pytest tests/ -v -``` +| Config | Parameters | Dimensions | Use Case | +|:---|:---|:---|:---| +| [`pocket_4k`](configs/pocket_4k.yaml) | ~100K | 256d, 4L, 4H | ESP32 / microcontrollers | +| [`stories_base`](configs/stories_base.yaml) | ~1.33M | 256d, 6L, 4H | Development / testing | +| [`flagship_12m`](configs/flagship_12m.yaml) | ~12.5M | 320d, 8L, 5H | Quality demos | +| [`mixed_precision`](configs/mixed_precision.yaml) | Configurable | Hybrid 1.58/4-bit | Experimental | -Coverage: -- BitLinear layer: quantization, STE gradients, edge cases -- Transformer: forward pass, KV-cache, gist injection -- Export/Import: ATOM format round-trip -- Kernel Parity: 9 critical tests (bit-exact verification) -- Thermal Monitor: auto-pause/resume behavior +Load any config with: -### Pre-Commit Hooks -```bash -# Install hooks -pre-commit install - -# Enforces: -# - black (formatting) -# - isort (import ordering) -# - flake8 (linting) -# - parity check (on kernel changes) +```python +from atomic_1bit.config import load_config, config_to_atomic +config = config_to_atomic(load_config("configs/stories_base.yaml")) ``` -### GitHub Actions CI -Automated testing on every push/PR: -- Build C++ kernel (CPU backend) -- Run pytest suite -- Verify kernel parity -- Lint code (black, flake8, isort) -- Benchmark performance (regression detection) - -See [.github/workflows/ci.yml](.github/workflows/ci.yml). - --- -## ๐ŸŒ Community +## Requirements -### Contributing -We welcome contributions! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for: -- Development environment setup -- Code style guidelines -- Testing requirements (parity verification is mandatory) -- PR checklist +| Dependency | Version | Purpose | +|:---|:---|:---| +| Python | 3.8+ | Training and evaluation | +| PyTorch | >= 1.13.0 | Model training | +| tiktoken | >= 0.5.0 | Tokenization | +| datasets | >= 2.14.0 | HuggingFace datasets | +| NumPy | >= 1.24.0 | Reference math | +| matplotlib | >= 3.7.0 | Benchmark plots | +| psutil | >= 5.9.0 | Thermal monitoring | +| tqdm | >= 4.65.0 | Progress bars | +| PyYAML | >= 6.0 | Config files | +| GCC/Clang | C++17 | C++ inference engine | -### Code of Conduct -- **Correctness before speed** -- **Parity before optimization** -- **Measured claims only** -- **Deployment-focused research** +**Hardware**: Any machine with a CPU. Apple Silicon recommended for Metal backend. NVIDIA GPU optional for CUDA backend. Tested down to ESP32-S3 for embedded inference. --- -## ๐Ÿ“ฆ Repository Structure +## Running Tests -``` -atomic_1bit/ -โ”œโ”€โ”€ nn/ # BitLinear layer, quantization -โ”œโ”€โ”€ model/ # AtomicTransformer, GistEncoder -โ”œโ”€โ”€ training/ # Training scripts (base, pocket, instruct) -โ”œโ”€โ”€ evaluation/ # Quality metrics (PPL, repetition, diversity) -โ”œโ”€โ”€ python/ # Python inference wrapper, chat interface -โ”œโ”€โ”€ utils/ # Export tools, thermal monitor -โ””โ”€โ”€ core/ # C++ kernel source - โ”œโ”€โ”€ Makefile # Build system (BACKEND=CPU/METAL/CUDA) - โ””โ”€โ”€ backends/ # Backend implementations - -embedded/ -โ”œโ”€โ”€ atomic_lib.h # Header-only C++ inference library -โ”œโ”€โ”€ atomic_runner.cpp # Standalone binary -โ””โ”€โ”€ platforms/ # Platform-specific demos - โ”œโ”€โ”€ esp32/ # ESP32 microcontroller - โ”œโ”€โ”€ rpi/ # Raspberry Pi 4 - โ””โ”€โ”€ wasm/ # WebAssembly browser demo - -benchmarks/ # Performance evaluation -โ”œโ”€โ”€ run_suite.py # Main benchmark script -โ””โ”€โ”€ platforms/ # Platform-specific benchmarks - -tests/ # Pytest test suite (71 tests) -configs/ # YAML model presets -docs/ # Architecture documentation -tools/ # Parity checks, utilities -weights/ # Trained model checkpoints +```bash +# Run the full test suite +pytest tests/ -v + +# Run specific test modules +pytest tests/test_bitlinear.py -v +pytest tests/test_kernel_parity.py -v ``` --- -## ๐ŸŽ“ Research Context - -Atomic-1Bit is based on **BitNet b1.58** (Microsoft Research, 2024), which introduced 1.58-bit ternary quantization for language models. This project provides: +## Contributing -1. **End-to-End Implementation**: Training, evaluation, export, deployment -2. **Parity-Verified Runtime**: Bit-exact C++ inference -3. **Real Hardware Targets**: ESP32, Raspberry Pi, WebAssembly -4. **Comprehensive Documentation**: Theory and practice - -For the mathematical foundations and design rationale, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). +We welcome contributions. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on submitting issues, pull requests, and code style expectations. --- -## ๐Ÿ“„ License +## Roadmap -MIT License - See [LICENSE](LICENSE) for details. +See [ROADMAP.md](ROADMAP.md) for the full development plan. -**Concept**: BitNet b1.58 (Microsoft Research) -**Implementation**: Atomic-1Bit (Pierre Guirguis) +- **v1.0** -- Parity-verified ternary inference (done) +- **v1.2** -- Hardware-native backends: Metal, CUDA (done) +- **v1.3** -- Model scaling, evaluation harness, 12.5M config (done) +- **v2.0** -- YAML configs, tokenizer abstraction, model presets (done) +- **Next** -- SIMD acceleration (AVX2/NEON), mobile demos, mixed-precision training --- -## ๐Ÿš€ What's Next? +## License -See [ROADMAP.md](ROADMAP.md) for planned features: -- v1.4: Higher quality flagship model (target PPL <50) -- v2.0: Mixed-precision support (2-bit, 4-bit hybrid) -- v2.1: Mobile deployment demos (Android, iOS) -- v3.0: FPGA/ASIC exploration +MIT License. See [LICENSE](LICENSE) for details. ---- +## Contact -*Built with โš›๏ธ by the Atomic-1Bit project. High intelligence, low compute.* +- **Author**: [@guirguispierre](https://github.com/guirguispierre) +- **Issues**: [GitHub Issues](https://github.com/guirguispierre/Atomic-1Bit/issues) +- **Discussions**: [GitHub Discussions](https://github.com/guirguispierre/Atomic-1Bit/discussions) diff --git a/atomic_1bit/__init__.py b/atomic_1bit/__init__.py new file mode 100644 index 0000000..c998e05 --- /dev/null +++ b/atomic_1bit/__init__.py @@ -0,0 +1 @@ +"""Atomic-1Bit: Bare-metal inference engine for 1.58-bit ternary language models.""" diff --git a/atomic_1bit/model/gist.py b/atomic_1bit/model/gist.py index fc53215..6eb3469 100644 --- a/atomic_1bit/model/gist.py +++ b/atomic_1bit/model/gist.py @@ -8,22 +8,22 @@ class GistConfig: dim: int = 512 class GistEncoder(nn.Module): + """Encodes a token sequence into a single "gist" vector via mean pooling. + + Used to compress system prompts into a fixed-size embedding that can be + injected into the attention stream at zero inference cost. + """ + def __init__(self, config: GistConfig): super().__init__() - # Simple averaging usage: mode='mean' - # In a real scenario, this could be an LSTM or small Transformer. self.embedding = nn.EmbeddingBag(config.vocab_size, config.dim, mode='mean') - + def forward(self, input_ids): """ - input_ids: (Batch, Seq) - Returns: (Batch, Dim) + Args: + input_ids: (Batch, Seq) token indices + + Returns: + (Batch, Dim) mean-pooled embedding vector """ - # EmbeddingBag expects inputs differently depending on usage, - # but for fixed length batch, usually it needs 1D input + offsets - # OR 2D input. - # If input is 2D (Batch, Seq), EmbeddingBag handles it if we don't pass offsets? - # Actually EmbeddingBag with 2D input and no offsets works if include_last_offset=False? - # Let's verify standard usage. - # "If input is 2D of shape (B, N), it will be treated as B bags (sequences) each of fixed length N." return self.embedding(input_ids) diff --git a/atomic_1bit/tests/debug_crash.py b/atomic_1bit/tests/debug_crash.py deleted file mode 100644 index 52ff4b6..0000000 --- a/atomic_1bit/tests/debug_crash.py +++ /dev/null @@ -1,39 +0,0 @@ -import torch -import sys -import os -import traceback - -# Add project root to path -sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..")) - -from atomic_1bit.nn.layers import BitLinear -from atomic_1bit.model.transformer import BitAttention, AtomicConfig - -def debug(): - print("--- Debugging ---") - - # 1. Test BitLinear - print("\n1. Testing BitLinear...") - try: - layer = BitLinear(64, 64) - x = torch.randn(2, 32, 64) - y = layer(x) - print(f"BitLinear Output: {y.shape}") - except Exception: - traceback.print_exc() - return - - # 2. Test BitAttention - print("\n2. Testing BitAttention...") - try: - config = AtomicConfig(dim=64, heads=4) - attn = BitAttention(config) - x = torch.randn(2, 32, 64) - y = attn(x) - print(f"BitAttention Output: {y.shape}") - except Exception: - traceback.print_exc() - return - -if __name__ == "__main__": - debug() diff --git a/docs/INSTALL.md b/docs/INSTALL.md new file mode 100644 index 0000000..04a385c --- /dev/null +++ b/docs/INSTALL.md @@ -0,0 +1,108 @@ +# Installation Guide + +## Requirements + +- **OS**: macOS (Apple Silicon recommended) or Linux +- **Python**: 3.8 or higher +- **Compiler**: GCC/G++ or Clang with C++17 support (for C++ inference only) + +## Python Setup + +### 1. Clone the repository + +```bash +git clone https://github.com/guirguispierre/Atomic-1Bit.git +cd Atomic-1Bit +``` + +### 2. Install dependencies + +```bash +pip install -r requirements.txt +``` + +This installs: +- `torch` -- Model training and inference +- `tiktoken` -- Tokenization (GPT-2/GPT-4 compatible) +- `datasets` -- HuggingFace dataset loading +- `numpy` -- Reference math and verification +- `matplotlib` -- Benchmark plotting +- `psutil` -- Thermal monitoring +- `tqdm` -- Progress bars +- `pyyaml` -- Config file parsing + +### 3. Verify installation + +```bash +python3 -c "import torch; import tiktoken; print('Dependencies OK')" +``` + +## C++ Runtime (Optional) + +The C++ inference engine is optional -- you only need it if you want to run models on bare metal or embedded devices. + +### Build the engine + +```bash +cd embedded +g++ -O3 -std=c++17 atomic_runner.cpp -o runner +``` + +### Build with Metal backend (macOS only) + +```bash +cd atomic_1bit/core +make BACKEND=METAL +``` + +### Build with CUDA backend (NVIDIA GPU) + +```bash +cd atomic_1bit/core +make BACKEND=CUDA +``` + +### Verify the build + +```bash +cd embedded +./runner --help +``` + +## Pre-commit Hooks (Development) + +If you plan to contribute, install the pre-commit hooks: + +```bash +pip install pre-commit +pre-commit install +``` + +This enables automatic formatting (Black, isort) and linting (flake8) on each commit. + +## Troubleshooting + +**`ModuleNotFoundError: No module named 'tiktoken'`** + +```bash +pip install tiktoken +``` + +**C++ compilation fails with "no member named 'format'"** + +Your compiler may not support C++17. Update GCC to 7+ or Clang to 5+: +```bash +# Ubuntu/Debian +sudo apt update && sudo apt install g++-11 + +# macOS (Xcode command line tools) +xcode-select --install +``` + +**Metal backend not found (macOS)** + +Ensure Xcode command line tools are installed and you're on macOS 12+. + +**Thermal monitoring shows "sensors unavailable"** + +On Apple Silicon, temperature sensors may require `sudo`. The thermal monitor disables itself gracefully when sensors aren't accessible. This doesn't affect model training or inference. diff --git a/docs/USAGE.md b/docs/USAGE.md new file mode 100644 index 0000000..63666ff --- /dev/null +++ b/docs/USAGE.md @@ -0,0 +1,165 @@ +# Usage Guide + +This guide covers the main workflows: training models, running inference, exporting to C++, and evaluating quality. + +## Training + +### TinyStories (Default) + +Train a 1.33M parameter model on the TinyStories dataset: + +```bash +python3 atomic_1bit/training/train.py +``` + +- Saves checkpoints to `weights/` +- Default config: 256d, 6 layers, 4 heads, 128 context length +- Includes thermal safety monitoring (auto-pauses if CPU > 80C) + +### Instruction Model (12.5M Parameters) + +Train the flagship model on Alpaca-cleaned: + +```bash +python3 atomic_1bit/training/train_instruct.py +``` + +- Uses gradient accumulation (effective batch size 256) +- Cosine learning rate schedule with warmup +- Saves to `weights/instruct_final.pt` + +### Pocket Model (ESP32 Optimized) + +Train a tiny model with 4K vocabulary for embedded deployment: + +```bash +python3 atomic_1bit/training/train_pocket.py +``` + +- Vocabulary filtered to 4096 tokens +- Optimized for ESP32-S3 memory constraints +- Saves to `weights/pocket_final.pt` + +### Using YAML Configs + +Load model configuration from YAML files: + +```python +from atomic_1bit.config import load_config, config_to_atomic + +config = config_to_atomic(load_config("configs/flagship_12m.yaml")) +``` + +Available presets in `configs/`: +- `stories_base.yaml` -- 1.33M params, development/testing +- `flagship_12m.yaml` -- 12.5M params, quality demos +- `pocket_4k.yaml` -- Tiny model for microcontrollers +- `mixed_precision.yaml` -- Experimental hybrid quantization + +## Inference + +### Python Chat Interface + +Interactive text generation: + +```bash +python3 atomic_1bit/python/chat.py +``` + +### Kernel Verification + +Verify C++ and Python produce identical output: + +```bash +python3 atomic_1bit/python/inference.py +``` + +### C++ Bare-Metal Inference + +After exporting a model (see below): + +```bash +cd embedded +./runner --model atomic_model.bin --steps 100 --temp 0.7 --seed 42 --start_token 58 +``` + +Flags: +- `--steps` -- Number of tokens to generate +- `--temp` -- Temperature (0.0 = greedy, 0.7 = creative) +- `--seed` -- Random seed for reproducibility +- `--start_token` -- Initial token ID + +## Exporting Models + +Convert a PyTorch checkpoint to C++ binary format: + +```bash +python3 atomic_1bit/utils/export_to_cpp.py \ + --model weights/stories_final.pt \ + --output embedded/atomic_model.bin \ + --dim 256 --depth 6 --heads 4 --vocab_size 4096 --context_len 128 +``` + +The `--dim`, `--depth`, `--heads`, `--vocab_size`, and `--context_len` flags **must match** the config used during training. Mismatched dimensions will produce garbage output. + +### Exporting with Gist Tokens + +To embed a pre-computed system prompt: + +```bash +# 1. Generate the gist vector +python3 atomic_1bit/utils/gen_gist.py \ + --model weights/stories_final.pt \ + --prompt "You are a helpful storyteller." + +# 2. Export with gist +python3 atomic_1bit/utils/export_to_cpp.py \ + --model weights/stories_final.pt \ + --output embedded/atomic_model.bin \ + --gist weights/gist.bin \ + --dim 256 --depth 6 --heads 4 --vocab_size 4096 --context_len 128 +``` + +## Evaluation + +### Run the Evaluation Suite + +```bash +python3 atomic_1bit/evaluation/run_eval.py +``` + +This measures: +- **Perplexity** -- Language model quality +- **Repetition rate** -- Degenerate output detection +- **Coherence** -- Semantic consistency +- **Diversity** -- Vocabulary and n-gram variety + +### Benchmarking + +Compare against FP16 baselines: + +```bash +python3 benchmarks/run_suite.py +``` + +Generate benchmark plots: + +```bash +python3 scripts/generate_plots.py +``` + +Results are saved to `benchmarks/results.json` and charts to `assets/`. + +## ESP32 Deployment + +See [embedded/ESP32_PORT_GUIDE.md](../embedded/ESP32_PORT_GUIDE.md) for instructions on running models on ESP32-S3 microcontrollers, including memory analysis, streaming weight loading, and bit-packing strategies. + +## Thermal Safety + +Long training runs include automatic thermal monitoring: + +- **Auto-pause** when system temperature exceeds 80C +- **Auto-resume** when temperature drops below 70C +- **Checkpoint save** before any thermal pause + +The monitor disables itself gracefully on systems where temperature sensors aren't accessible. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..70657f6 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,33 @@ +# Examples + +Runnable example scripts for Atomic-1Bit. + +## Scripts + +### `quickstart.py` + +End-to-end demo: creates a model, runs a forward pass, quantizes weights, and shows how the ternary kernel works. No trained checkpoint needed. + +```bash +python3 examples/quickstart.py +``` + +### `generate.py` + +Loads a trained checkpoint and generates text. Requires a trained model in `weights/`. + +```bash +# First, train a model: +python3 atomic_1bit/training/train.py + +# Then generate: +python3 examples/generate.py +``` + +## Requirements + +All examples use the same dependencies as the main project: + +```bash +pip install -r requirements.txt +``` diff --git a/examples/generate.py b/examples/generate.py new file mode 100644 index 0000000..ce0a6e8 --- /dev/null +++ b/examples/generate.py @@ -0,0 +1,103 @@ +""" +Atomic-1Bit Text Generation Example + +Loads a trained checkpoint and generates text interactively. +Requires a trained model -- run `python3 atomic_1bit/training/train.py` first. + +Usage: + python3 examples/generate.py + python3 examples/generate.py --checkpoint weights/stories_final.pt --temp 0.7 +""" + +import sys +import os +import argparse + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +import torch +import tiktoken +from atomic_1bit.model.transformer import AtomicTransformer, AtomicConfig + + +def main(): + parser = argparse.ArgumentParser(description="Generate text with Atomic-1Bit") + parser.add_argument( + "--checkpoint", + type=str, + default="weights/stories_final.pt", + help="Path to trained model checkpoint", + ) + parser.add_argument( + "--temp", type=float, default=0.7, help="Sampling temperature (0=greedy)" + ) + parser.add_argument( + "--tokens", type=int, default=100, help="Number of tokens to generate" + ) + args = parser.parse_args() + + if not os.path.exists(args.checkpoint): + print(f"Checkpoint not found: {args.checkpoint}") + print("Train a model first: python3 atomic_1bit/training/train.py") + sys.exit(1) + + # Load checkpoint + print(f"Loading checkpoint: {args.checkpoint}") + checkpoint = torch.load(args.checkpoint, map_location="cpu", weights_only=False) + + # Extract config from checkpoint or use defaults + config = AtomicConfig( + vocab_size=checkpoint.get("vocab_size", 4096), + dim=checkpoint.get("dim", 256), + depth=checkpoint.get("depth", 6), + heads=checkpoint.get("heads", 4), + context_length=checkpoint.get("context_len", 128), + ) + + model = AtomicTransformer(config) + model.load_state_dict(checkpoint["model_state_dict"]) + model.eval() + + param_count = sum(p.numel() for p in model.parameters()) + print(f"Model loaded: {param_count:,} parameters") + + # Tokenizer + enc = tiktoken.get_encoding("gpt2") + + # Interactive generation + print(f"\nGenerating {args.tokens} tokens at temperature {args.temp}") + print("Type a prompt and press Enter. Type 'quit' to exit.\n") + + while True: + try: + prompt = input(">>> ") + except (EOFError, KeyboardInterrupt): + break + + if prompt.strip().lower() in ("quit", "exit", "q"): + break + + if not prompt.strip(): + continue + + tokens = enc.encode(prompt) + input_ids = torch.tensor([tokens], dtype=torch.long) + + # Truncate to context length + if input_ids.shape[1] > config.context_length - args.tokens: + input_ids = input_ids[:, -(config.context_length - args.tokens) :] + + with torch.no_grad(): + output = model.generate( + input_ids, max_new_tokens=args.tokens, temperature=args.temp + ) + + generated = output[0].tolist() + text = enc.decode(generated) + print(f"\n{text}\n") + + print("Done.") + + +if __name__ == "__main__": + main() diff --git a/examples/quickstart.py b/examples/quickstart.py new file mode 100644 index 0000000..7d1faa7 --- /dev/null +++ b/examples/quickstart.py @@ -0,0 +1,100 @@ +""" +Atomic-1Bit Quickstart Demo + +Demonstrates the core concepts without needing a trained checkpoint: +1. Creates a small ternary transformer +2. Shows how BitLinear quantizes weights to {-1, 0, 1} +3. Runs a forward pass +4. Generates sample tokens + +Usage: + python3 examples/quickstart.py +""" + +import sys +import os + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +import torch +from atomic_1bit.model.transformer import AtomicTransformer, AtomicConfig +from atomic_1bit.nn.layers import BitLinear, weight_quant + + +def main(): + print("=" * 60) + print("Atomic-1Bit Quickstart Demo") + print("=" * 60) + + # --- 1. Create a small model --- + config = AtomicConfig( + vocab_size=4096, + dim=128, + depth=4, + heads=4, + context_length=64, + ) + + model = AtomicTransformer(config) + param_count = sum(p.numel() for p in model.parameters()) + print(f"\nModel created: {param_count:,} parameters") + print(f" Dimensions: {config.dim}d, {config.depth} layers, {config.heads} heads") + print(f" Context length: {config.context_length}") + print(f" Vocab size: {config.vocab_size}") + + # --- 2. Show weight quantization --- + print("\n--- Weight Quantization Demo ---") + sample_weight = model.layers[0].attn.q_proj.weight.data + quantized, scale = weight_quant(sample_weight) + + unique_vals = torch.unique(quantized.round()) + print(f"Original weight range: [{sample_weight.min():.4f}, {sample_weight.max():.4f}]") + print(f"Quantized to: {sorted([int(v.item()) for v in unique_vals])}") + print(f"Scale factor: {scale.item():.4f}") + + # Count ternary distribution + total = quantized.numel() + n_neg = (quantized.round() == -1).sum().item() + n_zero = (quantized.round() == 0).sum().item() + n_pos = (quantized.round() == 1).sum().item() + print(f"Distribution: -1={n_neg/total:.1%}, 0={n_zero/total:.1%}, +1={n_pos/total:.1%}") + print(f"Sparsity (zero weights): {n_zero/total:.1%}") + + # --- 3. Forward pass --- + print("\n--- Forward Pass ---") + tokens = torch.randint(0, config.vocab_size, (1, 16)) # batch=1, seq=16 + print(f"Input shape: {tokens.shape}") + + with torch.no_grad(): + logits = model(tokens) + + print(f"Output logits shape: {logits.shape}") + print(f"Output range: [{logits.min():.4f}, {logits.max():.4f}]") + + # --- 4. Generate tokens --- + print("\n--- Token Generation ---") + prompt = torch.randint(0, config.vocab_size, (1, 4)) + print(f"Prompt tokens: {prompt[0].tolist()}") + + with torch.no_grad(): + generated = model.generate(prompt, max_new_tokens=20, temperature=0.8) + + new_tokens = generated[0, 4:].tolist() + print(f"Generated {len(new_tokens)} tokens: {new_tokens}") + + # --- 5. Model size estimate --- + print("\n--- Size Comparison ---") + fp32_size = param_count * 4 # 4 bytes per float32 param + ternary_size = param_count * 2 / 8 # 2 bits per ternary weight + print(f"FP32 size: {fp32_size / 1024:.1f} KB") + print(f"Ternary size: {ternary_size / 1024:.1f} KB (packed)") + print(f"Reduction: {(1 - ternary_size / fp32_size) * 100:.0f}%") + + print("\n" + "=" * 60) + print("Done. To train a real model, run:") + print(" python3 atomic_1bit/training/train.py") + print("=" * 60) + + +if __name__ == "__main__": + main() diff --git a/v1.3_quality_report.txt b/v1.3_quality_report.txt deleted file mode 100644 index b9c02a9..0000000 --- a/v1.3_quality_report.txt +++ /dev/null @@ -1,2 +0,0 @@ -Perplexity: 54.79 -Avg Repetition Rate: 0.0%