""" Speculative Decoding — Professional Demo Author: Aravind Kumar Nalukurthi """ import gradio as gr import plotly.graph_objects as go try: from speculative.decoder import SpeculativeDecoder, AutoregressiveBaseline except Exception: SpeculativeDecoder = None AutoregressiveBaseline = None CSS = """ * { box-sizing: border-box; } body, .gradio-container { background: #000 !important; font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', sans-serif !important; color: #f5f5f7 !important; } .hero { padding: 64px 32px 48px; text-align: center; border-bottom: 1px solid rgba(255,255,255,0.07); } .hero-badge { display: inline-block; background: rgba(255,69,58,0.12); color: #ff453a; font-size: 11px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; padding: 5px 14px; border-radius: 20px; border: 1px solid rgba(255,69,58,0.2); margin-bottom: 22px; } .hero-title { font-size: 48px; font-weight: 700; color: #f5f5f7; line-height: 1.06; letter-spacing: -0.025em; margin: 0 0 18px; } .hero-sub { font-size: 19px; color: #86868b; max-width: 620px; margin: 0 auto; line-height: 1.55; } .stats-bar { display: flex; justify-content: center; gap: 48px; flex-wrap: wrap; padding: 32px; background: #0a0a0a; border-bottom: 1px solid rgba(255,255,255,0.07); } .stat { text-align: center; } .stat-val { font-size: 30px; font-weight: 700; color: #ff453a; letter-spacing: -0.02em; } .stat-label { font-size: 12px; color: #6e6e73; margin-top: 3px; font-weight: 500; } .section { padding: 36px 32px; border-bottom: 1px solid rgba(255,255,255,0.06); } .sec-label { font-size: 12px; font-weight: 600; color: #6e6e73; letter-spacing: 0.09em; text-transform: uppercase; margin: 0 0 18px; } .card { background: #111; border: 1px solid rgba(255,255,255,0.08); border-radius: 14px; padding: 22px 24px; margin-bottom: 10px; } .card-title { font-size: 16px; font-weight: 600; color: #f5f5f7; margin: 0 0 8px; } .card-body { font-size: 14px; color: #86868b; line-height: 1.6; margin: 0; } .token-row { display: flex; flex-wrap: wrap; gap: 6px; padding: 16px; background: #0a0a0a; border-radius: 10px; margin: 12px 0; font-family: 'SF Mono', 'Fira Code', monospace; font-size: 14px; } .token-accepted { background: rgba(48,209,88,0.15); color: #30d158; border: 1px solid rgba(48,209,88,0.25); padding: 4px 10px; border-radius: 6px; } .token-rejected { background: rgba(255,69,58,0.1); color: #ff453a; border: 1px solid rgba(255,69,58,0.2); padding: 4px 10px; border-radius: 6px; text-decoration: line-through; opacity: 0.7; } .token-corrected { background: rgba(191,90,242,0.15); color: #bf5af2; border: 1px solid rgba(191,90,242,0.25); padding: 4px 10px; border-radius: 6px; } .token-bonus { background: rgba(10,132,255,0.15); color: #0a84ff; border: 1px solid rgba(10,132,255,0.25); padding: 4px 10px; border-radius: 6px; } .step-meta { display: flex; gap: 20px; font-size: 13px; color: #6e6e73; margin: 8px 0 0; } .step-meta span { color: #f5f5f7; } footer { display: none !important; } """ STEPS = [ { "prompt": "The quick brown fox", "tokens": [ ("jumps", "accepted"), ("over", "accepted"), ("the", "accepted"), ("lazy", "accepted"), ("dog", "accepted"), ], "accepted": 5, "k": 5, "bonus": True, "desc": "All 5 draft tokens accepted. Bonus token sampled from target model.", }, { "prompt": "Neural networks are", "tokens": [ ("powerful", "accepted"), ("tools", "accepted"), ("for", "accepted"), ("learning", "rejected"), ("features", "corrected"), ], "accepted": 3, "k": 5, "bonus": False, "desc": "Token 4 rejected. Target model samples corrected token from adjusted distribution.", }, { "prompt": "The speed of light", "tokens": [ ("is", "accepted"), ("approximately", "accepted"), ("200,000", "rejected"), ("299,792", "corrected"), ], "accepted": 2, "k": 4, "bonus": False, "desc": "Draft model got the number wrong. Target model corrects it.", }, { "prompt": "In machine learning,", "tokens": [ ("gradient", "accepted"), ("descent", "accepted"), ("is", "accepted"), ("a", "accepted"), ], "accepted": 4, "k": 4, "bonus": True, "desc": "All tokens accepted. K=4 here — fewer drafts, still a win.", }, ] BENCH = { "k_values": [1, 2, 3, 4, 5, 6, 7, 8], "speedup": [1.12, 1.35, 1.56, 1.72, 1.87, 1.83, 1.76, 1.65], "theory": [1 + k * 0.71 for k in [1,2,3,4,5,6,7,8]], } def render_step(idx): step = STEPS[idx] token_html = "" for tok, status in step["tokens"]: token_html += f'{tok}' if step.get("bonus"): token_html += '+bonus' return f"""
LLMs generate one word at a time — each word costs a full forward pass. Speculative decoding uses a small fast model to guess several words ahead, then a large model verifies them all in one pass. Result: 1.87× faster with mathematically identical output.
A large model (e.g., GPT-4o, 70B parameters) is slow but accurate. A small draft model (e.g., GPT-2, 124M parameters) is fast but sometimes wrong. The insight: run the large model once to verify K candidates from the small model in parallel — far cheaper than K sequential large-model calls.
For each draft token t, compute α = min(1, p_target(t) / p_draft(t)). Accept with probability α. On rejection, sample a corrected token from (p_target − α·p_draft).clamp(0). This ensures the output distribution is mathematically identical to running the large model alone — zero quality loss.
When all K draft tokens are accepted, the large model's final forward pass generates one extra "bonus" token for free — since we already have its output distribution. This increases throughput beyond the naive speedup estimate.
No API key or GPU needed. "Step Visualizer" shows token-by-token acceptance/rejection. "Benchmark" shows speedup vs draft length K. "The Math" shows the rejection sampling proof.
Click a step above to visualize it.
At K=5, the extra verification overhead of longer drafts starts to outweigh the speedup. Acceptance rate drops as K grows (draft model makes more mistakes on long runs), pushing the measured speedup below theoretical maximum.
Code follows strict syntactic rules — the draft model's distribution closely matches the target on deterministic patterns like indentation, keywords, and brackets. Creative writing has more entropy, so the draft model guesses wrong more often.