Team Ai
Apppublic

EXDai/speculative-decoding-simulator

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
index.html297 linesDownload Raw Back to root
1<!DOCTYPE html>2<html lang="en">3<head>4<meta charset="UTF-8">5<meta name="viewport" content="width=device-width, initial-scale=1.0">6<title>Speculative Decoding — EXD Ep5</title>7<style>8:root {9  --bg:#0d1117; --surface:#161b22; --border:#30363d;10  --text:#c9d1d9; --dim:#8b949e; --accent:#58a6ff;11  --hit:#3fb950; --miss:#f85149; --draft:#bc8cff;12}13*{margin:0;padding:0;box-sizing:border-box}14body{15  background:var(--bg);color:var(--text);16  font:14px ui-monospace,SFMono-Regular,monospace;17  display:flex;justify-content:center;padding:2rem 1rem;18}19.w{max-width:720px;width:100%}20h1{font-size:1.1rem;margin-bottom:.2rem}21.sub{font-size:.75rem;color:var(--dim);margin-bottom:1rem}22.controls{display:flex;gap:1rem;flex-wrap:wrap;align-items:flex-end;margin-bottom:1rem}23.cg{display:flex;flex-direction:column;gap:.2rem}24.cg label{font-size:.65rem;color:var(--dim);text-transform:uppercase}25.cg select{padding:.3rem .5rem;border-radius:4px;border:1px solid var(--border);background:var(--surface);color:var(--text);font-family:monospace;font-size:.8rem;cursor:pointer}26.btn-row{display:flex;gap:.5rem;align-items:center}27.btn{background:var(--surface);color:var(--accent);border:1px solid var(--accent);padding:.35rem .8rem;border-radius:5px;cursor:pointer;font-family:monospace;font-size:.75rem;font-weight:600}28.btn:hover{background:rgba(88,166,255,.12)}29.btn.pri{background:var(--accent);color:var(--bg)}30.btn:disabled{opacity:.4;cursor:not-allowed}31.step{font-size:.7rem;color:var(--dim);margin-left:.5rem}32.sec{font-size:.65rem;color:var(--dim);text-transform:uppercase;margin-bottom:.3rem}33.pipe{display:flex;gap:6px;padding:.7rem;margin-bottom:1rem;background:var(--surface);border:1px solid var(--border);border-radius:8px;min-height:44px;flex-wrap:wrap;align-items:center}34.ps{padding:.4rem .6rem;border-radius:6px;border:2px solid var(--border);font-size:.7rem;text-align:center;transition:all .3s}35.ps.on{border-color:var(--accent);box-shadow:0 0 8px rgba(88,166,255,.25)}36.ps .l{font-size:.55rem;color:var(--dim);display:block}37.ps .v{font-size:.75rem;display:block;margin-top:1px}38.tokens{display:flex;gap:5px;flex-wrap:wrap;margin-bottom:1rem;min-height:50px;align-items:center}39.tok{padding:5px 10px;border-radius:6px;font-size:.75rem;font-weight:600;border:2px solid transparent;text-align:center;transition:all .3s}40.tok .t{display:block;min-width:40px}41.tok .lb{font-size:.5rem;color:var(--dim);margin-top:2px}42.t-OK{background:var(--surface);border-color:var(--border);color:var(--text)}43.t-HIT{background:rgba(63,185,80,.12);border-color:var(--hit);color:var(--hit)}44.t-MISS{background:rgba(248,81,73,.12);border-color:var(--miss);color:var(--miss);text-decoration:line-through}45.t-DRAFT{background:rgba(188,140,255,.1);border-color:var(--draft);color:var(--draft)}46.info{font-size:.7rem;color:var(--dim);padding:.6rem .8rem;border-left:3px solid var(--accent);background:rgba(88,166,255,.04);border-radius:0 6px 6px 0;line-height:1.5;min-height:2.5em}47.info code{color:var(--accent);background:rgba(88,166,255,.1);padding:0 4px;border-radius:3px}48.stats{display:flex;gap:1rem;margin-bottom:1rem;padding:.6rem;background:var(--surface);border:1px solid var(--border);border-radius:8px;flex-wrap:wrap}49.stat{text-align:center;min-width:70px}50.stat .n{font-size:1.1rem;font-weight:700;color:var(--accent);font-family:monospace}51.stat .lb{font-size:.55rem;color:var(--dim)}52.stat.bad .n{color:var(--miss)}53</style>54</head>55<body>56<div class="w">57<h1>Speculative Decoding — Step by Step</h1>58<div class="sub">Draft model proposes tokens ahead, full model verifies · EXD Ep5</div>59 60<div class="controls">61  <div class="cg">62    <label>Spec Depth <span id="dv">2</span></label>63    <select id="depth">64      <option value="2" selected>2 tokens</option>65      <option value="3">3 tokens</option>66    </select>67  </div>68  <div class="btn-row">69    <button class="btn" id="prev">&#x2190; Prev</button>70    <button class="btn pri" id="step">Next Step</button>71    <button class="btn" id="reset">Reset</button>72    <span class="step" id="st">Step 0</span>73  </div>74</div>75 76<div class="sec">Pipeline</div>77<div class="pipe" id="pipe"></div>78 79<div class="sec">Tokens</div>80<div class="tokens" id="trow"></div>81 82<div class="stats" id="stats"></div>83 84<div class="info" id="info">85  <strong>Speculative decoding</strong>: a lightweight draft model guesses tokens ahead.<br>86  The full model verifies them all in <em>one forward pass</em>.<br>87  <span style="color:var(--hit)">&#9632; Green</span> = correct guess (free speed) &middot;88  <span style="color:var(--miss)">&#9632; Red</span> = wrong guess (wasted, struck out)<br>89  <code>Space</code> = next, <code>&#x2190;</code> = previous. Switch depth to compare.90</div>91</div>92 93<script>94var PREFIX = "The quick brown";95var d = function(id) { return document.getElementById(id); };96var idx = 0;97var depth = 2;98 99// ---- STATIC SCRIPTS ----100// Each script step: {phase, info, drafts: [{token,correct}] or null, confirm: [tokens_to_add_to_conf]}101// phase: "draft" | "verify" | "done"102// confirm only on verify steps: tokens to add to conf (hits + any miss correction, in order)103 104var SCRIPT_2 = [105  {phase:"draft", info:"<strong>Draft model</strong> guesses 2 tokens ahead: <em>fox</em>, <em>jumps</em>",106   drafts:[{token:"fox",correct:true},{token:"jumps",correct:true}]},107  {phase:"verify", info:"<strong>Full model</strong> verifies: 2 hit(s). Speed gained!",108   drafts:[{token:"fox",correct:true},{token:"jumps",correct:true}], confirm:["fox","jumps"]},109  {phase:"draft", info:"<strong>Draft model</strong> guesses 2 tokens ahead: <em>over</em>, <em>the</em> — but one is wrong!",110   drafts:[{token:"over",correct:true},{token:"???",correct:false}]},111  {phase:"verify", info:"<strong>Full model</strong> verifies: 1 hit(s), 1 miss. Corrects <em>??? &rarr; the</em>.",112   drafts:[{token:"over",correct:true},{token:"???",correct:false,correction:"the"}], confirm:["over","the"]},113  {phase:"draft", info:"<strong>Draft model</strong> guesses 2 tokens ahead: <em>lazy</em>, <em>dog</em>",114   drafts:[{token:"lazy",correct:true},{token:"dog",correct:true}]},115  {phase:"verify", info:"<strong>Full model</strong> verifies: 2 hit(s). Done! &#x1f389;",116   drafts:[{token:"lazy",correct:true},{token:"dog",correct:true}], confirm:["lazy","dog"]},117  {phase:"done", info:"<strong>Done!</strong> Generated 9 tokens in 3 forward passes (6 steps). Speculation saved ~50% of the passes.", drafts:[]},118];119 120var SCRIPT_3 = [121  {phase:"draft", info:"<strong>Draft model</strong> guesses 3 tokens ahead: <em>fox</em>, <em>jumps</em>, <em>over</em>",122   drafts:[{token:"fox",correct:true},{token:"jumps",correct:true},{token:"over",correct:true}]},123  {phase:"verify", info:"<strong>Full model</strong> verifies: 3 hit(s) in one pass! &#x1f680;",124   drafts:[{token:"fox",correct:true},{token:"jumps",correct:true},{token:"over",correct:true}], confirm:["fox","jumps","over"]},125  {phase:"draft", info:"<strong>Draft model</strong> guesses 3 tokens ahead: <em>the</em>, <em>lazy</em>, <em>dog</em> — but one is wrong!",126   drafts:[{token:"the",correct:true},{token:"???",correct:false},{token:"dog",correct:true}]},127  {phase:"verify", info:"<strong>Full model</strong> verifies: 1 hit(s), 1 miss. Corrects <em>??? &rarr; lazy</em>. Remaining drafts accepted. Done!",128   drafts:[{token:"the",correct:true},{token:"???",correct:false,correction:"lazy"},{token:"dog",correct:true}], confirm:["the","lazy","dog"]},129  {phase:"done", info:"<strong>Done!</strong> Generated 9 tokens in 2 forward passes (4 steps). Depth 3 got 3 tokens in one go, but a miss wasted extra work.", drafts:[]},130];131 132var script = SCRIPT_2.slice();133var conf = [];134var total_hits = 0, total_misses = 0;135 136function getScript() {137  return Number(d("depth").value) === 3 ? SCRIPT_3 : SCRIPT_2;138}139 140function reset() {141  var s = getScript();142  script = s.map(function(x) { return JSON.parse(JSON.stringify(x)); });143  conf = PREFIX.split(" ");144  total_hits = 0;145  total_misses = 0;146  idx = -1;147  depth = Number(d("depth").value);148  d("dv").textContent = depth;149  render();150  d("st").textContent = "Step " + idx;151  d("info").innerHTML = "Prefix: <strong>" + PREFIX + "</strong> &#x2192; target: <strong>fox jumps over the lazy dog</strong>. Depth = " + depth + ". Click <code>Next Step</code>.";152  updateButtons();153}154 155function nextStep() {156  if (idx >= 0 && idx >= script.length) return;157  if (idx === -1) {158    // Initial state: just advance to first frame159    idx = 0;160    d("st").textContent = "Step " + idx;161    d("info").innerHTML = script[0].info;162    updateButtons();163    render();164    return;165  }166  var frame = script[idx];167  depth = Number(d("depth").value);168  d("dv").textContent = depth;169 170  if (frame.phase === "verify" && frame.confirm) {171    for (var i = 0; i < frame.confirm.length; i++) {172      conf.push(frame.confirm[i]);173    }174    // Count hits/misses from drafts175    if (frame.drafts) {176      for (var i = 0; i < frame.drafts.length; i++) {177        if (frame.drafts[i].correct) total_hits++;178        else total_misses++;179      }180    }181  }182  idx++;183  d("st").textContent = "Step " + idx;184  d("info").innerHTML = frame.info;185  updateButtons();186  render();187}188 189function prevStep() {190  if (idx <= -1) return;191  var target = idx - 1;192  // Replay from start up to target193  conf = PREFIX.split(" ");194  total_hits = 0;195  total_misses = 0;196  depth = Number(d("depth").value);197  script = getScript().map(function(x) { return JSON.parse(JSON.stringify(x)); });198  for (var i = 0; i < target; i++) {199    var frame = script[i];200    if (frame.phase === "verify" && frame.confirm) {201      for (var j = 0; j < frame.confirm.length; j++) conf.push(frame.confirm[j]);202      if (frame.drafts) {203        for (var j = 0; j < frame.drafts.length; j++) {204          if (frame.drafts[j].correct) total_hits++;205          else total_misses++;206        }207      }208    }209  }210  idx = target;211  d("st").textContent = idx === -1 ? "Step 0" : "Step " + idx;212  if (idx === -1) {213    d("info").innerHTML = "Prefix: <strong>" + PREFIX + "</strong> &#x2192; target: <strong>fox jumps over the lazy dog</strong>. Depth = " + depth + ".";214  } else {215    d("info").innerHTML = script[idx] ? script[idx].info : "";216  }217  updateButtons();218  render();219}220 221function updateButtons() {222  var pastEnd = idx >= 0 && idx >= script.length;223  var atDone = idx >= 0 && idx < script.length && script[idx].phase === "done";224  d("step").disabled = pastEnd || atDone;225  d("prev").disabled = idx <= -1;226  if (pastEnd) d("st").textContent = "Done";227}228 229function render() {230  var cur = (idx >= 0 && idx < script.length) ? script[idx] : null;231  var pipe = d("pipe");232  var ph = idx === -1 ? "" : (cur ? cur.phase : "done");233  var dm = ph === "draft" ? ' on' : '';234  var fm = ph === "verify" ? ' on' : '';235  var dn = (ph === "done" || idx >= script.length) ? ' on' : '';236  pipe.innerHTML = '<div class="ps' + dm + '"><span class="l">Draft Model</span><span class="v">guesses ' + depth + ' tokens</span></div>' +237    '<span style="color:var(--dim);margin:0 4px">&#8594;</span>' +238    '<div class="ps' + fm + '"><span class="l">Full Model</span><span class="v">verifies in 1 pass</span></div>' +239    '<span style="color:var(--dim);margin:0 4px">&#8594;</span>' +240    '<div class="ps' + dn + '"><span class="l">' + ((ph === "done" || idx >= script.length) ? 'Done' : 'Repeat') + '</span><span class="v">' + ((ph === "done" || idx >= script.length) ? 'complete' : 'until EOS') + '</span></div>';241 242  var html = "";243  for (var i = 0; i < conf.length; i++) {244    var isPrefix = i < PREFIX.split(" ").length;245    html += '<div class="tok t-OK"><span class="t">' + conf[i] + '</span><span class="lb">' + (isPrefix ? 'prefix' : 'generated') + '</span></div>';246  }247  if (cur && cur.drafts && cur.drafts.length > 0) {248    var isVerify = cur.phase === "verify";249    if (isVerify) {250      html += '<div style="position:relative;display:flex;gap:4px;align-items:center;padding:6px 6px 4px;border:2px dashed var(--accent);border-radius:8px;background:rgba(88,166,255,.04);margin-top:2px">';251      html += '<span style="position:absolute;top:-8px;left:6px;font-size:.5rem;color:var(--accent);background:var(--surface);padding:0 4px">verified in 1 pass</span>';252    }253    for (var i = 0; i < cur.drafts.length; i++) {254      var cls, label;255      if (cur.phase === "draft") {256        cls = "t-DRAFT";257        label = "drafting...";258      } else {259        cls = cur.drafts[i].correct ? "t-HIT" : "t-MISS";260        label = cur.drafts[i].correct ? "hit" : "miss";261      }262      if (!cur.drafts[i].correct && cur.drafts[i].correction) {263        // Split card: left = struck-out miss, right = green correction264        html += '<div class="tok ' + cls + '" style="display:flex;gap:8px;align-items:center;text-decoration:none">' +265          '<div style="text-align:center"><span class="t" style="text-decoration:line-through">' + cur.drafts[i].token + '</span><span class="lb">' + label + '</span></div>' +266          '<span style="color:var(--dim);font-size:.65rem">&rarr;</span>' +267          '<div class="tok t-HIT" style="margin:0;text-decoration:none"><span class="t">' + cur.drafts[i].correction + '</span><span class="lb">corrected</span></div>' +268          '</div>';269      } else {270        html += '<div class="tok ' + cls + '"><span class="t">' + cur.drafts[i].token + '</span><span class="lb">' + label + '</span></div>';271      }272    } // end draft tokens loop273    if (isVerify) { html += '</div>'; }274  }275  d("trow").innerHTML = html || '<span style="color:var(--dim);font-size:.75rem">no tokens</span>';276 277  var passes = idx > 0 ? Math.ceil(idx / 2) : 0;278  d("stats").innerHTML =279    '<div class="stat"><div class="n">' + conf.length + '</div><div class="lb">tokens out</div></div>' +280    '<div class="stat"><div class="n">' + passes + '</div><div class="lb">forward passes</div></div>' +281    '<div class="stat"><div class="n">' + total_hits + '</div><div class="lb">hits</div></div>' +282    '<div class="stat' + (total_misses > 0 ? ' bad' : '') + '"><div class="n">' + total_misses + '</div><div class="lb">misses</div></div>';283}284 285d("step").onclick = nextStep;286d("prev").onclick = prevStep;287d("reset").onclick = reset;288d("depth").onchange = reset;289document.onkeydown = function(e) {290  if (e.key === " " || e.key === "Enter") { e.preventDefault(); nextStep(); }291  if (e.key === "ArrowLeft") { e.preventDefault(); prevStep(); }292};293reset();294</script>295</body>296</html>297