| <div class="d3-parameter-calculator"></div> |
|
|
| <style> |
| .d3-parameter-calculator { |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
| line-height: 1.5; |
| color: var(--text-color); |
| } |
| |
| .d3-parameter-calculator .controls-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); |
| gap: 16px; |
| margin-bottom: 20px; |
| } |
| |
| .d3-parameter-calculator .control-group { |
| display: flex; |
| flex-direction: column; |
| gap: 6px; |
| } |
| |
| .d3-parameter-calculator .control-group label { |
| font-size: 0.8em; |
| font-weight: 700; |
| color: var(--text-color); |
| } |
| |
| .d3-parameter-calculator .control-group input { |
| padding: 6px 10px; |
| border: 1px solid var(--border-color); |
| border-radius: 6px; |
| font-size: 0.85em; |
| background: var(--surface-bg); |
| color: var(--text-color); |
| } |
| |
| .d3-parameter-calculator .control-group input:focus { |
| outline: none; |
| border-color: var(--primary-color); |
| background: var(--surface-bg); |
| } |
| |
| .d3-parameter-calculator .slider-group { |
| display: flex; |
| flex-direction: column; |
| gap: 6px; |
| } |
| |
| .d3-parameter-calculator .slider-group label { |
| font-size: 0.8em; |
| font-weight: 700; |
| color: var(--text-color); |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| } |
| |
| .d3-parameter-calculator .slider-value { |
| font-size: 0.75em; |
| color: var(--muted-color); |
| font-weight: 600; |
| } |
| |
| |
| .d3-parameter-calculator input[type="range"] { |
| -webkit-appearance: none; |
| appearance: none; |
| background: transparent; |
| cursor: pointer; |
| height: 6px; |
| border-radius: 3px; |
| position: relative; |
| } |
| |
| |
| .d3-parameter-calculator .slider-group { |
| position: relative; |
| } |
| |
| .d3-parameter-calculator .slider-group::before { |
| content: ''; |
| position: absolute; |
| bottom: 0; |
| left: 0; |
| width: 100%; |
| height: 6px; |
| background: var(--border-color); |
| border-radius: 3px; |
| pointer-events: none; |
| } |
| |
| .d3-parameter-calculator .slider-group::after { |
| content: ''; |
| position: absolute; |
| bottom: 0; |
| left: 0; |
| width: var(--progress-width, 0%); |
| height: 6px; |
| background: var(--primary-color); |
| border-radius: 3px; |
| transition: width 0.1s ease; |
| pointer-events: none; |
| } |
| |
| |
| .d3-parameter-calculator input[type="range"]::-webkit-slider-track { |
| background: var(--border-color); |
| height: 6px; |
| border-radius: 3px; |
| } |
| |
| |
| .d3-parameter-calculator input[type="range"]::-webkit-slider-thumb { |
| -webkit-appearance: none; |
| appearance: none; |
| background: var(--primary-color); |
| height: 18px; |
| width: 18px; |
| border-radius: 50%; |
| cursor: pointer; |
| border: 1px solid rgba(0, 0, 0, 0.2); |
| transition: all 0.2s ease; |
| margin-top: 2px; |
| } |
| |
| .d3-parameter-calculator input[type="range"]::-webkit-slider-thumb:hover { |
| transform: scale(1.1); |
| box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); |
| } |
| |
| |
| .d3-parameter-calculator input[type="range"]::-moz-range-track { |
| background: var(--border-color); |
| height: 6px; |
| border-radius: 3px; |
| border: none; |
| } |
| |
| |
| .d3-parameter-calculator input[type="range"]::-moz-range-thumb { |
| background: var(--primary-color); |
| height: 18px; |
| width: 18px; |
| border-radius: 50%; |
| cursor: pointer; |
| border: 1px solid rgba(0, 0, 0, 0.2); |
| transition: all 0.2s ease; |
| margin-top: 2px; |
| } |
| |
| .d3-parameter-calculator input[type="range"]::-moz-range-thumb:hover { |
| transform: scale(1.1); |
| box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); |
| } |
| |
| |
| .d3-parameter-calculator input[type="range"]::-moz-range-progress { |
| background: var(--primary-color); |
| height: 6px; |
| border-radius: 3px; |
| } |
| |
| |
| .d3-parameter-calculator .total-params-container { |
| background: var(--page-bg); |
| border: 1px solid var(--border-color); |
| border-radius: 8px; |
| padding: 20px; |
| margin: 16px 0 20px 0; |
| text-align: center; |
| width: 100%; |
| } |
| |
| .d3-parameter-calculator .total-params { |
| font-size: 1.8em; |
| font-weight: bold; |
| color: var(--primary-color); |
| margin: 0 0 4px 0; |
| } |
| |
| .d3-parameter-calculator .total-params-label { |
| font-size: 0.8em; |
| font-weight: 500; |
| color: var(--muted-color); |
| margin: 0; |
| } |
| |
| .d3-parameter-calculator .breakdown { |
| display: grid; |
| grid-template-columns: 1fr 1fr; |
| gap: 12px; |
| margin-bottom: 24px; |
| } |
| |
| .d3-parameter-calculator .component { |
| background: var(--page-bg); |
| border: 1px solid var(--border-color); |
| border-radius: 8px; |
| padding: 16px; |
| position: relative; |
| } |
| |
| .d3-parameter-calculator .component-title { |
| font-weight: 700; |
| color: var(--text-color); |
| margin-bottom: 8px; |
| font-size: 1em; |
| text-transform: uppercase; |
| letter-spacing: 0.5px; |
| text-align: center; |
| } |
| |
| .d3-parameter-calculator .component-description { |
| font-size: 0.8em; |
| color: var(--muted-color); |
| margin-bottom: 12px; |
| line-height: 1.3; |
| text-align: center; |
| } |
| |
| .d3-parameter-calculator .component-params { |
| font-weight: 700; |
| color: var(--primary-color); |
| font-size: 1.2em; |
| text-align: center; |
| margin: 12px 0 8px 0; |
| padding: 8px; |
| } |
| |
| .d3-parameter-calculator .calculation-section { |
| background: var(--page-bg); |
| border: 1px solid var(--border-color); |
| border-radius: 6px; |
| padding: 10px; |
| margin: 8px 0; |
| } |
| |
| .d3-parameter-calculator .component-calculation { |
| font-family: 'Courier New', monospace; |
| font-size: 0.8em; |
| color: var(--text-color); |
| font-weight: 600; |
| text-align: center; |
| margin-bottom: 6px; |
| } |
| |
| .d3-parameter-calculator .component-formula { |
| font-size: 0.75em; |
| color: var(--text-color); |
| opacity: 0.7; |
| font-style: italic; |
| text-align: center; |
| border-top: 1px solid var(--border-color); |
| padding-top: 6px; |
| } |
| |
| .d3-parameter-calculator .comparison { |
| display: grid; |
| grid-template-columns: 1fr 1fr 1fr; |
| gap: 12px; |
| margin: 16px 0 0 0; |
| } |
| |
| .d3-parameter-calculator .config-card { |
| background: var(--page-bg); |
| border: 1px solid var(--border-color); |
| border-radius: 6px; |
| padding: 12px; |
| text-align: center; |
| flex: 1; |
| min-width: 0; |
| } |
| |
| .d3-parameter-calculator .config-title { |
| font-size: 0.75em; |
| color: var(--text-color); |
| margin-bottom: 4px; |
| font-weight: 700; |
| text-transform: uppercase; |
| letter-spacing: 0.5px; |
| } |
| |
| .d3-parameter-calculator .config-value { |
| font-weight: 600; |
| color: var(--primary-color); |
| font-size: 0.9em; |
| } |
| |
| .d3-parameter-calculator .formula-box { |
| background: transparent; |
| border: none; |
| padding: 12px 0; |
| margin: 12px 0 0 0; |
| font-family: 'Courier New', monospace; |
| font-size: 0.75em; |
| color: var(--text-color); |
| text-align: center; |
| } |
| |
| |
| @media (max-width: 768px) { |
| .d3-parameter-calculator .controls-grid { |
| grid-template-columns: 1fr; |
| gap: 12px; |
| } |
| |
| .d3-parameter-calculator .breakdown { |
| grid-template-columns: 1fr; |
| gap: 12px; |
| } |
| |
| .d3-parameter-calculator .comparison { |
| grid-template-columns: 1fr; |
| gap: 12px; |
| } |
| |
| .d3-parameter-calculator .total-params { |
| font-size: 1.5em; |
| } |
| |
| .d3-parameter-calculator .component-params { |
| font-size: 1.1em; |
| } |
| } |
| |
| @media (max-width: 480px) { |
| .d3-parameter-calculator { |
| padding: 16px; |
| } |
| |
| .d3-parameter-calculator .total-params-container { |
| padding: 16px; |
| } |
| |
| .d3-parameter-calculator .component { |
| padding: 12px; |
| } |
| |
| .d3-parameter-calculator .config-card { |
| padding: 10px; |
| } |
| |
| .d3-parameter-calculator .total-params { |
| font-size: 1.3em; |
| } |
| } |
| </style> |
|
|
| <script> |
| (() => { |
| const bootstrap = () => { |
| const scriptEl = document.currentScript; |
| let container = scriptEl ? scriptEl.previousElementSibling : null; |
| if (!(container && container.classList && container.classList.contains('d3-parameter-calculator'))) { |
| const candidates = Array.from(document.querySelectorAll('.d3-parameter-calculator')) |
| .filter((el) => !(el.dataset && el.dataset.mounted === 'true')); |
| container = candidates[candidates.length - 1] || null; |
| } |
| if (!container) return; |
| if (container.dataset) { |
| if (container.dataset.mounted === 'true') return; |
| container.dataset.mounted = 'true'; |
| } |
| |
| // Create the calculator interface |
| container.innerHTML = ` |
| <div class="controls-grid"> |
| <div class="slider-group"> |
| <label for="vocabSize">Vocabulary Size <span class="slider-value" id="vocabValue">128k</span></label> |
| <input type="range" id="vocabSize" min="32000" max="256000" step="1000" value="128256"> |
| </div> |
| |
| <div class="slider-group"> |
| <label for="hiddenSize">Hidden Size <span class="slider-value" id="hiddenValue">2048</span></label> |
| <input type="range" id="hiddenSize" min="512" max="8192" step="64" value="2048"> |
| </div> |
| |
| <div class="slider-group"> |
| <label for="numLayers">Layers <span class="slider-value" id="layersValue">16</span></label> |
| <input type="range" id="numLayers" min="4" max="64" step="1" value="16"> |
| </div> |
| |
| <div class="slider-group"> |
| <label for="numHeads">Attention Heads <span class="slider-value" id="headsValue">32</span></label> |
| <input type="range" id="numHeads" min="8" max="128" step="1" value="32"> |
| </div> |
| |
| <div class="slider-group"> |
| <label for="numKVHeads">KV Heads <span class="slider-value" id="kvHeadsValue">32</span></label> |
| <input type="range" id="numKVHeads" min="1" max="64" step="1" value="32"> |
| </div> |
| |
| <div class="slider-group"> |
| <label for="intermediateSize">Intermediate Size <span class="slider-value" id="intermediateValue">8192</span></label> |
| <input type="range" id="intermediateSize" min="2048" max="32768" step="256" value="8192"> |
| </div> |
| |
| <div class="control-group" style="grid-column: 1 / -1;"> |
| <label for="tieEmbeddings">Tie Embeddings</label> |
| <select id="tieEmbeddings"> |
| <option value="true">Yes</option> |
| <option value="false">No</option> |
| </select> |
| </div> |
| </div> |
| |
| <div class="total-params-container"> |
| <div class="total-params" id="totalParams">1.46B</div> |
| <div class="total-params-label">Parameters</div> |
| </div> |
| |
| <div class="breakdown"> |
| <div class="component"> |
| <div class="component-title">Embeddings</div> |
| <div class="component-description">Input + Output Projection</div> |
| <div class="component-params" id="embeddingParams">524M</div> |
| <div class="calculation-section"> |
| <div class="component-calculation" id="embeddingCalculation">128k × 2048 × 2</div> |
| <div class="component-formula" id="embeddingFormula">vocab_size × hidden_size × 2</div> |
| </div> |
| </div> |
| |
| <div class="component"> |
| <div class="component-title">Attention Layers</div> |
| <div class="component-description">Q, K, V, O projections</div> |
| <div class="component-params" id="attentionParams">268M</div> |
| <div class="calculation-section"> |
| <div class="component-calculation" id="attentionCalculation">16 × 2048² × 4</div> |
| <div class="component-formula" id="attentionFormula">layers × hidden_size² × 4</div> |
| </div> |
| </div> |
| |
| <div class="component"> |
| <div class="component-title">Feed Forward</div> |
| <div class="component-description">Up, Gate, Down projections</div> |
| <div class="component-params" id="ffnParams">805M</div> |
| <div class="calculation-section"> |
| <div class="component-calculation" id="ffnCalculation">16 × 2048 × 8192 × 3</div> |
| <div class="component-formula" id="ffnFormula">layers × hidden_size × intermediate_size × 3</div> |
| </div> |
| </div> |
| |
| <div class="component"> |
| <div class="component-title">Layer Norms</div> |
| <div class="component-description">Input + Attention norms</div> |
| <div class="component-params" id="lnParams">131K</div> |
| <div class="calculation-section"> |
| <div class="component-calculation" id="lnCalculation">16 × 2048 × 2</div> |
| <div class="component-formula" id="lnFormula">layers × hidden_size × 2</div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="comparison"> |
| <div class="config-card"> |
| <div class="config-title">Attention Type</div> |
| <div class="config-value" id="attentionType">MHA</div> |
| </div> |
| <div class="config-card"> |
| <div class="config-title">Embedding Strategy</div> |
| <div class="config-value" id="embeddingStrategy">Tied</div> |
| </div> |
| <div class="config-card"> |
| <div class="config-title">Params per Layer</div> |
| <div class="config-value" id="paramsPerLayer">67M</div> |
| </div> |
| </div> |
| `; |
| |
| function updateSliderValues() { |
| const vocabSize = parseInt(container.querySelector('#vocabSize').value) || 0; |
| const hiddenSize = parseInt(container.querySelector('#hiddenSize').value) || 0; |
| const numLayers = parseInt(container.querySelector('#numLayers').value) || 0; |
| const numHeads = parseInt(container.querySelector('#numHeads').value) || 0; |
| const numKVHeads = parseInt(container.querySelector('#numKVHeads').value) || 0; |
| const intermediateSize = parseInt(container.querySelector('#intermediateSize').value) || 0; |
| |
| container.querySelector('#vocabValue').innerHTML = vocabSize >= 1000 ? `${(vocabSize / 1000).toFixed(0)} <span style="opacity: 0.6;">k</span>` : vocabSize.toString(); |
| container.querySelector('#hiddenValue').textContent = hiddenSize.toString(); |
| container.querySelector('#layersValue').textContent = numLayers.toString(); |
| container.querySelector('#headsValue').textContent = numHeads.toString(); |
| container.querySelector('#kvHeadsValue').textContent = numKVHeads.toString(); |
| container.querySelector('#intermediateValue').textContent = intermediateSize.toString(); |
| |
| // Update progress bars |
| updateProgressBars(); |
| } |
| |
| function updateProgressBars() { |
| const sliders = container.querySelectorAll('input[type="range"]'); |
| sliders.forEach(slider => { |
| const sliderGroup = slider.closest('.slider-group'); |
| if (sliderGroup) { |
| const min = parseFloat(slider.min); |
| const max = parseFloat(slider.max); |
| const value = parseFloat(slider.value); |
| const percentage = ((value - min) / (max - min)) * 100; |
| sliderGroup.style.setProperty('--progress-width', `${percentage}%`); |
| } |
| }); |
| } |
| |
| function calculateParameters() { |
| const vocabSize = parseInt(container.querySelector('#vocabSize').value) || 0; |
| const hiddenSize = parseInt(container.querySelector('#hiddenSize').value) || 0; |
| const numLayers = parseInt(container.querySelector('#numLayers').value) || 0; |
| const numHeads = parseInt(container.querySelector('#numHeads').value) || 0; |
| const numKVHeads = parseInt(container.querySelector('#numKVHeads').value) || 0; |
| const intermediateSize = parseInt(container.querySelector('#intermediateSize').value) || 0; |
| const tieEmbeddings = container.querySelector('#tieEmbeddings').value === 'true'; |
| |
| updateSliderValues(); |
| |
| // Calculate each component |
| const embeddingParams = vocabSize * hiddenSize * (tieEmbeddings ? 1 : 2); |
| |
| // Attention parameters - corrected formula |
| let attentionParams; |
| if (numKVHeads === numHeads) { |
| // MHA: 4 full projections (Q, K, V, O) |
| attentionParams = numLayers * 4 * hiddenSize * hiddenSize; |
| } else { |
| // GQA/MQA: Q + O are full size, K + V are reduced |
| attentionParams = numLayers * hiddenSize * hiddenSize * (2 + 2 * numKVHeads / numHeads); |
| } |
| |
| // FFN parameters (up, gate, down) |
| const ffnParams = numLayers * hiddenSize * intermediateSize * 3; |
| |
| // Layer norm parameters |
| const lnParams = numLayers * hiddenSize * 2 + hiddenSize; // +1 for final layer norm |
| |
| // Total |
| const totalParams = embeddingParams + attentionParams + ffnParams + lnParams; |
| |
| // Format numbers with reduced opacity suffixes |
| const formatNumber = (num) => { |
| if (num >= 1000000000) return `${(num / 1000000000).toFixed(2)} <span style="opacity: 0.6;">B</span>`; |
| if (num >= 1000000) return `${(num / 1000000).toFixed(0)} <span style="opacity: 0.6;">M</span>`; |
| if (num >= 1000) return `${(num / 1000).toFixed(0)} <span style="opacity: 0.6;">K</span>`; |
| return num.toString(); |
| }; |
| |
| // Update displays |
| container.querySelector('#totalParams').innerHTML = formatNumber(totalParams); |
| container.querySelector('#embeddingParams').innerHTML = formatNumber(embeddingParams); |
| container.querySelector('#attentionParams').innerHTML = formatNumber(attentionParams); |
| container.querySelector('#ffnParams').innerHTML = formatNumber(ffnParams); |
| container.querySelector('#lnParams').innerHTML = formatNumber(lnParams); |
| |
| // Update calculations and formulas |
| const vocabDisplay = vocabSize >= 1000 ? `${(vocabSize / 1000).toFixed(0)} <span style="opacity: 0.6;">k</span>` : vocabSize.toString(); |
| |
| container.querySelector('#embeddingCalculation').innerHTML = |
| tieEmbeddings ? `${vocabDisplay} × ${hiddenSize}` : `${vocabDisplay} × ${hiddenSize} × 2`; |
| container.querySelector('#embeddingFormula').textContent = |
| tieEmbeddings ? 'vocab_size × hidden_size' : 'vocab_size × hidden_size × 2'; |
| |
| if (numKVHeads === numHeads) { |
| container.querySelector('#attentionCalculation').textContent = |
| `${numLayers} × ${hiddenSize}² × 4`; |
| container.querySelector('#attentionFormula').textContent = |
| 'layers × hidden_size² × 4'; |
| } else { |
| container.querySelector('#attentionCalculation').textContent = |
| `${numLayers} × ${hiddenSize}² × ${(2 + 2 * numKVHeads / numHeads).toFixed(2)}`; |
| container.querySelector('#attentionFormula').textContent = |
| 'layers × hidden_size² × (2 + 2 × kv_heads/num_heads)'; |
| } |
| |
| container.querySelector('#ffnCalculation').textContent = |
| `${numLayers} × ${hiddenSize} × ${intermediateSize} × 3`; |
| container.querySelector('#ffnFormula').textContent = |
| 'layers × hidden_size × intermediate_size × 3'; |
| |
| container.querySelector('#lnCalculation').textContent = |
| `${numLayers} × ${hiddenSize} × 2`; |
| container.querySelector('#lnFormula').textContent = |
| 'layers × hidden_size × 2'; |
| |
| // Update config display |
| let attentionType = 'MHA'; |
| if (numKVHeads === 1) attentionType = 'MQA'; |
| else if (numKVHeads < numHeads) attentionType = 'GQA'; |
| |
| container.querySelector('#attentionType').textContent = attentionType; |
| container.querySelector('#embeddingStrategy').textContent = tieEmbeddings ? 'Tied' : 'Separate'; |
| container.querySelector('#paramsPerLayer').innerHTML = |
| formatNumber((attentionParams + ffnParams + lnParams) / numLayers); |
| |
| // Update progress bars |
| updateProgressBars(); |
| } |
| |
| // Add event listeners |
| container.querySelector('#vocabSize').addEventListener('input', calculateParameters); |
| container.querySelector('#hiddenSize').addEventListener('input', calculateParameters); |
| container.querySelector('#numLayers').addEventListener('input', calculateParameters); |
| container.querySelector('#numHeads').addEventListener('input', calculateParameters); |
| container.querySelector('#numKVHeads').addEventListener('input', calculateParameters); |
| container.querySelector('#intermediateSize').addEventListener('input', calculateParameters); |
| container.querySelector('#tieEmbeddings').addEventListener('change', calculateParameters); |
| |
| // Initialize |
| updateSliderValues(); |
| calculateParameters(); |
| }; |
| |
| if (document.readyState === 'loading') { |
| document.addEventListener('DOMContentLoaded', bootstrap, { once: true }); |
| } else { |
| bootstrap(); |
| } |
| })(); |
| </script> |