| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>LLM Decoding Strategies Demo</title> |
| | <style> |
| | body { |
| | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| | max-width: 1200px; |
| | margin: 0 auto; |
| | padding: 20px; |
| | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| | min-height: 100vh; |
| | } |
| | .container { |
| | background: white; |
| | border-radius: 15px; |
| | padding: 30px; |
| | box-shadow: 0 10px 30px rgba(0,0,0,0.2); |
| | } |
| | h1 { |
| | text-align: center; |
| | color: #333; |
| | margin-bottom: 30px; |
| | font-size: 2.5em; |
| | } |
| | .vocab-section { |
| | background: #f8f9fa; |
| | padding: 20px; |
| | border-radius: 10px; |
| | margin-bottom: 30px; |
| | border-left: 5px solid #667eea; |
| | } |
| | .logits-section { |
| | background: #e3f2fd; |
| | padding: 20px; |
| | border-radius: 10px; |
| | margin-bottom: 30px; |
| | border-left: 5px solid #2196f3; |
| | } |
| | .strategy { |
| | background: #fff; |
| | border: 2px solid #e0e0e0; |
| | border-radius: 10px; |
| | padding: 20px; |
| | margin-bottom: 20px; |
| | transition: all 0.3s ease; |
| | } |
| | .strategy:hover { |
| | border-color: #667eea; |
| | box-shadow: 0 5px 15px rgba(0,0,0,0.1); |
| | } |
| | .strategy h3 { |
| | color: #667eea; |
| | margin-bottom: 15px; |
| | font-size: 1.4em; |
| | } |
| | .probs-table { |
| | width: 100%; |
| | border-collapse: collapse; |
| | margin: 15px 0; |
| | font-size: 0.9em; |
| | } |
| | .probs-table th, .probs-table td { |
| | padding: 8px 12px; |
| | border: 1px solid #ddd; |
| | text-align: center; |
| | } |
| | .probs-table th { |
| | background: #667eea; |
| | color: white; |
| | } |
| | .probs-table tr:nth-child(even) { |
| | background: #f9f9f9; |
| | } |
| | .selected { |
| | background: #4caf50 !important; |
| | color: white; |
| | font-weight: bold; |
| | } |
| | .filtered { |
| | background: #ffcdd2 !important; |
| | color: #666; |
| | } |
| | .result { |
| | background: #e8f5e8; |
| | padding: 15px; |
| | border-radius: 8px; |
| | margin-top: 15px; |
| | border-left: 4px solid #4caf50; |
| | } |
| | .controls { |
| | background: #f5f5f5; |
| | padding: 20px; |
| | border-radius: 10px; |
| | margin-bottom: 20px; |
| | } |
| | .control-group { |
| | margin-bottom: 15px; |
| | } |
| | .control-group label { |
| | display: inline-block; |
| | width: 120px; |
| | font-weight: bold; |
| | } |
| | .control-group input { |
| | padding: 5px; |
| | border: 1px solid #ddd; |
| | border-radius: 4px; |
| | width: 100px; |
| | } |
| | button { |
| | background: #667eea; |
| | color: white; |
| | border: none; |
| | padding: 10px 20px; |
| | border-radius: 5px; |
| | cursor: pointer; |
| | font-size: 16px; |
| | transition: background 0.3s; |
| | } |
| | button:hover { |
| | background: #5a67d8; |
| | } |
| | .logits-display { |
| | font-family: monospace; |
| | font-size: 1.1em; |
| | background: #f0f0f0; |
| | padding: 10px; |
| | border-radius: 5px; |
| | margin: 10px 0; |
| | } |
| | </style> |
| | </head> |
| | <body> |
| | <div class="container"> |
| | <h1>🤖 LLM Decoding Strategies Demo</h1> |
| | |
| | <div class="vocab-section"> |
| | <h2>📚 Vocabulary</h2> |
| | <p><strong>Tokens:</strong> ["the", "cat", "dog", "runs", "jumps", "quickly", ".", "and"]</p> |
| | <p><strong>Indices:</strong> [0, 1, 2, 3, 4, 5, 6, 7]</p> |
| | </div> |
| |
|
| | <div class="controls"> |
| | <h2>🎛️ Controls</h2> |
| | <div class="control-group"> |
| | <label>Temperature:</label> |
| | <input type="number" id="temperature" value="1.0" step="0.1" min="0.1" max="3.0"> |
| | </div> |
| | <div class="control-group"> |
| | <label>Top-k:</label> |
| | <input type="number" id="topk" value="3" min="1" max="8"> |
| | </div> |
| | <div class="control-group"> |
| | <label>Top-p:</label> |
| | <input type="number" id="topp" value="0.8" step="0.1" min="0.1" max="1.0"> |
| | </div> |
| | <button onclick="generateNewLogits()">🎲 Generate New Logits</button> |
| | </div> |
| |
|
| | <div class="logits-section"> |
| | <h2>📊 Current Logits</h2> |
| | <div id="logits-display" class="logits-display"></div> |
| | </div> |
| |
|
| | <div id="strategies-container"></div> |
| | </div> |
| |
|
| | <script> |
| | const vocab = ["the", "cat", "dog", "runs", "jumps", "quickly", ".", "and"]; |
| | let currentLogits = []; |
| | |
| | function generateRandomLogits() { |
| | return Array.from({length: 8}, () => (Math.random() - 0.5) * 6); |
| | } |
| | |
| | function softmax(logits, temperature = 1.0) { |
| | const scaledLogits = logits.map(x => x / temperature); |
| | const maxLogit = Math.max(...scaledLogits); |
| | const expLogits = scaledLogits.map(x => Math.exp(x - maxLogit)); |
| | const sumExp = expLogits.reduce((a, b) => a + b, 0); |
| | return expLogits.map(x => x / sumExp); |
| | } |
| | |
| | function greedyDecoding(logits) { |
| | const probs = softmax(logits); |
| | const maxIndex = probs.indexOf(Math.max(...probs)); |
| | return { |
| | selectedToken: vocab[maxIndex], |
| | selectedIndex: maxIndex, |
| | probs: probs, |
| | method: "Selected highest probability token" |
| | }; |
| | } |
| | |
| | function temperatureSampling(logits, temperature) { |
| | const probs = softmax(logits, temperature); |
| | const selectedIndex = sampleFromDistribution(probs); |
| | return { |
| | selectedToken: vocab[selectedIndex], |
| | selectedIndex: selectedIndex, |
| | probs: probs, |
| | method: `Temperature = ${temperature}, then sampled` |
| | }; |
| | } |
| | |
| | function topKSampling(logits, k) { |
| | const probs = softmax(logits); |
| | const indexed = probs.map((p, i) => ({prob: p, index: i})); |
| | indexed.sort((a, b) => b.prob - a.prob); |
| | |
| | const topK = indexed.slice(0, k); |
| | const topKProbs = new Array(vocab.length).fill(0); |
| | const sumTopK = topK.reduce((sum, item) => sum + item.prob, 0); |
| | |
| | topK.forEach(item => { |
| | topKProbs[item.index] = item.prob / sumTopK; |
| | }); |
| | |
| | const selectedIndex = sampleFromDistribution(topKProbs); |
| | return { |
| | selectedToken: vocab[selectedIndex], |
| | selectedIndex: selectedIndex, |
| | probs: topKProbs, |
| | originalProbs: probs, |
| | method: `Filtered to top-${k} tokens, then sampled` |
| | }; |
| | } |
| | |
| | function topPSampling(logits, p) { |
| | const probs = softmax(logits); |
| | const indexed = probs.map((prob, i) => ({prob, index: i})); |
| | indexed.sort((a, b) => b.prob - a.prob); |
| | |
| | let cumSum = 0; |
| | let cutoff = 0; |
| | for (let i = 0; i < indexed.length; i++) { |
| | cumSum += indexed[i].prob; |
| | if (cumSum >= p) { |
| | cutoff = i + 1; |
| | break; |
| | } |
| | } |
| | |
| | const nucleus = indexed.slice(0, cutoff); |
| | const nucleusProbs = new Array(vocab.length).fill(0); |
| | const sumNucleus = nucleus.reduce((sum, item) => sum + item.prob, 0); |
| | |
| | nucleus.forEach(item => { |
| | nucleusProbs[item.index] = item.prob / sumNucleus; |
| | }); |
| | |
| | const selectedIndex = sampleFromDistribution(nucleusProbs); |
| | return { |
| | selectedToken: vocab[selectedIndex], |
| | selectedIndex: selectedIndex, |
| | probs: nucleusProbs, |
| | originalProbs: probs, |
| | method: `Nucleus sampling with p=${p} (${cutoff} tokens)` |
| | }; |
| | } |
| | |
| | function sampleFromDistribution(probs) { |
| | const r = Math.random(); |
| | let cumSum = 0; |
| | for (let i = 0; i < probs.length; i++) { |
| | cumSum += probs[i]; |
| | if (r <= cumSum) { |
| | return i; |
| | } |
| | } |
| | return probs.length - 1; |
| | } |
| | |
| | function createProbabilityTable(probs, selectedIndex, originalProbs = null, title = "Probabilities") { |
| | let html = `<table class="probs-table"> |
| | <thead> |
| | <tr><th>Token</th><th>Index</th><th>${title}</th></tr> |
| | </thead> |
| | <tbody>`; |
| | |
| | probs.forEach((prob, i) => { |
| | let className = ''; |
| | if (i === selectedIndex) className = 'selected'; |
| | else if (originalProbs && probs[i] === 0) className = 'filtered'; |
| | |
| | html += `<tr class="${className}"> |
| | <td>${vocab[i]}</td> |
| | <td>${i}</td> |
| | <td>${prob.toFixed(4)}</td> |
| | </tr>`; |
| | }); |
| | |
| | html += '</tbody></table>'; |
| | return html; |
| | } |
| | |
| | function displayStrategy(title, result, description) { |
| | const hasOriginal = result.originalProbs !== undefined; |
| | |
| | let html = `<div class="strategy"> |
| | <h3>${title}</h3> |
| | <p><strong>Method:</strong> ${result.method}</p> |
| | <p>${description}</p>`; |
| | |
| | if (hasOriginal) { |
| | html += createProbabilityTable(result.originalProbs, -1, null, "Original Probs"); |
| | html += createProbabilityTable(result.probs, result.selectedIndex, result.originalProbs, "Filtered Probs"); |
| | } else { |
| | html += createProbabilityTable(result.probs, result.selectedIndex); |
| | } |
| | |
| | html += `<div class="result"> |
| | <strong>🎯 Selected Token:</strong> "${result.selectedToken}" (index: ${result.selectedIndex}) |
| | </div> |
| | </div>`; |
| | |
| | return html; |
| | } |
| | |
| | function runAllStrategies() { |
| | const temperature = parseFloat(document.getElementById('temperature').value); |
| | const k = parseInt(document.getElementById('topk').value); |
| | const p = parseFloat(document.getElementById('topp').value); |
| | |
| | const greedy = greedyDecoding(currentLogits); |
| | const tempSampling = temperatureSampling(currentLogits, temperature); |
| | const topK = topKSampling(currentLogits, k); |
| | const topP = topPSampling(currentLogits, p); |
| | |
| | let html = ''; |
| | |
| | html += displayStrategy( |
| | "🎯 Greedy Decoding", |
| | greedy, |
| | "Always selects the token with the highest probability. Deterministic and safe, but can be repetitive." |
| | ); |
| | |
| | html += displayStrategy( |
| | "🌡️ Temperature Sampling", |
| | tempSampling, |
| | "Applies temperature scaling to logits before sampling. Lower temperature = more focused, higher temperature = more random." |
| | ); |
| | |
| | html += displayStrategy( |
| | "🔝 Top-k Sampling", |
| | topK, |
| | "Only considers the k most probable tokens, zeros out the rest, then samples from the filtered distribution." |
| | ); |
| | |
| | html += displayStrategy( |
| | "🎯 Top-p (Nucleus) Sampling", |
| | topP, |
| | "Dynamically selects the smallest set of tokens whose cumulative probability exceeds p, then samples from them." |
| | ); |
| | |
| | document.getElementById('strategies-container').innerHTML = html; |
| | } |
| | |
| | function generateNewLogits() { |
| | currentLogits = generateRandomLogits(); |
| | document.getElementById('logits-display').innerHTML = |
| | `<strong>Raw Logits:</strong> [${currentLogits.map(x => x.toFixed(2)).join(', ')}]`; |
| | runAllStrategies(); |
| | } |
| | |
| | |
| | generateNewLogits(); |
| | |
| | |
| | document.getElementById('temperature').addEventListener('input', runAllStrategies); |
| | document.getElementById('topk').addEventListener('input', runAllStrategies); |
| | document.getElementById('topp').addEventListener('input', runAllStrategies); |
| | </script> |
| | </body> |
| | </html> |