Skip to content

About

A 777M-parameter Mixture-of-Experts language model trained from scratch for $55. Multi-head Latent Attention, DeepSeekMoE, own tokenizer.

Topics

Resources

Stars

11 stars

Watchers

2 watching

Forks

Latest commit

 

History

108 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Moonfrost AI

A 777M-parameter Mixture-of-Experts language model trained from scratch.

Model · Base · Dataset · Site · Architecture · Pipeline · Benchmarks · Training runs


Moonfrost is a decoder-only language model of 777,148,032 parameters, of which 161,036,224 are active for any given token. It follows DeepSeek-V2: Multi-head Latent Attention over a compressed key-value cache, and DeepSeekMoE feed-forward layers where a router selects 3 of 32 experts per layer alongside one shared expert.

It was pretrained on approximately 6 billion tokens of FineWeb-Edu across two phases, then fine-tuned for chat on 2.7 billion tokens of instruction data. Training took about 12 H100-hours at a total cost of roughly $55.

This repository contains the complete pipeline: tokenizer training, the model definition, pretraining and supervised fine-tuning loops, the evaluation harness, the cloud orchestration with its budget guards, and a local chat server. The architecture is implemented from the published papers rather than adapted from released code.

Property Value
Parameters 777,148,032 total, 161,036,224 active per token
Pretraining ~6B tokens, validation loss 2.976
Fine-tuning 2.7B tokens over 1.7 epochs, validation loss 1.2484
Held-out perplexity 51.64
Training throughput 179,000 tokens/second, 1x H100
Inference 16.8 tokens/second, fp32, batch 1, RTX 3050

The chat interface, light theme

The chat interface, dark theme

Run

git clone https://github.com/whoashish115/moonfrost-ai
cd moonfrost
pip install -r requirements.txt
python training/server.py

The server selects the checkpoint with the lowest validation loss and opens a chat interface at http://localhost:8000. Weights are pulled from Hugging Face or read from a local checkpoints/ directory. Inference runs locally: no account, no API key, no telemetry. Conversation history is stored in a SQLite file under training/db/.

Interface

The chat interface is a single HTML file served by FastAPI, with replies streamed over server-sent events. No build step and no framework.

Area What is there
Conversations Streaming replies, stop mid-generation, edit and regenerate, per-message token count, tokens/second, elapsed time and the checkpoint that produced it
Organising Pinned chats in their own section, archive, full-text search, rename, workspaces, export to JSON, and a queue so several messages can be sent before the first reply lands
Code Syntax highlighting for Java, Python, JavaScript, TypeScript, C-family, SQL and shell, with copy buttons. Unfenced code is detected by shape, because the model rarely writes fences
Appearance 15 theme presets, independent light and dark palettes, three contrast levels, and every colour individually overridable
Prompts 148 prompts auditioned against this model and kept only if the answer completed and held together, shown as chips above the composer
About page An interactive chart placing this model against 28 others from GPT-2 to o1, on capability, scale or token efficiency, built from docs/eval.json so it cannot drift from what was measured
Input File attachments for text formats, dictation through the Web Speech API, a composer that expands for long messages, and full keyboard control
Checkpoints Switch between base and instruct models at runtime, with a warning when a base checkpoint is loaded, since a base model answers every question with fluent nonsense

Benchmarks

Five-shot, 250 examples per benchmark, scored by the log-probability of each answer continuation. Reference models were run through the same harness on the same examples rather than quoted from their model cards, since prompt wording and length normalisation shift these scores by several points: Qwen2.5-0.5B publishes 47.5 on MMLU and scores 34.4 here. Both Moonfrost checkpoints are listed, because the instruction tune does not leave these numbers where pretraining left them.

Benchmark Chance Moonfrost Base Moonfrost Instruct v1 Moonfrost Instruct v2 SmolLM2-135M SmolLM2-360M Qwen2.5-0.5B
ARC-Easy 25.0 54.8 52.4 44.4 62.8 68.4 64.4
ARC-Challenge 25.0 25.2 24.4 24.4 27.6 37.2 34.8
HellaSwag 25.0 36.0 38.4 37.2 40.0 43.6 42.4
WinoGrande 50.0 51.2 53.2 54.0 54.0 56.0 56.8
BoolQ 50.0 62.4 61.2 58.8 62.0 63.6 65.2
MMLU 25.0 28.8 30.0 30.8 32.4 36.8 34.4

Benchmark comparison

The model ranks last or near-last on most tasks. Six billion tokens for 777 million parameters is roughly eight tokens per parameter, against a compute-optimal ratio near twenty and against reference models trained on two to eighteen trillion tokens at half the size. The training budget produced conversational behaviour rather than knowledge.

Almost every difference between the three Moonfrost columns is inside the noise; the one that is not is ARC-Easy, falling 54.8, 52.4, 44.4 across the base and the two tunes, which is what chat formatting costs on a multiple-choice stem. The noise floor, the sensitivity of these scores to the five-shot prompt, and the per-benchmark zero-shot figures are in docs/benchmarks.md.

Training

Stage Data Steps Tokens Result
Pretrain phase 1 FineWeb-Edu shards 0-2 9,542 ~2.8B val loss 3.1486 at step 9,500
Pretrain phase 2 FineWeb-Edu shards 3-7 ~12,200 ~3.1B val loss 2.976 at step 11,000
Chat fine-tune smol-smoltalk, smoltalk, persona set 9,201 2.7B val loss 1.2484 at step 7,800

