(function () { const STEP_KEY = 'transformer_tutorial_step'; const DONE_KEY = 'transformer_tutorial_done'; const FOOTER_NORMAL = '

Click Continue to close this window and try it yourself. ' + 'When you are ready, click Continue Tutorial in the top right to move on ' + '— or use Back to revisit the previous step.'; const FOOTER_LAST = '

Click Finish to close the tutorial — you have seen the whole ' + 'pipeline. Back still works if you want to revisit anything.'; let currentStep = 0; let driverObj = null; function safeGet(key) { try { return localStorage.getItem(key); } catch (e) { return null; } } function safeSet(key, value) { try { localStorage.setItem(key, value); } catch (e) { /* ignore */ } } function safeRemove(key) { try { localStorage.removeItem(key); } catch (e) { /* ignore */ } } function buildSteps() { const raw = [ { element: '#model-dropdown', title: 'Step 1 of 12 — Pick a model', body: 'Everything starts here. Different transformer models have different ' + 'sizes, training data, and quirks, so what you see downstream depends on ' + 'which one you load.

' + 'Recommended: GPT-2 (124M). It is small enough to run quickly on ' + 'any machine and well-studied, so the attention patterns you will see ' + 'have known interpretations.' }, { element: '#prompt-section', title: 'Step 2 of 12 — Give it something to predict', body: 'The prompt is what the model tries to continue. The four buttons above ' + 'load prompts chosen to activate specific, studyable behaviors.

' + 'Recommended: click "Understand repetition". Its prompt ' + '("The cat sat on the mat. The cat sat on the") triggers induction ' + 'heads — attention detectors that complete repeated patterns. We will ' + 'come back to those in the ablation step.' }, { element: '#max-new-tokens-slider', title: 'Step 3 of 12 — How many words to generate', body: 'This controls how far the model extends the prompt. One word is enough ' + 'to see the whole pipeline in action — every extra word makes the ' + 'visualizations busier without adding new concepts.

' + 'Leave this at 1 for your first run.' }, { element: '#beam-width-slider', title: 'Step 4 of 12 — Explore alternative completions', body: 'Language models do not pick just one next word — they assign ' + 'probabilities to every word in the vocabulary. Beam search keeps ' + 'the top N candidates so you can compare them side-by-side.

' + 'Bumping this to 3 or 5 is a great way to see where the model is ' + 'confident vs. uncertain.' }, { element: '#generate-btn', title: 'Step 5 of 12 — Run the model', body: 'Clicking Analyze runs the prompt through every layer and records the ' + 'internal activations. This is what powers everything below — nothing in ' + 'the pipeline or ablation tools is available until this runs.

' + 'Close this window, click Analyze, and wait for the pipeline to appear ' + 'before moving on.' }, { element: '.stage-tokenization > summary', title: 'Step 6 of 12 — Stage 1: Tokenization', body: 'Models do not read words — they read tokens, integer IDs for ' + 'chunks of text. This stage shows exactly how your prompt was sliced up. ' + 'Surprising splits (like "running" → "run" + "ning") explain a lot of ' + 'later model behavior.

' + 'Close this window and click the stage header to expand it, then skim ' + 'the tokens.' }, { element: '.stage-embedding > summary', title: 'Step 7 of 12 — Stage 2: Embedding', body: 'Each token ID gets converted into a vector of numbers (its "meaning") ' + 'plus a second vector encoding its position in the sentence. ' + 'Without position encoding, the model could not tell "dog bites man" ' + 'from "man bites dog".' }, { element: '.stage-attention > summary', title: 'Step 8 of 12 — Stage 3: Attention', body: 'This is the heart of the transformer. Each token looks at every other ' + 'token and decides how much to pay attention to it. Different heads ' + 'specialize: some track previous tokens, some detect duplicates, some ' + 'complete patterns (induction). The bars show which specialties fired ' + 'most on your prompt.' }, { element: '.stage-mlp > summary', title: 'Step 9 of 12 — Stage 4: Knowledge Retrieval (MLP)', body: 'After attention has shuffled information between tokens, the MLP layer ' + 'transforms each token vector independently. It is where the model\'s ' + 'factual and associative knowledge lives — expand → nonlinear activation ' + '→ compress.' }, { element: '.stage-output > summary', title: 'Step 10 of 12 — Stage 5: Output Selection', body: 'The final vector is projected onto the full vocabulary, producing a ' + 'probability for every possible next token. The chart shows the top 5 ' + 'candidates. This is where the abstract math becomes a concrete word.' }, { element: '#ablation-tool', title: 'Step 11 of 12 — Test a head\'s importance', body: 'Now the fun part. Ablation means silencing a specific attention ' + 'head to see how much the output changes. If removing a head barely ' + 'affects the prediction, it was not doing much here. If it wrecks the ' + 'prediction, that head was critical.' }, { element: '#ablation-category-buttons', title: 'Step 12 of 12 — Ablate the induction heads', body: 'Because you used the repetition prompt, the Induction quick-' + 'select button is the most interesting one to try. Close this window, ' + 'click Induction to add every induction head, then hit Run Ablation.' + '

You should see the ablated output diverge from the original — ' + 'concrete evidence that induction heads are what make the model complete ' + '"The cat sat on the" with "mat". That is mechanistic interpretability ' + 'in action.' } ]; const lastIndex = raw.length - 1; return raw.map((s, i) => ({ element: s.element, popover: { title: s.title, description: s.body + (i === lastIndex ? FOOTER_LAST : FOOTER_NORMAL) } })); } function ensureFloatBtn() { let btn = document.getElementById('tutorial-float-btn'); if (btn) return btn; btn = document.createElement('button'); btn.id = 'tutorial-float-btn'; btn.className = 'tutorial-float-btn'; btn.type = 'button'; btn.innerHTML = '▶ Continue Tutorial' + ''; btn.addEventListener('click', (e) => { if (e.target.closest('.tutorial-float-btn-dismiss')) { e.stopPropagation(); finishTour(); } else { resumeTour(); } }); document.body.appendChild(btn); return btn; } function showFloatBtn() { const btn = ensureFloatBtn(); btn.style.display = 'flex'; } function hideFloatBtn() { const btn = document.getElementById('tutorial-float-btn'); if (btn) btn.style.display = 'none'; } function createDriver() { if (!window.driver || !window.driver.js || !window.driver.js.driver) { console.error('Driver.js not loaded — tutorial cannot start'); return null; } return window.driver.js.driver({ showProgress: false, allowClose: true, nextBtnText: 'Continue', doneBtnText: 'Finish', prevBtnText: 'Back', showButtons: ['previous', 'next', 'close'], steps: buildSteps(), onNextClick: () => { const steps = buildSteps(); const nextIndex = currentStep + 1; if (nextIndex >= steps.length) { if (driverObj) driverObj.destroy(); finishTour(); return; } // Step 11 (index 10) has no action for the user to take, so // advance straight to step 12 without minimizing to the // floating "Continue Tutorial" button. if (currentStep === 10) { currentStep = nextIndex; safeSet(STEP_KEY, String(currentStep)); driverObj.moveNext(); return; } if (driverObj) driverObj.destroy(); currentStep = nextIndex; safeSet(STEP_KEY, String(currentStep)); showFloatBtn(); }, onPrevClick: () => { currentStep = Math.max(0, currentStep - 1); safeSet(STEP_KEY, String(currentStep)); if (driverObj) driverObj.destroy(); showFloatBtn(); }, onCloseClick: () => { finishTour(); } }); } function startFresh() { safeRemove(DONE_KEY); currentStep = 0; safeSet(STEP_KEY, '0'); hideFloatBtn(); driverObj = createDriver(); if (driverObj) driverObj.drive(0); } function resumeTour() { hideFloatBtn(); driverObj = createDriver(); if (driverObj) driverObj.drive(currentStep); } function finishTour() { safeSet(DONE_KEY, 'true'); safeRemove(STEP_KEY); if (driverObj) { try { driverObj.destroy(); } catch (e) { /* ignore */ } driverObj = null; } hideFloatBtn(); } function restoreFromStorage() { if (safeGet(DONE_KEY) === 'true') return; const saved = safeGet(STEP_KEY); if (saved === null) return; const n = parseInt(saved, 10); if (isNaN(n) || n < 0) return; currentStep = n; showFloatBtn(); } // Event delegation — Dash renders the DOM after load, so the header button // may not exist when this script first runs. document.addEventListener('click', (e) => { if (e.target.closest && e.target.closest('#start-tutorial-btn')) { startFresh(); } }); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', restoreFromStorage); } else { restoreFromStorage(); } })();