Instructions to use samairtimer/nanollm_wiki_20.2M with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use samairtimer/nanollm_wiki_20.2M with MLX:
# Make sure mlx-lm is installed # pip install --upgrade mlx-lm # if on a CUDA device, also pip install mlx[cuda] # Generate text with mlx-lm from mlx_lm import load, generate model, tokenizer = load("samairtimer/nanollm_wiki_20.2M") prompt = "Once upon a time in" text = generate(model, tokenizer, prompt=prompt, verbose=True) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- MLX LM
How to use samairtimer/nanollm_wiki_20.2M with MLX LM:
Generate or start a chat session
# Install MLX LM uv tool install mlx-lm # Generate some text mlx_lm.generate --model "samairtimer/nanollm_wiki_20.2M" --prompt "Once upon a time"
- Atomic Chat
| license: mit | |
| language: | |
| - en | |
| tags: | |
| - mlx | |
| - nanoLLM | |
| - causal-lm | |
| - text-generation | |
| datasets: | |
| - chonkie-ai/wikipedia-100k | |
| pipeline_tag: text-generation | |
| library_name: mlx | |
| widget: | |
| - text: "Cats are" | |
| example_title: "Cats" | |
| # NanoLLM 20.2M Wiki | |
| `nanollm_wiki_20.2M` is a lightweight, decoder-only transformer model designed for educational purposes, fast training experimentation, and local generation on Apple Silicon using Apple's **MLX** framework. | |
| The model is trained on a 100k subset of English Wikipedia articles (`chonkie-ai/wikipedia-100k`) to demonstrate basic autoregressive text generation capabilities with minimal compute requirements. | |
| ## Model Details | |
| - **Developed by:** [samairtimer](https://huggingface.co/samairtimer) | |
| - **Model Type:** Decoder-only Transformer (Custom NanoLLM Architecture) | |
| - **Language(s):** English | |
| - **License:** MIT | |
| - **Framework:** Apple MLX | |
| - **Tokenizer:** GPT-2 (via `tiktoken`, vocab size 50,257) | |
| ## Model Architecture & Hyperparameters | |
| Unlike standard Llama or GPT architectures, this model is a **highly simplified attention-only transformer** that omits the typical Feed-Forward Neural Network (FFN) sub-blocks and layer normalization to keep training extremely fast and lightweight. | |
| | Parameter | Value | Detail | | |
| |---|---|---| | |
| | **`vocab_size`** | 50,257 | GPT-2 Tiktoken Vocabulary | | |
| | **`maxlen`** (Context Length) | 128 | Maximum context window | | |
| | **`embed_dim`** | 192 | Hidden size dimension | | |
| | **`num_transformer_blocks`** | 6 | Number of decoder layers | | |
| | **`num_heads`** | 6 | Multi-head attention heads (32-dim heads) | | |
| | **`feed_forward_dim`** | 512 | Configured but omitted in runtime computation | | |
| ### Parameter Count Breakdown | |
| The model contains exactly **20,208,000 parameters (~20.2M)**. | |
| $$ | |
| \text{Total Parameters} = \text{Embeddings} + \text{Transformer Blocks} + \text{Output Projection} | |
| $$ | |
| 1. **Embedding Layer:** | |
| - Token Embeddings: $50,257 \times 192 = 9,649,344$ | |
| - Positional Embeddings: $128 \times 192 = 24,576$ | |
| - *Subtotal:* **9,673,920 parameters** | |
| 2. **Transformer Blocks (6 Layers):** | |
| - Each layer contains only a Multi-head Attention block (no biases, no feed-forward projection): | |
| - Query Projection: $192 \times 192 = 36,864$ | |
| - Key Projection: $192 \times 192 = 36,864$ | |
| - Value Projection: $192 \times 192 = 36,864$ | |
| - Output Projection: $192 \times 192 = 36,864$ | |
| - *Subtotal per layer:* $147,456$ parameters | |
| - *Total for 6 layers:* $147,456 \times 6 = \mathbf{884,736}$ **parameters** | |
| 3. **Output Layer (LM Head):** | |
| - Untied weights, linear layer with `bias=False`: $192 \times 50,257 = \mathbf{9,649,344}$ **parameters** | |
| $$\text{Grand Total} = 9,673,920 + 884,736 + 9,649,344 = 20,208,000 \text{ parameters}$$ | |
| --- | |
| ## Training Recipe | |
| ### Dataset | |
| - **Name:** `chonkie-ai/wikipedia-100k` | |
| - **Size:** 100,000 high-quality parsed English Wikipedia samples (~449 MB of raw text). | |
| - **Format:** Combined articles delimited by the `<|endoftext|>` token. | |
| ### Training Configuration | |
| - **Optimizer:** AdamW (`learning_rate=3e-4`) | |
| - **Batch Size:** 32 | |
| - **Sequence Length:** 128 tokens | |
| - **Epochs:** 5 epochs (~15,600 steps total) | |
| - **Precision:** float32 (saving to `.safetensors` size of ~80.8 MB) | |
| - **Hardware:** Apple Silicon Mac GPU (accelerated via MLX Metal backend) | |
| ### Loss Curve | |
| * **Initial Loss:** 10.8269 | |
| * **End of Epoch 1 (Step 3100):** 5.3371 | |
| * **End of Epoch 2 (Step 6200):** 5.0588 | |
| * **End of Epoch 3 (Step 9350):** 4.8099 | |
| * **End of Epoch 4 (Step 12450):** 4.4191 | |
| * **End of Epoch 5 (Step 15600):** 4.3014 | |
| --- | |
| ## How to Use | |
| To use this model for text generation, define the architecture matching the parameters and load the saved `.safetensors` weights using MLX. | |
| ### 1. Requirements | |
| Install the dependencies: | |
| ```bash | |
| pip install mlx tiktoken huggingface_hub | |
| ``` | |
| ### 2. Inference Code | |
| ```python | |
| import os | |
| import mlx.core as mx | |
| import mlx.nn as nn | |
| import tiktoken | |
| from huggingface_hub import hf_hub_download | |
| # Define model architecture identical to training | |
| class TokenAndPositionEmbedding(nn.Module): | |
| def __init__(self, maxlen: int, vocab_size: int, embed_dim: int): | |
| super().__init__() | |
| self.token_emb = nn.Embedding(vocab_size, embed_dim) | |
| self.pos_emb = nn.Embedding(maxlen, embed_dim) | |
| def __call__(self, x): | |
| seq_len = x.shape[1] | |
| positions = mx.arange(seq_len)[None, :] | |
| return self.token_emb(x) + self.pos_emb(positions) | |
| class TransformerBlock(nn.Module): | |
| def __init__(self, emed_dim: int, num_heads: int, ff_dim: int): | |
| super().__init__() | |
| self.attention = nn.MultiHeadAttention(emed_dim, num_heads) | |
| def __call__(self, x, mask=None): | |
| attn_out = self.attention(x, x, x, mask=mask) | |
| return x + attn_out | |
| class NanoLLM(nn.Module): | |
| def __init__(self, maxlen: int, vocab_size: int, embed_dim: int, num_heads: int, feed_forward_dim: int, num_transformer_blocks: int): | |
| super().__init__() | |
| self.maxlen = maxlen | |
| self.embedding = TokenAndPositionEmbedding(maxlen, vocab_size, embed_dim) | |
| self.transformer_blocks = [ | |
| TransformerBlock(embed_dim, num_heads, feed_forward_dim) | |
| for _ in range(num_transformer_blocks) | |
| ] | |
| self.output_layer = nn.Linear(embed_dim, vocab_size, bias=False) | |
| def __call__(self, token_ids): | |
| seq_len = token_ids.shape[1] | |
| mask = nn.MultiHeadAttention.create_additive_causal_mask(seq_len) | |
| x = self.embedding(token_ids) | |
| for block in self.transformer_blocks: | |
| x = block(x, mask=mask) | |
| return self.output_layer(x) | |
| # 1. Download weights from Hugging Face | |
| repo_id = "samairtimer/nanollm_wiki_20.2M" | |
| weights_path = hf_hub_download(repo_id=repo_id, filename="wikipedia_checkpoint.safetensors") | |
| # 2. Initialize and load weights | |
| tokenizer = tiktoken.get_encoding("gpt2") | |
| model = NanoLLM( | |
| maxlen=128, | |
| vocab_size=tokenizer.n_vocab, | |
| embed_dim=192, | |
| num_heads=6, | |
| feed_forward_dim=512, | |
| num_transformer_blocks=6 | |
| ) | |
| model.load_weights(weights_path) | |
| print("Model loaded successfully!") | |
| # 3. Autoregressive Generation function | |
| def generate(model, tokenizer, prompt, max_new_tokens=100, temperature=0.6): | |
| tokens = tokenizer.encode(prompt) | |
| x = mx.array(tokens)[None, :] | |
| end_token_id = tokenizer.encode('<|endoftext|>', allowed_special={'<|endoftext|>'})[0] | |
| print(prompt, end="", flush=True) | |
| for _ in range(max_new_tokens): | |
| if x.shape[1] > model.maxlen: | |
| x = x[:, -model.maxlen:] | |
| logits = model(x) | |
| next_token_logits = logits[0, -1, :] | |
| if temperature == 0.0: | |
| next_token = mx.argmax(next_token_logits, axis=-1).item() | |
| else: | |
| next_token = mx.random.categorical(next_token_logits / temperature, axis=-1).item() | |
| if next_token == end_token_id: | |
| break | |
| word = tokenizer.decode([next_token]) | |
| print(word, end="", flush=True) | |
| x = mx.concatenate([x, mx.array([[next_token]])], axis=1) | |
| print("\n") | |
| # Run generation | |
| generate(model, tokenizer, "Cats are", max_new_tokens=50, temperature=0.4) | |
| ``` | |
| --- | |
| ## Limitations & Biases | |
| - **Extremely Small Scale:** At 20.2M parameters, the model is not capable of complex reasoning, logical deduction, or factual lookup. | |
| - **Attention-Only:** The omission of MLP (Feed-Forward) layers limits the key-value lookup and relational storage capacity of the network, resulting in higher recurrence of repetitive structures. | |
| - **Short Context Window:** The 128-token context window restricts its capacity to maintain long-term coherence. | |
| - **Factual Inaccuracies:** The model is trained on a small subset of Wikipedia (100k entries) for 5 epochs. Any generated output should be treated strictly as synthetic text generation rather than historical or scientific fact. | |