| <!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> |