Alleinzellgaenger commited on
Commit
4589db1
·
1 Parent(s): 7e18603

Add hover mechanic

Browse files
archive/data.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "tokens": ["This", "is", "a", "sample", "sentence", "."],
3
+ "attention": [
4
+ [
5
+ [
6
+ [0.1, 0.2, 0.3, 0.4, 0.5, 0.6],
7
+ [0.2, 0.1, 0.4, 0.3, 0.6, 0.5],
8
+ [0.3, 0.4, 0.1, 0.2, 0.5, 0.6],
9
+ [0.4, 0.3, 0.2, 0.1, 0.6, 0.5],
10
+ [0.5, 0.6, 0.4, 0.3, 0.1, 0.2],
11
+ [0.6, 0.5, 0.3, 0.4, 0.2, 0.1]
12
+ ]
13
+ ]
14
+ ]
15
+ }
frontend/index.html CHANGED
@@ -82,12 +82,19 @@
82
  autofocus
83
  ></textarea>
84
  <button type="submit">Process</button>
85
- <button id="downloadBtn">Download JSON</button>
86
  </form>
87
 
88
  <!-- Output Area -->
89
  <div id="output"></div>
 
 
 
 
 
 
 
90
  <div id="tokenContainer"></div>
 
91
  </section>
92
  </main>
93
 
 
82
  autofocus
83
  ></textarea>
84
  <button type="submit">Process</button>
 
85
  </form>
86
 
87
  <!-- Output Area -->
88
  <div id="output"></div>
89
+ </section>
90
+ <section class="hover-visualization">
91
+ <h2>Hover Visualization</h2>
92
+ <p>
93
+ Hover over the tokens to see the attention weights for the selected head. Click on a token
94
+ to see the attention weights for that token across all heads.
95
+ </p>
96
  <div id="tokenContainer"></div>
97
+
98
  </section>
99
  </main>
100
 
