A complete, research-grade implementation of the modern LLM pipeline — from raw web data through pre-training, instruction tuning, reinforcement learning from human feedback, and production API deployment. Built for engineers who want to understand and build every layer themselves.
To better understand this project, first visit this link for a visualization of the project and what I built: Link
Then, if you want to learn each topic in a tutorial format, read this file thoroughly: Link
Quick Start • Architecture • Training • API • Evaluation • Contributing
- Overview
- What Makes This Different
- System Requirements
- Quick Start
- Project Structure
- Architecture Deep Dive
- Training Pipeline
- Evaluation & Benchmarks
- API Reference
- UI Guide
- Configuration
- Docker Deployment
- Performance & Scaling
- Troubleshooting
- Contributing
- References
LLM Playground is a production-grade framework implementing the complete lifecycle of a Large Language Model from first principles. Every component — from the BPE tokenizer to the PPO trainer — is written to be readable, extensible, and correct.
Raw Web Data
│
▼
┌─────────────────────────────────────────────────────────┐
│ DATA PIPELINE │
│ Web Crawler → CommonCrawl → RefinedWeb / Dolma / │
│ FineWeb cleaning → BPE Tokenization → Binary Shards │
└────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ MODEL ARCHITECTURES │
│ GPT │ LLaMA-style │ DeepSeek (MLA+MoE) │ Qwen │ Gemma │
└────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ TRAINING PIPELINE │
│ Pre-training → SFT (LoRA) → Reward Model → PPO/RLHF │
└────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ EVALUATION SUITE │
│ Perplexity │ BLEU │ ROUGE │ MMLU │ GSM8K │ ELO Arena │
└────────────────────────┬────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ PRODUCTION DEPLOYMENT │
│ FastAPI (OpenAI-compatible) + Gradio UI + Docker │
└─────────────────────────────────────────────────────────┘
Most LLM repositories either wrap an existing framework (HuggingFace, LitGPT) or implement only one piece of the puzzle. This project is different:
| Feature | This Project | HuggingFace | nanoGPT | LitGPT |
|---|---|---|---|---|
| Full data pipeline | ✅ | ❌ | ❌ | ❌ |
| Custom BPE tokenizer | ✅ | ❌ | ❌ | ❌ |
| Multiple architectures | ✅ | ✅ | ❌ | ✅ |
| MoE implementation | ✅ | partial | ❌ | partial |
| MLA (DeepSeek) | ✅ | ❌ | ❌ | ❌ |
| Custom LoRA (no PEFT dep) | ✅ | ❌ | ❌ | ❌ |
| Full PPO RLHF | ✅ | partial | ❌ | ❌ |
| Verifiable rewards | ✅ | ❌ | ❌ | ❌ |
| ELO leaderboard | ✅ | ❌ | ❌ | ❌ |
| OpenAI-compatible API | ✅ | ❌ | ❌ | ❌ |
| Production UI | ✅ | ❌ | ❌ | ❌ |
| Fully documented | ✅ | partial | ✅ | partial |
CPU: 8-core modern processor
RAM: 16 GB
Disk: 50 GB
Python: 3.10+
GPU: 1× NVIDIA A100 40GB or 2× RTX 3090 24GB
RAM: 64 GB
Disk: 500 GB NVMe SSD
CUDA: 12.1+
Python: 3.11
GPU: 8× NVIDIA A100 80GB (640GB total VRAM)
RAM: 512 GB
Disk: 4 TB NVMe (RAID)
Network: InfiniBand 400Gb/s
CUDA: 12.1+
| Model Size | FP32 | BF16 | INT8 | Minimum GPU |
|---|---|---|---|---|
| 125M | 0.5 GB | 0.25 GB | 0.13 GB | RTX 3060 |
| 1.3B | 5.2 GB | 2.6 GB | 1.3 GB | RTX 3080 |
| 7B | 28 GB | 14 GB | 7 GB | A100 40GB |
| 13B | 52 GB | 26 GB | 13 GB | 2× A100 40GB |
| 70B | 280 GB | 140 GB | 70 GB | 8× A100 80GB |
# Clone
git clone https://github.com/AdilShamim8/LLM-Playground-101.git
cd llm-playground
# Configure
cp .env.example .env
# Edit .env: set CUDA_VISIBLE_DEVICES, WANDB_API_KEY, etc.
# Launch all services
docker-compose up --buildServices will be available at:
- API: http://localhost:8000
- API Docs: http://localhost:8000/docs
- UI: http://localhost:7860
# 1. Clone and enter
git clone https://github.com/AdilShamim8/LLM-Playground-101.git
cd llm-playground
# 2. Create isolated environment
python3.11 -m venv .venv
source .venv/bin/activate # Linux/Mac
# .venv\Scripts\activate # Windows
# 3. Install PyTorch with CUDA 12.1
pip install torch==2.1.0 torchvision \
--index-url https://download.pytorch.org/whl/cu121
# 4. Install project (development mode)
pip install -e ".[dev]"
# 5. (Optional) Flash Attention — significant speedup
pip install flash-attn --no-build-isolation
# 6. (Optional) DeepSpeed — multi-GPU training
pip install deepspeed
# 7. Configure
cp .env.example .env# Verify installation first
python run_verification.py
# Expected output:
# ============================================================
# LLM PLAYGROUND — FULL RUNTIME VERIFICATION
# ============================================================
# ✅ RMSNorm forward
# ✅ RotaryEmbedding forward
# ... (40 tests)
# RESULTS: 40 passed / 0 failed / 40 total
# ✅ ALL TESTS PASSED — PROJECT IS COMPLETE
# Then run the full pipeline
chmod +x scripts/run_pipeline.sh
./scripts/run_pipeline.sh# minimal_example.py
import torch
from models import GPTModel, ModelConfig
from generation import TextGenerator
from generation.strategies import GenerationConfig
# 1. Define a small model (runs on CPU)
config = ModelConfig(
vocab_size=50257,
hidden_size=256,
num_layers=4,
num_heads=4,
intermediate_size=1024,
max_position_embeddings=512,
use_rope=True,
use_rmsnorm=True,
use_swiglu=True,
)
# 2. Build model
model = GPTModel(config)
print(f"Parameters: {model.num_parameters()/1e6:.1f}M")
# 3. Forward pass
input_ids = torch.randint(0, 50257, (1, 16))
output = model(input_ids=input_ids)
print(f"Logits shape: {output.logits.shape}")
# → torch.Size([1, 16, 50257])
# 4. Compute loss
labels = torch.randint(0, 50257, (1, 16))
output_with_loss = model(input_ids=input_ids, labels=labels)
print(f"Loss: {output_with_loss.loss.item():.4f}")
# → Loss: 10.8xxx (random init ≈ log(50257))
# 5. Train one step
optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4)
output_with_loss.loss.backward()
optimizer.step()
optimizer.zero_grad()
print("✅ Forward + backward pass successful")python minimal_example.pyllm_playground/
│
├── 📄 README.md ← You are here
├── 📄 requirements.txt ← All dependencies
├── 📄 setup.py ← Package installation
├── 📄 docker-compose.yml ← Multi-service orchestration
├── 📄 Dockerfile ← CUDA-enabled container
├── 📄 .env.example ← Environment template
├── 📄 run_verification.py ← 40-test runtime verification
│
├── 📁 configs/
│ ├── base_config.yaml ← Model & data defaults
│ ├── training_config.yaml ← Pre-train / SFT / PPO hyperparams
│ └── evaluation_config.yaml ← Benchmark settings
│
├── 📁 data/ ← Complete data pipeline
│ ├── crawling/
│ │ ├── web_crawler.py ← Async polite web crawler
│ │ ├── common_crawl.py ← WARC/WET downloader
│ │ └── utils.py ← URL normalization, dedup
│ ├── cleaning/
│ │ ├── quality_filters.py ← 10+ heuristic filters
│ │ ├── refined_web.py ← MinHash LSH deduplication
│ │ ├── dolma_cleaner.py ← Gopher + PII + toxicity
│ │ └── fineweb_cleaner.py ← Educational quality scoring
│ └── tokenization/
│ ├── bpe_tokenizer.py ← Full byte-level BPE
│ └── utils.py ← Corpus → binary shards
│
├── 📁 models/ ← All model architectures
│ ├── layers.py ← RMSNorm, RoPE, SwiGLU, etc.
│ ├── attention.py ← MHA, GQA, KV-Cache
│ ├── transformer.py ← Pre-LN Transformer block
│ ├── gpt.py ← GPT causal language model
│ └── architectures/
│ ├── deepseek.py ← MLA + MoE (DeepSeek-V2/V3)
│ ├── gemma.py ← Logit softcap (Gemma 1/2)
│ └── qwen.py ← Dynamic NTK RoPE (Qwen 1/2)
│
├── 📁 training/ ← Training pipelines
│ ├── pretrain.py ← Pre-training loop
│ ├── sft.py ← SFT + custom LoRA
│ ├── utils.py ← Collators, samplers, checkpointing
│ └── rlhf/
│ ├── reward_model.py ← Bradley-Terry preference training
│ ├── ppo_trainer.py ← Full PPO with GAE + KL control
│ └── verifiable_tasks.py ← Math / Code / Format rewards
│
├── 📁 generation/ ← Text generation
│ ├── strategies.py ← Greedy, beam, top-k, top-p
│ └── sampler.py ← Unified TextGenerator interface
│
├── 📁 evaluation/ ← Evaluation suite
│ ├── metrics.py ← Perplexity, BLEU, ROUGE
│ ├── benchmarks.py ← MMLU, GSM8K, HellaSwag
│ └── human_eval.py ← ELO rating + leaderboard
│
├── 📁 api/ ← FastAPI backend
│ ├── main.py ← App factory + middleware
│ ├── model_registry.py ← Shared model registry
│ ├── routes/
│ │ ├── chat.py ← OpenAI-compatible chat
│ │ ├── models.py ← Load/unload/info endpoints
│ │ └── evaluation.py ← Eval jobs + leaderboard
│ └── middleware/
│ └── auth.py ← JWT + API key + rate limiting
│
├── 📁 ui/ ← Gradio frontend
│ ├── app.py ← Main application
│ └── components/
│ ├── chat_panel.py ← Chat interface
│ ├── param_panel.py ← Parameter controls
│ ├── eval_panel.py ← Evaluation panel
│ └── model_panel.py ← Model management
│
└── 📁 tests/ ← Test suite
├── test_models.py ← Architecture tests
├── test_generation.py ← Generation strategy tests
├── test_data.py ← Data pipeline tests
└── test_training.py ← Training component tests
The data pipeline transforms raw internet text into tokenized training data through a multi-stage cleaning and filtering process, inspired by the best practices from RefinedWeb, Dolma, and FineWeb.
Web Crawler (data/crawling/web_crawler.py)
Our async BFS crawler implements production-grade politeness:
from data import WebCrawler, CrawlConfig
import asyncio
config = CrawlConfig(
max_depth=3, # BFS depth limit
max_pages=50_000, # Total page cap
delay=1.0, # Seconds between requests per domain
max_concurrent=10, # Simultaneous connections
respect_robots=True, # Always honour robots.txt
allowed_domains=[ # Optional domain whitelist
"wikipedia.org",
"arxiv.org",
],
)
crawler = WebCrawler(config)
pages = asyncio.run(crawler.crawl([
"https://en.wikipedia.org/wiki/Machine_learning",
"https://arxiv.org/abs/2303.08774",
]))
crawler.save_results("./data/raw/crawl.jsonl")
print(f"Collected: {len(pages)} pages")Key design decisions:
- Async I/O:
aiohttp+ semaphore for controlled concurrency - robots.txt cache: Fetched once per domain, not per URL
- Content extraction:
trafilaturafirst, BeautifulSoup fallback - Deduplication: SHA-256 content hashing during crawl
Common Crawl (data/crawling/common_crawl.py)
from data import CommonCrawlProcessor, CommonCrawlConfig
config = CommonCrawlConfig(
crawl_id="CC-MAIN-2023-50", # Specific crawl snapshot
num_segments=10, # WET file segments to process
max_pages_per_segment=100_000, # Pages per segment
languages=["en"], # Language filter
output_dir="./data/raw/cc",
)
processor = CommonCrawlProcessor(config)
total = processor.process_and_save("./data/raw/cc/output.jsonl")
print(f"Extracted: {total:,} documents")Three complementary cleaning pipelines, each targeting different quality dimensions:
RefinedWeb (Falcon LLM approach)
Input documents
│
├─→ URL-based filtering (block known spam/adult domains)
├─→ Text quality heuristics (10+ filters)
│ - Length: 100 ≤ chars ≤ 100,000
│ - Word count: 20 ≤ words ≤ 100,000
│ - Average word length: 3.0 ≤ avg ≤ 10.0
│ - Symbol ratio: < 10%
│ - Digit ratio: < 20%
│ - Uppercase ratio: < 30%
│ - Line repetition ratio: < 30%
│ - Unique word ratio: > 20%
│ - HTML remnant ratio: < 5%
│ - Bullet line ratio: < 90%
├─→ Content safety (PII redaction, harmful content)
├─→ Exact deduplication (SHA-256)
└─→ Near-duplicate removal (MinHash LSH, threshold=0.85)
└─→ 128 hash permutations, band size=4
Dolma (Allen AI approach)
Input documents
│
├─→ Text normalization (Unicode NFC, whitespace)
├─→ Gopher quality filters (8 checks)
│ - Word count thresholds
│ - Mean word length (3.0–10.0)
│ - Symbol/word ratio < 0.1
│ - Bullet/ellipsis line ratios
│ - Duplicate line character fraction < 20%
│ - Duplicate paragraph character fraction < 20%
│ - Top n-gram repetition fraction < 20%
├─→ Language identification (fasttext, confidence ≥ 0.65)
├─→ Toxicity filtering (pattern matching + domain check)
├─→ PII redaction (SSN, CC, email, phone, IP, passport)
└─→ Exact deduplication
FineWeb (HuggingFace approach)
Input documents
│
├─→ Quality filtering (same as RefinedWeb)
└─→ Educational quality scoring
- High-value keywords: "therefore", "hypothesis",
"evidence", "research", "analysis" (+2.0 weight)
- Medium keywords: "because", "explain" (+1.0)
- Low-value: "click", "subscribe", "sale" (-3.0)
- Structure bonus: paragraphs + sentence count
- Sentence length bonus: 10–30 words ideal
└─→ Keep if educational_score ≥ 0.2
from data import RefinedWebCleaner, RefinedWebConfig
from data import DolmaTextCleaner, DolmaConfig
from data import FineWebCleaner, FineWebConfig
# RefinedWeb
rw = RefinedWebCleaner(RefinedWebConfig())
stats = rw.process_file("./raw.jsonl", "./refined.jsonl")
print(f"Passed: {stats['passed']}/{stats['total']}")
# Dolma
dolma = DolmaTextCleaner(DolmaConfig())
stats = dolma.process_file("./raw.jsonl", "./dolma.jsonl")
# FineWeb (educational filter)
fw = FineWebCleaner(FineWebConfig(min_educational_score=0.3))
stats = fw.process_file("./raw.jsonl", "./fineweb.jsonl")Byte-Level BPE (data/tokenization/bpe_tokenizer.py)
We implement GPT-2 style Byte-Pair Encoding from scratch, with no
dependency on the tokenizers library:
How it works:
Step 1: Pre-tokenization (regex split)
"Hello, world!" → ["Hello", ",", " world", "!"]
Step 2: Byte encoding (256 base tokens)
"Hello" → ('H', 'e', 'l', 'l', 'o')
Each byte mapped to printable unicode character
Step 3: BPE merge loop (vocab_size - 256 - special_tokens) times:
Iteration 1: Find most frequent pair ("l", "l") → merge to "ll"
Iteration 2: Find most frequent pair ("He", "llo") → merge to "Hello"
... continues until vocab_size reached
Step 4: Encode by applying learned merges greedily
from data.tokenization.bpe_tokenizer import BPEConfig, ByteLevelBPETokenizer
config = BPEConfig(
vocab_size=50257,
min_frequency=2,
special_tokens={
"bos_token": "<|startoftext|>",
"eos_token": "<|endoftext|>",
"unk_token": "<|unk|>",
"pad_token": "<|pad|>",
}
)
# Train on corpus
tokenizer = ByteLevelBPETokenizer(config)
with open("./data/processed/clean.jsonl") as f:
texts = [json.loads(l)["text"] for l in f]
tokenizer.train(texts)
tokenizer.save("./tokenizers/bpe_50k")
# Use
ids = tokenizer.encode("Hello, world!")
# → [1, 31373, 11, 995, 0, 2] (with BOS/EOS)
text = tokenizer.decode(ids, skip_special_tokens=True)
# → "Hello, world!"
# Batch encoding with padding
batch = tokenizer.encode_batch(
["Short text", "A longer sentence here"],
padding=True,
max_length=128,
truncation=True,
)
# → {"input_ids": [[...], [...]], "attention_mask": [[...], [...]]}Corpus Preparation:
from data.tokenization.utils import (
tokenize_corpus_to_bin,
split_dataset,
estimate_token_count,
)
# Estimate before committing to full tokenization
estimate = estimate_token_count(
"./data/processed/",
tokenizer,
sample_size=1000,
)
print(f"Estimated: {estimate['estimated_total_tokens_billions']:.2f}B tokens")
# Tokenize → binary shards
stats = tokenize_corpus_to_bin(
input_path="./data/processed/", # Directory of JSONL files
output_dir="./data/tokenized/",
tokenizer=tokenizer,
max_tokens_per_file=100_000_000, # 100M tokens per shard
add_eos=True, # Separate documents with EOS
)
print(f"Shards: {stats['num_shards']}, Tokens: {stats['total_tokens']:,}")
# Split train/val/test
splits = split_dataset(
data_dir="./data/tokenized/",
output_dir="./data/split/",
val_ratio=0.005, # 0.5% validation
test_ratio=0.001, # 0.1% test
)
print(f"Train: {splits.train_tokens:,} tokens")All architectures share the same ModelConfig base and forward interface
(input_ids → GPTOutput), making them interchangeable in the training
pipeline.
Input IDs [B, S]
│
▼
Token Embedding [B, S, H] ← nn.Embedding(vocab_size, hidden_size)
│
├─ (if not use_rope): + Position Embedding [B, S, H]
│
▼
┌──────────────────────────────────────────┐
│ TransformerBlock × N │
│ │
│ x → RMSNorm → Attention → x + residual │
│ x → RMSNorm → FFN → x + residual │
│ │
│ Attention variants: │
│ MHA: Q,K,V all have num_heads heads │
│ GQA: K,V have num_kv_heads < heads │
│ │
│ FFN variants: │
│ GPT2MLP: fc1 → GeLU → fc2 │
│ SwiGLU: gate(SiLU) ⊙ up → down │
└──────────────────────────────────────────┘
│
▼
Final RMSNorm [B, S, H]
│
▼
LM Head [B, S, V] ← Linear (weights tied to embedding)
│
▼
Logits / Loss
from models import GPTModel, ModelConfig
# GPT-2 small equivalent
config = ModelConfig(
vocab_size=50257,
hidden_size=768,
num_layers=12,
num_heads=12,
num_kv_heads=12, # = num_heads → standard MHA
intermediate_size=3072,
max_position_embeddings=2048,
use_rope=False, # GPT-2 uses absolute positions
use_rmsnorm=False, # GPT-2 uses LayerNorm
use_swiglu=False, # GPT-2 uses GeLU
use_gqa=False,
tie_word_embeddings=True,
)
# LLaMA-style
config = ModelConfig(
vocab_size=32000,
hidden_size=4096,
num_layers=32,
num_heads=32,
num_kv_heads=8, # GQA: 4 queries share 1 KV head
intermediate_size=11008,
max_position_embeddings=4096,
use_rope=True, # Rotary embeddings
use_rmsnorm=True, # RMSNorm
use_swiglu=True, # SwiGLU
use_gqa=True, # Grouped Query Attention
rope_theta=10000.0,
)
model = GPTModel(config)
print(f"Parameters: {model.num_parameters()/1e9:.2f}B")DeepSeek-V2/V3 introduces two major innovations:
Multi-head Latent Attention (MLA) — Compresses the KV cache:
Standard MHA KV cache per token:
2 × num_heads × head_dim × num_layers bytes
MLA KV cache per token:
kv_lora_rank × num_layers bytes (≈ 5-13× smaller)
How:
c_KV = W_DKV × x # Compress to latent (kv_lora_rank)
K_nope, V = W_UK × c_KV # Up-project: non-RoPE K and V
K_rope = W_KR × x # Separate RoPE key
K = concat(K_rope, K_nope) # Final key
Mixture of Experts (MoE) — Sparse FFN:
Token x
│
├──→ Shared Experts (always active)
│ Expert_1(x) + Expert_2(x) + ...
│
└──→ Router: softmax(W_g × x)
│
└──→ Top-K experts selected
w_1×E_i(x) + w_2×E_j(x) + ...
(only K of N experts compute)
│
└──→ Sum: shared_out + routed_out
from models.architectures.deepseek import DeepSeekConfig, DeepSeekModel
config = DeepSeekConfig(
vocab_size=102400,
hidden_size=5120,
num_layers=60,
num_heads=128,
num_kv_heads=128,
intermediate_size=12288,
# MLA
use_mla=True,
kv_lora_rank=512,
q_lora_rank=1536,
qk_rope_head_dim=64,
qk_nope_head_dim=128,
v_head_dim=128,
# MoE
use_moe=True,
num_experts=160,
num_experts_per_token=6,
num_shared_experts=2,
moe_intermediate_size=1536,
first_k_dense_layers=1,
# Load balancing
expert_load_balance_coef=0.01,
router_z_loss_coef=0.001,
)
model = DeepSeekModel(config)from models.architectures.gemma import GemmaConfig, GemmaModel
config = GemmaConfig(
vocab_size=256000,
hidden_size=3072,
num_layers=28,
num_heads=16,
num_kv_heads=16,
intermediate_size=24576,
# Gemma-specific
logit_soft_cap=30.0, # Final logit tanh cap
attn_logit_softcap=50.0, # Attention logit tanh cap
use_logit_softcap=True,
hidden_act="gelu", # "gelu" or "relu2"
# Embedding scaling: multiply by sqrt(hidden_size)
# GemmaRMSNorm: uses (1 + weight) formulation
)
model = GemmaModel(config)from models.architectures.qwen import QwenConfig, QwenModel
config = QwenConfig(
vocab_size=151936,
hidden_size=4096,
num_layers=32,
num_heads=32,
num_kv_heads=32,
intermediate_size=22016,
kv_channels=128,
# Qwen-specific
seq_length=8192, # Training context length
use_dynamic_ntk=True, # Auto-scale RoPE beyond training len
use_logn_attn=True, # Log-N attention scaling
# Dynamic NTK: if seq_len > seq_length:
# alpha = (seq_len/seq_length)^(dim/(dim-2))
# base = original_base * alpha
)
model = QwenModel(config)All strategies support batched generation, KV-caching, and streaming:
Greedy: Always argmax. Fast, deterministic, repetitive.
x_t = argmax P(x | x_{<t})
Top-k: Sample from top-k tokens only.
Filter: keep top-k by probability
Sample: x_t ~ Uniform(top-k)
Top-p: Nucleus sampling. Dynamic vocabulary.
Sort by prob descending, keep CDF ≤ p
x_t ~ Categorical(filtered)
Temperature: Scale logits before softmax.
T < 1: sharper (more confident)
T > 1: flatter (more random)
logits_scaled = logits / T
Beam Search: Maintain B hypotheses in parallel.
Score = log P(y₁...yₜ) / length_penalty
Best final hypothesis selected.
from generation import TextGenerator
from generation.strategies import GenerationConfig
gen_config = GenerationConfig(
max_new_tokens=512,
temperature=0.7,
top_k=50,
top_p=0.9,
repetition_penalty=1.1,
no_repeat_ngram_size=3,
)
generator = TextGenerator(model, tokenizer, gen_config)
# Standard generation
text = generator.generate(
"The theory of neural scaling laws suggests",
strategy="sampling", # "greedy" | "sampling" | "beam_search"
)
print(text)
# Streaming (token by token)
for token in generator.generate_stream("Explain transformers:"):
print(token, end="", flush=True)
# Batch generation
results = generator.batch_generate(
prompts=["Question 1:", "Question 2:", "Question 3:"],
strategy="greedy",
)Pre-training learns the base language model from billions of tokens using next-token prediction (causal language modeling).
Key design choices:
- Cosine LR decay with linear warmup (GPT-3 style)
- AdamW with decoupled weight decay
- Weight decay only on 2D+ parameters (not bias/norm)
- Gradient clipping at 1.0
- Mixed precision BF16 (preferred over FP16 for stability)
- Gradient checkpointing to reduce VRAM usage
from models import GPTModel, ModelConfig
from training import PretrainTrainer, PretrainConfig, PretrainDataset
# Model
model = GPTModel(ModelConfig(
vocab_size=50257,
hidden_size=768,
num_layers=12,
num_heads=12,
intermediate_size=3072,
use_rope=True,
use_rmsnorm=True,
use_swiglu=True,
))
# Config
config = PretrainConfig(
data_path="./data/split",
max_seq_length=2048,
batch_size=32,
gradient_accumulation_steps=8, # Effective batch = 256
max_steps=100_000,
warmup_steps=2_000,
learning_rate=3e-4,
min_lr=3e-5, # 10× decay at end
weight_decay=0.1,
bf16=True,
output_dir="./checkpoints/pretrain",
save_steps=5_000,
eval_steps=1_000,
wandb_project="llm_playground",
)
# Data
train_data = PretrainDataset(config.data_path, config.max_seq_length, "train")
val_data = PretrainDataset(config.data_path, config.max_seq_length, "val")
# Train
trainer = PretrainTrainer(model, config)
trainer.train(train_data, val_data)Learning Rate Schedule:
LR
│ /‾‾‾‾‾‾‾‾‾‾──────────────────────────────────\
│ / \
│/ ──── min_lr
└────────────────────────────────────────────────────► steps
↑warmup↑ ↑max_steps↑
Expected Training Metrics (125M model on 10B tokens):
| Step | Loss | Perplexity | LR |
|---|---|---|---|
| 1K | 6.8 | 897 | 1.5e-4 |
| 5K | 4.2 | 66.7 | 3.0e-4 |
| 20K | 3.1 | 22.2 | 2.8e-4 |
| 50K | 2.7 | 14.9 | 1.8e-4 |
| 100K | 2.4 | 11.0 | 3.0e-5 |
SFT teaches the model to follow instructions by training only on assistant responses (loss masking on user/system tokens).
Loss Masking — Critical Detail:
Conversation:
[SYSTEM] You are helpful. → labels = -100 (masked)
[USER] What is 2+2? → labels = -100 (masked)
[ASST] It equals 4. → labels = real tokens ✓
[USER] Why? → labels = -100 (masked)
[ASST] Because 2+2=4 by def. → labels = real tokens ✓
Custom LoRA (no HuggingFace dependency):
# LoRA: W' = W + (B @ A) × (α/r)
# A: (r, in_features) - kaiming init
# B: (out_features, r) - zeros init → starts identical to base
# Trainable params: r×(in+out) vs in×out baselinefrom models import GPTModel
from training import SFTTrainer, SFTConfig, SFTDataset
model = GPTModel.from_pretrained("./checkpoints/pretrain/best")
config = SFTConfig(
data_path="./data/sft",
max_length=2048,
batch_size=8,
gradient_accumulation_steps=4,
num_epochs=3,
learning_rate=2e-5,
use_lora=True,
lora_r=16, # Rank (higher = more capacity)
lora_alpha=32, # Scaling = alpha/r = 2.0
lora_dropout=0.05,
lora_target_modules=[
"q_proj", "k_proj", "v_proj", "out_proj",
"gate_proj", "up_proj", "down_proj",
],
bf16=True,
output_dir="./checkpoints/sft",
)
# Data format options (all supported):
# 1. ChatML messages format
# 2. Alpaca instruction format
# 3. Simple prompt/response format
dataset = SFTDataset("./data/sft", tokenizer, config)
trainer = SFTTrainer(model, tokenizer, config)
trainer.train(dataset)Preparing SFT Data:
{"messages": [
{"role": "system", "content": "You are helpful."},
{"role": "user", "content": "Explain gradient descent."},
{"role": "assistant", "content": "Gradient descent is an optimization algorithm..."}
]}
{"instruction": "Write a sorting function.", "output": "def sort(arr):\n return sorted(arr)"}
{"prompt": "What is RLHF?", "response": "RLHF stands for..."}The reward model learns to predict human preference scores from pairwise comparison data (chosen vs. rejected responses).
Bradley-Terry Model:
P(response_a > response_b) = sigmoid(r_a - r_b)
Training loss: L = -E[log(sigmoid(r_chosen - r_rejected))]
Where r = scalar reward head output
from models import GPTModel
from training.rlhf import RewardModel, RewardModelConfig, RewardModelTrainer
from training.rlhf import PreferenceDataset
# Build reward model from SFT checkpoint
backbone = GPTModel.from_pretrained("./checkpoints/sft/final")
rm = RewardModel(backbone, RewardModelConfig(hidden_size=768))
# Preference data format:
# {"prompt": "...", "chosen": "...", "rejected": "..."}
dataset = PreferenceDataset("./data/preference", tokenizer)
trainer = RewardModelTrainer(rm, RewardModelConfig())
trainer.train(dataset)
# Evaluate
chosen_ids = tokenizer.encode("Good response", ...)
rejected_ids = tokenizer.encode("Bad response", ...)
reward_good = rm(chosen_ids)
reward_bad = rm(rejected_ids)
print(f"Good: {reward_good.item():.3f}, Bad: {reward_bad.item():.3f}")
# Good: 2.341, Bad: -1.872PPO (Proximal Policy Optimization) fine-tunes the SFT model to maximize reward while staying close to the reference policy via KL divergence penalty.
Full Algorithm:
For each training step:
1. ROLLOUT PHASE (no gradients)
├─ Sample batch of prompts from dataset
├─ Generate responses with current policy π_θ
├─ Score responses: r = RM(prompt, response)
├─ Compute KL penalty: KL = log π_θ(a|s) - log π_ref(a|s)
├─ Total reward: R_t = r × 1[last token] - β × KL_t
├─ Compute values: V(s_t) with critic network
└─ Compute GAE advantages: A_t = Σ (γλ)^k δ_{t+k}
δ_t = r_t + γV(s_{t+1}) - V(s_t)
2. UPDATE PHASE (with gradients, PPO_EPOCHS times)
├─ Actor loss (PPO clip):
│ ratio = π_θ(a|s) / π_old(a|s)
│ L_actor = -min(ratio × A, clip(ratio, 1±ε) × A)
├─ Critic loss (MSE):
│ L_critic = MSE(V(s), returns)
└─ Total: L = L_actor + vf_coef × L_critic - ent_coef × H
3. ADAPTIVE KL CONTROL
├─ If KL > 1.5 × target_kl: β *= 2
└─ If KL < 0.67 × target_kl: β /= 2
from models import GPTModel
from training.rlhf import PPOTrainer, PPOConfig
actor = GPTModel.from_pretrained("./checkpoints/sft/final")
ref = GPTModel.from_pretrained("./checkpoints/sft/final") # frozen
critic = GPTModel.from_pretrained("./checkpoints/sft/final")
rm = RewardModel(GPTModel.from_pretrained("./checkpoints/rm/final"), ...)
config = PPOConfig(
total_steps=1000,
batch_size=64,
ppo_epochs=4, # Inner optimization epochs
gamma=1.0, # Discount (1.0 for language tasks)
lam=0.95, # GAE lambda
clip_range=0.2, # PPO epsilon
vf_coef=0.1, # Value loss weight
kl_coef=0.1, # Initial KL penalty coefficient
target_kl=6.0, # Target KL for adaptive control
max_new_tokens=256,
normalize_rewards=True, # Normalize RM scores per batch
reward_clip=10.0, # Clip extreme rewards
)
trainer = PPOTrainer(actor, critic, ref, rm, tokenizer, config)
# Load prompts
with open("./data/ppo_prompts.txt") as f:
prompts = f.readlines()
trainer.train(prompts)For tasks with ground-truth answers, we bypass the reward model entirely and use verifiable signals directly.
from training.rlhf import MathVerifier, CodeVerifier, FormatVerifier
# Math: compare extracted numbers
math_v = MathVerifier()
result = math_v.compute_reward(
prompt="Solve: 15 × 23 = ?",
response="Let me calculate: 15 × 23 = 15 × 20 + 15 × 3 "
"= 300 + 45 = 345. The answer is 345.",
ground_truth="345"
)
print(f"Reward: {result.reward}, Passed: {result.passed}")
# Reward: 1.1 (bonus for showing work), Passed: True
# Code: execute and compare output
code_v = CodeVerifier(timeout=10)
result = code_v.compute_reward(
prompt="Write code to print 'Hello, World!'",
response="```python\nprint('Hello, World!')\n```",
ground_truth="Hello, World!"
)
print(f"Reward: {result.reward}") # 1.0 if output matches
# Format: check structural requirements
fmt_v = FormatVerifier()
result = fmt_v.compute_reward(
prompt="Return a JSON object",
response='{"name": "Alice", "age": 30}',
ground_truth="json"
)
print(f"Reward: {result.reward}") # 1.0 for valid JSONfrom evaluation import EvaluationSuite
suite = EvaluationSuite(model=model, tokenizer=tokenizer)
# Language modeling quality
ppl_result = suite.evaluate_lm(test_texts)
print(f"Perplexity: {ppl_result['perplexity']:.2f}")
# Generation quality
results = suite.evaluate_generation(
hypotheses=generated_texts,
references=reference_texts,
)
print(f"BLEU-4: {results['bleu']:.1f}")
print(f"ROUGE-1: {results['rouge1']:.1f}")
print(f"ROUGE-L: {results['rougeL']:.1f}")from evaluation import MMLUBenchmark, GSM8KBenchmark
from generation import TextGenerator
# MMLU — 57 academic subjects, 5-shot
mmlu = MMLUBenchmark(model, tokenizer)
results = mmlu.evaluate(
data_dir="./data/benchmarks/mmlu",
num_few_shot=5,
)
print(f"MMLU Average: {results['mmlu_avg']*100:.1f}%")
print(f"Best subject: {max(results['per_subject'], key=results['per_subject'].get)}")
# GSM8K — grade school math, chain-of-thought
generator = TextGenerator(model, tokenizer, GenerationConfig())
gsm8k = GSM8KBenchmark(model, tokenizer, generator)
result = gsm8k.evaluate(gsm8k_examples, num_few_shot=8)
print(f"GSM8K: {result.accuracy*100:.1f}%")Benchmark Reference Scores:
| Model | MMLU | GSM8K | Perplexity |
|---|---|---|---|
| GPT-2 (124M) | 32.4% | 1.2% | 29.4 |
| GPT-2 XL (1.5B) | 41.8% | 3.8% | 18.3 |
| LLaMA-7B | 63.4% | 11.0% | 7.8 |
| LLaMA-13B | 69.7% | 17.8% | 6.9 |
| GPT-4 (est.) | 86.4% | 92.0% | — |
We implement the Chatbot Arena (LMSYS) methodology:
from evaluation import HumanEvalCollector
collector = HumanEvalCollector(storage_path="./data/human_eval")
# Submit comparisons
collector.submit_comparison(
prompt="Explain quantum entanglement simply.",
model_a="llm-playground-v1",
response_a="Quantum entanglement is when two particles...",
model_b="llm-playground-v2",
response_b="Imagine two coins that always show opposite faces...",
winner="b", # Human judge preferred model B
judge_id="evaluator_01",
)
# Get leaderboard
board = collector.get_leaderboard()
for rank in board:
print(
f"#{rank['rank']} {rank['model']:30s} "
f"ELO: {rank['elo']:6.1f} "
f"Win Rate: {rank['win_rate']:5.1f}% "
f"95% CI: [{rank['ci_95_low']:.1f}, {rank['ci_95_high']:.1f}]"
)ELO System:
Expected score: E_a = 1 / (1 + 10^((R_b - R_a) / 400))
Update rule: R_a_new = R_a + K × (S_a - E_a)
K = 32 (sensitivity factor)
S_a = 1 (win), 0.5 (tie), 0 (loss)
Properties:
- New models start at ELO 1000
- ELO difference of 400 → 91% expected win rate
- Converges to stable rankings given enough comparisons
The API is fully compatible with the OpenAI Chat Completions specification.
# JWT Token
curl -H "Authorization: Bearer <token>" http://localhost:8000/api/v1/...
# API Key (simpler for server-to-server)
curl -H "X-API-Key: your-api-key" http://localhost:8000/api/v1/...# Non-streaming
curl -X POST http://localhost:8000/api/v1/chat/completions \
-H "Content-Type: application/json" \
-H "X-API-Key: dev-key-123" \
-d '{
"model": "llm-playground-7b",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is the capital of France?"}
],
"temperature": 0.7,
"max_tokens": 256,
"top_p": 0.9,
"top_k": 50,
"repetition_penalty": 1.1
}'
# Response
{
"id": "chatcmpl-a3f9b2c1",
"object": "chat.completion",
"created": 1703001234,
"model": "llm-playground-7b",
"choices": [{
"index": 0,
"message": {"role": "assistant", "content": "The capital of France is Paris."},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 28,
"completion_tokens": 9,
"total_tokens": 37,
"latency_seconds": 0.423
}
}# Streaming
curl -X POST http://localhost:8000/api/v1/chat/completions \
-H "Content-Type: application/json" \
-H "X-API-Key: dev-key-123" \
-d '{"model": "demo", "messages": [...], "stream": true}'
# Server-Sent Events response:
data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":"The"},...}]}
data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":" capital"},...}]}
...
data: [DONE]# Load a model (admin only)
curl -X POST http://localhost:8000/api/v1/models/load \
-H "X-API-Key: dev-key-123" \
-d '{
"model_name": "my-model",
"model_path": "./checkpoints/sft/final",
"tokenizer_path": "./tokenizers/bpe_50k",
"device": "auto",
"dtype": "bfloat16"
}'
# List loaded models
curl http://localhost:8000/api/v1/models/list \
-H "X-API-Key: dev-key-123"
# Model info
curl http://localhost:8000/api/v1/models/my-model/info \
-H "X-API-Key: dev-key-123"
# Unload
curl -X DELETE http://localhost:8000/api/v1/models/my-model \
-H "X-API-Key: dev-key-123"# Start evaluation job
curl -X POST http://localhost:8000/api/v1/evaluate \
-H "X-API-Key: dev-key-123" \
-d '{
"model_name": "my-model",
"task": "bleu",
"hypotheses": ["the cat sat on the mat"],
"references": ["the cat sat on the mat"]
}'
# → {"job_id": "eval-a3b9c2d1", "status": "pending"}
# Poll result
curl http://localhost:8000/api/v1/evaluate/eval-a3b9c2d1 \
-H "X-API-Key: dev-key-123"
# Get leaderboard
curl http://localhost:8000/api/v1/leaderboard \
-H "X-API-Key: dev-key-123"
# Submit human preference
curl -X POST "http://localhost:8000/api/v1/leaderboard/compare" \
-H "X-API-Key: dev-key-123" \
-d "model_a=model-v1&model_b=model-v2&winner=b&prompt=Hello&..."import requests
class LLMPlaygroundClient:
def __init__(self, base_url="http://localhost:8000", api_key="dev-key-123"):
self.base = base_url
self.headers = {"X-API-Key": api_key}
def chat(self, messages, model="demo", **kwargs):
resp = requests.post(
f"{self.base}/api/v1/chat/completions",
json={"model": model, "messages": messages, **kwargs},
headers=self.headers,
)
return resp.json()["choices"][0]["message"]["content"]
def stream(self, messages, model="demo"):
import json
resp = requests.post(
f"{self.base}/api/v1/chat/completions",
json={"model": model, "messages": messages, "stream": True},
headers=self.headers,
stream=True,
)
for line in resp.iter_lines():
if line and line.startswith(b"data: "):
data = line[6:]
if data.strip() == b"[DONE]":
break
chunk = json.loads(data)
token = chunk["choices"][0]["delta"].get("content", "")
if token:
yield token
# Usage
client = LLMPlaygroundClient()
# Standard call
answer = client.chat([{"role": "user", "content": "Hello!"}])
print(answer)
# Streaming
for token in client.stream([{"role": "user", "content": "Tell me a story"}]):
print(token, end="", flush=True)Launch the Gradio interface:
# Local
python ui/app.py
# Or via API server (recommended)
docker-compose up uiNavigate to http://localhost:7860
┌─────────────────────────────────────┬──────────────────┐
│ Conversation │ ⚙️ Parameters │
│ │ │
│ 👤 Hello! How are you? │ Model: [demo ▼] │
│ 🤖 I'm doing great! How can I... │ │
│ │ 🌡️ Temperature │
│ ┌───────────────────────────────┐ │ [──●────] 0.70 │
│ │ Your message... │ │ │
│ └───────────────────────────────┘ │ 🎯 Top-p │
│ [Send 🚀] [🗑️ Clear] │ [────●──] 0.90 │
│ │ │
│ System Prompt: │ 🔝 Top-k │
│ [You are a helpful assistant...] │ [──●────] 50 │
└─────────────────────────────────────┴──────────────────┘
Compare all three strategies side-by-side with the same prompt:
Prompt: [The future of artificial intelligence will...]
Greedy Sampling (top-p) Beam Search (B=4)
────────────── ──────────────── ─────────────────
The future of The future of The future of
AI will be AI could be very AI will likely be
determined by different from shaped by advances
the development what we imagine... in both compute...
Run benchmarks directly from the UI:
- Select task (BLEU, ROUGE, Perplexity, MMLU, GSM8K)
- Paste hypotheses and references
- View results as formatted JSON
- Refresh ELO leaderboard in real time
All settings controlled via YAML configs and .env:
# configs/base_config.yaml
model:
architecture: "gpt" # gpt | deepseek | gemma | qwen
hidden_size: 768
num_layers: 12
num_heads: 12
num_kv_heads: 12 # = num_heads → MHA; < → GQA
intermediate_size: 3072
max_position_embeddings: 2048
vocab_size: 50257
use_rope: true
use_rmsnorm: true
use_swiglu: true
use_gqa: false
dropout: 0.1
# configs/training_config.yaml
pretraining:
batch_size: 32
gradient_accumulation_steps: 8
learning_rate: 3.0e-4
min_lr: 3.0e-5
warmup_steps: 2000
max_steps: 100000
weight_decay: 0.1
bf16: true
gradient_checkpointing: true
sft:
learning_rate: 2.0e-5
num_epochs: 3
use_lora: true
lora_r: 16
lora_alpha: 32
rlhf:
ppo:
kl_coef: 0.1
target_kl: 6.0
clip_range: 0.2
ppo_epochs: 4# .env
CUDA_VISIBLE_DEVICES=0,1,2,3
WANDB_API_KEY=your-key
JWT_SECRET_KEY=your-long-random-secret
DEV_MODE=false
RATE_LIMIT_REQUESTS=100
RATE_LIMIT_WINDOW=3600# docker-compose.yml services:
# api — FastAPI on port 8000
# ui — Gradio on port 7860
# redis — Rate limiting / caching
# postgres — Experiment tracking
# Start all services
docker-compose up -d
# GPU-specific service
docker-compose up -d api
# Scale API workers
docker-compose up -d --scale api=3
# View logs
docker-compose logs -f api
# Health check
curl http://localhost:8000/health
# {"status":"healthy","uptime":42.1,"requests":157,"version":"1.0.0"}
# Stop everything
docker-compose down# 1. Flash Attention (2-4× faster attention)
pip install flash-attn --no-build-isolation
# Automatically used when available (checked in MultiHeadAttention)
# 2. Gradient checkpointing (reduce VRAM by ~40%)
model.enable_gradient_checkpointing()
# 3. Mixed precision
config = PretrainConfig(bf16=True) # BF16 preferred for stability
# 4. Gradient accumulation (simulate larger batch)
config = PretrainConfig(
batch_size=4,
gradient_accumulation_steps=32, # Effective batch = 128
)
# 5. Compile (PyTorch 2.0+)
model = torch.compile(model)# Install DeepSpeed
pip install deepspeed
# ZeRO Stage 2: optimizer state sharding
deepspeed --num_gpus=4 training/pretrain.py \
--deepspeed configs/deepspeed_zero2.json
# ZeRO Stage 3: full parameter sharding (largest models)
deepspeed --num_gpus=8 training/pretrain.py \
--deepspeed configs/deepspeed_zero3.json| Setup | Model | Tokens/sec | GPU Util |
|---|---|---|---|
| 1× A100 40GB, BF16 | 125M | ~450K | 78% |
| 1× A100 40GB, Flash | 125M | ~820K | 91% |
| 4× A100 40GB, DDP | 125M | ~3.1M | 88% |
| 8× A100 80GB, DS-Z3 | 7B | ~112K | 94% |
# CUDA version mismatch
nvcc --version # Check CUDA version
python -c "import torch; print(torch.version.cuda)"
# Flash Attention build fails
pip install ninja packaging
pip install flash-attn --no-build-isolation
# Out of memory during import
export PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:512# Loss is NaN
# → Reduce learning rate (3e-4 → 1e-4)
# → Enable gradient clipping (already in our trainer: max_grad_norm=1.0)
# → Check for bad data (empty strings, corrupted tokens)
# Loss not decreasing
# → Check data is shuffled
# → Verify labels are not all -100 (SFT masking bug)
# → Check learning rate schedule (print scheduler.get_lr())
# VRAM OOM during training
config = PretrainConfig(
batch_size=4, # Reduce batch
gradient_accumulation_steps=64, # Compensate
bf16=True, # Half precision
)
model.enable_gradient_checkpointing() # Trade compute for memory
# SFT: model not following instructions
# → Check loss masking is working (some labels should be real, not -100)
# → Increase training data diversity
# → Reduce learning rate (avoid catastrophic forgetting)# Port already in use
lsof -i :8000 && kill -9 <PID>
# Model not found error
# → Load model first: POST /api/v1/models/load
# → Check model name matches exactly
# Streaming not working
# → Ensure client reads SSE properly (iter_lines, not iter_content)
# → Check firewall/proxy isn't buffering responses# Full test suite
pytest tests/ -v --tb=short
# Specific module
pytest tests/test_models.py -v
# With coverage
pytest tests/ --cov=. --cov-report=html
# Runtime verification (most comprehensive)
python run_verification.pyWe welcome contributions at every level.
git clone https://github.com/your-org/llm-playground.git
cd llm-playground
pip install -e ".[dev]"
# Code style
black .
isort .
# Type checking
mypy . --ignore-missing-imports
# Tests must pass
python run_verification.py
pytest tests/ -v| Area | Difficulty | Impact |
|---|---|---|
| Add BERTScore metric | Easy | Medium |
| Add HellaSwag benchmark | Easy | Medium |
| Implement DPO trainer | Medium | High |
| Add speculative decoding | Medium | High |
| Implement MoE with Expert Parallelism | Hard | Very High |
| Add multi-modal (vision) support | Hard | Very High |
- Tests pass:
python run_verification.py - New features have unit tests
- Docstrings updated
- No circular imports introduced
- Config changes documented in YAML files
- Memory-efficient (no unnecessary
.cuda()calls)
[1] Radford et al. (2019) - Language Models are Unsupervised Multitask Learners
→ GPT-2 architecture, BPE tokenization
[2] Brown et al. (2020) - Language Models are Few-Shot Learners
→ GPT-3, scaling laws, few-shot prompting
[3] Touvron et al. (2023) - LLaMA: Open and Efficient Foundation Models
→ RoPE, RMSNorm, SwiGLU, GQA
[4] Ouyang et al. (2022) - Training language models to follow instructions
→ InstructGPT, PPO for RLHF, reward modeling
[5] Penedo et al. (2023) - The RefinedWeb Dataset for Falcon LLM
→ Web data cleaning, MinHash deduplication
[6] Soldaini et al. (2024) - Dolma: an Open Corpus for LM Pretraining
→ Gopher quality filters, PII redaction, toxicity filtering
[7] Lozhkov et al. (2024) - FineWeb-Edu: The finest collection of educational content
→ Educational quality scoring
[8] Liu et al. (2024) - DeepSeek-V2: A Strong, Economical, and Efficient MoE LM
→ Multi-head Latent Attention, Mixture of Experts
[9] Team et al. (2024) - Gemma: Open Models Based on Gemini Research
→ Logit soft-capping, embedding scaling
[10] Bai et al. (2023) - Qwen Technical Report
→ Dynamic NTK RoPE, Log-N attention
[11] Dao et al. (2022) - FlashAttention: Fast Memory-Efficient Exact Attention
→ Flash Attention algorithm
[12] Hu et al. (2021) - LoRA: Low-Rank Adaptation of Large Language Models
→ LoRA fine-tuning
[13] Schulman et al. (2017) - Proximal Policy Optimization Algorithms
→ PPO algorithm
[14] Su et al. (2021) - RoFormer: Enhanced Transformer with Rotary Position Embedding
→ RoPE implementation
[15] Ziegler et al. (2019) - Fine-Tuning Language Models from Human Preferences
→ RLHF foundations, KL reward shaping
[16] Zheng et al. (2023) - Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena
→ ELO rating for LLMs, arena methodology
1. Start here: Radford 2019 (GPT-2) → understand base architecture
2. Scale up: Brown 2020 (GPT-3) → understand training at scale
3. Modernize: Touvron 2023 (LLaMA) → RoPE, RMSNorm, SwiGLU, GQA
4. Align: Ouyang 2022 (InstructGPT) → SFT + RLHF pipeline
5. Data: Penedo 2023 (RefinedWeb) → production data cleaning
6. Efficiency: Dao 2022 (FlashAttn) → attention optimization
7. PEFT: Hu 2021 (LoRA) → parameter-efficient tuning
8. Frontier: Liu 2024 (DeepSeek-V2) → MLA + MoE architectures
This project is licensed under the MIT License - see the LICENSE file for details.
- Website: Adil Shamim
- GitHub: Adil Shamim
- Create an issue in this repository for questions or suggestions
⭐ If you find this repository helpful, please consider giving it a star! ⭐