Files
worldmodel/JEPA/math/animations/hermite_explainer.html
T
gaojie 5de86388db
Sync to site1 / sync (push) Has been cancelled
Add interactive HTML pages for planning explainer and spectral explainer
- Created planning_explainer.html to demonstrate orthogonal invariance and optimal planning with interactive controls and visualizations.
- Created spectral_explainer.html to illustrate spectral decomposition and linear identifiability, featuring adjustable spectral weights and real-time correlation calculations.
2026-06-02 05:05:39 +08:00

428 lines
20 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hermite 多项式 × OU 衰减 — 交互式探索</title>
<!--
设计系统决策(合同)
─────────────────────────────────────────────
调性:科学可视化 / 学术交互工具(非营销页)
背景:石墨黑 #0e1116 面板 #161b22 描边 #232b36
语义色:
· 线性 d=1"想要的成分" → 青蓝 var(--linear) #4cc9f0
· 高阶非线性 d≥2(被衰减) → 暖橙 var(--nl) #f4a261
· 高斯密度底纹 → 冷灰青 半透明
文字:主 #e6edf3 次 #8b949e 弱 #5c6672
字体:正文衬线 'Spectral',界面无衬线 'Sora',数据/公式等宽 'JetBrains Mono'
原则:无启动页、自动演示 + 可手动接管、ρ 滑块与阶数为核心交互
-->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Spectral:ital,wght@0,400;0,600;1,400&family=Sora:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet" />
<style>
:root{
--bg:#0e1116; --panel:#161b22; --panel-2:#1b222c; --line:#232b36;
--txt:#e6edf3; --txt-2:#8b949e; --txt-3:#5c6672;
--linear:#4cc9f0; --linear-soft:rgba(76,201,240,.16);
--nl:#f4a261; --nl-soft:rgba(244,162,97,.16);
--gauss:rgba(150,170,190,.12);
--grid:#1c2530;
--r:14px;
}
*{box-sizing:border-box;}
html,body{margin:0;height:100%;}
body{
background:
radial-gradient(1200px 700px at 78% -8%, rgba(76,201,240,.06), transparent 60%),
var(--bg);
color:var(--txt);
font-family:'Sora',system-ui,sans-serif;
-webkit-font-smoothing:antialiased;
overflow:hidden;
}
.app{
height:100vh; display:grid;
grid-template-columns: 300px 1fr 320px;
grid-template-rows: auto 1fr;
grid-template-areas:
"head head head"
"ctrl stage read";
gap:18px; padding:22px 26px;
}
/* ── header ── */
header{grid-area:head; display:flex; align-items:baseline; gap:18px; flex-wrap:wrap;}
header .kicker{
font:500 12px/1 'JetBrains Mono',monospace; letter-spacing:.32em;
color:var(--linear); text-transform:uppercase;
}
header h1{
margin:0; font-family:'Spectral',serif; font-weight:600;
font-size:30px; letter-spacing:.01em;
}
header h1 em{color:var(--txt-2); font-style:italic; font-weight:400;}
header .sub{font-size:13px; color:var(--txt-2); max-width:560px; line-height:1.5;}
.card{background:var(--panel); border:1px solid var(--line); border-radius:var(--r);}
/* ── left controls ── */
.controls{grid-area:ctrl; padding:20px; display:flex; flex-direction:column; gap:22px; overflow-y:auto;}
.block .label{
font:600 11px/1 'JetBrains Mono',monospace; letter-spacing:.18em;
color:var(--txt-3); text-transform:uppercase; margin-bottom:12px;
}
/* rho slider */
.rho-val{font:700 40px/1 'JetBrains Mono',monospace; color:var(--linear); letter-spacing:.02em;}
.rho-val small{font-size:14px; color:var(--txt-2); font-weight:400;}
input[type=range]{
-webkit-appearance:none; width:100%; height:4px; border-radius:4px;
background:linear-gradient(90deg,var(--linear) 0%, var(--linear) var(--fill,70%), #2a323d var(--fill,70%));
margin-top:14px; cursor:pointer;
}
input[type=range]::-webkit-slider-thumb{
-webkit-appearance:none; width:18px; height:18px; border-radius:50%;
background:#eaf6fb; border:3px solid var(--linear);
box-shadow:0 0 0 4px var(--linear-soft); cursor:grab;
}
input[type=range]::-moz-range-thumb{
width:18px;height:18px;border-radius:50%;background:#eaf6fb;border:3px solid var(--linear);cursor:grab;
}
.hint{font-size:12px; color:var(--txt-3); line-height:1.55; margin-top:10px;}
/* degree toggles */
.degrees{display:flex; flex-direction:column; gap:8px;}
.deg{
display:flex; align-items:center; gap:12px; padding:10px 12px;
border:1px solid var(--line); border-radius:10px; cursor:pointer;
background:var(--panel-2); transition:.18s; -webkit-user-select:none; user-select:none;
}
.deg:hover{border-color:#36404d;}
.deg.on{border-color:currentColor; background:color-mix(in oklch, currentColor 12%, var(--panel-2));}
.deg .dot{width:11px;height:11px;border-radius:3px;background:currentColor; flex:none;}
.deg .nm{font:500 13px 'JetBrains Mono',monospace; color:var(--txt);}
.deg .eq{margin-left:auto; font:400 12px 'JetBrains Mono',monospace; color:var(--txt-2);}
.deg.d1{color:var(--linear);}
.deg.dN{color:var(--nl);}
.play{
margin-top:4px; display:flex; gap:8px;
}
.play button{
flex:1; padding:10px; border-radius:10px; border:1px solid var(--line);
background:var(--panel-2); color:var(--txt); font:500 13px 'Sora'; cursor:pointer; transition:.16s;
}
.play button:hover{border-color:var(--linear); color:var(--linear);}
.play button.live{background:var(--linear-soft); border-color:var(--linear); color:var(--linear);}
/* ── center stage ── */
.stage{grid-area:stage; padding:18px 20px 16px; display:flex; flex-direction:column; gap:14px; min-height:0;}
.stage .ttl{display:flex; align-items:baseline; justify-content:space-between;}
.stage .ttl h2{margin:0; font-family:'Spectral',serif; font-weight:600; font-size:18px;}
.stage .ttl span{font:400 12px 'JetBrains Mono',monospace; color:var(--txt-3);}
.canvas-wrap{position:relative; flex:1; min-height:0; border-radius:10px; overflow:hidden; background:#0b0e13; border:1px solid var(--line);}
canvas{display:block; width:100%; height:100%;}
.corr-wrap{height:150px; flex:none; border-radius:10px; overflow:hidden; background:#0b0e13; border:1px solid var(--line); position:relative;}
.corr-cap{position:absolute; top:10px; left:14px; font:400 12px 'JetBrains Mono',monospace; color:var(--txt-3);}
/* ── right reading ── */
.reading{grid-area:read; padding:22px; display:flex; flex-direction:column; gap:18px; overflow-y:auto;}
.reading h3{margin:0; font-family:'Spectral',serif; font-weight:600; font-size:17px;}
.takeaway{
background:var(--panel-2); border:1px solid var(--line); border-left:none;
border-radius:10px; padding:16px; font-size:13.5px; line-height:1.62; color:var(--txt);
}
.takeaway .big{font-family:'JetBrains Mono',monospace; color:var(--linear); font-weight:700;}
.formula{
font:500 15px 'JetBrains Mono',monospace; color:var(--txt);
background:#0b0e13; border:1px solid var(--line); border-radius:8px;
padding:14px 16px; line-height:1.7; letter-spacing:.01em;
}
.formula .hi{color:var(--linear);} .formula .hl{color:var(--nl);}
.verdict{
margin-top:auto; padding:16px; border-radius:10px;
background:linear-gradient(135deg, var(--linear-soft), transparent 70%);
border:1px solid color-mix(in oklch,var(--linear) 40%, var(--line));
font-family:'Spectral',serif; font-size:14px; line-height:1.6;
}
.verdict b{color:var(--linear);}
.src{font:400 11px 'JetBrains Mono',monospace; color:var(--txt-3);}
.navbar{position:fixed;bottom:14px;left:50%;transform:translateX(-50%);display:flex;gap:6px;background:rgba(22,27,34,.9);border:1px solid var(--line);border-radius:30px;padding:6px 10px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);z-index:20;}
.navbar a{font:500 12px 'JetBrains Mono',monospace;color:var(--txt-2);text-decoration:none;padding:5px 11px;border-radius:20px;}
.navbar a:hover{color:var(--linear);}
.navbar a.on{background:var(--linear-soft);color:var(--linear);}
::-webkit-scrollbar{width:8px;height:8px;}
::-webkit-scrollbar-thumb{background:#2a323d;border-radius:8px;}
@media (max-width:1080px){
.app{grid-template-columns:1fr; grid-template-rows:auto auto auto auto; grid-template-areas:"head" "ctrl" "stage" "read"; overflow:auto; height:auto;}
body{overflow:auto;}
.canvas-wrap{height:340px;flex:none;}
}
</style>
</head>
<body>
<div class="app">
<header>
<div style="display:flex;flex-direction:column;gap:6px;">
<span class="kicker">LeJEPA · Topic 1</span>
<h1>Hermite 多项式 <em>与 OU 过程的 ρᵈ 衰减</em></h1>
</div>
<p class="sub">拖动相关性 <b style="color:var(--linear)">ρ</b>、切换不同阶数的 Hermite 成分,实时观察:为什么 LeJEPA 的对齐损失会<strong>只青睐线性成分</strong>——这正是「线性可识别性」定理 1 的核心直觉。</p>
</header>
<!-- 左:控制 -->
<section class="card controls">
<div class="block">
<div class="label">相关性 ρ(OU 过程)</div>
<div class="rho-val"><span id="rhoTxt">0.70</span><small> ∈ (0, 1)</small></div>
<input id="rho" type="range" min="0.05" max="0.97" step="0.01" value="0.70" aria-label="相关性 ρ" title="相关性 ρ" />
<div class="hint">ρ 越大,正样本对 (z, z′) 越相似。第 d 阶成分的时间相关性为 <b style="color:var(--txt)">ρᵈ</b>,高阶随 d 指数衰减。</div>
</div>
<div class="block">
<div class="label">显示哪些 Hermite 成分</div>
<div class="degrees" id="degList"></div>
</div>
<div class="block">
<div class="label">演示</div>
<div class="play">
<button id="autoBtn" class="live">⏸ 自动扫描 ρ</button>
<button id="resetBtn">↺ 复位</button>
</div>
<div class="hint">自动模式下 ρ 在 0.05↔0.97 之间往返,观察条形图的相对高度如何变化。</div>
</div>
</section>
<!-- 中:画布 -->
<section class="card stage">
<div class="ttl">
<h2>前几阶 Hermite 多项式 Heₙ(z)</h2>
<span>底纹 = 高斯密度 N(0,1),概率质量集中在 [-3, 3]</span>
</div>
<div class="canvas-wrap"><canvas id="curve"></canvas></div>
<div class="corr-wrap">
<div class="corr-cap">各阶成分对正样本对的相关性 = ρᵈ · (n! 归一化后)</div>
<canvas id="bars"></canvas>
</div>
</section>
<!-- 右:解读 -->
<section class="card reading">
<h3>实时解读</h3>
<div class="formula">
E[Heₙ(z)·Heₙ(z)]<br>
&nbsp;&nbsp;= <span class="hi">ρ</span><sup>n</sup> · n!
</div>
<div class="takeaway" id="takeaway"></div>
<h3 style="margin-top:4px;">谱权重的含义</h3>
<div class="formula" style="font-size:13.5px;">
<span class="hi">w₁ = 1</span> → 100% 线性(想要的)<br>
<span class="hl">w_d&gt;0 (d≥2)</span> → 残留非线性
</div>
<div class="verdict" id="verdict"></div>
<div class="src">来源:JEPA/math/01_hermite_polynomials.md</div>
</section>
</div>
<nav class="navbar" id="nav"></nav>
<script>
const NAV=[["01","Hermite","hermite_explainer.html"],["02","OU·Mehler","ou_mehler_explainer.html"],["03","谱分解","spectral_explainer.html"],["04","高斯唯一","gaussian_uniqueness_explainer.html"],["05","近似界","approx_bound_explainer.html"],["06","规划等价","planning_explainer.html"]];
const navEl=document.getElementById('nav');NAV.forEach(([n,t,f])=>{const a=document.createElement('a');a.href=f;a.textContent=n+' '+t;if(f==='hermite_explainer.html')a.className='on';navEl.appendChild(a);});
/* ───────── Hermite(概率论版 Heₙ)系数与求值 ───────── */
// 递推: He_{n+1} = z·He_n n·He_{n-1}
function hermiteVal(n, z){
let h0 = 1, h1 = z;
if(n===0) return 1;
if(n===1) return z;
for(let k=1;k<n;k++){ const h2 = z*h1 - k*h0; h0=h1; h1=h2; }
return h1;
}
function factorial(n){ let f=1; for(let i=2;i<=n;i++) f*=i; return f; }
const DEGREES = [
{d:1, name:'He₁(z) = z', cls:'d1', color:'#4cc9f0', on:true, eq:'线性 ✓'},
{d:2, name:'He₂(z) = z²−1', cls:'dN', color:'#f4a261', on:true, eq:'二次'},
{d:3, name:'He₃(z) = z³−3z', cls:'dN', color:'#e76f51', on:false, eq:'三次'},
{d:4, name:'He₄(z) = z⁴−6z²+3', cls:'dN', color:'#bc6c8f', on:false, eq:'四次'},
];
/* ───────── 状态 ───────── */
let rho = 0.70;
let auto = true, autoDir = -1, lastTick = performance.now();
/* ───────── 构建阶数开关 ───────── */
const degList = document.getElementById('degList');
DEGREES.forEach((g,i)=>{
const el = document.createElement('div');
el.className = 'deg '+g.cls+(g.on?' on':'');
el.style.color = g.color;
el.innerHTML = `<span class="dot"></span><span class="nm">${g.name}</span><span class="eq">${g.eq}</span>`;
el.addEventListener('click', ()=>{ g.on=!g.on; el.classList.toggle('on',g.on); draw(); });
degList.appendChild(el);
});
/* ───────── 滑块 ───────── */
const rhoEl = document.getElementById('rho');
const rhoTxt = document.getElementById('rhoTxt');
function setRho(v, fromUser){
rho = Math.max(0.05, Math.min(0.97, v));
rhoEl.value = rho;
rhoTxt.textContent = rho.toFixed(2);
rhoEl.style.setProperty('--fill', ((rho-0.05)/(0.97-0.05)*100).toFixed(1)+'%');
if(fromUser){ auto=false; document.getElementById('autoBtn').classList.remove('live'); document.getElementById('autoBtn').textContent='▶ 自动扫描 ρ'; }
draw();
}
rhoEl.addEventListener('input', e=> setRho(+e.target.value, true));
document.getElementById('autoBtn').addEventListener('click', e=>{
auto = !auto;
e.target.classList.toggle('live', auto);
e.target.textContent = auto ? '⏸ 自动扫描 ρ' : '▶ 自动扫描 ρ';
lastTick = performance.now();
});
document.getElementById('resetBtn').addEventListener('click', ()=>{
DEGREES.forEach((g,i)=>{ g.on = (g.d<=2); degList.children[i].classList.toggle('on',g.on); });
setRho(0.70,false);
});
/* ───────── Canvas ───────── */
const curve = document.getElementById('curve'), cctx = curve.getContext('2d');
const bars = document.getElementById('bars'), bctx = bars.getContext('2d');
function fit(cv){ const r=cv.getBoundingClientRect(), dpr=devicePixelRatio||1; cv.width=r.width*dpr; cv.height=r.height*dpr; return {w:r.width,h:r.height,dpr};}
function drawCurves(){
const {w,h,dpr}=fit(curve); cctx.setTransform(dpr,0,0,dpr,0,0); cctx.clearRect(0,0,w,h);
const padL=44, padR=16, padT=14, padB=26;
const x0=padL, x1=w-padR, y0=padT, y1=h-padB;
const zMin=-3.2, zMax=3.2, vMin=-6, vMax=6;
const sx = z => x0 + (z-zMin)/(zMax-zMin)*(x1-x0);
const sy = v => y1 - (Math.max(vMin,Math.min(vMax,v))-vMin)/(vMax-vMin)*(y1-y0);
// 高斯密度底纹
cctx.beginPath();
for(let i=0;i<=240;i++){ const z=zMin+(zMax-zMin)*i/240; const g=Math.exp(-z*z/2); const v=g*9-0.2;
const px=sx(z), py=sy(v); i?cctx.lineTo(px,py):cctx.moveTo(px,py); }
cctx.lineTo(sx(zMax),sy(vMin)); cctx.lineTo(sx(zMin),sy(vMin)); cctx.closePath();
cctx.fillStyle='rgba(150,170,190,.10)'; cctx.fill();
// 网格 + 轴
cctx.strokeStyle='#1c2530'; cctx.lineWidth=1; cctx.font="11px 'JetBrains Mono',monospace"; cctx.fillStyle='#5c6672';
for(let v=vMin; v<=vMax; v+=3){ const py=sy(v); cctx.beginPath(); cctx.moveTo(x0,py); cctx.lineTo(x1,py); cctx.stroke(); cctx.fillText(v.toString().padStart(2,' '), 8, py+4); }
for(let z=-3; z<=3; z++){ const px=sx(z); cctx.beginPath(); cctx.moveTo(px,y0); cctx.lineTo(px,y1); cctx.strokeStyle='#161d27'; cctx.stroke(); cctx.fillText(z.toString(), px-4, y1+18); }
// 零轴
cctx.strokeStyle='#33404e'; cctx.lineWidth=1.5; cctx.beginPath(); cctx.moveTo(x0,sy(0)); cctx.lineTo(x1,sy(0)); cctx.stroke();
// 各阶曲线
DEGREES.filter(g=>g.on).forEach(g=>{
cctx.beginPath();
for(let i=0;i<=320;i++){ const z=zMin+(zMax-zMin)*i/320; const v=hermiteVal(g.d,z);
const px=sx(z), py=sy(v); i?cctx.lineTo(px,py):cctx.moveTo(px,py); }
cctx.lineWidth = g.d===1?3:2; cctx.strokeStyle=g.color;
cctx.shadowColor=g.color; cctx.shadowBlur = g.d===1?10:5; cctx.stroke(); cctx.shadowBlur=0;
// 标签
const lz=2.6, lv=hermiteVal(g.d,lz);
cctx.fillStyle=g.color; cctx.font="600 12px 'JetBrains Mono',monospace";
cctx.fillText('He'+g.d, sx(lz)+4, sy(lv));
});
}
function drawBars(){
const {w,h,dpr}=fit(bars); bctx.setTransform(dpr,0,0,dpr,0,0); bctx.clearRect(0,0,w,h);
const items = DEGREES.filter(g=>g.on);
const padT=34, padB=24, padL=18, padR=18;
const y0=padT, y1=h-padB;
const n=items.length || 1;
const gap=22, bw=Math.min(90,(w-padL-padR-gap*(n-1))/n);
const groupW=bw*n+gap*(n-1);
const startX=(w-groupW)/2;
// 基线(=ρ,线性成分的相关性,作为参考)
const refY = y1 - (rho)*(y1-y0);
bctx.strokeStyle='rgba(76,201,240,.45)'; bctx.setLineDash([5,4]); bctx.lineWidth=1.5;
bctx.beginPath(); bctx.moveTo(padL,refY); bctx.lineTo(w-padR,refY); bctx.stroke(); bctx.setLineDash([]);
bctx.fillStyle='rgba(76,201,240,.8)'; bctx.font="500 11px 'JetBrains Mono',monospace";
bctx.fillText('ρ¹ = '+rho.toFixed(2), w-padR-70, refY-6);
items.forEach((g,i)=>{
const val=Math.pow(rho,g.d); // ρ^d
const x=startX+i*(bw+gap);
const bh=val*(y1-y0);
const yTop=y1-bh;
// 柱
const grd=bctx.createLinearGradient(0,yTop,0,y1);
grd.addColorStop(0,g.color); grd.addColorStop(1,'color-mix' in CSS ? g.color : g.color);
bctx.fillStyle=g.color; bctx.globalAlpha = g.d===1?1:0.85;
bctx.fillRect(x,yTop,bw,bh); bctx.globalAlpha=1;
// 数值
bctx.fillStyle=g.color; bctx.font="700 13px 'JetBrains Mono',monospace"; bctx.textAlign='center';
bctx.fillText('ρ'+g.d+' = '+val.toFixed(3), x+bw/2, yTop-7);
bctx.fillStyle='#8b949e'; bctx.font="400 11px 'JetBrains Mono',monospace";
bctx.fillText('He'+g.d, x+bw/2, y1+15);
bctx.textAlign='left';
});
}
/* ───────── 文案更新 ───────── */
function updateText(){
const r=rho;
const r1=r, r2=r*r, r3=r*r*r;
const ratio=(r2/r1*100).toFixed(0); // ρ²/ρ
const take=document.getElementById('takeaway');
take.innerHTML =
`当前 ρ = <span class="big">${r.toFixed(2)}</span>`+
`线性成分相关性 = ρ¹ = <b style="color:var(--linear)">${r1.toFixed(3)}</b>`+
`二次成分仅 = ρ² = <b style="color:var(--nl)">${r2.toFixed(3)}</b>`+
`(保留约 <b>${ratio}%</b>),三次成分 ρ³ = <b style="color:var(--nl)">${r3.toFixed(3)}</b>。`+
`<br><br>阶数越高,<b style="color:var(--nl)">衰减越快</b>——对齐损失要<strong>最大化</strong>相关性,自然只会保留 <b style="color:var(--linear)">d=1 的线性成分</b>。`;
const v=document.getElementById('verdict');
v.innerHTML =
`因为非线性成分(d≥2)的贡献 <b>ρᵈ &lt; ρ</b> 恒成立,`+
`LeJEPA 的唯一最优解就是 <b>h(z) = Q·z</b>(正交线性映射)。`+
`<br>这就是<b>线性可识别性(定理 1</b>的全部直觉。`;
}
/* ───────── 主绘制 ───────── */
function draw(){ drawCurves(); drawBars(); updateText(); }
/* ───────── 自动扫描动画 ───────── */
function loop(now){
if(auto){
const dt=(now-lastTick)/1000; lastTick=now;
let v=rho + autoDir*dt*0.22;
if(v<=0.05){ v=0.05; autoDir=1; }
if(v>=0.97){ v=0.97; autoDir=-1; }
setRho(v,false);
} else { lastTick=now; }
requestAnimationFrame(loop);
}
/* ───────── 键盘热键 ───────── */
document.addEventListener('keydown', e=>{
if(e.code==='Space'){ e.preventDefault(); document.getElementById('autoBtn').click(); }
else if(e.key==='ArrowLeft'){ setRho(rho-0.03,true); }
else if(e.key==='ArrowRight'){ setRho(rho+0.03,true); }
else if(e.key==='0'){ document.getElementById('resetBtn').click(); }
});
window.addEventListener('resize', draw);
/* ───────── 初始化 ───────── */
setRho(0.70,false);
draw();
requestAnimationFrame(loop);
</script>
</body>
</html>