Download app/src/content/embeds/feature-frequency.html from seonglae/ControlRL: direct link, hf CLI and curl.
- Browser
- Download file 15.3 kB
-
https://huggingface.co/spaces/seonglae/ControlRL/resolve/main/app/src/content/embeds/feature-frequency.html
- Command line
-
hf download hf://spaces/seonglae/ControlRL/app/src/content/embeds/feature-frequency.html
-
curl -L -o feature-frequency.html https://huggingface.co/spaces/seonglae/ControlRL/resolve/main/app/src/content/embeds/feature-frequency.html
15.3 kB
| <!-- | |
| Feature Selection Frequency (Zipf-like) | |
| Horizontal bar chart showing the top-10 most frequently selected SAE features | |
| by the CRL policy on GSM8K layer 20. Data from feature_analysis_20.json "success" category. | |
| Feature IDs link to Neuronpedia. | |
| --> | |
| <div class="feature-frequency"></div> | |
| <style> | |
| .feature-frequency { | |
| position: relative; | |
| font-family: system-ui, -apple-system, sans-serif; | |
| width: 100%; | |
| } | |
| .feature-frequency .chart-card { | |
| background: var(--page-bg); | |
| border: 1px solid var(--border-color); | |
| border-radius: 16px; | |
| padding: 24px; | |
| } | |
| .feature-frequency .chart-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: baseline; | |
| margin-bottom: 20px; | |
| flex-wrap: wrap; | |
| gap: 8px; | |
| } | |
| .feature-frequency .chart-title { | |
| font-size: 15px; | |
| font-weight: 700; | |
| color: var(--text-color); | |
| } | |
| .feature-frequency .chart-subtitle { | |
| font-size: 12px; | |
| color: var(--muted-color); | |
| font-weight: 500; | |
| } | |
| .feature-frequency svg { | |
| display: block; | |
| width: 100%; | |
| height: auto; | |
| overflow: visible; | |
| } | |
| .feature-frequency .axis path { stroke: none; } | |
| .feature-frequency .axis line { stroke: var(--grid-color); } | |
| .feature-frequency .axis text { fill: var(--tick-color); font-size: 11px; } | |
| .feature-frequency .grid line { stroke: var(--grid-color); stroke-dasharray: 2, 3; } | |
| .feature-frequency .grid path { stroke: none; } | |
| .feature-frequency .bar-rect { | |
| cursor: pointer; | |
| transition: opacity 0.15s ease; | |
| } | |
| .feature-frequency .bar-rect:hover { opacity: 0.82; } | |
| .feature-frequency .bar-count { | |
| font-size: 11px; | |
| font-weight: 600; | |
| fill: var(--text-color); | |
| pointer-events: none; | |
| } | |
| .feature-frequency .y-label { | |
| font-size: 11px; | |
| fill: var(--text-color); | |
| cursor: pointer; | |
| text-decoration: none; | |
| } | |
| .feature-frequency .y-label:hover { | |
| fill: #4F46E5; | |
| text-decoration: underline; | |
| } | |
| .feature-frequency .y-desc { | |
| font-size: 10px; | |
| fill: var(--muted-color); | |
| pointer-events: none; | |
| } | |
| .feature-frequency .axis-label { | |
| fill: var(--text-color); | |
| font-size: 12px; | |
| font-weight: 600; | |
| } | |
| .feature-frequency .d3-tooltip { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| transform: translate(-9999px, -9999px); | |
| pointer-events: none; | |
| padding: 10px 14px; | |
| border-radius: 12px; | |
| font-size: 12px; | |
| line-height: 1.5; | |
| border: 1px solid var(--border-color); | |
| background: var(--surface-bg); | |
| color: var(--text-color); | |
| box-shadow: 0 8px 32px rgba(0,0,0,.22), 0 2px 8px rgba(0,0,0,.10); | |
| backdrop-filter: saturate(1.12) blur(8px); | |
| -webkit-backdrop-filter: saturate(1.12) blur(8px); | |
| opacity: 0; | |
| transition: opacity 0.15s ease; | |
| z-index: 20; | |
| max-width: 260px; | |
| } | |
| .feature-frequency .entropy-badge { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 5px; | |
| padding: 4px 12px; | |
| border-radius: 999px; | |
| border: 1px solid var(--border-color); | |
| font-size: 12px; | |
| font-weight: 600; | |
| color: var(--text-color); | |
| background: var(--surface-bg, var(--page-bg)); | |
| } | |
| .feature-frequency .entropy-badge .dot { | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| background: #4F46E5; | |
| } | |
| @media (max-width: 600px) { | |
| .feature-frequency .chart-card { | |
| padding: 14px; | |
| } | |
| .feature-frequency .y-label { | |
| font-size: 10px; | |
| } | |
| .feature-frequency .y-desc { | |
| font-size: 9px; | |
| } | |
| } | |
| </style> | |
| <script> | |
| (function() { | |
| var ensureD3 = function(cb) { | |
| if (window.d3 && typeof window.d3.select === 'function') return cb(); | |
| var s = document.getElementById('d3-cdn-script'); | |
| if (!s) { | |
| s = document.createElement('script'); | |
| s.id = 'd3-cdn-script'; | |
| s.src = 'https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js'; | |
| document.head.appendChild(s); | |
| } | |
| var onReady = function() { | |
| if (window.d3 && typeof window.d3.select === 'function') cb(); | |
| }; | |
| s.addEventListener('load', onReady, { once: true }); | |
| if (window.d3) onReady(); | |
| }; | |
| var bootstrap = function() { | |
| var candidates = Array.from(document.querySelectorAll('.feature-frequency')) | |
| .filter(function(el) { return !el.dataset.mounted; }); | |
| var container = candidates[candidates.length - 1]; | |
| if (!container) return; | |
| container.dataset.mounted = 'true'; | |
| container.style.position = 'relative'; | |
| var d3sel = window.d3; | |
| /* ββ Data (real from feature_analysis_20.json "success" category) ββ */ | |
| var data = [ | |
| {rank: 1, feature: 2198, count: 699, label: "summaries, reflections, or clarifications"}, | |
| {rank: 2, feature: 6787, count: 644, label: "cellular biology and histology terms"}, | |
| {rank: 3, feature: 5890, count: 613, label: "technical terms and specifications"}, | |
| {rank: 4, feature: 6512, count: 518, label: "instructions and steps for processes"}, | |
| {rank: 5, feature: 14802, count: 473, label: "code structures, functions, methods"}, | |
| {rank: 6, feature: 16326, count: 452, label: "comparative measurements or evaluations"}, | |
| {rank: 7, feature: 8587, count: 439, label: "procedural language, claims and benefits"}, | |
| {rank: 8, feature: 9488, count: 425, label: "API functions and documentation"}, | |
| {rank: 9, feature: 5423, count: 409, label: "parentheses and numerical data"}, | |
| {rank: 10, feature: 9276, count: 399, label: "punctuation and conversational cues"} | |
| ]; | |
| var totalCount = data.reduce(function(s, d) { return s + d.count; }, 0); | |
| /* ββ Tooltip ββ */ | |
| var tip = document.createElement('div'); | |
| tip.className = 'd3-tooltip'; | |
| var tipInner = document.createElement('div'); | |
| tip.appendChild(tipInner); | |
| container.appendChild(tip); | |
| function showTip(event, html) { | |
| tipInner.innerHTML = html; | |
| var coords = d3sel.pointer(event, container); | |
| var tipRect = tip.getBoundingClientRect(); | |
| var contW = container.getBoundingClientRect().width; | |
| var contH = container.getBoundingClientRect().height; | |
| var tx = coords[0] + 14; | |
| var ty = coords[1] - 10; | |
| if (tx + (tipRect.width || 200) > contW - 10) tx = coords[0] - (tipRect.width || 200) - 14; | |
| if (ty + (tipRect.height || 80) > contH - 10) ty = coords[1] - (tipRect.height || 80) - 10; | |
| if (ty < 0) ty = coords[1] + 20; | |
| tip.style.opacity = '1'; | |
| tip.style.transform = 'translate(' + Math.round(tx) + 'px, ' + Math.round(ty) + 'px)'; | |
| } | |
| function hideTip() { | |
| tip.style.opacity = '0'; | |
| tip.style.transform = 'translate(-9999px, -9999px)'; | |
| } | |
| /* ββ Card ββ */ | |
| var card = document.createElement('div'); | |
| card.className = 'chart-card'; | |
| container.appendChild(card); | |
| /* Header */ | |
| var header = document.createElement('div'); | |
| header.className = 'chart-header'; | |
| header.innerHTML = | |
| '<div>' + | |
| '<div class="chart-title">Feature Selection Frequency</div>' + | |
| '<div class="chart-subtitle">GSM8K \u00b7 Layer 20 \u00b7 Top-10 most selected SAE features</div>' + | |
| '</div>' + | |
| '<div class="entropy-badge"><span class="dot"></span>Diversity H = 6.695</div>'; | |
| card.appendChild(header); | |
| /* SVG container */ | |
| var svgWrap = document.createElement('div'); | |
| svgWrap.style.position = 'relative'; | |
| card.appendChild(svgWrap); | |
| var svg = d3sel.select(svgWrap).append('svg'); | |
| var defs = svg.append('defs'); | |
| /* Gradient definition */ | |
| var gradId = 'ff-grad-' + Math.random().toString(36).slice(2); | |
| var grad = defs.append('linearGradient') | |
| .attr('id', gradId) | |
| .attr('x1', '0%').attr('y1', '0%') | |
| .attr('x2', '0%').attr('y2', '100%'); | |
| grad.append('stop').attr('offset', '0%').attr('stop-color', '#4F46E5'); | |
| grad.append('stop').attr('offset', '100%').attr('stop-color', '#a3a3a3'); | |
| var gRoot = svg.append('g'); | |
| var gGrid = gRoot.append('g').attr('class', 'grid'); | |
| var gBars = gRoot.append('g').attr('class', 'bars'); | |
| var gAxes = gRoot.append('g').attr('class', 'axes'); | |
| var hasAnimated = false; | |
| /* ββ Color interpolation per rank ββ */ | |
| function barColor(rank) { | |
| var t = (rank - 1) / (data.length - 1); | |
| return d3sel.interpolateRgb('#4F46E5', '#a3a3a3')(t); | |
| } | |
| /* ββ Neuronpedia URL ββ */ | |
| function neuronpediaUrl(featureId) { | |
| return 'https://neuronpedia.org/gemma-2-2b/20-gemmascope-res-16k/' + featureId; | |
| } | |
| /* ββ Render ββ */ | |
| function render(animate) { | |
| var containerWidth = container.clientWidth || 700; | |
| var totalWidth = Math.max(340, Math.round(containerWidth)); | |
| /* Responsive margin: narrower left margin on mobile */ | |
| var isNarrow = totalWidth < 500; | |
| var margin = { | |
| top: 8, | |
| right: 48, | |
| bottom: 40, | |
| left: isNarrow ? 130 : 200 | |
| }; | |
| var innerWidth = totalWidth - margin.left - margin.right; | |
| var barHeight = isNarrow ? 22 : 26; | |
| var barPadding = isNarrow ? 4 : 6; | |
| var totalBarArea = data.length * (barHeight + barPadding) - barPadding; | |
| var totalHeight = margin.top + totalBarArea + margin.bottom; | |
| svg.attr('viewBox', '0 0 ' + totalWidth + ' ' + totalHeight) | |
| .attr('preserveAspectRatio', 'xMidYMid meet'); | |
| gRoot.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); | |
| /* Scales */ | |
| var xMax = d3sel.max(data, function(d) { return d.count; }); | |
| var xScale = d3sel.scaleLinear() | |
| .domain([0, xMax * 1.08]) | |
| .range([0, innerWidth]); | |
| var yScale = d3sel.scaleBand() | |
| .domain(data.map(function(d) { return d.rank; })) | |
| .range([0, totalBarArea]) | |
| .paddingInner(barPadding / (barHeight + barPadding)) | |
| .paddingOuter(0); | |
| /* Grid lines */ | |
| gGrid.selectAll('*').remove(); | |
| gGrid.append('g') | |
| .call(d3sel.axisBottom(xScale) | |
| .ticks(isNarrow ? 4 : 6) | |
| .tickSize(totalBarArea) | |
| .tickFormat('') | |
| ) | |
| .call(function(g) { g.select('.domain').remove(); }); | |
| /* ββ Bars ββ */ | |
| var barGroups = gBars.selectAll('g.bar-group') | |
| .data(data, function(d) { return d.rank; }); | |
| var barEnter = barGroups.enter().append('g').attr('class', 'bar-group'); | |
| barEnter.append('rect').attr('class', 'bar-rect'); | |
| barEnter.append('text').attr('class', 'bar-count'); | |
| var barMerge = barEnter.merge(barGroups); | |
| barMerge.attr('transform', function(d) { | |
| return 'translate(0,' + yScale(d.rank) + ')'; | |
| }); | |
| var rects = barMerge.select('.bar-rect') | |
| .attr('y', 0) | |
| .attr('height', yScale.bandwidth()) | |
| .attr('rx', 4) | |
| .attr('ry', 4) | |
| .attr('fill', function(d) { return barColor(d.rank); }); | |
| if (animate) { | |
| rects | |
| .attr('x', 0) | |
| .attr('width', 0) | |
| .transition() | |
| .duration(800) | |
| .delay(function(d, i) { return i * 40; }) | |
| .ease(d3sel.easeCubicOut) | |
| .attr('width', function(d) { return xScale(d.count); }); | |
| } else { | |
| rects | |
| .attr('x', 0) | |
| .attr('width', function(d) { return xScale(d.count); }); | |
| } | |
| /* Hover */ | |
| rects | |
| .on('mousemove', function(event, d) { | |
| var pct = ((d.count / totalCount) * 100).toFixed(1); | |
| var html = | |
| '<div style="font-weight:700;margin-bottom:2px;">F' + d.feature + '</div>' + | |
| '<div style="font-size:11px;color:var(--muted-color);margin-bottom:6px;">' + d.label + '</div>' + | |
| '<div style="display:flex;gap:16px;">' + | |
| '<div><span style="font-size:10px;color:var(--muted-color);">Count</span><br><strong style="font-size:16px;">' + d.count + '</strong></div>' + | |
| '<div><span style="font-size:10px;color:var(--muted-color);">Share</span><br><strong style="font-size:16px;">' + pct + '%</strong></div>' + | |
| '<div><span style="font-size:10px;color:var(--muted-color);">Rank</span><br><strong style="font-size:16px;">#' + d.rank + '</strong></div>' + | |
| '</div>'; | |
| showTip(event, html); | |
| }) | |
| .on('mouseleave', function() { hideTip(); }) | |
| .on('click', function(event, d) { | |
| window.open(neuronpediaUrl(d.feature), '_blank', 'noopener'); | |
| }); | |
| /* Count labels at end of bar */ | |
| var countLabels = barMerge.select('.bar-count') | |
| .attr('y', yScale.bandwidth() / 2) | |
| .attr('dy', '0.35em') | |
| .text(function(d) { return d.count; }); | |
| if (animate) { | |
| countLabels | |
| .attr('x', 4) | |
| .attr('opacity', 0) | |
| .transition() | |
| .duration(400) | |
| .delay(function(d, i) { return i * 40 + 600; }) | |
| .attr('x', function(d) { return xScale(d.count) + 6; }) | |
| .attr('opacity', 1); | |
| } else { | |
| countLabels | |
| .attr('x', function(d) { return xScale(d.count) + 6; }) | |
| .attr('opacity', 1); | |
| } | |
| barGroups.exit().remove(); | |
| /* ββ Y-axis labels (feature IDs + descriptions) ββ */ | |
| gAxes.selectAll('*').remove(); | |
| data.forEach(function(d) { | |
| var yPos = yScale(d.rank) + yScale.bandwidth() / 2; | |
| /* Feature ID as clickable link */ | |
| var link = gAxes.append('a') | |
| .attr('href', neuronpediaUrl(d.feature)) | |
| .attr('target', '_blank') | |
| .attr('rel', 'noopener'); | |
| link.append('text') | |
| .attr('class', 'y-label') | |
| .attr('x', -8) | |
| .attr('y', yPos) | |
| .attr('dy', isNarrow ? '0.35em' : '-0.15em') | |
| .attr('text-anchor', 'end') | |
| .attr('font-weight', 600) | |
| .text('F' + d.feature); | |
| /* Description below feature ID (only on wider viewports) */ | |
| if (!isNarrow) { | |
| gAxes.append('text') | |
| .attr('class', 'y-desc') | |
| .attr('x', -8) | |
| .attr('y', yPos) | |
| .attr('dy', '1.0em') | |
| .attr('text-anchor', 'end') | |
| .text(d.label); | |
| } | |
| }); | |
| /* X-axis */ | |
| gAxes.append('g') | |
| .attr('class', 'axis x-axis') | |
| .attr('transform', 'translate(0,' + totalBarArea + ')') | |
| .call(d3sel.axisBottom(xScale).ticks(isNarrow ? 4 : 6)); | |
| gAxes.append('text') | |
| .attr('class', 'axis-label') | |
| .attr('text-anchor', 'middle') | |
| .attr('x', innerWidth / 2) | |
| .attr('y', totalBarArea + 34) | |
| .text('Selection Count'); | |
| } | |
| /* ββ Initial render (no animation) then observe for scroll-in ββ */ | |
| render(false); | |
| var obs = new IntersectionObserver(function(entries) { | |
| entries.forEach(function(entry) { | |
| if (entry.isIntersecting && !hasAnimated) { | |
| hasAnimated = true; | |
| render(true); | |
| obs.disconnect(); | |
| } | |
| }); | |
| }, { threshold: 0.15 }); | |
| obs.observe(container); | |
| /* Resize */ | |
| if (window.ResizeObserver) { | |
| new ResizeObserver(function() { | |
| if (hasAnimated) render(false); | |
| }).observe(container); | |
| } | |
| }; | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', function() { ensureD3(bootstrap); }, { once: true }); | |
| } else { | |
| ensureD3(bootstrap); | |
| } | |
| })(); | |
| </script> | |