Spaces:
Paused
Paused
| <!-- Generation Gap: Teacher-forced vs model-generated probe performance --> | |
| <div class="gengap-viz"> | |
| <svg id="gengap-svg"></svg> | |
| </div> | |
| <style> | |
| .gengap-viz { position: relative; width: 100%; } | |
| #gengap-svg { width: 100%; height: 300px; } | |
| .gengap-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) { #gengap-svg { height: 260px; } } | |
| </style> | |
| <script src="https://d3js.org/d3.v7.min.js"></script> | |
| <script> | |
| (function(){ | |
| const container = document.querySelector('.gengap-viz'); | |
| const svg = d3.select('#gengap-svg'); | |
| const tip = d3.select(container).append('div').attr('class','gengap-tip').style('display','none'); | |
| // Model parameter sizes for sorting/annotation | |
| const paramMap = { | |
| 'GPT-2': 0.124, 'GPT-2 Medium': 0.345, 'GPT-2 Large': 0.774, | |
| 'Gemma-2-2B': 2, 'Llama-3.2-1B': 1, 'Qwen2-1.5B': 1.5, | |
| 'Qwen2-7B': 7, 'Mistral-7B': 7 | |
| }; | |
| function fmtParams(b) { return b >= 1 ? b+'B' : Math.round(b*1000)+'M'; } | |
| let gapData = null; | |
| fetch('data/generation_gap.json').then(r => r.json()).then(data => { gapData = data; draw(); }); | |
| function draw() { | |
| svg.selectAll('*').remove(); | |
| if (!gapData) return; | |
| const data = gapData; | |
| const models = data.models, tf = data.teacher_forced, gen = data.generated; | |
| // Filter out models with anomalous tf (< 0.55, data quality issues), sort by param count | |
| const valid = models.map((m,i) => ({m, tf: tf[i], gen: gen[i], gap: tf[i]-gen[i], params: paramMap[m]||0})) | |
| .filter(d => d.tf > 0.55) | |
| .sort((a,b) => a.params - b.params); | |
| const rect = document.getElementById('gengap-svg').getBoundingClientRect(); | |
| const W = rect.width, H = rect.height; | |
| const mg = {top:20,right:16,bottom:50,left:50}; | |
| const w = W-mg.left-mg.right, h = H-mg.top-mg.bottom; | |
| svg.attr('viewBox',`0 0 ${W} ${H}`); | |
| const g = svg.append('g').attr('transform',`translate(${mg.left},${mg.top})`); | |
| const xBand = d3.scaleBand().domain(valid.map(d=>d.m)).range([0,w]).padding(0.25); | |
| const y = d3.scaleLinear().domain([0.4, 1.0]).range([h,0]).nice(); | |
| // Grid | |
| y.ticks(4).forEach(v => { | |
| g.append('line').attr('x1',0).attr('x2',w).attr('y1',y(v)).attr('y2',y(v)) | |
| .attr('stroke','var(--grid-color)').attr('stroke-dasharray','2,3'); | |
| }); | |
| // Chance line | |
| g.append('line').attr('x1',0).attr('x2',w).attr('y1',y(0.5)).attr('y2',y(0.5)) | |
| .attr('stroke','var(--border-color)').attr('stroke-dasharray','6,4'); | |
| valid.forEach((d,i) => { | |
| const cx = xBand(d.m) + xBand.bandwidth()/2; | |
| const yTF = y(Math.max(d.tf,0.4)), yGen = y(Math.max(d.gen,0.4)); | |
| // Dumbbell connector line | |
| const conn = g.append('line').attr('x1',cx).attr('x2',cx) | |
| .attr('y1',yTF).attr('y2',yTF) | |
| .attr('stroke','var(--border-color)').attr('stroke-width',2).attr('opacity',0.4); | |
| conn.transition().duration(500).delay(i*80).attr('y2',yGen); | |
| // Gap fill - capsule shape (rect with semicircle caps) | |
| const gapW = xBand.bandwidth()*0.3; | |
| const gapR = gapW/2; | |
| const gapH = Math.max(0, yGen - yTF); | |
| if (gapH > 0) { | |
| const capsule = g.append('path') | |
| .attr('d', `M${cx-gapR},${yTF} A${gapR},${gapR} 0 0,1 ${cx+gapR},${yTF} | |
| L${cx+gapR},${yGen} A${gapR},${gapR} 0 0,1 ${cx-gapR},${yGen} Z`) | |
| .attr('fill','#ef4444').attr('opacity',0); | |
| capsule.transition().duration(500).delay(i*80).attr('opacity',0.1); | |
| } | |
| // Teacher-forced dot (filled) | |
| const dotTF = g.append('circle').attr('cx',cx).attr('cy',yTF) | |
| .attr('r',0).attr('fill','#ea580c').attr('stroke','var(--surface-bg)').attr('stroke-width',2); | |
| dotTF.transition().duration(400).delay(i*80+200).attr('r',6); | |
| // Generated dot (outlined) | |
| const dotGen = g.append('circle').attr('cx',cx).attr('cy',yGen) | |
| .attr('r',0).attr('fill','var(--surface-bg)').attr('stroke','#a8a29e').attr('stroke-width',2.5); | |
| dotGen.transition().duration(400).delay(i*80+300).attr('r',5); | |
| // Gap annotation | |
| if (d.gap > 0.05) { | |
| g.append('text').attr('x',cx+xBand.bandwidth()*0.3+4).attr('y',(yTF+yGen)/2+3) | |
| .attr('text-anchor','start').attr('fill','#ef4444').attr('font-size',9).attr('font-weight',700) | |
| .attr('opacity',0).transition().duration(300).delay(i*80+500).attr('opacity',0.8) | |
| .text('-'+(d.gap*100).toFixed(0)+'pp'); | |
| } | |
| // Hover area | |
| g.append('rect').attr('x',xBand(d.m)).attr('y',0).attr('width',xBand.bandwidth()).attr('height',h) | |
| .attr('fill','transparent').attr('cursor','pointer') | |
| .on('mouseenter', function(e) { | |
| tip.style('display','block') | |
| .html(`<strong>${d.m}</strong> <span style="opacity:0.5">${fmtParams(d.params)}</span><br/>`+ | |
| `Teacher-forced: <span style="color:#ea580c;font-weight:700">${d.tf.toFixed(3)}</span><br/>`+ | |
| `Generated: <span style="color:#a8a29e;font-weight:700">${d.gen.toFixed(3)}</span><br/>`+ | |
| `Gap: <strong style="color:#ef4444">${(d.gap*100).toFixed(1)}pp</strong>`) | |
| .style('left',Math.min(e.offsetX+12,W-180)+'px').style('top',(e.offsetY-70)+'px'); | |
| }).on('mouseleave',()=>tip.style('display','none')); | |
| }); | |
| // Legend | |
| const lg = g.append('g').attr('transform',`translate(${w-140},2)`); | |
| lg.append('circle').attr('cx',5).attr('cy',5).attr('r',5).attr('fill','#ea580c'); | |
| lg.append('text').attr('x',14).attr('y',9).attr('fill','var(--muted-color)').attr('font-size',10).text('Teacher-forced'); | |
| lg.append('circle').attr('cx',5).attr('cy',21).attr('r',4).attr('fill','var(--surface-bg)').attr('stroke','#a8a29e').attr('stroke-width',2); | |
| lg.append('text').attr('x',14).attr('y',25).attr('fill','var(--muted-color)').attr('font-size',10).text('Generated'); | |
| // Axes - x-axis with model name + param size | |
| const xAxis = g.append('g').attr('transform',`translate(0,${h})`).call(d3.axisBottom(xBand).tickSize(0)); | |
| xAxis.selectAll('text').attr('fill','var(--text-color)').attr('font-size',9) | |
| .attr('transform','rotate(-25)').attr('text-anchor','end'); | |
| g.append('g').call(d3.axisLeft(y).ticks(4).tickFormat(d3.format('.2f'))) | |
| .selectAll('text').attr('fill','var(--tick-color)'); | |
| 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('AUC'); | |
| svg.selectAll('.domain').attr('stroke','var(--axis-color)'); | |
| svg.selectAll('.tick line').attr('stroke','var(--axis-color)'); | |
| } | |
| new ResizeObserver(draw).observe(document.getElementById('gengap-svg')); | |
| })(); | |
| </script> | |