Upload index.html
Browse files- index.html +226 -19
index.html
CHANGED
|
@@ -1,19 +1,226 @@
|
|
| 1 |
-
|
| 2 |
-
<
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
<style>
|
| 3 |
+
.pe-wrap { font-family: var(--font-sans); padding: 1rem 0; }
|
| 4 |
+
.pe-title { font-size: 15px; font-weight: 500; color: var(--color-text-primary); margin-bottom: 4px; }
|
| 5 |
+
.pe-sub { font-size: 12px; color: var(--color-text-secondary); margin-bottom: 16px; }
|
| 6 |
+
.pe-canvas-wrap { position: relative; width: 100%; }
|
| 7 |
+
.pe-axis-label { font-size: 12px; color: var(--color-text-secondary); text-align: center; }
|
| 8 |
+
.pe-y-label { writing-mode: vertical-rl; transform: rotate(180deg); font-size: 12px; color: var(--color-text-secondary); display: flex; align-items: center; justify-content: center; min-width: 20px; }
|
| 9 |
+
.pe-row { display: flex; align-items: stretch; gap: 8px; }
|
| 10 |
+
.pe-legend { display: flex; align-items: center; gap: 8px; margin-top: 10px; font-size: 11px; color: var(--color-text-secondary); }
|
| 11 |
+
.pe-legend-bar { flex: 1; height: 12px; border-radius: 3px; }
|
| 12 |
+
.annotation-box { background: var(--color-background-secondary); border: 0.5px solid var(--color-border-tertiary); border-radius: 8px; padding: 10px 14px; margin-top: 14px; font-size: 12px; color: var(--color-text-secondary); line-height: 1.6; }
|
| 13 |
+
.ann-row { display: flex; gap: 16px; }
|
| 14 |
+
.ann-col { flex: 1; }
|
| 15 |
+
.ann-head { font-weight: 500; font-size: 12px; color: var(--color-text-primary); margin-bottom: 3px; }
|
| 16 |
+
.highlight-col { border: 2px solid #EF9F27; border-radius: 3px; position: absolute; pointer-events: none; }
|
| 17 |
+
.highlight-row { border: 2px solid #1D9E75; border-radius: 3px; position: absolute; pointer-events: none; }
|
| 18 |
+
.pe-controls { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; align-items: center; }
|
| 19 |
+
.pe-btn { background: var(--color-background-secondary); border: 0.5px solid var(--color-border-secondary); border-radius: 6px; padding: 4px 12px; font-size: 12px; cursor: pointer; color: var(--color-text-primary); }
|
| 20 |
+
.pe-btn.active { background: var(--color-background-info); color: var(--color-text-info); border-color: var(--color-border-info); }
|
| 21 |
+
.tooltip { position: absolute; background: var(--color-background-primary); border: 0.5px solid var(--color-border-secondary); border-radius: 6px; padding: 6px 10px; font-size: 11px; color: var(--color-text-primary); pointer-events: none; opacity: 0; transition: opacity 0.15s; white-space: nowrap; z-index: 10; }
|
| 22 |
+
</style>
|
| 23 |
+
<div class="pe-wrap">
|
| 24 |
+
<div class="pe-title">Positional Encoding Matrix</div>
|
| 25 |
+
<div class="pe-sub">Each cell = PE(pos, i). Hover to inspect. Click a column (dimension) or row (position) to highlight it.</div>
|
| 26 |
+
|
| 27 |
+
<div class="pe-controls">
|
| 28 |
+
<span style="font-size:12px;color:var(--color-text-secondary)">Hover mode:</span>
|
| 29 |
+
<button class="pe-btn active" id="btn-cell" onclick="setMode('cell')">Cell value</button>
|
| 30 |
+
<button class="pe-btn" id="btn-col" onclick="setMode('col')">Column (dimension)</button>
|
| 31 |
+
<button class="pe-btn" id="btn-row" onclick="setMode('row')">Row (position)</button>
|
| 32 |
+
</div>
|
| 33 |
+
|
| 34 |
+
<div class="pe-row">
|
| 35 |
+
<div class="pe-y-label" id="ylabel">Sequence position <em>(pos β)</em></div>
|
| 36 |
+
<div style="flex:1;position:relative;">
|
| 37 |
+
<canvas id="pe-canvas" style="width:100%;display:block;cursor:crosshair;"></canvas>
|
| 38 |
+
<div class="tooltip" id="tt"></div>
|
| 39 |
+
</div>
|
| 40 |
+
</div>
|
| 41 |
+
<div style="margin-left:28px">
|
| 42 |
+
<div class="pe-axis-label">Embedding dimension <em>(i β)</em></div>
|
| 43 |
+
<div style="display:flex;justify-content:space-between;font-size:10px;color:var(--color-text-secondary);margin-top:2px;margin-left:2px;margin-right:2px">
|
| 44 |
+
<span>i=0 (fast)</span><span>i=128</span><span>i=256 (slow)</span>
|
| 45 |
+
</div>
|
| 46 |
+
</div>
|
| 47 |
+
|
| 48 |
+
<div class="pe-legend" style="margin-left:28px">
|
| 49 |
+
<span>β1</span>
|
| 50 |
+
<canvas id="legend-bar" width="300" height="12" style="flex:1;border-radius:3px;"></canvas>
|
| 51 |
+
<span>+1</span>
|
| 52 |
+
</div>
|
| 53 |
+
|
| 54 |
+
<div class="annotation-box" id="ann-box">
|
| 55 |
+
<div class="ann-row">
|
| 56 |
+
<div class="ann-col">
|
| 57 |
+
<div class="ann-head">β‘ Fast dimensions (small <em>i</em>, left)</div>
|
| 58 |
+
The sine wave completes many full cycles across just a few positions. Adjacent words look very different here β the model uses this to detect <strong>neighbors</strong>.
|
| 59 |
+
</div>
|
| 60 |
+
<div class="ann-col">
|
| 61 |
+
<div class="ann-head">π’ Slow dimensions (large <em>i</em>, right)</div>
|
| 62 |
+
The wavelength is enormous (up to 10,000Β·2Ο). Values barely change between consecutive words, but differ noticeably over hundreds of positions β telling the model about <strong>long-range structure</strong>.
|
| 63 |
+
</div>
|
| 64 |
+
</div>
|
| 65 |
+
</div>
|
| 66 |
+
</div>
|
| 67 |
+
|
| 68 |
+
<script>
|
| 69 |
+
const ROWS = 50;
|
| 70 |
+
const COLS = 256;
|
| 71 |
+
const D_MODEL = 512;
|
| 72 |
+
|
| 73 |
+
function pe(pos, i) {
|
| 74 |
+
const denom = Math.pow(10000, (2 * Math.floor(i / 2)) / D_MODEL);
|
| 75 |
+
return (i % 2 === 0) ? Math.sin(pos / denom) : Math.cos(pos / denom);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
const matrix = [];
|
| 79 |
+
for (let r = 0; r < ROWS; r++) {
|
| 80 |
+
matrix[r] = [];
|
| 81 |
+
for (let c = 0; c < COLS; c++) {
|
| 82 |
+
matrix[r][c] = pe(r, c);
|
| 83 |
+
}
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
function valToColor(v, alpha) {
|
| 87 |
+
const t = (v + 1) / 2;
|
| 88 |
+
const isDark = matchMedia('(prefers-color-scheme: dark)').matches;
|
| 89 |
+
if (isDark) {
|
| 90 |
+
if (t > 0.5) {
|
| 91 |
+
const s = (t - 0.5) * 2;
|
| 92 |
+
const r = Math.round(30 + s * 200), g = Math.round(40 + s * 100), b = Math.round(80 + s * 80);
|
| 93 |
+
return `rgba(${r},${g},${b},${alpha})`;
|
| 94 |
+
} else {
|
| 95 |
+
const s = (0.5 - t) * 2;
|
| 96 |
+
const r = Math.round(30 + s * 180), g = Math.round(40 + s * 40), b = Math.round(80 + s * 120);
|
| 97 |
+
return `rgba(${r},${g},${b},${alpha})`;
|
| 98 |
+
}
|
| 99 |
+
} else {
|
| 100 |
+
if (t > 0.5) {
|
| 101 |
+
const s = (t - 0.5) * 2;
|
| 102 |
+
const r = Math.round(248 - s * 130), g = Math.round(248 - s * 30), b = Math.round(248 - s * 200);
|
| 103 |
+
return `rgba(${r},${g},${b},${alpha})`;
|
| 104 |
+
} else {
|
| 105 |
+
const s = (0.5 - t) * 2;
|
| 106 |
+
const r = Math.round(248 - s * 50), g = Math.round(248 - s * 140), b = Math.round(248 - s * 20);
|
| 107 |
+
return `rgba(${r},${g},${b},${alpha})`;
|
| 108 |
+
}
|
| 109 |
+
}
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
const canvas = document.getElementById('pe-canvas');
|
| 113 |
+
const ctx = canvas.getContext('2d');
|
| 114 |
+
let cellW, cellH, dpr;
|
| 115 |
+
|
| 116 |
+
function drawMatrix(highlightCol = -1, highlightRow = -1) {
|
| 117 |
+
const w = canvas.offsetWidth;
|
| 118 |
+
const h = Math.round(w * (ROWS / COLS) * 1.4);
|
| 119 |
+
dpr = window.devicePixelRatio || 1;
|
| 120 |
+
canvas.width = w * dpr;
|
| 121 |
+
canvas.height = h * dpr;
|
| 122 |
+
canvas.style.height = h + 'px';
|
| 123 |
+
ctx.scale(dpr, dpr);
|
| 124 |
+
cellW = w / COLS;
|
| 125 |
+
cellH = h / ROWS;
|
| 126 |
+
|
| 127 |
+
for (let r = 0; r < ROWS; r++) {
|
| 128 |
+
for (let c = 0; c < COLS; c++) {
|
| 129 |
+
const v = matrix[r][c];
|
| 130 |
+
const isHL = (highlightCol >= 0 && c === highlightCol) || (highlightRow >= 0 && r === highlightRow);
|
| 131 |
+
ctx.fillStyle = valToColor(v, isHL ? 1 : (highlightCol >= 0 || highlightRow >= 0 ? 0.35 : 1));
|
| 132 |
+
ctx.fillRect(c * cellW, r * cellH, cellW + 0.5, cellH + 0.5);
|
| 133 |
+
}
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
if (highlightCol >= 0) {
|
| 137 |
+
ctx.strokeStyle = '#EF9F27';
|
| 138 |
+
ctx.lineWidth = 2;
|
| 139 |
+
ctx.strokeRect(highlightCol * cellW + 1, 1, cellW - 1, ROWS * cellH - 2);
|
| 140 |
+
}
|
| 141 |
+
if (highlightRow >= 0) {
|
| 142 |
+
ctx.strokeStyle = '#1D9E75';
|
| 143 |
+
ctx.lineWidth = 2;
|
| 144 |
+
ctx.strokeRect(1, highlightRow * cellH + 1, COLS * cellW - 2, cellH - 1);
|
| 145 |
+
}
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
function drawLegend() {
|
| 149 |
+
const lb = document.getElementById('legend-bar');
|
| 150 |
+
const lctx = lb.getContext('2d');
|
| 151 |
+
const w = lb.offsetWidth || 300;
|
| 152 |
+
lb.width = w * (window.devicePixelRatio || 1);
|
| 153 |
+
lb.style.width = '100%';
|
| 154 |
+
for (let x = 0; x < w; x++) {
|
| 155 |
+
const v = (x / w) * 2 - 1;
|
| 156 |
+
lctx.fillStyle = valToColor(v, 1);
|
| 157 |
+
lctx.fillRect(x * (window.devicePixelRatio || 1), 0, (window.devicePixelRatio || 1), 12);
|
| 158 |
+
}
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
let mode = 'cell';
|
| 162 |
+
function setMode(m) {
|
| 163 |
+
mode = m;
|
| 164 |
+
['cell','col','row'].forEach(k => document.getElementById('btn-'+k).classList.toggle('active', k === m));
|
| 165 |
+
drawMatrix();
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
canvas.addEventListener('mousemove', function(e) {
|
| 169 |
+
const rect = canvas.getBoundingClientRect();
|
| 170 |
+
const x = e.clientX - rect.left;
|
| 171 |
+
const y = e.clientY - rect.top;
|
| 172 |
+
const col = Math.floor(x / (rect.width / COLS));
|
| 173 |
+
const row = Math.floor(y / (rect.height / ROWS));
|
| 174 |
+
if (col < 0 || col >= COLS || row < 0 || row >= ROWS) return;
|
| 175 |
+
|
| 176 |
+
const tt = document.getElementById('tt');
|
| 177 |
+
const v = matrix[row][col].toFixed(4);
|
| 178 |
+
|
| 179 |
+
if (mode === 'cell') {
|
| 180 |
+
drawMatrix();
|
| 181 |
+
tt.innerHTML = `<b>pos=${row}, i=${col}</b><br>value = ${v}<br>${col % 2 === 0 ? 'sin' : 'cos'} wave`;
|
| 182 |
+
} else if (mode === 'col') {
|
| 183 |
+
drawMatrix(-1, -1);
|
| 184 |
+
drawMatrix(col, -1);
|
| 185 |
+
const speed = col < 20 ? 'β‘ fast' : col < 100 ? 'β‘ moderate' : 'π’ slow';
|
| 186 |
+
tt.innerHTML = `<b>Dimension i=${col}</b> (${speed})<br>wavelength β ${(2 * Math.PI * Math.pow(10000, col / D_MODEL)).toFixed(0)}`;
|
| 187 |
+
} else {
|
| 188 |
+
drawMatrix(-1, row);
|
| 189 |
+
tt.innerHTML = `<b>Position pos=${row}</b><br>Word at index ${row} in the sequence`;
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
tt.style.left = Math.min(x + 10, rect.width - 180) + 'px';
|
| 193 |
+
tt.style.top = Math.max(y - 36, 0) + 'px';
|
| 194 |
+
tt.style.opacity = '1';
|
| 195 |
+
});
|
| 196 |
+
|
| 197 |
+
canvas.addEventListener('mouseleave', function() {
|
| 198 |
+
document.getElementById('tt').style.opacity = '0';
|
| 199 |
+
drawMatrix();
|
| 200 |
+
});
|
| 201 |
+
|
| 202 |
+
canvas.addEventListener('click', function(e) {
|
| 203 |
+
const rect = canvas.getBoundingClientRect();
|
| 204 |
+
const x = e.clientX - rect.left;
|
| 205 |
+
const y = e.clientY - rect.top;
|
| 206 |
+
const col = Math.floor(x / (rect.width / COLS));
|
| 207 |
+
const row = Math.floor(y / (rect.height / ROWS));
|
| 208 |
+
if (mode === 'col') {
|
| 209 |
+
const speed = col < 20 ? 'fast (high frequency)' : col < 100 ? 'moderate frequency' : 'slow (low frequency)';
|
| 210 |
+
const ann = document.getElementById('ann-box');
|
| 211 |
+
ann.innerHTML = `<div class="ann-row"><div class="ann-col"><div class="ann-head" style="color:#BA7517">Dimension i=${col} β ${speed}</div>
|
| 212 |
+
Looking down this column (vertical), the value at each row changes ${col < 30 ? '<strong>rapidly</strong> β adjacent words look very different. The model can distinguish neighbors.' : col > 150 ? '<strong>barely at all</strong> between neighbors β but significantly over hundreds of positions. Good for long-range context.' : 'at a moderate rate.'}<br><br>Wavelength β ${(2 * Math.PI * Math.pow(10000, col / D_MODEL)).toFixed(0)} radians.</div></div>`;
|
| 213 |
+
} else if (mode === 'row') {
|
| 214 |
+
const ann = document.getElementById('ann-box');
|
| 215 |
+
ann.innerHTML = `<div class="ann-row"><div class="ann-col"><div class="ann-head" style="color:#0F6E56">Position pos=${row} β the lookup table row for word ${row}</div>
|
| 216 |
+
Reading across this row (horizontal), you see how the 512-dimensional encoding vector for <em>any word at position ${row}</em> looks: left side oscillates fast, right side is nearly flat. When the Transformer sees a word here, it adds this entire row to the word's embedding.</div></div>`;
|
| 217 |
+
}
|
| 218 |
+
});
|
| 219 |
+
|
| 220 |
+
setTimeout(() => {
|
| 221 |
+
drawMatrix();
|
| 222 |
+
drawLegend();
|
| 223 |
+
}, 50);
|
| 224 |
+
|
| 225 |
+
window.addEventListener('resize', () => { drawMatrix(); drawLegend(); });
|
| 226 |
+
</script>
|