The phases read disjoint shards, so no document was seen twice. They also ran on separate machines, which works because the learning rate is parameterised by elapsed fraction of training rather than by step: phase 2 resumed at fraction 0.5227 and the cosine curve continued rather than restarting. Only the weights crossed the boundary, which is why phase 2 re-warms for 150 steps.

Loss curves

Those panels show what was logged, which is less than what was run. Phase 1 was attempted twice: the first try died at step 1,730 when its client connection dropped, and that failed attempt is what docs/logs/base1_train_log.jsonl contains. The successful run's log was on a Modal volume that got deleted with the account. training/recover_logs.py pulled back what Modal still retained of each stopped app, which is where phase 1's numbers above come from; those fragments are in docs/logs/recovered/. The middle of both pretraining runs is gone, and the charts say so on their face.

Loss, learning rate, gradient norm and throughput for all three runs are on Weights & Biases, replayed step by step from the JSON Lines logs in docs/logs/. There was no W&B client attached during training, so training/export_to_wandb.py reconstructs the runs afterwards; it also writes plain CSV for anything that would rather read that.

Layout

moonfrost/
├── training/
│   ├── model.py              MLA attention and the MoE layer, 908 lines
│   ├── config.py             every structural number, and the parameter counters
│   ├── train.py              pretraining loop
│   ├── sft_train.py          chat fine-tuning
│   ├── sft_data.py           conversations into packed rows, and those rows on disk
│   ├── persona_data.py       generates the identity set
│   ├── chat_format.py        the wire format and the streaming decoder
│   ├── evaluate_model.py     benchmarks, perplexity, generation speed
│   ├── make_charts.py        the charts, drawn from the measured results
│   ├── cloud_run.py          the cloud training pipeline, with cost guards
│   ├── deploy_demo.py        serves the model on Modal with scale-to-zero
│   ├── recover_logs.py       rebuilds lost logs from retained console output
│   ├── server.py             FastAPI chat server
│   ├── static/
│   │   ├── index.html        the markup, and nothing else
│   │   ├── css/              one stylesheet per region of the interface
│   │   ├── js/               one script per concern, loaded in declaration order
│   │   ├── cats/             the mark and the tiled background
│   │   └── icons/            favicons and the web manifest
│   └── tests/                102 checks over the model, data path and training plan
├── docs/
│   ├── architecture.md       what the model is
│   ├── pipeline.md           what every script does, in execution order
│   ├── benchmarks.md         how it was measured and what the numbers mean
│   ├── training.md           the runs, the hyperparameters, and what went wrong
│   ├── eval*.json            measured results, read directly by the About page
│   ├── charts/               benchmark, scale and loss charts
│   ├── logs/                 raw training logs, including recovered fragments
│   └── wandb/                the same logs as CSV
├── tokenizer/                the trained byte-level BPE vocabulary
├── checkpoints/              .pt files, not in git
├── requirements.txt
└── README.md

Reproduce

python training/tests/verify_all.py         # 52 checks on the model and data path
python training/tests/verify_cloud_run.py   # 17 checks on the training plan and budget
python training/evaluate_model.py           # benchmarks, perplexity, speed
python training/make_charts.py              # redraw the charts from those results

The cloud pipeline is one command per stage and refuses to import if any account's worst case exceeds its cap:

modal run training/cloud_run.py --stage account1 --spawn

Seeds are fixed throughout: 1337 for data shuffles, 5 and 11 for sampling probes.

Limits

The model fabricates facts confidently, particularly on topics sparse in educational web text. It performs no arithmetic or multi-step reasoning, has no tool use, no retrieval and no memory across conversations. English only, with a 1,024-token context. Open-ended procedural questions tend to run to the token limit without concluding, while short definitional questions terminate cleanly.

There is no safety tuning, no RLHF and no content filtering at either the data or the output stage. This is a demonstration of a complete training pipeline at small scale, not a deployable assistant.

References

Written from scratch against these papers rather than adapted from released code.

Paper What it contributes
DeepSeek-V2 Multi-head Latent Attention, DeepSeekMoE
DeepSeek-V3 routing and load-balancing refinements
Attention Is All You Need the transformer
RoFormer rotary position embeddings
GLU Variants SwiGLU
RMSNorm normalisation without mean subtraction
GShard capacity-based expert dispatch
Switch Transformer the load-balancing auxiliary loss
Chinchilla the twenty-tokens-per-parameter ratio
FlashAttention the fused kernel used through SDPA
BPE for NMT byte-pair encoding

Data and tooling: FineWeb-Edu for pretraining, smol-smoltalk and smoltalk for chat, SmolLM2 and Qwen2.5 as benchmark references, and PyTorch, Hugging Face tokenizers and transformers, FastAPI and Modal.

Citation

@misc{kumar2026moonfrost,
  title  = {Moonfrost: A 777M Mixture-of-Experts Language Model
            Trained From Scratch},
  author = {Kumar, Ashish},
  year   = {2026},
  url    = {https://github.com/whoashish115/moonfrost-ai}
}

License

Apache 2.0, Copyright 2026 Ashish Kumar. The full text is in LICENSE.

About

A 777M-parameter Mixture-of-Experts language model trained from scratch for $55. Multi-head Latent Attention, DeepSeekMoE, own tokenizer.

Topics

Resources

Stars

11 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages