import React from 'react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; interface AttentionStatsProps { attentionData: { tokens: string[]; attention_matrix: number[][]; num_layers: number; num_heads: number; }; } export default function AttentionStats({ attentionData }: AttentionStatsProps) { // Calculate statistics from attention matrix const calculateStats = () => { const { attention_matrix, tokens } = attentionData; if (!attention_matrix || attention_matrix.length === 0) { return { avgAttention: [], maxAttention: [], entropy: [] }; } const avgAttention = attention_matrix.map(row => { const sum = row.reduce((a, b) => a + b, 0); return sum / row.length; }); const maxAttention = attention_matrix.map(row => Math.max(...row)); // Calculate entropy for each position const entropy = attention_matrix.map(row => { const sum = row.reduce((a, b) => a + b, 0); if (sum === 0) return 0; const probs = row.map(v => v / sum); return -probs.reduce((e, p) => { if (p === 0) return e; return e + p * Math.log2(p); }, 0); }); return { avgAttention, maxAttention, entropy }; }; const stats = calculateStats(); // Prepare data for chart. // avgAttention has one entry per matrix row (one per OUTPUT token), while // attentionData.tokens is the full input+output sequence. Offset into the // output-token slice so each stat lines up with the token it describes, // instead of pairing output stats with the first (input) tokens. const rowTokenOffset = Math.max(0, attentionData.tokens.length - stats.avgAttention.length); const chartData = attentionData.tokens.slice(rowTokenOffset, rowTokenOffset + stats.avgAttention.length).map((token, idx) => ({ position: idx, token: token.length > 10 ? token.substring(0, 10) + '...' : token, avgAttention: stats.avgAttention[idx]?.toFixed(4) || 0, maxAttention: stats.maxAttention[idx]?.toFixed(4) || 0, entropy: stats.entropy[idx]?.toFixed(4) || 0, })); // Calculate global statistics const globalStats = { avgAttention: stats.avgAttention.reduce((a, b) => a + b, 0) / stats.avgAttention.length || 0, maxAttention: stats.maxAttention.length ? Math.max(...stats.maxAttention) : 0, avgEntropy: stats.entropy.reduce((a, b) => a + b, 0) / stats.entropy.length || 0, }; return (
Token: {data.token}
Avg: {data.avgAttention}
Max: {data.maxAttention}
Entropy: {data.entropy}