ebe2e35
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 | <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🧠 Transformer Parameter Calculator</title>
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=IBM+Plex+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
overflow-x: hidden;
}
#root {
width: 100%;
min-height: 100vh;
}
input[type=range] {
-webkit-appearance: none;
background: #162030 !important;
height: 3px !important;
border-radius: 2px;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 14px;
height: 14px;
border-radius: 50%;
background: #c084fc;
box-shadow: 0 0 6px #c084fc66;
cursor: pointer;
}
input[type=range]::-moz-range-thumb {
width: 14px;
height: 14px;
border: none;
border-radius: 50%;
background: #c084fc;
box-shadow: 0 0 6px #c084fc66;
cursor: pointer;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect, useRef } = React;
/* ════════════════════════════════════════════
CALC ENGINE — pure general Transformer
════════════════════════════════════════════ */
function calcAll({ vocab_size: V, embedding_dim: d, num_layers: L, intermediate_size: ff, num_heads: H }) {
const token_emb = V * d;
const pos_emb = 1024 * d;
const embedding_total = token_emb + pos_emb;
const attention = 4 * (d * d + d);
const fc1 = d * ff + ff;
const fc2 = ff * d + d;
const feedforward = fc1 + fc2;
const layernorm = 2 * (2 * d);
const per_block = attention + feedforward + layernorm;
const all_layers = L * per_block;
const final_ln = 2 * d;
const lm_head_untied = V * d;
const total_tied = embedding_total + all_layers + final_ln;
const total_untied = total_tied + lm_head_untied;
return { token_emb, pos_emb, embedding_total, attention, fc1, fc2, feedforward, layernorm, per_block, all_layers, final_ln, lm_head_untied, total_tied, total_untied, d, ff, L, V, H };
}
/* ════════════════════════════════════════════
ANIMATED COUNTER
════════════════════════════════════════════ */
function useAnim(target) {
const [v, setV] = useState(target);
const cur = useRef(target);
const raf = useRef(null);
useEffect(() => {
const go = () => {
const diff = target - cur.current;
if (Math.abs(diff) < 1) { cur.current = target; setV(target); return; }
cur.current += diff * 0.14;
setV(Math.round(cur.current));
raf.current = requestAnimationFrame(go);
};
raf.current = requestAnimationFrame(go);
return () => cancelAnimationFrame(raf.current);
}, [target]);
return v;
}
/* ── helpers ── */
const fmt = n => n.toLocaleString();
const fmtM = n => (n / 1e6).toFixed(2) + "M";
/* ── palette ── */
const P = { emb:"#38bdf8", attn:"#c084fc", ff:"#fb923c", ln:"#34d399", head:"#fb7185", bg:"#08101a", card:"#0e1825", border:"#162030" };
/* ════════════════════════════════════════════
SLIDER
════════════════════════════════════════════ */
function Slider({ label, value, min, max, step, onChange }) {
return (
<div style={{ display:"flex", flexDirection:"column", gap:4 }}>
<div style={{ display:"flex", justifyContent:"space-between" }}>
<span style={{ color:"#7a8fa3", fontSize:11, fontFamily:"'IBM Plex Mono',monospace" }}>{label}</span>
<span style={{ color:"#e2e8f0", fontSize:12, fontWeight:700, fontFamily:"'IBM Plex Mono',monospace" }}>{fmt(value)}</span>
</div>
<input type="range" min={min} max={max} step={step} value={value} onChange={e => onChange(+e.target.value)}
style={{ width:"100%", accentColor:"#c084fc", height:3, cursor:"pointer", outline:"none" }} />
</div>
);
}
/* ════════════════════════════════════════════
EXPANDABLE SECTION
════════════════════════════════════════════ */
function Section({ color, icon, title, params, total, open, onToggle, children }) {
const barW = useAnim(Math.max((params / total) * 100, 0.8));
return (
<div style={{ borderRadius:10, border:`1px solid ${open ? color+"40" : P.border}`, background: open?"#0b1520":P.card, overflow:"hidden", boxShadow: open?`0 2px 20px ${color}15`:"none", transition:"all .25s" }}>
<div onClick={onToggle} style={{ display:"flex", alignItems:"center", padding:"10px 14px", cursor:"pointer", userSelect:"none", gap:10 }}>
<span style={{ fontSize:17 }}>{icon}</span>
<div style={{ flex:1, minWidth:0 }}>
<div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:5 }}>
<span style={{ color:"#e2e8f0", fontSize:13, fontWeight:600, fontFamily:"'Syne',sans-serif" }}>{title}</span>
<span style={{ color, fontSize:12, fontWeight:700, fontFamily:"'IBM Plex Mono',monospace" }}>{fmtM(params)}</span>
</div>
<div style={{ height:4, borderRadius:2, background:"#162030", overflow:"hidden" }}>
<div style={{ width:`${barW}%`, height:"100%", borderRadius:2, background:`linear-gradient(90deg,${color},${color}99)`, boxShadow:`0 0 5px ${color}55`, transition:"width .45s cubic-bezier(.4,0,.2,1)" }}/>
</div>
</div>
<span style={{ color:"#3a5068", fontSize:9, transition:"transform .2s", transform: open?"rotate(90deg)":"rotate(0)" }}>▶</span>
</div>
{open && <div style={{ borderTop:`1px solid ${color}18`, padding:"10px 14px 13px", background:"#070e18" }}>{children}</div>}
</div>
);
}
/* ── row inside expanded ── */
function Row({ label, dim, value, color }) {
return (
<div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", padding:"2.5px 0" }}>
<span style={{ color:"#94aab8", fontSize:11.5, fontFamily:"'IBM Plex Mono',monospace" }}>{label}</span>
<span style={{ display:"flex", gap:10, alignItems:"center" }}>
{dim && <span style={{ color:"#3a5068", fontSize:10, fontFamily:"'IBM Plex Mono',monospace" }}>{dim}</span>}
<span style={{ color, fontSize:11.5, fontWeight:700, fontFamily:"'IBM Plex Mono',monospace" }}>{fmtM(value)}</span>
</span>
</div>
);
}
const Divider = () => <div style={{ borderTop:"1px solid #162030", margin:"6px 0" }}/>;
/* ════════════════════════════════════════════
APP ROOT
════════════════════════════════════════════ */
function App() {
const [cfg, setCfg] = useState({ vocab_size:50259, embedding_dim:768, num_layers:12, intermediate_size:3072, num_heads:12 });
const [tied, setTied] = useState(true);
const [open, setOpen] = useState({ emb:true, attn:false, ff:false, ln:false });
const c = calcAll(cfg);
const total = tied ? c.total_tied : c.total_untied;
const animTotal = useAnim(total);
const set = k => v => setCfg(p=>({...p,[k]:v}));
const tog = k => () => setOpen(p=>({...p,[k]:!p[k]}));
const memFp32 = ((total*4)/1e6).toFixed(1);
const memFp16 = ((total*2)/1e6).toFixed(1);
const memTrain = ((total*4*4)/1e9).toFixed(2);
const segments = [
{ label:"Embedding", val:c.embedding_total, color:P.emb },
{ label:`Layers ×${c.L}`, val:c.all_layers, color:P.attn },
{ label:"Final LN", val:c.final_ln, color:P.ln },
...(!tied ? [{ label:"LM Head", val:c.lm_head_untied, color:P.head }] : []),
];
return (
<div style={{ minHeight:"100vh", background:P.bg, color:"#e2e8f0", fontFamily:"'Syne',sans-serif", padding:"24px 14px 40px", backgroundImage:"radial-gradient(ellipse at 15% 80%,#38bdf808 0%,transparent 55%),radial-gradient(ellipse at 85% 10%,#c084fc06 0%,transparent 55%)" }}>
<div style={{ maxWidth:520, margin:"0 auto" }}>
{/* TITLE */}
<h1 style={{ textAlign:"center", fontSize:22, fontWeight:800, margin:"0 0 4px", letterSpacing:"-0.02em" }}>
🧠 Transformer <span style={{ color:"#c084fc" }}>Parameter</span> Calculator
</h1>
<p style={{ textAlign:"center", color:"#3a5068", fontSize:11, margin:"0 0 22px", fontFamily:"'IBM Plex Mono',monospace" }}>
Slide → watch every layer recalculate live
</p>
{/* BIG TOTAL */}
<div style={{ borderRadius:16, border:"1px solid #162030", background:"linear-gradient(145deg,#0e1825,#0a1420)", padding:"18px 20px", textAlign:"center", marginBottom:18, boxShadow:"0 4px 28px #c084fc0e, inset 0 1px 0 #1e2d3d" }}>
<div style={{ color:"#3a5068", fontSize:10, fontFamily:"'IBM Plex Mono',monospace", letterSpacing:"0.12em", textTransform:"uppercase", marginBottom:2 }}>Total Parameters</div>
<div style={{ fontSize:32, fontWeight:800, letterSpacing:"-0.02em", color:"#f1f5f9" }}>{fmt(animTotal)}</div>
<div style={{ color:"#c084fc", fontSize:13, fontWeight:700, fontFamily:"'IBM Plex Mono',monospace", marginTop:1 }}>≈ {fmtM(animTotal)}</div>
<div style={{ display:"flex", justifyContent:"center", gap:8, marginTop:12, flexWrap:"wrap" }}>
{[["FP32",memFp32+" MB","#38bdf8"],["FP16",memFp16+" MB","#34d399"],["Train","~"+memTrain+" GB","#fb923c"]].map(([l,v,col])=>(
<div key={l} style={{ background:"#08101a", borderRadius:7, padding:"3px 11px", border:`1px solid ${col}1e` }}>
<span style={{ color:"#3a5068", fontSize:9.5, fontFamily:"'IBM Plex Mono',monospace" }}>{l} </span>
<span style={{ color:col, fontSize:11, fontWeight:700, fontFamily:"'IBM Plex Mono',monospace" }}>{v}</span>
</div>
))}
</div>
</div>
{/* SLIDERS */}
<div style={{ borderRadius:12, border:"1px solid #162030", background:P.card, padding:"14px 16px", marginBottom:18, display:"flex", flexDirection:"column", gap:13 }}>
<div style={{ color:"#3a5068", fontSize:9.5, fontFamily:"'IBM Plex Mono',monospace", letterSpacing:"0.1em", textTransform:"uppercase" }}>⚙️ Model Config</div>
<Slider label="vocab_size" value={cfg.vocab_size} min={1000} max={100000} step={1000} onChange={set("vocab_size")} />
<Slider label="embedding_dim (d)" value={cfg.embedding_dim} min={64} max={4096} step={64} onChange={set("embedding_dim")} />
<Slider label="num_layers (L)" value={cfg.num_layers} min={1} max={48} step={1} onChange={set("num_layers")} />
<Slider label="intermediate_size" value={cfg.intermediate_size} min={128} max={16384} step={128} onChange={set("intermediate_size")} />
<Slider label="num_heads (H)" value={cfg.num_heads} min={1} max={64} step={1} onChange={set("num_heads")} />
{/* tied toggle */}
<div style={{ display:"flex", alignItems:"center", gap:8, paddingTop:5, borderTop:"1px solid #162030", marginTop:2 }}>
<span style={{ color:"#7a8fa3", fontSize:11, fontFamily:"'IBM Plex Mono',monospace" }}>LM Head:</span>
{[["Tied",tied,()=>setTied(true)],["Untied",!tied,()=>setTied(false)]].map(([lbl,on,fn])=>(
<button key={lbl} onClick={fn} style={{ background: on?"#c084fc22":"#162030", border:`1px solid ${on?"#c084fc55":"#162030"}`, color: on?"#c084fc":"#7a8fa3", borderRadius:20, padding:"4px 14px", fontSize:11, fontFamily:"'IBM Plex Mono',monospace", cursor:"pointer", transition:"all .2s" }}>
{lbl}{on?" ✓":""}
</button>
))}
</div>
</div>
{/* LAYER BREAKDOWN */}
<div style={{ color:"#3a5068", fontSize:9.5, fontFamily:"'IBM Plex Mono',monospace", letterSpacing:"0.1em", textTransform:"uppercase", marginBottom:8 }}>📊 Layer Breakdown</div>
<div style={{ display:"flex", flexDirection:"column", gap:7 }}>
{/* EMBEDDING */}
<Section color={P.emb} icon="🪙" title="Embedding" params={c.embedding_total} total={total} open={open.emb} onToggle={tog("emb")}>
<Row label="Token Emb" dim={`[${fmt(cfg.vocab_size)} × ${cfg.embedding_dim}]`} value={c.token_emb} color={P.emb} />
<Row label="Position Emb" dim={`[1024 × ${cfg.embedding_dim}]`} value={c.pos_emb} color={P.emb} />
<Divider/>
<Row label="Total Embedding" value={c.embedding_total} color={P.emb} />
<div style={{ marginTop:7, padding:"5px 9px", borderRadius:6, background:"#0a1a24", border:"1px solid #162030" }}>
<span style={{ color:"#3a5068", fontSize:10.5, fontFamily:"'IBM Plex Mono',monospace" }}>
Each of {fmt(cfg.vocab_size)} tokens → {cfg.embedding_dim}-dim vector<br/>
Position emb: 1024 (max seq_len) × {cfg.embedding_dim}
</span>
</div>
</Section>
{/* ATTENTION */}
<Section color={P.attn} icon="🔍" title="Multi-Head Attention (per block)" params={c.attention} total={total} open={open.attn} onToggle={tog("attn")}>
{["Q (Query)","K (Key)","V (Value)","Out Proj"].map(name=>(
<Row key={name} label={name} dim={`[${cfg.embedding_dim}×${cfg.embedding_dim}] + bias`} value={cfg.embedding_dim*cfg.embedding_dim+cfg.embedding_dim} color={P.attn} />
))}
<Divider/>
<span style={{ color:"#7a8fa3", fontSize:10.5, fontFamily:"'IBM Plex Mono',monospace" }}>
<span style={{ color:"#c084fc" }}>4 × (d² + d)</span> = 4 × ({cfg.embedding_dim}² + {cfg.embedding_dim}) = <strong style={{ color:P.attn }}>{fmtM(c.attention)}</strong>
</span>
<div style={{ marginTop:6, padding:"5px 9px", borderRadius:6, background:"#14101a", border:"1px solid #162030" }}>
<span style={{ color:"#3a5068", fontSize:10.5, fontFamily:"'IBM Plex Mono',monospace" }}>
{cfg.num_heads} heads → head_dim = {cfg.embedding_dim}/{cfg.num_heads} = {Math.floor(cfg.embedding_dim/cfg.num_heads)}<br/>
Param count stays the same — only head_dim changes!
</span>
</div>
</Section>
{/* FFN */}
<Section color={P.ff} icon="⚡" title="Feed Forward Network (per block)" params={c.feedforward} total={total} open={open.ff} onToggle={tog("ff")}>
<Row label="FC1 (expand)" dim={`[${cfg.embedding_dim}×${cfg.intermediate_size}]+bias`} value={c.fc1} color={P.ff} />
<Row label="FC2 (compress)" dim={`[${cfg.intermediate_size}×${cfg.embedding_dim}]+bias`} value={c.fc2} color={P.ff} />
<Divider/>
<span style={{ color:"#7a8fa3", fontSize:10.5, fontFamily:"'IBM Plex Mono',monospace" }}>
<span style={{ color:"#fb923c" }}>(d×ff + ff) + (ff×d + d)</span> = <strong style={{ color:P.ff }}>{fmtM(c.feedforward)}</strong>
</span>
<div style={{ marginTop:6, padding:"5px 9px", borderRadius:6, background:"#1a1408", border:"1px solid #162030" }}>
<span style={{ color:"#3a5068", fontSize:10.5, fontFamily:"'IBM Plex Mono',monospace" }}>
FC1: {cfg.embedding_dim} → {cfg.intermediate_size} (typically 4×d)<br/>
FC2: {cfg.intermediate_size} → {cfg.embedding_dim} | ReLU/GELU in between
</span>
</div>
</Section>
{/* LAYERNORM */}
<Section color={P.ln} icon="📐" title="Layer Norm ×2 (per block)" params={c.layernorm} total={total} open={open.ln} onToggle={tog("ln")}>
<Row label="LN1 (before Attn)" dim={`γ[${cfg.embedding_dim}] + β[${cfg.embedding_dim}]`} value={2*cfg.embedding_dim} color={P.ln} />
<Row label="LN2 (before FFN)" dim={`γ[${cfg.embedding_dim}] + β[${cfg.embedding_dim}]`} value={2*cfg.embedding_dim} color={P.ln} />
<Divider/>
<span style={{ color:"#7a8fa3", fontSize:10.5, fontFamily:"'IBM Plex Mono',monospace" }}>
<span style={{ color:"#34d399" }}>2 × (d + d)</span> = 2 × 2 × {cfg.embedding_dim} = <strong style={{ color:P.ln }}>{fmt(c.layernorm)}</strong>
</span>
</Section>
{/* PER BLOCK */}
<div style={{ borderRadius:9, border:"1px dashed #253545", background:"#0a1420", padding:"9px 14px", display:"flex", justifyContent:"space-between", alignItems:"center" }}>
<span style={{ color:"#94aab8", fontSize:12, fontWeight:600, fontFamily:"'Syne',sans-serif" }}>📦 1 Transformer Block</span>
<span style={{ color:"#e2e8f0", fontSize:13, fontWeight:700, fontFamily:"'IBM Plex Mono',monospace" }}>{fmtM(c.per_block)}</span>
</div>
{/* ALL LAYERS */}
<div style={{ borderRadius:9, border:"1px solid #1e2d3d", background:"#0b1520", padding:"9px 14px", display:"flex", justifyContent:"space-between", alignItems:"center" }}>
<span style={{ color:"#94aab8", fontSize:12, fontWeight:600, fontFamily:"'Syne',sans-serif" }}>🏗️ All Layers × {cfg.num_layers}</span>
<span style={{ color:P.attn, fontSize:13, fontWeight:700, fontFamily:"'IBM Plex Mono',monospace" }}>{fmtM(c.all_layers)}</span>
</div>
{/* FINAL LN */}
<div style={{ borderRadius:9, border:"1px solid #162030", background:P.card, padding:"9px 14px", display:"flex", justifyContent:"space-between", alignItems:"center" }}>
<span style={{ color:"#94aab8", fontSize:12, fontFamily:"'Syne',sans-serif", fontWeight:600 }}>📏 Final LayerNorm</span>
<span style={{ color:P.ln, fontSize:12, fontWeight:700, fontFamily:"'IBM Plex Mono',monospace" }}>{fmt(c.final_ln)}</span>
</div>
{/* LM HEAD */}
<div style={{ borderRadius:9, border:"1px solid #162030", background:P.card, padding:"9px 14px", display:"flex", justifyContent:"space-between", alignItems:"center" }}>
<span style={{ color:"#94aab8", fontSize:12, fontFamily:"'Syne',sans-serif", fontWeight:600 }}>🔗 LM Head {tied?"(tied)":"(untied)"}</span>
<span style={{ color: tied?"#3a5068":P.head, fontSize:12, fontWeight:700, fontFamily:"'IBM Plex Mono',monospace" }}>
{tied ? "0 (shared w/ Emb)" : fmtM(c.lm_head_untied)}
</span>
</div>
</div>
{/* DISTRIBUTION BAR */}
<div style={{ marginTop:22 }}>
<div style={{ color:"#3a5068", fontSize:9.5, fontFamily:"'IBM Plex Mono',monospace", letterSpacing:"0.1em", textTransform:"uppercase", marginBottom:8 }}>📈 Distribution</div>
<div style={{ borderRadius:13, border:"1px solid #162030", background:P.card, padding:"16px 16px 14px" }}>
<div style={{ display:"flex", height:20, borderRadius:10, overflow:"hidden", marginBottom:14 }}>
{segments.map((s,i)=>{
const pct=(s.val/total)*100;
return pct>0.5?(
<div key={i} style={{ width:`${pct}%`, background:s.color, display:"flex", alignItems:"center", justifyContent:"center", transition:"width .5s cubic-bezier(.4,0,.2,1)" }}>
{pct>6&&<span style={{ color:"#08101a", fontSize:9, fontWeight:700, fontFamily:"'IBM Plex Mono',monospace" }}>{s.label.split(" ")[0]}</span>}
</div>
):null;
})}
</div>
<div style={{ display:"flex", flexWrap:"wrap", gap:"7px 16px" }}>
{segments.map((s,i)=>(
<div key={i} style={{ display:"flex", alignItems:"center", gap:6 }}>
<div style={{ width:10, height:10, borderRadius:3, background:s.color }}/>
<span style={{ color:"#7a8fa3", fontSize:10.5, fontFamily:"'IBM Plex Mono',monospace" }}>{s.label}</span>
<span style={{ color:s.color, fontSize:10.5, fontWeight:700, fontFamily:"'IBM Plex Mono',monospace" }}>{((s.val/total)*100).toFixed(1)}%</span>
</div>
))}
</div>
</div>
</div>
{/* FORMULA CHEATSHEET */}
<div style={{ marginTop:18 }}>
<div style={{ borderRadius:12, border:"1px solid #162030", background:"#070e18", padding:"14px 16px" }}>
<div style={{ color:"#3a5068", fontSize:9.5, fontFamily:"'IBM Plex Mono',monospace", letterSpacing:"0.1em", textTransform:"uppercase", marginBottom:8 }}>📝 Formula Cheatsheet</div>
{[
["Embedding", "V×d + seq×d", `${fmt(cfg.vocab_size)}×${cfg.embedding_dim} + 1024×${cfg.embedding_dim}`, P.emb],
["Attention", "4 × (d² + d)", `4×(${cfg.embedding_dim}²+${cfg.embedding_dim})`, P.attn],
["FFN", "(d×ff+ff)+(ff×d+d)", `(${cfg.embedding_dim}×${cfg.intermediate_size}+…)+(…)`, P.ff],
["LayerNorm", "2 × 2d", `2×2×${cfg.embedding_dim}`, P.ln],
["Per Block", "Attn + FFN + LN", fmtM(c.per_block), "#e2e8f0"],
["Total", "Emb + L×Block + FinalLN", fmtM(total), "#c084fc"],
].map(([name,formula,expanded,col],i)=>(
<div key={i} style={{ display:"flex", alignItems:"flex-start", gap:8, padding:"4px 0", borderTop: i?"1px solid #162030":"none" }}>
<span style={{ color:col, fontSize:11, fontWeight:700, fontFamily:"'IBM Plex Mono',monospace", minWidth:78 }}>{name}</span>
<span style={{ color:"#7a8fa3", fontSize:10.5, fontFamily:"'IBM Plex Mono',monospace", flex:1 }}>{formula}</span>
<span style={{ color:"#3a5068", fontSize:10, fontFamily:"'IBM Plex Mono',monospace", textAlign:"right", maxWidth:150 }}>{expanded}</span>
</div>
))}
</div>
</div>
{/* FOOTER */}
<p style={{ textAlign:"center", color:"#253545", fontSize:10, fontFamily:"'IBM Plex Mono',monospace", marginTop:22 }}>
seq_len = 1024 assumed • Train mem ≈ 4× model (params + grads + Adam m & v)
</p>
</div>
</div>
);
}
// Render the app
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
</body>
</html> |