▶ 展示 · 回索引
08-three-methods.html
<!doctype html><html lang="zh-Hant"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>KP11 08 Three Methods</title><style>
:root{--bg:#071521;--panel:#0f2637;--panel2:#102c40;--ink:#f7fbff;--muted:#a9bfd0;--cyan:#4fd1c5;--amber:#f6c85f;--coral:#ff7b72;--lime:#8fd694;--violet:#b794f4;--blue:#63b3ed}
*{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;background:#02080d;color:var(--ink);font-family:Inter,"Noto Sans TC","Microsoft JhengHei",sans-serif}
.wrap{width:100vw;height:100vh;display:grid;place-items:center;background:radial-gradient(circle at 18% 0%,#10344d 0,#02080d 58%,#000 100%)}
.stage{position:relative;width:min(100vw,177.7778vh);height:min(56.25vw,100vh);aspect-ratio:16/9;background:var(--bg);overflow:hidden}
canvas{position:absolute;inset:0;width:100%;height:100%;touch-action:none}
.hint{position:absolute;right:2.1%;top:1.9%;font-size:.8vw;color:#9ab4c7;background:#071521cc;border:1px solid #ffffff18;border-radius:999px;padding:.5em .75em;z-index:5}
@media(max-width:900px){.hint{font-size:1.4vw}}
</style></head><body><div class="wrap"><div class="stage"><canvas id="c" width="1600" height="900"></canvas><div class="hint">互動:點擊 / 拖曳 • Click / Drag</div></div></div><script>
const c=document.getElementById('c'),ctx=c.getContext('2d');const W=1600,H=900;
const P={bg:'#071521',panel:'#0f2637',panel2:'#102c40',ink:'#f7fbff',muted:'#a9bfd0',cyan:'#4fd1c5',amber:'#f6c85f',coral:'#ff7b72',lime:'#8fd694',violet:'#b794f4',blue:'#63b3ed',grid:'#17384d'};
let zones=[],drag=null;
function reset(){zones=[];ctx.clearRect(0,0,W,H);ctx.fillStyle=P.bg;ctx.fillRect(0,0,W,H);for(let x=0;x<W;x+=80)line(x,0,x,H,'#0b2434',1);for(let y=0;y<H;y+=80)line(0,y,W,y,'#0b2434',1)}
function line(x1,y1,x2,y2,color='#fff',lw=3,dash=[]){ctx.save();ctx.strokeStyle=color;ctx.lineWidth=lw;ctx.setLineDash(dash);ctx.beginPath();ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);ctx.stroke();ctx.restore()}
function rr(x,y,w,h,r=20,fill=P.panel,stroke=null,lw=2){ctx.beginPath();ctx.roundRect(x,y,w,h,r);if(fill){ctx.fillStyle=fill;ctx.fill()}if(stroke){ctx.strokeStyle=stroke;ctx.lineWidth=lw;ctx.stroke()}}
function txt(t,x,y,size=30,color=P.ink,align='left',weight=700){ctx.fillStyle=color;ctx.font=`${weight} ${size}px Inter,Noto Sans TC,Microsoft JhengHei,sans-serif`;ctx.textAlign=align;ctx.textBaseline='middle';ctx.fillText(t,x,y)}
function wrap(t,x,y,maxw,lh=34,size=24,color=P.muted,weight=550){
ctx.fillStyle=color;ctx.font=`${weight} ${size}px Inter,Noto Sans TC,Microsoft JhengHei,sans-serif`;ctx.textAlign='left';ctx.textBaseline='top';
t=String(t).replace(/\\n/g,'\n');let ln='',yy=y;
for(const ch of t){if(ch==='\n'){if(ln)ctx.fillText(ln,x,yy);ln='';yy+=lh;continue} const q=ln+ch;
if(ctx.measureText(q).width>maxw&&ln){ctx.fillText(ln,x,yy);ln=ch;yy+=lh}else ln=q}
if(ln)ctx.fillText(ln,x,yy);return yy+lh}
function title(zh,en,no){txt(`${String(no).padStart(2,'0')} ${zh}`,70,68,40,P.ink,'left',900);txt(en,72,111,22,P.cyan,'left',650);line(70,146,1530,146,P.grid,2)}
function chip(t,x,y,w,color){rr(x,y,w,40,20,color+'20',color,2);txt(t,x+w/2,y+20,18,color,'center',800)}
function btn(label,x,y,w,h,onClick,active=false,color=P.cyan){rr(x,y,w,h,15,active?color+'28':'#ffffff0c',active?color:'#ffffff22',2);txt(label,x+w/2,y+h/2,19,active?color:P.ink,'center',800);zones.push({type:'button',x,y,w,h,onClick})}
function slider(x,y,w,min,max,value,onChange,label,fmt=v=>v.toFixed(2)){line(x,y,x+w,y,'#ffffff35',8);const t=(value-min)/(max-min),kx=x+t*w;ctx.fillStyle=P.amber;ctx.beginPath();ctx.arc(kx,y,15,0,Math.PI*2);ctx.fill();txt(label,x,y-31,20,P.muted,'left',650);txt(fmt(value),x+w,y-31,22,P.amber,'right',850);zones.push({type:'slider',x:x-20,y:y-28,w:w+40,h:56,min,max,onChange})}
function arrow(x1,y1,x2,y2,color=P.cyan,lw=5){line(x1,y1,x2,y2,color,lw);const a=Math.atan2(y2-y1,x2-x1),s=14;ctx.fillStyle=color;ctx.beginPath();ctx.moveTo(x2,y2);ctx.lineTo(x2-s*Math.cos(a-.5),y2-s*Math.sin(a-.5));ctx.lineTo(x2-s*Math.cos(a+.5),y2-s*Math.sin(a+.5));ctx.closePath();ctx.fill()}
function gauss(cx,base,amp,sig,color,lw=5,dash=[]){ctx.save();ctx.setLineDash(dash);ctx.beginPath();for(let x=cx-260;x<=cx+260;x+=3){const y=base-amp*Math.exp(-.5*((x-cx)/sig)**2);x===cx-260?ctx.moveTo(x,y):ctx.lineTo(x,y)}ctx.strokeStyle=color;ctx.lineWidth=lw;ctx.stroke();ctx.restore()}
function pos(e){const r=c.getBoundingClientRect();return{x:(e.clientX-r.left)*W/r.width,y:(e.clientY-r.top)*H/r.height}}
c.addEventListener('pointerdown',e=>{const p=pos(e);for(const z of zones){if(p.x>=z.x&&p.x<=z.x+z.w&&p.y>=z.y&&p.y<=z.y+z.h){if(z.type==='button'){z.onClick();return}if(z.type==='slider'){drag=z;const t=Math.max(0,Math.min(1,(p.x-(z.x+20))/(z.w-40)));z.onChange(z.min+t*(z.max-z.min));return}}}});
c.addEventListener('pointermove',e=>{if(!drag)return;const p=pos(e),t=Math.max(0,Math.min(1,(p.x-(drag.x+20))/(drag.w-40)));drag.onChange(drag.min+t*(drag.max-drag.min))});
addEventListener('pointerup',()=>drag=null);
let s=0;
function render(){reset();title('三大流派:ML、MAP、Bayesian Inference','Three methods compared',8);
const A=[['ML','Data only','單一點','小樣本最脆弱',P.cyan],['MAP','Prior + Data','單一點','先驗可穩定小樣本',P.amber],['Bayes','Full posterior','完整分佈','最誠實但積分最昂貴',P.violet]];
A.forEach((a,i)=>{const x=120+i*500;rr(x,280,400,355,26,s===i?P.panel2:P.panel,s===i?a[4]:'#ffffff15',3);txt(a[0],x+200,335,36,a[4],'center',900);txt(a[1],x+200,395,23,P.ink,'center',800);chip(a[2],x+95,455,210,a[4]);wrap(a[3],x+55,535,290,34,22,P.muted,600);zones.push({type:'button',x,y:280,w:400,h:355,onClick:()=>{s=i;render()}})});
const note=['大資料有效率,小資料容易被 noise 拉走。','以 prior regularize,但仍丟掉 posterior 寬度。','保留 posterior uncertainty 並進入 predictive distribution。'][s];
rr(250,710,1100,90,22,'#ffffff08');txt(note,800,755,26,P.lime,'center',800)}
render();</script></body></html>