Text Generation
Transformers
Safetensors
English
mixture-of-experts
Mixture of Experts
from-scratch
ablation
research
Instructions to use OliverSundaram/MoE-Study with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use OliverSundaram/MoE-Study with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="OliverSundaram/MoE-Study")# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("OliverSundaram/MoE-Study", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use OliverSundaram/MoE-Study with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "OliverSundaram/MoE-Study" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "OliverSundaram/MoE-Study", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/OliverSundaram/MoE-Study
- SGLang
How to use OliverSundaram/MoE-Study with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "OliverSundaram/MoE-Study" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "OliverSundaram/MoE-Study", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "OliverSundaram/MoE-Study" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "OliverSundaram/MoE-Study", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use OliverSundaram/MoE-Study with Docker Model Runner:
docker model run hf.co/OliverSundaram/MoE-Study
Add dense and top-2-of-4 MoE checkpoints, tokenizer, and benchmark assets
Browse files- README.md +207 -0
- assets/arc_challenge.png +0 -0
- assets/arc_easy.png +0 -0
- assets/hellaswag.png +0 -0
- assets/lambada_openai.png +0 -0
- assets/piqa.png +0 -0
- assets/speed.png +0 -0
- assets/wikitext.png +0 -0
- assets/winogrande.png +0 -0
- dense/config.json +18 -0
- dense/final_state.pt +3 -0
- dense/model.safetensors +3 -0
- moe/config.json +18 -0
- moe/final_state.pt +3 -0
- moe/model.safetensors +3 -0
- tokenizer.json +0 -0
- tokenizer_config.json +13 -0
README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
library_name: transformers
|
| 6 |
+
pipeline_tag: text-generation
|
| 7 |
+
inference: false
|
| 8 |
+
datasets:
|
| 9 |
+
- nampdn-ai/tiny-textbooks
|
| 10 |
+
tags:
|
| 11 |
+
- mixture-of-experts
|
| 12 |
+
- moe
|
| 13 |
+
- from-scratch
|
| 14 |
+
- ablation
|
| 15 |
+
- research
|
| 16 |
+
---
|
| 17 |
+
|
| 18 |
+
# MoE-Study — Dense vs. Mixture-of-Experts, matched active parameters
|
| 19 |
+
|
| 20 |
+
Two decoder-only language models trained **from scratch** under identical conditions, differing in
|
| 21 |
+
exactly one thing: whether the feed-forward block is a **dense MLP** or a **sparse top-2-of-4 MoE**.
|
| 22 |
+
|
| 23 |
+
Both checkpoints live in this one repo:
|
| 24 |
+
|
| 25 |
+
| Subfolder | Model | Total params | Active params/token |
|
| 26 |
+
|---------------------|----------------|--------------|---------------------|
|
| 27 |
+
| [`dense/`](./dense) | Dense FFN | 150.1M | 150.1M |
|
| 28 |
+
| [`moe/`](./moe) | Top-2-of-4 MoE | 206.8M | ~150.1M |
|
| 29 |
+
|
| 30 |
+
The MoE's active-parameter count matches Dense **by construction** — 2 of 4 experts at half the hidden
|
| 31 |
+
size means identical compute per token. The MoE only spends more *memory* for extra capacity.
|
| 32 |
+
|
| 33 |
+
Full write-up, training code, and evaluation harness:
|
| 34 |
+
**[github.com/OliverSundaram/MoE-Study](https://github.com/OliverSundaram/MoE-Study)**
|
| 35 |
+
|
| 36 |
+
---
|
| 37 |
+
|
| 38 |
+
## ⚠️ These are research artifacts, not usable models
|
| 39 |
+
|
| 40 |
+
Read this before downloading.
|
| 41 |
+
|
| 42 |
+
- Trained for **one epoch** on ~40.7M tokens — neither model is close to converged.
|
| 43 |
+
- **WikiText word perplexity is 551 (Dense) and 1,378 (MoE).** Generations are largely incoherent.
|
| 44 |
+
- **0.0% on LAMBADA** for both — at the task floor.
|
| 45 |
+
- No instruction tuning, no RLHF, no safety filtering of any kind.
|
| 46 |
+
|
| 47 |
+
They exist to answer one narrow question: *at matched active compute and matched budget, does sparsity
|
| 48 |
+
help?* They are not fit for any downstream use.
|
| 49 |
+
|
| 50 |
+
---
|
| 51 |
+
|
| 52 |
+
## Getting the weights
|
| 53 |
+
|
| 54 |
+
These are a custom architecture, not a variant of an existing one. The modeling code is not included
|
| 55 |
+
here, so `from_pretrained` on this repo alone will not build the model.
|
| 56 |
+
|
| 57 |
+
Clone [the GitHub repo](https://github.com/OliverSundaram/MoE-Study) — it carries the model definition
|
| 58 |
+
and loading instructions, and points back at these subfolders for the weights.
|
| 59 |
+
|
| 60 |
+
---
|
| 61 |
+
|
| 62 |
+
## Model details
|
| 63 |
+
|
| 64 |
+
### Shared architecture
|
| 65 |
+
|
| 66 |
+
Both models are the same custom decoder-only transformer:
|
| 67 |
+
|
| 68 |
+
| | |
|
| 69 |
+
|---------------------|--------------------------------------------------------------------|
|
| 70 |
+
| Layers | 12 |
|
| 71 |
+
| Attention heads | 12 |
|
| 72 |
+
| Embedding dim | 768 |
|
| 73 |
+
| Context length | 1024 |
|
| 74 |
+
| Vocabulary | 50,257 (GPT-2 tokenizer) |
|
| 75 |
+
| Attention | **Multi-Query** — one shared K/V projection across all query heads |
|
| 76 |
+
| Normalization | Custom pre-norm (learned scale + shift) |
|
| 77 |
+
| Position embeddings | Learned absolute |
|
| 78 |
+
| Weight tying | None — separate input embedding and output head |
|
| 79 |
+
|
| 80 |
+
### The one difference
|
| 81 |
+
|
| 82 |
+
| | `dense/` | `moe/` |
|
| 83 |
+
|--------------|------------------|------------------------------------------------|
|
| 84 |
+
| FFN block | 2-layer GELU MLP | 4 experts, top-2 routed |
|
| 85 |
+
| `hidden_dim` | 3072 | 1536 (per expert) |
|
| 86 |
+
| Router | — | linear → softmax → top-2, renormalized |
|
| 87 |
+
| Aux loss | — | load-balancing term, summed over all 12 layers |
|
| 88 |
+
|
| 89 |
+
Both models share the **same** unmodified GPT-2 tokenizer, stored once at the repo root.
|
| 90 |
+
|
| 91 |
+
---
|
| 92 |
+
|
| 93 |
+
## Training
|
| 94 |
+
|
| 95 |
+
Identical for both models. Single consumer GPU, no cloud.
|
| 96 |
+
|
| 97 |
+
| Setting | Value |
|
| 98 |
+
|---------------|----------------------------------------------------------------------------------------|
|
| 99 |
+
| Data | [`nampdn-ai/tiny-textbooks`](https://huggingface.co/datasets/nampdn-ai/tiny-textbooks) |
|
| 100 |
+
| Tokens | 39,717 chunks × 1024 = **~40.67M** |
|
| 101 |
+
| Epochs | **1** (19,858 steps) |
|
| 102 |
+
| Batch size | 2 × grad accum 4 = effective **8** |
|
| 103 |
+
| Optimizer | AdamW, lr `3e-4`, weight decay `0.1` (no decay on 1-D params) |
|
| 104 |
+
| Schedule | `OneCycleLR`, cosine, 3% warmup |
|
| 105 |
+
| Grad clipping | max-norm `1.0` |
|
| 106 |
+
| Precision | AMP autocast + `GradScaler` |
|
| 107 |
+
| Seed | 42 |
|
| 108 |
+
| Hardware | 1× NVIDIA RTX 4060, 8 GB VRAM |
|
| 109 |
+
| Wall-clock | ~44.6 min (Dense) · ~59.8 min (MoE) |
|
| 110 |
+
|
| 111 |
+
### Final losses
|
| 112 |
+
|
| 113 |
+
| | Dense | MoE |
|
| 114 |
+
|----------------------------|-----------|-----------|
|
| 115 |
+
| Train loss (final step) | 5.166 | 5.936 |
|
| 116 |
+
| **Test loss (pure LM)** | **5.063** | **5.911** |
|
| 117 |
+
| Test loss (+ unscaled aux) | n/a | 17.91 |
|
| 118 |
+
|
| 119 |
+
Dense has the lower loss at **every** checkpoint.
|
| 120 |
+
|
| 121 |
+
---
|
| 122 |
+
|
| 123 |
+
## Evaluation
|
| 124 |
+
|
| 125 |
+
All benchmarks via [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness) on the
|
| 126 |
+
final checkpoints.
|
| 127 |
+
|
| 128 |
+
| Benchmark | Shots | Metric | Dense | MoE | abs(Δ) | Winner |
|
| 129 |
+
|------------------|-------|-------------------|-----------|-----------|--------|----------|
|
| 130 |
+
| ARC-Easy | 0 | `acc` | **29.2%** | 27.4% | 1.8 | 🔵 Dense |
|
| 131 |
+
| PIQA | 0 | `acc` | **55.0%** | 54.1% | 0.9 | 🔵 Dense |
|
| 132 |
+
| WikiText | 0 | `word_perplexity` | **551.0** | 1,377.8 | 826.8 | 🔵 Dense |
|
| 133 |
+
| LAMBADA (OpenAI) | 0 | `acc` | 0.0% | 0.0% | 0.0 | ⚪ Tie |
|
| 134 |
+
| WinoGrande | 5 | `acc` | 50.2% | **50.7%** | 0.5 | 🟠 MoE |
|
| 135 |
+
| HellaSwag | 10 | `acc_norm` | 24.9% | **25.1%** | 0.2 | 🟠 MoE |
|
| 136 |
+
| ARC-Challenge | 25 | `acc_norm` | 22.9% | **23.0%** | 0.1 | 🟠 MoE |
|
| 137 |
+
|
| 138 |
+
**How to read this:**
|
| 139 |
+
|
| 140 |
+
- Dense wins on everything sensitive to raw LLM quality — perplexity, ARC-Easy, PIQA.
|
| 141 |
+
- WinoGrande, HellaSwag, and ARC-Challenge are won by MoE, but with such a negligible difference, that they could be considered to have an equal accuracy
|
| 142 |
+
|
| 143 |
+
### Inference speed
|
| 144 |
+
|
| 145 |
+
Greedy decoding, 32-token prompt → 64 new tokens, 5 trials, 2 warmup, no KV cache.
|
| 146 |
+
|
| 147 |
+
| Model | Tokens/sec | Total params | Active params/token |
|
| 148 |
+
|-------|-------------------|--------------|---------------------|
|
| 149 |
+
| Dense | **106.49 ± 0.30** | 150.1M | 150.1M |
|
| 150 |
+
| MoE | 34.40 ± 0.08 | 206.8M | ~150.1M |
|
| 151 |
+
|
| 152 |
+
MoE is **~3.1× slower** despite matched active compute — an artifact of unoptimized expert dispatch, not
|
| 153 |
+
a property of the architecture.
|
| 154 |
+
|
| 155 |
+
<details>
|
| 156 |
+
<summary><b>Benchmark charts</b></summary>
|
| 157 |
+
|
| 158 |
+

|
| 159 |
+

|
| 160 |
+

|
| 161 |
+

|
| 162 |
+

|
| 163 |
+

|
| 164 |
+

|
| 165 |
+

|
| 166 |
+
</details>
|
| 167 |
+
|
| 168 |
+
---
|
| 169 |
+
|
| 170 |
+
## Findings
|
| 171 |
+
|
| 172 |
+
**1. Dense won every metric that wasn't already at chance.**
|
| 173 |
+
Most clearly on WikiText perplexity — 551 vs 1,378, a 2.5× gap.
|
| 174 |
+
|
| 175 |
+
**2. The routing math is correct.**
|
| 176 |
+
Active parameters match Dense almost exactly. Matched active compute simply didn't buy matched quality
|
| 177 |
+
at this budget.
|
| 178 |
+
|
| 179 |
+
**3. Routing stayed balanced.**
|
| 180 |
+
The load-balancing term sat on its theoretical floor, so the MoE's gap is not explained by experts
|
| 181 |
+
collapsing onto each other.
|
| 182 |
+
|
| 183 |
+
**4. Extra capacity needs extra tokens.**
|
| 184 |
+
The MoE has 38% more parameters but saw the same ~40.7M tokens — likely far too few to train 4 experts
|
| 185 |
+
per layer, each seeing only a routed fraction of the stream.
|
| 186 |
+
---
|
| 187 |
+
|
| 188 |
+
## Citation
|
| 189 |
+
|
| 190 |
+
```bibtex
|
| 191 |
+
@misc{sundaram2026moestudy,
|
| 192 |
+
author = {Sundaram, Oliver},
|
| 193 |
+
title = {MoE-Study: Dense vs. Mixture-of-Experts at Matched Active Parameters},
|
| 194 |
+
year = {2026},
|
| 195 |
+
url = {https://github.com/OliverSundaram/MoE-Study}
|
| 196 |
+
}
|
| 197 |
+
```
|
| 198 |
+
|
| 199 |
+
## Acknowledgments
|
| 200 |
+
|
| 201 |
+
- [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness) (EleutherAI) — evaluation
|
| 202 |
+
- [nampdn-ai/tiny-textbooks](https://huggingface.co/datasets/nampdn-ai/tiny-textbooks) — training corpus
|
| 203 |
+
- [Hugging Face `transformers`](https://github.com/huggingface/transformers) — base classes and tokenizer
|
| 204 |
+
|
| 205 |
+
## License
|
| 206 |
+
|
| 207 |
+
MIT
|
assets/arc_challenge.png
ADDED
|
assets/arc_easy.png
ADDED
|
assets/hellaswag.png
ADDED
|
assets/lambada_openai.png
ADDED
|
assets/piqa.png
ADDED
|
assets/speed.png
ADDED
|
assets/wikitext.png
ADDED
|
assets/winogrande.png
ADDED
|
dense/config.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"LLM"
|
| 4 |
+
],
|
| 5 |
+
"context_length": 1024,
|
| 6 |
+
"dtype": "float32",
|
| 7 |
+
"emb_dim": 768,
|
| 8 |
+
"hidden_dim": 3072,
|
| 9 |
+
"is_moe": false,
|
| 10 |
+
"model_type": "custom_llm",
|
| 11 |
+
"n_experts": null,
|
| 12 |
+
"n_heads": 12,
|
| 13 |
+
"n_layers": 12,
|
| 14 |
+
"qkv_bias": false,
|
| 15 |
+
"top_k": null,
|
| 16 |
+
"transformers_version": "5.15.0",
|
| 17 |
+
"vocab_size": 50257
|
| 18 |
+
}
|
dense/final_state.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ac21bb0a177dbeea395f7fc451c6dc10b079ddebaa971ab4ff976eac00ecbf4b
|
| 3 |
+
size 1201509219
|
dense/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f1143cf1b87fa9e4cc8504f31a87058889922e4836e3907bd028e8db56ef2ab0
|
| 3 |
+
size 600351628
|
moe/config.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"LLM"
|
| 4 |
+
],
|
| 5 |
+
"context_length": 1024,
|
| 6 |
+
"dtype": "float32",
|
| 7 |
+
"emb_dim": 768,
|
| 8 |
+
"hidden_dim": 1536,
|
| 9 |
+
"is_moe": true,
|
| 10 |
+
"model_type": "custom_llm",
|
| 11 |
+
"n_experts": 4,
|
| 12 |
+
"n_heads": 12,
|
| 13 |
+
"n_layers": 12,
|
| 14 |
+
"qkv_bias": false,
|
| 15 |
+
"top_k": 2,
|
| 16 |
+
"transformers_version": "5.15.0",
|
| 17 |
+
"vocab_size": 50257
|
| 18 |
+
}
|
moe/final_state.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8107ca89b5111e58627130c68161136a9000ed0b342a1593ea4db05d8d7b9c00
|
| 3 |
+
size 1655436555
|
moe/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:de50a32d2c285961cd36bd51a5f4fe7e5593897318d31ab665f4d1573c1854d2
|
| 3 |
+
size 827267812
|
tokenizer.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"add_prefix_space": false,
|
| 3 |
+
"backend": "tokenizers",
|
| 4 |
+
"bos_token": "<|endoftext|>",
|
| 5 |
+
"eos_token": "<|endoftext|>",
|
| 6 |
+
"errors": "replace",
|
| 7 |
+
"is_local": false,
|
| 8 |
+
"local_files_only": false,
|
| 9 |
+
"model_max_length": 1024,
|
| 10 |
+
"pad_token": null,
|
| 11 |
+
"tokenizer_class": "GPT2Tokenizer",
|
| 12 |
+
"unk_token": "<|endoftext|>"
|
| 13 |
+
}
|