ConfidenceManifold / app /src /content /embeds /layer-heatmap.html
seonglae-holistic's picture
fix: relative data paths for subpath hosting, correct publish date to Feb 8
72696dc
Raw
History Blame Contribute Delete
5.29 kB
<!-- Layer Similarity Heatmap: Cross-layer cosine similarity for Mistral-7B -->
<div class="layer-heatmap">
<svg id="heatmap-svg"></svg>
</div>
<style>
.layer-heatmap { position: relative; width: 100%; }
#heatmap-svg { width: 100%; height: 400px; }
.heatmap-tip {
position: absolute; background: var(--surface-bg); border: 1px solid var(--border-color);
border-radius: 8px; padding: 6px 10px; font-size: 11px; pointer-events: none;
box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 10; color: var(--text-color);
}
@media (max-width: 640px) { #heatmap-svg { height: 320px; } }
</style>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
(function(){
const container = document.querySelector('.layer-heatmap');
const svg = d3.select('#heatmap-svg');
const tip = d3.select(container).append('div').attr('class','heatmap-tip').style('display','none');
fetch('data/layer_similarity.json').then(r => r.json()).then(data => draw(data));
function draw(data) {
svg.selectAll('*').remove();
const matrix = data.matrix;
const n = matrix.length;
const phases = data.phases;
const rect = document.getElementById('heatmap-svg').getBoundingClientRect();
const W = rect.width, H = rect.height;
const m = { top: 30, right: 60, bottom: 40, left: 50 };
const w = W - m.left - m.right, h = H - m.top - m.bottom;
const cellW = w / n, cellH = h / n;
svg.attr('viewBox', `0 0 ${W} ${H}`);
const g = svg.append('g').attr('transform', `translate(${m.left},${m.top})`);
// Color scale: orange ramp (cream → orange → dark)
const color = d3.scaleLinear().domain([0, 0.5, 1]).range(['#fff7ed', '#ea580c', '#431407']).interpolate(d3.interpolateRgb);
// Cells
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
const v = matrix[i][j];
g.append('rect')
.attr('x', j * cellW).attr('y', i * cellH)
.attr('width', cellW + 0.5).attr('height', cellH + 0.5)
.attr('fill', color(v)).attr('rx', 0.5)
.on('mouseenter', function(e) {
tip.style('display','block')
.html(`Layer ${i} \u2192 ${j}<br/>Similarity: <strong>${v.toFixed(3)}</strong>`)
.style('left', Math.min(e.offsetX+12,W-140)+'px').style('top',(e.offsetY-30)+'px');
})
.on('mouseleave', () => tip.style('display','none'));
}
}
// Phase brackets
const phaseColors = {'early':'#ea580c','middle':'#f59e0b','late':'#c2410c'};
const phaseLabels = {'early':'I','middle':'II','late':'III'};
Object.entries(phases).forEach(([name, [s, e]]) => {
const c = phaseColors[name];
// Bottom bracket
g.append('line').attr('x1',s*cellW).attr('x2',(e+1)*cellW).attr('y1',h+4).attr('y2',h+4)
.attr('stroke',c).attr('stroke-width',3).attr('stroke-linecap','round');
g.append('text').attr('x',((s+e+1)/2)*cellW).attr('y',h+16)
.attr('text-anchor','middle').attr('fill',c).attr('font-size',9).attr('font-weight',600)
.text(phaseLabels[name]);
// Left bracket
g.append('line').attr('x1',-4).attr('x2',-4).attr('y1',s*cellH).attr('y2',(e+1)*cellH)
.attr('stroke',c).attr('stroke-width',3).attr('stroke-linecap','round');
});
// Axes
const tickLayers = n <= 16 ? d3.range(0,n) : d3.range(0,n,Math.ceil(n/8));
g.append('g').attr('transform',`translate(0,${h})`)
.call(d3.axisBottom(d3.scaleLinear().domain([0,n-1]).range([cellW/2,w-cellW/2])).tickValues(tickLayers).tickFormat(d=>'L'+d))
.selectAll('text').attr('fill','var(--tick-color)').attr('font-size',9);
g.append('g')
.call(d3.axisLeft(d3.scaleLinear().domain([0,n-1]).range([cellH/2,h-cellH/2])).tickValues(tickLayers).tickFormat(d=>'L'+d))
.selectAll('text').attr('fill','var(--tick-color)').attr('font-size',9);
// Color legend
const lgW = 12, lgH = h * 0.6;
const lg = g.append('g').attr('transform',`translate(${w+16},${(h-lgH)/2})`);
const lgScale = d3.scaleLinear().domain([0, 1]).range([lgH, 0]);
const defs = svg.append('defs');
const grad = defs.append('linearGradient').attr('id','hm-grad').attr('x1','0').attr('y1','1').attr('x2','0').attr('y2','0');
grad.append('stop').attr('offset','0%').attr('stop-color',color(0));
grad.append('stop').attr('offset','50%').attr('stop-color',color(0.5));
grad.append('stop').attr('offset','100%').attr('stop-color',color(1));
lg.append('rect').attr('width',lgW).attr('height',lgH).attr('rx',2).attr('fill','url(#hm-grad)');
lg.append('g').attr('transform',`translate(${lgW+2},0)`)
.call(d3.axisRight(lgScale).ticks(3).tickFormat(d3.format('.1f')))
.selectAll('text').attr('fill','var(--muted-color)').attr('font-size',9);
// Title
g.append('text').attr('x',w/2).attr('y',-12).attr('text-anchor','middle')
.attr('fill','var(--muted-color)').attr('font-size',11).text(data.model + ' \u2014 Cross-layer direction similarity');
svg.selectAll('.domain').attr('stroke','var(--axis-color)');
svg.selectAll('.tick line').attr('stroke','var(--axis-color)');
}
new ResizeObserver(() => {
fetch('data/layer_similarity.json').then(r=>r.json()).then(draw);
}).observe(document.getElementById('heatmap-svg'));
})();
</script>