Files
worldmodel/JEPA/math/animations/gaussian_uniqueness_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

197 lines
17 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>高斯唯一性(Sturm-Liouville)— 交互式探索</title>
<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);--ok:#56d364;--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 330px;grid-template-rows:auto 1fr;grid-template-areas:"head head head" "ctrl stage read";gap:18px;padding:22px 26px;}
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;}
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);}
.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;}
.aval{font:700 40px/1 'JetBrains Mono',monospace;color:var(--linear);}
.aval 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,40%),#2a323d var(--fill,40%));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);}
input[type=range]::-moz-range-thumb{width:18px;height:18px;border-radius:50%;background:#eaf6fb;border:3px solid var(--linear);}
.hint{font-size:12px;color:var(--txt-3);line-height:1.55;margin-top:10px;}
.preset{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px;}
.preset button{flex:1 1 45%;padding:9px 6px;border-radius:8px;border:1px solid var(--line);background:var(--panel-2);color:var(--txt-2);font:500 11px 'Sora';cursor:pointer;}
.preset button:hover{border-color:var(--linear);color:var(--linear);}
.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;}
.play button.live{background:var(--linear-soft);border-color:var(--linear);color:var(--linear);}
.badge{display:inline-block;padding:4px 10px;border-radius:20px;font:600 12px 'JetBrains Mono',monospace;}
.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);}
.grid2{flex:1;display:grid;grid-template-rows:1fr 1fr;gap:14px;min-height:0;}
.canvas-wrap{position:relative;min-height:0;border-radius:10px;overflow:hidden;background:#0b0e13;border:1px solid var(--line);}
.canvas-wrap .cap{position:absolute;top:9px;left:13px;font:400 12px 'JetBrains Mono',monospace;color:var(--txt-3);}
canvas{display:block;width:100%;height:100%;}
.reading{grid-area:read;padding:22px;display:flex;flex-direction:column;gap:16px;overflow-y:auto;}
.reading h3{margin:0;font-family:'Spectral',serif;font-weight:600;font-size:17px;}
.r2box{position:relative;height:150px;border-radius:10px;overflow:hidden;background:#0b0e13;border:1px solid var(--line);}
.formula{font:500 13.5px 'JetBrains Mono',monospace;background:#0b0e13;border:1px solid var(--line);border-radius:8px;padding:13px 15px;line-height:1.7;}
.formula .hi{color:var(--linear);}.formula .hl{color:var(--nl);}
.verdict{margin-top:auto;padding:16px;border-radius:10px;font-family:'Spectral',serif;font-size:14px;line-height:1.6;}
.verdict.win{background:linear-gradient(135deg,rgba(86,211,100,.16),transparent 70%);border:1px solid rgba(86,211,100,.4);}
.verdict.lose{background:linear-gradient(135deg,var(--nl-soft),transparent 70%);border:1px solid rgba(244,162,97,.4);}
.verdict b{color:var(--ok);}
.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:repeat(4,auto);grid-template-areas:"head" "ctrl" "stage" "read";overflow:auto;height:auto;}body{overflow:auto;}.grid2{height:520px;flex:none;}}
</style>
</head>
<body>
<div class="app">
<header>
<div style="display:flex;flex-direction:column;gap:6px;">
<span class="kicker">LeJEPA · Topic 4 · 定理 2</span>
<h1>高斯唯一性 <em>Sturm-Liouville</em></h1>
</div>
<p class="sub">扫描广义正态分布的形状参数 <b style="color:var(--linear)">α</b>,看密度 p(z;α) 的<strong>得分函数 (log p)</strong> 是否线性——只有 α=2(高斯)时得分线性,线性可识别性才成立。</p>
</header>
<section class="card controls">
<div class="block">
<div class="label">形状参数 α</div>
<div class="aval"><span id="aTxt">2.00</span></div>
<div id="aName" style="margin-top:8px;"></div>
<input id="alpha" type="range" min="0.6" max="6" step="0.05" value="2" aria-label="形状参数 α" title="形状参数 α" />
<div class="hint">广义正态 p(z;α) ∝ exp(−|z/β|^α),β 调成单位方差。α=1 拉普拉斯,α=2 高斯,α→∞ 均匀。</div>
<div class="preset">
<button data-a="1">α=1 拉普拉斯</button>
<button data-a="2">α=2 高斯 ✓</button>
<button data-a="3">α=3 超高斯</button>
<button data-a="6">α=6 ≈均匀</button>
</div>
</div>
<div class="block">
<div class="label">得分函数线性度</div>
<div id="scoreBadge" class="badge"></div>
<div class="hint">得分 (log p) = (α/βᵅ)·|z|^{α−1}·sign(z)。仅 α=2 时退化为线性的 −z。</div>
</div>
<div class="block">
<div class="label">演示</div>
<div class="play"><button id="autoBtn" class="live">⏸ 自动扫描 α</button></div>
</div>
</section>
<section class="card stage">
<div class="ttl"><h2>密度与得分函数</h2><span>α=2 是唯一让得分变直线的点</span></div>
<div class="grid2">
<div class="canvas-wrap"><div class="cap">密度 p(z; α)</div><canvas id="density"></canvas></div>
<div class="canvas-wrap"><div class="cap">得分函数 (log p)′(z) — 直线 = 高斯</div><canvas id="score"></canvas></div>
</div>
</section>
<section class="card reading">
<h3>充要链条</h3>
<div class="formula">p 高斯<br>&nbsp;⟺ 得分 (log p) <span class="hi">线性</span><br>&nbsp;⟺ 第一特征函数仿射<br>&nbsp;<span class="hi">线性可识别性成立</span></div>
<h3 style="margin-top:2px;">线性可识别性 R²(α)</h3>
<div class="r2box"><canvas id="r2"></canvas></div>
<div class="verdict win" id="verdict"></div>
<div class="src">来源:JEPA/math/04_sturm_liouville_uniqueness.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 nav=document.getElementById('nav');NAV.forEach(([n,t,f])=>{const a=document.createElement('a');a.href=f;a.textContent=n+' '+t;if(f==='gaussian_uniqueness_explainer.html')a.className='on';nav.appendChild(a);});
let alpha=2, auto=true, dir=1, lastTick=performance.now();
function lgamma(x){const g=7,c=[0.99999999999980993,676.5203681218851,-1259.1392167224028,771.32342877765313,-176.61502916214059,12.507343278686905,-0.13857109526572012,9.9843695780195716e-6,1.5056327351493116e-7];if(x<0.5)return Math.log(Math.PI/Math.sin(Math.PI*x))-lgamma(1-x);x-=1;let a=c[0];const t=x+g+0.5;for(let i=1;i<g+2;i++)a+=c[i]/(x+i);return 0.5*Math.log(2*Math.PI)+(x+0.5)*Math.log(t)-t+Math.log(a);}
function beta_(a){return Math.exp(0.5*(lgamma(1/a)-lgamma(3/a)));}// unit variance scale
function density(z,a){const b=beta_(a);const norm=a/(2*b*Math.exp(lgamma(1/a)));return norm*Math.exp(-Math.pow(Math.abs(z/b),a));}
function score(z,a){const b=beta_(a);if(Math.abs(z)<1e-6)return 0;return -(a/Math.pow(b,a))*Math.pow(Math.abs(z),a-1)*Math.sign(z);}
// R²(α): 启发式建模——得分线性度。线性回归 score≈k·z 的 R²
function r2OfAlpha(a){
let sx=0,sy=0,sxx=0,sxy=0,syy=0,n=0;
for(let z=-3;z<=3;z+=0.05){const s=score(z,a);const wgt=density(z,a);sx+=z*wgt;sy+=s*wgt;sxx+=z*z*wgt;sxy+=z*s*wgt;syy+=s*s*wgt;n+=wgt;}
const mx=sx/n,my=sy/n;const cov=sxy/n-mx*my,vx=sxx/n-mx*mx,vy=syy/n-my*my;
const r=cov/Math.sqrt(vx*vy+1e-12);return Math.max(0,Math.min(1,r*r));
}
const alphaEl=document.getElementById('alpha'),aTxt=document.getElementById('aTxt'),aName=document.getElementById('aName');
function nameOf(a){if(a<0.9)return['重尾','#e76f51'];if(a<1.2)return['拉普拉斯 (α≈1)','#f4a261'];if(a<1.7)return['亚高斯','#f4a261'];if(a<2.3)return['高斯 (α=2) ✓','#56d364'];if(a<4)return['超高斯','#f4a261'];return['≈ 均匀','#e76f51'];}
function setAlpha(v,fromUser){alpha=Math.max(0.6,Math.min(6,v));alphaEl.value=alpha;aTxt.textContent=alpha.toFixed(2);alphaEl.style.setProperty('--fill',((alpha-0.6)/(6-0.6)*100).toFixed(1)+'%');const[nm,col]=nameOf(alpha);aName.innerHTML='<span class="badge" style="background:'+col+'22;color:'+col+'">'+nm+'</span>';if(fromUser){auto=false;const b=document.getElementById('autoBtn');b.classList.remove('live');b.textContent='▶ 自动扫描 α';}draw();}
alphaEl.addEventListener('input',e=>setAlpha(+e.target.value,true));
document.querySelectorAll('.preset button').forEach(b=>b.addEventListener('click',()=>setAlpha(+b.dataset.a,true)));
document.getElementById('autoBtn').addEventListener('click',e=>{auto=!auto;e.target.classList.toggle('live',auto);e.target.textContent=auto?'⏸ 自动扫描 α':'▶ 自动扫描 α';lastTick=performance.now();});
const dC=document.getElementById('density'),dx=dC.getContext('2d');
const sC=document.getElementById('score'),sx2=sC.getContext('2d');
const rC=document.getElementById('r2'),rx=rC.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 drawDensity(){const {w,h,dpr}=fit(dC);dx.setTransform(dpr,0,0,dpr,0,0);dx.clearRect(0,0,w,h);
const padL=20,padR=16,padT=22,padB=22,x0=padL,x1=w-padR,y0=padT,y1=h-padB,lim=3.4;
const sx=z=>x0+(z+lim)/(2*lim)*(x1-x0);
let mx=0;for(let z=-lim;z<=lim;z+=0.05)mx=Math.max(mx,density(z,alpha));
const sy=v=>y1-v/(mx*1.1)*(y1-y0);
dx.strokeStyle='#1c2530';dx.lineWidth=1;for(let g=-3;g<=3;g++){dx.beginPath();dx.moveTo(sx(g),y0);dx.lineTo(sx(g),y1);dx.stroke();}
// 高斯参考
dx.strokeStyle='rgba(86,211,100,.35)';dx.setLineDash([5,4]);dx.beginPath();for(let i=0,z=-lim;i<=200;i++,z=-lim+2*lim*i/200){const py=sy(density(z,2));i?dx.lineTo(sx(z),py):dx.moveTo(sx(z),py);}dx.stroke();dx.setLineDash([]);
// 当前密度
const isG=Math.abs(alpha-2)<0.08;dx.strokeStyle=isG?'#56d364':'#4cc9f0';dx.lineWidth=2.5;dx.shadowColor=dx.strokeStyle;dx.shadowBlur=8;dx.beginPath();for(let i=0,z=-lim;i<=240;i++,z=-lim+2*lim*i/240){const py=sy(density(z,alpha));i?dx.lineTo(sx(z),py):dx.moveTo(sx(z),py);}dx.stroke();dx.shadowBlur=0;
}
function drawScore(){const {w,h,dpr}=fit(sC);sx2.setTransform(dpr,0,0,dpr,0,0);sx2.clearRect(0,0,w,h);
const padL=20,padR=16,padT=18,padB=20,x0=padL,x1=w-padR,y0=padT,y1=h-padB,lim=2.8,vlim=4;
const sx=z=>x0+(z+lim)/(2*lim)*(x1-x0),sy=v=>y1-(Math.max(-vlim,Math.min(vlim,v))+vlim)/(2*vlim)*(y1-y0);
sx2.strokeStyle='#1c2530';sx2.lineWidth=1;for(let g=-2;g<=2;g++){sx2.beginPath();sx2.moveTo(sx(g),y0);sx2.lineTo(sx(g),y1);sx2.stroke();}
sx2.strokeStyle='#33404e';sx2.beginPath();sx2.moveTo(x0,sy(0));sx2.lineTo(x1,sy(0));sx2.stroke();
// 高斯参考 -z(线性)
sx2.strokeStyle='rgba(86,211,100,.4)';sx2.setLineDash([5,4]);sx2.lineWidth=1.5;sx2.beginPath();sx2.moveTo(sx(-lim),sy(lim));sx2.lineTo(sx(lim),sy(-lim));sx2.stroke();sx2.setLineDash([]);
sx2.fillStyle='rgba(86,211,100,.7)';sx2.font="11px 'JetBrains Mono',monospace";sx2.fillText('z (线性)',x1-78,sy(-lim)+14);
// 当前 score
const isG=Math.abs(alpha-2)<0.08;sx2.strokeStyle=isG?'#56d364':'#f4a261';sx2.lineWidth=2.5;sx2.shadowColor=sx2.strokeStyle;sx2.shadowBlur=7;sx2.beginPath();let first=true;for(let i=0,z=-lim;i<=240;i++,z=-lim+2*lim*i/240){const v=score(z,alpha);const py=sy(v);if(first){sx2.moveTo(sx(z),py);first=false;}else sx2.lineTo(sx(z),py);}sx2.stroke();sx2.shadowBlur=0;
}
function drawR2(){const {w,h,dpr}=fit(rC);rx.setTransform(dpr,0,0,dpr,0,0);rx.clearRect(0,0,w,h);
const padL=30,padR=14,padT=16,padB=22,x0=padL,x1=w-padR,y0=padT,y1=h-padB,aMin=0.6,aMax=6;
const sx=a=>x0+(a-aMin)/(aMax-aMin)*(x1-x0),sy=v=>y1-v*(y1-y0);
rx.strokeStyle='#1c2530';rx.lineWidth=1;rx.font="10px 'JetBrains Mono',monospace";rx.fillStyle='#5c6672';
[0,0.5,1].forEach(v=>{rx.beginPath();rx.moveTo(x0,sy(v));rx.lineTo(x1,sy(v));rx.stroke();rx.fillText(v.toFixed(1),4,sy(v)+3);});
[1,2,3,4,5,6].forEach(a=>{rx.fillText(a,sx(a)-3,y1+15);});
// 曲线
rx.strokeStyle='#4cc9f0';rx.lineWidth=2;rx.beginPath();for(let i=0;i<=160;i++){const a=aMin+(aMax-aMin)*i/160;const v=r2OfAlpha(a);i?rx.lineTo(sx(a),sy(v)):rx.moveTo(sx(a),sy(v));}rx.stroke();
// α=2 峰标
rx.strokeStyle='rgba(86,211,100,.5)';rx.setLineDash([4,4]);rx.beginPath();rx.moveTo(sx(2),y0);rx.lineTo(sx(2),y1);rx.stroke();rx.setLineDash([]);
rx.fillStyle='#56d364';rx.font="600 10px 'JetBrains Mono',monospace";rx.fillText('α=2 峰值',sx(2)+4,y0+10);
// 当前 α 标记
const cv=r2OfAlpha(alpha);rx.fillStyle=Math.abs(alpha-2)<0.08?'#56d364':'#f4a261';rx.beginPath();rx.arc(sx(alpha),sy(cv),4.5,0,7);rx.fill();
}
function draw(){
drawDensity();drawScore();drawR2();
const r2=r2OfAlpha(alpha), isG=Math.abs(alpha-2)<0.08;
const sb=document.getElementById('scoreBadge');
if(isG){sb.style.background='rgba(86,211,100,.18)';sb.style.color='#56d364';sb.textContent='线性 ✓(−z';}
else{sb.style.background='var(--nl-soft)';sb.style.color='#f4a261';sb.textContent='非线性 ✗';}
const v=document.getElementById('verdict');
if(isG){v.className='verdict win';v.innerHTML='<b>α = 2:得分函数 = z(线性)</b><br>第一特征函数仿射 → 线性可识别性成立,R² ≈ '+r2.toFixed(3)+'。高斯是<strong>唯一</strong>满足此条件的分布。';}
else{v.className='verdict lose';v.innerHTML='当前得分函数<b style="color:var(--nl)">非线性</b>(α≠2),第一特征函数不再仿射 → 线性可识别性失败,R² ≈ <b style="color:var(--nl)">'+r2.toFixed(3)+'</b>。把 α 拨回 <b style="color:var(--ok)">2</b> 看 R² 冲上峰值。';}
}
function loop(now){if(auto){const dt=(now-lastTick)/1000;lastTick=now;let v=alpha+dir*dt*0.9;if(v<=0.6){v=0.6;dir=1;}if(v>=6){v=6;dir=-1;}setAlpha(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')setAlpha(alpha-0.1,true);else if(e.key==='ArrowRight')setAlpha(alpha+0.1,true);});
window.addEventListener('resize',draw);
setAlpha(2,false);draw();requestAnimationFrame(loop);
</script>
</body>
</html>