Spaces:
Paused
Paused
| <!-- Layer Evolution: AUC and intrinsic dimension per layer across models --> | |
| <div class="layer-evo"> | |
| <div class="layer-controls"> | |
| <div class="model-legend" id="layer-legend"></div> | |
| <div class="metric-toggle"> | |
| <button class="metric-btn" data-metric="aucs">AUC</button> | |
| <button class="metric-btn" data-metric="dims">Intrinsic Dim</button> | |
| <button class="metric-btn active" data-metric="grassmann">Grassmann Dist</button> | |
| </div> | |
| </div> | |
| <svg id="layer-evo-svg"></svg> | |
| </div> | |
| <style> | |
| .layer-evo { position: relative; width: 100%; } | |
| #layer-evo-svg { width: 100%; height: 360px; } | |
| .layer-controls { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; } | |
| .metric-toggle { display: flex; gap: 2px; background: var(--surface-bg); border: 1px solid var(--border-color); border-radius: 8px; padding: 2px; flex-shrink: 0; } | |
| .metric-btn { | |
| padding: 4px 12px; border: none; border-radius: 6px; font-size: 11px; font-weight: 600; | |
| background: transparent; color: var(--muted-color); cursor: pointer; transition: all 0.2s; | |
| } | |
| .metric-btn.active { background: var(--primary-color); color: #fff; } | |
| #layer-legend { display: flex; flex-wrap: wrap; gap: 4px; } | |
| .lchip { | |
| display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px; border-radius: 12px; | |
| font-size: 10px; font-weight: 600; cursor: pointer; transition: all 0.2s; | |
| border: 1.5px solid transparent; user-select: none; | |
| } | |
| .lchip .cdot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; } | |
| .lchip.inactive { opacity: 0.25; } | |
| .lchip:hover { transform: scale(1.05); } | |
| .layer-tip { | |
| position: absolute; background: var(--surface-bg); border: 1px solid var(--border-color); | |
| border-radius: 8px; padding: 8px 12px; font-size: 11px; pointer-events: none; | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 10; color: var(--text-color); line-height: 1.5; | |
| } | |
| @media (max-width: 640px) { #layer-evo-svg { height: 280px; } .layer-controls { flex-direction: column; align-items: flex-start; } } | |
| </style> | |
| <script src="https://d3js.org/d3.v7.min.js"></script> | |
| <script> | |
| (function(){ | |
| const container = document.querySelector('.layer-evo'); | |
| const svg = d3.select('#layer-evo-svg'); | |
| const legendEl = document.getElementById('layer-legend'); | |
| const tip = d3.select(container).append('div').attr('class','layer-tip').style('display','none'); | |
| const colors = { | |
| 'GPT-2':'#fbbf24','GPT-2-Med':'#f59e0b','GPT-2-Large':'#d97706', | |
| 'Gemma-2B':'#fb923c','Qwen2-1.5B':'#f97316','Llama-1B':'#ea580c', | |
| 'Llama-3B':'#c2410c','Qwen2-7B':'#dc2626','Mistral-7B':'#7c2d12' | |
| }; | |
| let data = null, metric = 'grassmann', active = new Set(Object.keys(colors)), hovered = null; | |
| Object.entries(colors).forEach(([name, c]) => { | |
| const ch = document.createElement('span'); | |
| ch.className = 'lchip'; ch.dataset.model = name; | |
| ch.innerHTML = `<span class="cdot" style="background:${c}"></span>${name}`; | |
| ch.style.background = `color-mix(in srgb, ${c} 12%, transparent)`; | |
| ch.style.color = c; ch.style.borderColor = c; | |
| ch.addEventListener('click', () => { | |
| if (active.has(name)) { if (active.size === 1) return; active.delete(name); ch.classList.add('inactive'); } | |
| else { active.add(name); ch.classList.remove('inactive'); } | |
| draw(); | |
| }); | |
| ch.addEventListener('mouseenter', () => { hovered = name; draw(); }); | |
| ch.addEventListener('mouseleave', () => { hovered = null; draw(); }); | |
| legendEl.appendChild(ch); | |
| }); | |
| fetch('data/layer_evolution.json').then(r => r.json()).then(d => { data = d; draw(); }); | |
| function draw() { | |
| svg.selectAll('*').remove(); | |
| if (!data) return; | |
| const rect = document.getElementById('layer-evo-svg').getBoundingClientRect(); | |
| const W = rect.width, H = rect.height; | |
| const m = { top: 20, right: 16, bottom: 40, left: 50 }; | |
| const w = W - m.left - m.right, h = H - m.top - m.bottom; | |
| svg.attr('viewBox', `0 0 ${W} ${H}`); | |
| const g = svg.append('g').attr('transform', `translate(${m.left},${m.top})`); | |
| // X: normalized depth (0-100%) | |
| const x = d3.scaleLinear().domain([0, 100]).range([0, w]); | |
| // Y: depends on metric | |
| let yDomain; | |
| if (metric === 'aucs') yDomain = [0.45, 1.0]; | |
| else if (metric === 'grassmann') { | |
| const allG = Object.entries(data).filter(([n]) => active.has(n)).flatMap(([,d]) => d.grassmann || []); | |
| yDomain = [0, Math.ceil(d3.max(allG) * 2) / 2 + 0.5]; | |
| } else { | |
| const allDims = Object.entries(data).filter(([n]) => active.has(n)).flatMap(([,d]) => d.dims || []); | |
| yDomain = [0, Math.ceil(d3.max(allDims) / 10) * 10 + 5]; | |
| } | |
| const y = d3.scaleLinear().domain(yDomain).range([h, 0]).nice(); | |
| // Phase bands | |
| const phases = [{name:'I',start:0,end:30,color:'#ea580c'},{name:'II',start:30,end:70,color:'#f59e0b'},{name:'III',start:70,end:100,color:'#c2410c'}]; | |
| phases.forEach(p => { | |
| g.append('rect').attr('x',x(p.start)).attr('width',x(p.end)-x(p.start)).attr('y',0).attr('height',h) | |
| .attr('fill',p.color).attr('opacity',0.04).attr('rx',2); | |
| g.append('text').attr('x',(x(p.start)+x(p.end))/2).attr('y',12) | |
| .attr('text-anchor','middle').attr('fill',p.color).attr('font-size',9).attr('opacity',0.5) | |
| .attr('font-weight',600).text('Phase '+p.name); | |
| }); | |
| // Grid + axes | |
| g.append('g').selectAll('line').data(y.ticks(5)).enter().append('line') | |
| .attr('x1',0).attr('x2',w).attr('y1',d=>y(d)).attr('y2',d=>y(d)) | |
| .attr('stroke','var(--grid-color)').attr('stroke-dasharray','2,3'); | |
| g.append('g').attr('transform',`translate(0,${h})`).call(d3.axisBottom(x).ticks(5).tickFormat(d=>d+'%')) | |
| .selectAll('text').attr('fill','var(--tick-color)'); | |
| const yFmt = metric==='aucs' ? d3.format('.2f') : metric==='grassmann' ? d3.format('.1f') : d3.format('d'); | |
| const yLabel = metric==='aucs' ? 'AUC' : metric==='grassmann' ? 'Grassmann Distance' : 'Intrinsic Dimension (MLE)'; | |
| g.append('g').call(d3.axisLeft(y).ticks(5).tickFormat(yFmt)) | |
| .selectAll('text').attr('fill','var(--tick-color)'); | |
| g.append('text').attr('x',w/2).attr('y',h+35).attr('text-anchor','middle') | |
| .attr('fill','var(--muted-color)').attr('font-size',12).text('Normalized depth (%)'); | |
| g.append('text').attr('transform','rotate(-90)').attr('x',-h/2).attr('y',-38) | |
| .attr('text-anchor','middle').attr('fill','var(--muted-color)').attr('font-size',12) | |
| .text(yLabel); | |
| // Build all model line data | |
| const isG = metric === 'grassmann'; | |
| const allLines = []; | |
| Object.entries(data).forEach(([name, d]) => { | |
| const c = colors[name] || '#888'; | |
| if (!active.has(name)) return; | |
| const vals = d[metric] || []; | |
| if (!vals.length) return; | |
| const pts = isG | |
| ? vals.map((v, i) => ({ x: ((i + 0.5) / (d.layers - 1)) * 100, y: v, layer: i, layerTo: i + 1 })) | |
| : vals.map((v, i) => ({ x: (i / (d.layers - 1)) * 100, y: v, layer: i })); | |
| allLines.push({ name, c, pts, d, vals }); | |
| }); | |
| // Draw lines | |
| const lineGen = d3.line().x(p => x(p.x)).y(p => y(p.y)).curve(d3.curveMonotoneX); | |
| allLines.forEach(({ name, c, pts, d, vals }) => { | |
| const isH = hovered === name; | |
| const isDim = hovered && hovered !== name; | |
| if (isG && (isH || !hovered)) { | |
| const areaGen = d3.area().x(p => x(p.x)).y0(h).y1(p => y(p.y)).curve(d3.curveMonotoneX); | |
| g.append('path').datum(pts).attr('d', areaGen) | |
| .attr('fill', c).attr('opacity', isH ? 0.12 : 0.04); | |
| } | |
| g.append('path').datum(pts).attr('d', lineGen) | |
| .attr('fill','none').attr('stroke',c) | |
| .attr('stroke-width', isH ? 3.5 : 2).attr('opacity', isDim ? 0.12 : isH ? 1 : 0.8); | |
| if (!isG) { | |
| const bestI = d.bestLayer; | |
| if (bestI < vals.length) { | |
| const bx = (bestI / (d.layers - 1)) * 100; | |
| g.append('circle').attr('cx',x(bx)).attr('cy',y(vals[bestI])) | |
| .attr('r', isH ? 6 : 4).attr('fill',c).attr('stroke','var(--surface-bg)').attr('stroke-width',2) | |
| .attr('opacity', isDim ? 0.12 : 1); | |
| } | |
| } | |
| }); | |
| // Crosshair overlay | |
| const crossLine = g.append('line').attr('y1',0).attr('y2',h) | |
| .attr('stroke','var(--muted-color)').attr('stroke-width',0.8).attr('stroke-dasharray','4,3') | |
| .attr('opacity',0).attr('pointer-events','none'); | |
| const crossDots = g.append('g').attr('pointer-events','none'); | |
| const depthLabel = g.append('text').attr('y',-4).attr('text-anchor','middle') | |
| .attr('fill','var(--muted-color)').attr('font-size',9).attr('opacity',0); | |
| function interp(pts, xVal) { | |
| if (!pts.length) return null; | |
| if (xVal <= pts[0].x) return pts[0]; | |
| if (xVal >= pts[pts.length-1].x) return pts[pts.length-1]; | |
| for (let i = 0; i < pts.length - 1; i++) { | |
| if (xVal >= pts[i].x && xVal <= pts[i+1].x) { | |
| const t = (xVal - pts[i].x) / (pts[i+1].x - pts[i].x); | |
| return { y: pts[i].y + t * (pts[i+1].y - pts[i].y), layer: pts[i].layer, layerTo: pts[i].layerTo }; | |
| } | |
| } | |
| return null; | |
| } | |
| g.append('rect').attr('width',w).attr('height',h).attr('fill','transparent').attr('cursor','crosshair') | |
| .on('mousemove', function(e) { | |
| const [mx] = d3.pointer(e); | |
| const depth = x.invert(mx); | |
| crossLine.attr('x1',mx).attr('x2',mx).attr('opacity',0.5); | |
| depthLabel.attr('x',mx).attr('opacity',0.7).text(Math.round(depth)+'%'); | |
| crossDots.selectAll('*').remove(); | |
| const metricLabel = metric==='aucs' ? 'AUC' : metric==='grassmann' ? 'Dist' : 'Dim'; | |
| let rows = []; | |
| allLines.forEach(({ name, c, pts }) => { | |
| const isDim = hovered && hovered !== name; | |
| if (isDim) return; | |
| const p = interp(pts, depth); | |
| if (!p) return; | |
| crossDots.append('circle').attr('cx',mx).attr('cy',y(p.y)) | |
| .attr('r',4).attr('fill',c).attr('stroke','var(--surface-bg)').attr('stroke-width',1.5); | |
| const fVal = metric==='aucs' ? p.y.toFixed(3) : metric==='grassmann' ? p.y.toFixed(2) : Math.round(p.y); | |
| rows.push(`<span style="color:${c}">\u25CF</span> ${name}: <strong>${fVal}</strong>`); | |
| }); | |
| if (rows.length) { | |
| tip.style('display','block') | |
| .html(`<strong>${Math.round(depth)}% depth</strong><br/>${rows.join('<br/>')}`) | |
| .style('left', Math.min(e.offsetX+16, W-180)+'px') | |
| .style('top', Math.max(0, e.offsetY-20-rows.length*10)+'px'); | |
| } | |
| }) | |
| .on('mouseleave', function() { | |
| crossLine.attr('opacity',0); | |
| depthLabel.attr('opacity',0); | |
| crossDots.selectAll('*').remove(); | |
| tip.style('display','none'); | |
| }); | |
| svg.selectAll('.domain').attr('stroke','var(--axis-color)'); | |
| svg.selectAll('.tick line').attr('stroke','var(--axis-color)'); | |
| } | |
| container.querySelectorAll('.metric-btn').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| container.querySelectorAll('.metric-btn').forEach(b => b.classList.remove('active')); | |
| btn.classList.add('active'); | |
| metric = btn.dataset.metric; | |
| draw(); | |
| }); | |
| }); | |
| new ResizeObserver(draw).observe(document.getElementById('layer-evo-svg')); | |
| })(); | |
| </script> | |