Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Apertus-8B: Layer Structure (Interactive)</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Lora:ital,wght@0,400;0,600;1,400&display=swap" rel="stylesheet" /> | |
| <style> | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| body { | |
| background: #fff; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| min-height: 100vh; | |
| padding: 32px; | |
| } | |
| #stackGroup { transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1); } | |
| #detailGroup { transition: opacity 0.35s ease; } | |
| #hintEl { transition: opacity 0.3s ease; } | |
| </style> | |
| </head> | |
| <body> | |
| <svg id="viz" width="1100" height="560" viewBox="0 -20 980 500" | |
| style="font-family:Helvetica, Arial, sans-serif; display:block;"></svg> | |
| <script> | |
| const svg = document.getElementById('viz'); | |
| const ns = 'http://www.w3.org/2000/svg'; | |
| function el(tag, attrs, text) { | |
| const e = document.createElementNS(ns, tag); | |
| for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, v); | |
| if (text !== undefined) e.textContent = text; | |
| return e; | |
| } | |
| function txt(x, y, str, attrs = {}) { | |
| return el('text', { x, y, 'text-anchor': 'middle', | |
| 'font-family': 'Inter,sans-serif', 'font-size': 13, fill: '#444', ...attrs }, str); | |
| } | |
| function arrowRight(x1, y1, x2, y2, color = '#bbb', dash = '') { | |
| const g = el('g', {}); | |
| g.appendChild(el('line', { x1, y1, x2, y2, stroke: color, | |
| 'stroke-width': 2, 'stroke-linecap': 'round', | |
| ...(dash ? { 'stroke-dasharray': dash } : {}) })); | |
| const a = Math.atan2(y2 - y1, x2 - x1), L = 9, s = 0.4; | |
| g.appendChild(el('polygon', { | |
| points: `${x2},${y2} ${x2 - L*Math.cos(a-s)},${y2 - L*Math.sin(a-s)} ${x2 - L*Math.cos(a+s)},${y2 - L*Math.sin(a+s)}`, | |
| fill: color | |
| })); | |
| return g; | |
| } | |
| // ββ Layout ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const CY = 240; | |
| const N_LAYERS = 32; | |
| const REKT_W = 72; | |
| const REKT_H = 9; | |
| const REKT_GAP = 3; | |
| const totalH = N_LAYERS * REKT_H + (N_LAYERS - 1) * REKT_GAP; | |
| const stackTopY = CY - totalH / 2; | |
| const stackX = 88; // original left-side X (used when detail is shown) | |
| const INIT_OFFSET = 490 - stackX; // translateX to center stack initially (= 402) | |
| const detailX = 400; | |
| const detailW = 210; | |
| const detailH = 330; | |
| const detailY = CY - detailH / 2; | |
| const vecX = 630; | |
| // ββ Groups (detail behind stack) ββββββββββββββββββββββββββββββ | |
| const detailGroup = el('g', { id: 'detailGroup' }); | |
| detailGroup.style.opacity = '0'; | |
| const stackGroup = el('g', { id: 'stackGroup' }); | |
| stackGroup.style.transform = `translateX(${INIT_OFFSET}px)`; | |
| // ββ Top hint label (static) βββββββββββββββββββββββββββββββ | |
| svg.appendChild(el('text', { | |
| x: 490, y: -8, | |
| 'text-anchor': 'middle', | |
| 'font-family': 'Inter,sans-serif', | |
| 'font-size': 11, | |
| fill: '#bbb', | |
| 'font-style': 'normal', | |
| }, 'Select a layer to zoom into the Apertus-8B architecture')); | |
| svg.appendChild(detailGroup); | |
| svg.appendChild(stackGroup); | |
| // ββ Stack: header βββββββββββββββββββββββββββββββββββββββββββββ | |
| stackGroup.appendChild(txt(stackX, stackTopY - 28, 'Apertus-8B-Instruct', | |
| { 'font-size': 13, 'font-weight': 700, fill: '#2c4a7c' })); | |
| stackGroup.appendChild(txt(stackX, stackTopY - 14, '32 transformer layers', | |
| { 'font-size': 10, fill: '#aaa' })); | |
| const hintEl = txt(stackX, stackTopY + totalH + 18, 'hover to explore', | |
| { 'font-size': 10, fill: '#ccc', 'font-style': 'normal', id: 'hintEl' }); | |
| stackGroup.appendChild(hintEl); | |
| // ββ Stack: anchor labels (0 and 31) βββββββββββββββββββββββββββ | |
| stackGroup.appendChild(txt(stackX + REKT_W/2 + 18, stackTopY + 9, | |
| 'Layer 0', { 'font-size': 9, fill: '#ccc', 'text-anchor': 'start' })); | |
| stackGroup.appendChild(txt(stackX + REKT_W/2 + 18, | |
| stackTopY + (N_LAYERS - 1) * (REKT_H + REKT_GAP) + 9, | |
| 'Layer 31', { 'font-size': 9, fill: '#ccc', 'text-anchor': 'start' })); | |
| // ββ Stack: layer rects ββββββββββββββββββββββββββββββββββββββββ | |
| const layerRects = []; | |
| for (let i = 0; i < N_LAYERS; i++) { | |
| const ry = stackTopY + i * (REKT_H + REKT_GAP); | |
| const r = el('rect', { | |
| x: stackX - REKT_W / 2, y: ry, | |
| width: REKT_W, height: REKT_H, | |
| rx: 0, | |
| fill: '#e5e7eb', stroke: '#d1d5db', 'stroke-width': 0.8, | |
| 'data-layer': i | |
| }); | |
| r.style.cursor = 'pointer'; | |
| stackGroup.appendChild(r); | |
| layerRects.push(r); | |
| } | |
| // Dynamic hover label (inside stackGroup: moves with stack) | |
| const hoverLabel = txt(stackX + REKT_W/2 + 18, stackTopY, | |
| '', { 'font-size': 9, fill: '#2c4a7c', 'font-weight': 700, | |
| 'text-anchor': 'start', opacity: 0 }); | |
| stackGroup.appendChild(hoverLabel); | |
| // ββ Detail: zoom fan (dynamic) ββββββββββββββββββββββββββββββββ | |
| const zoomGroup = el('g', {}); | |
| detailGroup.appendChild(zoomGroup); | |
| function updateZoomLines(i) { | |
| while (zoomGroup.firstChild) zoomGroup.removeChild(zoomGroup.firstChild); | |
| const hlY = stackTopY + i * (REKT_H + REKT_GAP); | |
| const hlRight = stackX + REKT_W / 2; | |
| const zDstX = detailX - detailW / 2; | |
| zoomGroup.appendChild(el('polygon', { | |
| points: `${hlRight},${hlY} ${zDstX},${detailY} ${zDstX},${detailY+detailH} ${hlRight},${hlY+REKT_H}`, | |
| fill: '#f5f3ff', stroke: 'none' | |
| })); | |
| zoomGroup.appendChild(el('line', { | |
| x1: hlRight, y1: hlY, x2: zDstX, y2: detailY, | |
| stroke: '#aab9d2', 'stroke-width': 1, 'stroke-dasharray': '5 4' | |
| })); | |
| zoomGroup.appendChild(el('line', { | |
| x1: hlRight, y1: hlY + REKT_H, x2: zDstX, y2: detailY + detailH, | |
| stroke: '#aab9d2', 'stroke-width': 1, 'stroke-dasharray': '5 4' | |
| })); | |
| } | |
| // ββ Detail: box βββββββββββββββββββββββββββββββββββββββββββββββ | |
| // shadow | |
| detailGroup.appendChild(el('rect', { | |
| x: detailX - detailW/2 + 4, y: detailY + 4, | |
| width: detailW, height: detailH, rx: 0, fill: '#eef1f6', opacity: 0.5 | |
| })); | |
| // card | |
| detailGroup.appendChild(el('rect', { | |
| x: detailX - detailW/2, y: detailY, | |
| width: detailW, height: detailH, rx: 0, | |
| fill: '#fff', stroke: '#aab9d2', 'stroke-width': 2 | |
| })); | |
| // top accent bar | |
| detailGroup.appendChild(el('rect', { | |
| x: detailX - detailW/2, y: detailY, | |
| width: detailW, height: 5, rx: 0, fill: '#2c4a7c' | |
| })); | |
| const layerNumLabel = txt(detailX, detailY + 22, 'Layer 0', | |
| { 'font-size': 13, 'font-weight': 700, fill: '#1e1b4b' }); | |
| detailGroup.appendChild(layerNumLabel); | |
| function subBlock(cy, bg, border, label, sublabel) { | |
| const bW = detailW - 36, bH = sublabel ? 56 : 46; | |
| const bX = detailX - bW / 2; | |
| const bY = cy - bH / 2; | |
| detailGroup.appendChild(el('rect', { x: bX, y: bY, width: bW, height: bH, | |
| rx: 0, fill: bg, stroke: border, 'stroke-width': 1.3 })); | |
| detailGroup.appendChild(txt(detailX, bY + (sublabel ? bH/2 - 6 : bH/2 + 5), | |
| label, { 'font-size': 11, 'font-weight': 600, fill: '#333' })); | |
| if (sublabel) detailGroup.appendChild(txt(detailX, bY + bH/2 + 9, | |
| sublabel, { 'font-size': 9.5, fill: '#888' })); | |
| return { top: bY, bot: bY + bH }; | |
| } | |
| const attn = subBlock(detailY + 88, '#eef1f6', '#6c83a8', 'Multi-Head Self-Attention', '32Q / 8KV heads (GQA) Β· dβ = 128'); | |
| const ffn = subBlock(detailY + 192, '#fef3c7', '#fbbf24', 'Feed-Forward Network', '4096 β 21504 β 4096'); | |
| const norm = subBlock(detailY + 282, '#f0fdf4', '#86efac', 'RMSNorm + Residual', null); | |
| detailGroup.appendChild(arrowRight(detailX, attn.bot + 1, detailX, ffn.top - 1, '#bbb')); | |
| detailGroup.appendChild(arrowRight(detailX, ffn.bot + 1, detailX, norm.top - 1, '#bbb')); | |
| const skipX = detailX - detailW / 2 + 12; | |
| detailGroup.appendChild(el('path', { | |
| d: `M${skipX},${attn.top + 4} L${skipX},${norm.bot - 4}`, | |
| stroke: '#aab9d2', 'stroke-width': 1.5, 'stroke-dasharray': '4 3', fill: 'none' | |
| })); | |
| detailGroup.appendChild(txt(skipX, (attn.top + norm.bot) / 2 + 4, '+', | |
| { 'font-size': 11, fill: '#6c83a8', 'font-weight': 700 })); | |
| // input arrow + label | |
| detailGroup.appendChild(arrowRight(detailX, detailY - 28, detailX, detailY - 2, '#bbb')); | |
| detailGroup.appendChild(txt(detailX, detailY - 36, 'hβ β ββ΄β°βΉβΆ', | |
| { 'font-size': 13, fill: '#888', 'font-style': 'normal' })); | |
| // output arrow to vec | |
| detailGroup.appendChild(arrowRight(detailX + detailW/2 + 2, CY, vecX - 30, CY, '#555')); | |
| // ββ Detail: activation vector βββββββββββββββββββββββββββββββββ | |
| const vecW = 32; | |
| const vecH = 260; | |
| const vecTopY = CY - vecH / 2; | |
| detailGroup.appendChild(el('rect', { | |
| x: vecX - vecW/2, y: vecTopY, width: vecW, height: vecH, | |
| rx: 0, fill: '#e0e7ff', stroke: '#a5b4fc', 'stroke-width': 1.5 | |
| })); | |
| detailGroup.appendChild(txt(vecX, vecTopY - 12, '4 096 activations', | |
| { 'font-size': 13, fill: '#2c4a7c', 'font-weight': 700 })); | |
| detailGroup.appendChild(el('line', { | |
| x1: vecX+vecW/2+4, y1: vecTopY, x2: vecX+vecW/2+4, y2: vecTopY+vecH, | |
| stroke: '#bbb', 'stroke-width': 1 })); | |
| detailGroup.appendChild(el('line', { | |
| x1: vecX+vecW/2+4, y1: vecTopY, x2: vecX+vecW/2+9, y2: vecTopY, | |
| stroke: '#bbb', 'stroke-width': 1 })); | |
| detailGroup.appendChild(el('line', { | |
| x1: vecX+vecW/2+4, y1: vecTopY+vecH, x2: vecX+vecW/2+9, y2: vecTopY+vecH, | |
| stroke: '#bbb', 'stroke-width': 1 })); | |
| // ββ Detail: PCA + fan-out βββββββββββββββββββββββββββββββββββββ | |
| const pcaX = 780, pcaW = 64, pcaH = 40; | |
| detailGroup.appendChild(arrowRight(vecX + vecW/2 + 2, CY, pcaX - pcaW/2 - 2, CY, '#555')); | |
| detailGroup.appendChild(el('rect', { | |
| x: pcaX - pcaW/2, y: CY - pcaH/2, width: pcaW, height: pcaH, | |
| rx: 0, fill: '#f8fafc', stroke: '#94a3b8', 'stroke-width': 1.8 | |
| })); | |
| detailGroup.appendChild(txt(pcaX, CY + 5, 'PCA', | |
| { 'font-size': 13, 'font-weight': 700, fill: '#334155' })); | |
| const outX = 920; | |
| [CY - 90, CY, CY + 90].forEach((oy, i) => { | |
| detailGroup.appendChild(arrowRight(pcaX + pcaW/2 + 2, CY, outX - 22, oy, '#94a3b8')); | |
| detailGroup.appendChild(el('circle', { | |
| cx: outX, cy: oy, r: 20, | |
| fill: '#f1f5f9', stroke: '#94a3b8', 'stroke-width': 1.5 | |
| })); | |
| detailGroup.appendChild(txt(outX, oy - 3, ['1D','2D','3D'][i], | |
| { 'font-size': 11, 'font-weight': 700, fill: '#334155' })); | |
| detailGroup.appendChild(txt(outX, oy + 11, ['βΒΉ','βΒ²','βΒ³'][i], | |
| { 'font-size': 13, fill: '#94a3b8', 'font-style': 'normal' })); | |
| }); | |
| // ββ Interaction βββββββββββββββββββββββββββββββββββββββββββββββ | |
| let current = null; | |
| function show(i) { | |
| if (current === i) return; | |
| current = i; | |
| layerRects.forEach((r, idx) => { | |
| r.setAttribute('fill', idx === i ? '#2c4a7c' : '#e5e7eb'); | |
| r.setAttribute('stroke', idx === i ? '#4338ca' : '#d1d5db'); | |
| r.setAttribute('stroke-width', idx === i ? 1.5 : 0.8); | |
| }); | |
| const ry = stackTopY + i * (REKT_H + REKT_GAP); | |
| hoverLabel.setAttribute('y', ry + 9); | |
| hoverLabel.textContent = `Layer ${i}`; | |
| hoverLabel.setAttribute('opacity', 1); | |
| updateZoomLines(i); | |
| layerNumLabel.textContent = `Layer ${i}`; | |
| stackGroup.style.transform = 'translateX(0)'; | |
| detailGroup.style.opacity = '1'; | |
| hintEl.style.opacity = '0'; | |
| } | |
| function hide() { | |
| current = null; | |
| layerRects.forEach(r => { | |
| r.setAttribute('fill', '#e5e7eb'); | |
| r.setAttribute('stroke', '#d1d5db'); | |
| r.setAttribute('stroke-width', 0.8); | |
| }); | |
| hoverLabel.setAttribute('opacity', 0); | |
| stackGroup.style.transform = `translateX(${INIT_OFFSET}px)`; | |
| detailGroup.style.opacity = '0'; | |
| hintEl.style.opacity = '1'; | |
| } | |
| // Show on rect hover, update active layer when moving between rects | |
| layerRects.forEach((r, i) => r.addEventListener('mouseenter', () => show(i))); | |
| // Only hide when mouse leaves the entire SVG | |
| svg.addEventListener('mouseleave', hide); | |
| </script> | |
| </body> | |
| </html> | |