frontend/script.js CHANGED
@@ -16,6 +16,7 @@ document.getElementById('textForm').addEventListener('submit', async (e) => {
16
  }
17
  const data = await response.json();
18
  displayOutput(data);
 
19
  } catch (error) {
20
  console.error('Error:', error);
21
  document.getElementById('output').innerText = 'Error processing text.';
@@ -33,58 +34,14 @@ function displayOutput(data) {
33
  <pre>${JSON.stringify(data.attention, null, 2)}</pre>
34
  <div id="tokenContainer"></div>
35
  `;
36
-
37
- // If we have attention data, render tokens visually
38
- if (data.attention && data.attention.length > 0) {
39
- // By default, use the first layer’s attention matrix
40
- // (Or you can read the selected layer/head from the form)
41
- renderTokens(data.tokens, data.attention[0]);
42
- }
43
- }
44
-
45
- // Renders tokens in tokenContainer; sets up mouseover for highlighting
46
- function renderTokens(tokens, attentionMatrix) {
47
- const container = document.getElementById('tokenContainer');
48
- container.innerHTML = ""; // Clear old tokens
49
-
50
- tokens.forEach((token, index) => {
51
- const span = document.createElement("span");
52
- span.textContent = token + " ";
53
- span.dataset.tokenIndex = index;
54
-
55
- span.addEventListener('mouseover', () => {
56
- highlightAttention(index, attentionMatrix);
57
- });
58
-
59
- span.addEventListener('mouseout', () => {
60
- resetTokenSizes();
61
- });
62
-
63
- container.appendChild(span);
64
- });
65
  }
66
 
67
- // Highlights tokens based on attention weights for hovered token index
68
- function highlightAttention(hoveredIndex, attentionMatrix) {
69
- const container = document.getElementById("tokenContainer");
70
- const weights = attentionMatrix[hoveredIndex];
71
-
72
- // Normalize weights if desired
73
- const maxWeight = Math.max(...weights);
74
 
75
- // Some base styling
76
- const baseFontSize = 16; // px
77
- const maxIncrease = 10; // px to add at max weight
78
-
79
- // Iterate over spans (tokens) and scale font size
80
- Array.from(container.children).forEach((span, idx) => {
81
- const weight = weights[idx];
82
- const newFontSize = baseFontSize + (weight / maxWeight) * maxIncrease;
83
- span.style.fontSize = newFontSize + "px";
84
- });
85
  }
86
-
87
- // Resets token font sizes to the base
88
  function resetTokenSizes() {
89
  const container = document.getElementById("tokenContainer");
90
  Array.from(container.children).forEach((span) => {
@@ -92,35 +49,31 @@ function resetTokenSizes() {
92
  });
93
  }
94
 
95
- function downloadJSON(data, filename = 'data.json') {
96
- // Convert the data object to a JSON string
97
- const jsonStr = JSON.stringify(data, null, 2); // pretty print with 2-space indentation
98
-
99
- // Create a Blob from the JSON string
100
- const blob = new Blob([jsonStr], { type: 'application/json' });
101
-
102
- // Create an object URL from the Blob
103
- const url = URL.createObjectURL(blob);
104
-
105
- // Create a temporary anchor element
106
- const a = document.createElement('a');
107
- a.href = url;
108
- a.download = filename;
109
 
110
- // Append the anchor to the document body
111
- document.body.appendChild(a);
112
 
113
- // Programmatically click the anchor to trigger the download
114
- a.click();
 
 
115
 
116
- // Clean up by removing the anchor and revoking the object URL
117
- document.body.removeChild(a);
118
- URL.revokeObjectURL(url);
 
 
 
 
 
 
 
119
  }
120
-
121
- document.getElementById('downloadBtn').addEventListener('click', () => {
122
- const output = document.getElementById('output').innerText;
123
- if (output) {
124
- downloadJSON(JSON.parse(output), 'output.json');
125
- }
126
- });
 
16
  }
17
  const data = await response.json();
18
  displayOutput(data);
19
+ displayHoverTokens(data, 0, 0);
20
  } catch (error) {
21
  console.error('Error:', error);
22
  document.getElementById('output').innerText = 'Error processing text.';
 
34
  <pre>${JSON.stringify(data.attention, null, 2)}</pre>
35
  <div id="tokenContainer"></div>
36
  `;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  }
38
 
39
+ function displayHoverTokens(data, layer_idx, head_idx) {
40
+ const tokens = data.tokens;
41
+ const attentionMatrix = data.attention;
 
 
 
 
42
 
43
+ renderTokens(tokens, attentionMatrix, layer_idx, head_idx);
 
 
 
 
 
 
 
 
 
44
  }
 
 
45
  function resetTokenSizes() {
46
  const container = document.getElementById("tokenContainer");
47
  Array.from(container.children).forEach((span) => {
 
49
  });
50
  }
51
 
52
+ function highlightAttention(index, attentionData, layer_idx, head_idx){
53
+ const container = document.getElementById('tokenContainer');
54
+ // Retrieve the row corresponding to the hovered token.
55
+ const row = attentionData[layer_idx][head_idx][index];
56
+ if (!row) {
57
+ console.warn(`No attention data for token index ${index}`);
58
+ return;
59
+ }
 
 
 
 
 
 
60
 
61
+ // Only consider preceding tokens.
62
+ const weights = row.slice(0, index);
63
 
64
+ // Normalize using the maximum weight from the current row's causal part.
65
+ const maxWeight = Math.max(...attentionData[layer_idx][head_idx]) || 1;
66
+ const baseFontSize = 16;
67
+ const maxIncrease = 10;
68
 
69
+ Array.from(container.children).forEach((span, idx) => {
70
+ if (idx < index){
71
+ const weight = weights[idx];
72
+ // Calculate the new font size.
73
+ const newFontSize = baseFontSize + (weight / maxWeight) * maxIncrease;
74
+ span.style.fontSize = newFontSize + "px";
75
+ } else {
76
+ span.style.fontSize = baseFontSize + "px";
77
+ }
78
+ });
79
  }
 
 
 
 
 
 
 
frontend/styles.css CHANGED
@@ -173,6 +173,11 @@ button:hover {
173
  /* Token highlighting styles */
174
  #tokenContainer span {
175
  cursor: default;
176
- transition: font-size 0.2s ease, color 0.2s ease;
177
  color: #555;
 
 
 
 
178
  }
 
 
173
  /* Token highlighting styles */
174
  #tokenContainer span {
175
  cursor: default;
176
+ transition: font-size 0.9s ease;
177
  color: #555;
178
+ display: inline-block;
179
+ transition: font-size 0.9s ease;
180
+ margin-right: 4px;
181
+ padding: 2px 4px;
182
  }
183
+