AndreP-git commited on
Commit
8e35dd6
·
1 Parent(s): 2beb185

Update next

Browse files
Files changed (2) hide show
  1. app.js +87 -31
  2. index.html +247 -54
app.js CHANGED
@@ -167,21 +167,37 @@ const PRESET_MODELS =
167
  }
168
  };
169
 
 
 
 
 
 
 
 
170
  window.onload = function()
171
  {
172
 
173
  let selector = document.getElementById("preset");
174
 
175
 
176
- for(let name in PRESET_MODELS)
177
  {
178
- let option = document.createElement("option");
 
 
 
 
 
 
179
 
180
- option.text=name;
181
 
182
- option.value=name;
183
 
184
- selector.add(option);
 
 
 
185
  }
186
 
187
  selector.value = "Falcon-RW 7B";
@@ -190,7 +206,46 @@ window.onload = function()
190
 
191
  renderThetas();
192
 
193
- draw();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
 
195
  }
196
 
@@ -214,16 +269,7 @@ function loadPreset()
214
  document.getElementById("nkv").value=model.nkv;
215
 
216
 
217
- document.getElementById("status").innerHTML =
218
- `
219
- ✅ Loaded validated model:
220
- <b>${name}</b><br>
221
- Attention: ${model.attention}<br>
222
- d=${model.d},
223
- L=${model.L},
224
- nq=${model.nq},
225
- nkv=${model.nkv}
226
- `;
227
 
228
 
229
  draw();
@@ -304,6 +350,8 @@ function draw()
304
  let nin = Number(document.getElementById("nin").value);
305
 
306
 
 
 
307
  drawComponents(d,L,nq,nkv,nin);
308
 
309
  drawContour(d,L,nq,nkv,nin);
@@ -901,9 +949,17 @@ async function loadModel()
901
  async function loadRepo(repo)
902
  {
903
 
 
 
 
 
 
 
904
  let url =
905
  `https://huggingface.co/${repo}/resolve/main/config.json`;
906
 
 
 
907
 
908
  try
909
  {
@@ -950,17 +1006,7 @@ async function loadRepo(repo)
950
 
951
 
952
 
953
- document.getElementById("status").innerHTML =
954
- `
955
- ✅ Loaded <b>${repo}</b><br>
956
- Estimated dense size:
957
- ${paramsB.toFixed(1)}B parameters
958
- <br>
959
- d=${d},
960
- L=${L},
961
- nq=${nq},
962
- nkv=${nkv}
963
- `;
964
 
965
 
966
  draw();
@@ -970,20 +1016,30 @@ async function loadRepo(repo)
970
  catch(e)
971
  {
972
 
973
- document.getElementById("status").innerHTML =
974
- "⚠️ "+e.message;
975
 
976
  }
977
 
978
  }
979
 
980
- // redraw when sliders change
981
 
982
  document
983
  .querySelectorAll("input[type=number]")
984
  .forEach(
985
- x=>x.addEventListener("change",draw)
 
 
 
 
986
  );
987
 
 
 
 
 
 
 
 
988
 
989
  draw();
 
167
  }
168
  };
169
 
170
+ const PRESET_GROUPS =
171
+ {
172
+ "XS · ~1B": ["Llama 3.2 1B", "OPT 1.3B", "Qwen2 1.5B"],
173
+ "S · ~2B–3B": ["Gemma 2 2B", "OPT 2.7B", "Llama 3.2 3B", "Granite 3B"],
174
+ "M · ~6B–9B": ["OPT 6.7B", "Qwen2 7B", "Falcon-RW 7B", "Granite 8B", "Llama 3.1 8B", "Gemma 2 9B"]
175
+ };
176
+
177
  window.onload = function()
178
  {
179
 
180
  let selector = document.getElementById("preset");
181
 
182
 
183
+ for(let group in PRESET_GROUPS)
184
  {
185
+ let optgroup = document.createElement("optgroup");
186
+
187
+ optgroup.label=group;
188
+
189
+ for(let name of PRESET_GROUPS[group])
190
+ {
191
+ let option = document.createElement("option");
192
 
193
+ option.text=name;
194
 
195
+ option.value=name;
196
 
197
+ optgroup.appendChild(option);
198
+ }
199
+
200
+ selector.appendChild(optgroup);
201
  }
202
 
203
  selector.value = "Falcon-RW 7B";
 
206
 
207
  renderThetas();
208
 
209
+ loadPreset();
210
+
211
+ }
212
+
213
+ // kind: "ok" | "loading" | "error" | "custom"
214
+ function setStatus(kind,html)
215
+ {
216
+
217
+ let status = document.getElementById("status");
218
+
219
+ status.className = "status "+kind;
220
+
221
+ status.innerHTML = html;
222
+
223
+ }
224
+
225
+ function attentionLabel(nq,nkv)
226
+ {
227
+
228
+ if(nq===nkv) return "MHA";
229
+
230
+ if(nkv===1) return "MQA";
231
+
232
+ return `GQA (${+(nq/nkv).toFixed(1)}x)`;
233
+
234
+ }
235
+
236
+ // Derived quantities shown under the architecture fields
237
+ function updateSummary(d,L,nq,nkv)
238
+ {
239
+
240
+ // Same dense-Transformer approximation as validateModel
241
+ let paramsB = 12*L*d*d/1e9;
242
+
243
+ document.getElementById("archSummary").innerHTML =
244
+ `
245
+ <span class="chip">${attentionLabel(nq,nkv)}</span>
246
+ <span class="chip">R = ${+(nkv/nq).toFixed(3)}</span>
247
+ <span class="chip">≈ ${paramsB.toFixed(1)}B params (12Ld²)</span>
248
+ `;
249
 
250
  }
251
 
 
269
  document.getElementById("nkv").value=model.nkv;
270
 
271
 
272
+ setStatus("ok", `<b>${name}</b> · validated`);
 
 
 
 
 
 
 
 
 
273
 
274
 
275
  draw();
 
350
  let nin = Number(document.getElementById("nin").value);
351
 
352
 
353
+ updateSummary(d,L,nq,nkv);
354
+
355
  drawComponents(d,L,nq,nkv,nin);
356
 
357
  drawContour(d,L,nq,nkv,nin);
 
949
  async function loadRepo(repo)
950
  {
951
 
952
+ if(!repo)
953
+ {
954
+ setStatus("error", "Enter a repository id, e.g. tiiuae/falcon-rw-7b");
955
+ return;
956
+ }
957
+
958
  let url =
959
  `https://huggingface.co/${repo}/resolve/main/config.json`;
960
 
961
+ setStatus("loading", `Loading <b>${repo}</b>…`);
962
+
963
 
964
  try
965
  {
 
1006
 
1007
 
1008
 
1009
+ setStatus("ok", `<b>${repo}</b> · Hugging Face`);
 
 
 
 
 
 
 
 
 
 
1010
 
1011
 
1012
  draw();
 
1016
  catch(e)
1017
  {
1018
 
1019
+ setStatus("error", e.message);
 
1020
 
1021
  }
1022
 
1023
  }
1024
 
1025
+ // redraw when the architecture fields change
1026
 
1027
  document
1028
  .querySelectorAll("input[type=number]")
1029
  .forEach(
1030
+ x=>x.addEventListener("change",()=>
1031
+ {
1032
+ setStatus("custom", "Custom architecture");
1033
+ draw();
1034
+ })
1035
  );
1036
 
1037
+ document
1038
+ .getElementById("repo")
1039
+ .addEventListener("keydown",e=>
1040
+ {
1041
+ if(e.key==="Enter") loadModel();
1042
+ });
1043
+
1044
 
1045
  draw();
index.html CHANGED
@@ -3,6 +3,7 @@
3
  <html>
4
  <head>
5
  <meta charset="UTF-8">
 
6
  <title>SweetSpot v2 — Energy-per-Token demo</title>
7
 
8
  <script src="https://cdn.plot.ly/plotly-2.35.2.min.js"></script>
@@ -18,43 +19,232 @@
18
  padding: 20px;
19
  }
20
 
21
- .row {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  display: flex;
23
- gap: 20px;
 
 
24
  flex-wrap: wrap;
25
  }
26
 
27
- label {
28
- display: block;
29
- margin-top: 10px;
30
  }
31
 
32
- input[type="number"] {
33
- width: 120px;
 
 
 
 
34
  }
35
 
36
- button {
37
- margin-top: 10px;
38
- padding: 8px 15px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  }
40
 
41
  /* Input-token slider */
42
  #ninWrapper {
43
  width: 100%;
44
- margin-top: 25px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }
46
 
47
  #ninSlider {
48
  width: 100%;
49
  display: block;
50
- margin-top: 8px;
 
51
  }
52
 
53
  #ninLabels {
54
  display: flex;
55
  justify-content: space-between;
56
  font-size: 12px;
57
- color: #555;
58
  margin-top: 3px;
59
  }
60
 
@@ -124,65 +314,66 @@ Fitted parameters:
124
  <tbody id="thetaBody"></tbody>
125
  </table>
126
 
127
- <h4>Validated models:</h4>
128
 
129
- <select id="preset"></select>
130
-
131
- <button onclick="loadPreset()">
132
- Load selected model
133
- </button>
134
 
135
- <h4>
136
- Or load another public Hugging Face model (dense, &lt; 20B):
137
- </h4>
138
 
139
- <input
140
- id="repo"
141
- style="width:400px"
142
- placeholder="tiiuae/falcon-rw-7b"
143
 
144
- >
145
 
146
- <button onclick="loadModel()">
147
- Load model
148
- </button>
 
 
 
 
149
 
150
- <p id="status"></p>
151
 
152
- <h4>Architecture parameters</h4>
153
 
154
- <div class="row">
155
 
156
- <div>
157
- <label>Hidden size (d):</label>
158
- <input id="d" type="number" value="4096">
159
- </div>
160
 
161
- <div>
162
- <label>Layers (L):</label>
163
- <input id="L" type="number" value="36">
164
- </div>
165
 
166
- <div>
167
- <label>Query heads (<i>n</i><sub>q</sub>):</label>
168
- <input id="nq" type="number" value="64">
169
- </div>
170
 
171
- <div>
172
- <label>KV heads (<i>n</i><sub>kv</sub>):</label>
173
- <input id="nkv" type="number" value="64">
174
- </div>
175
 
176
  </div>
177
 
178
- <!-- Slider is OUTSIDE the architecture-parameter row -->
179
 
180
  <div id="ninWrapper">
181
 
182
- <label>
183
- Input Tokens (τ<sub>in</sub>):
184
- <span id="ninValue">64</span>
185
- </label>
186
 
187
  <input
188
  id="ninSlider"
@@ -204,6 +395,8 @@ placeholder="tiiuae/falcon-rw-7b"
204
 
205
  </div>
206
 
 
 
207
  <div id="plots">
208
 
209
  <div id="plotPanel">
 
3
  <html>
4
  <head>
5
  <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
  <title>SweetSpot v2 — Energy-per-Token demo</title>
8
 
9
  <script src="https://cdn.plot.ly/plotly-2.35.2.min.js"></script>
 
19
  padding: 20px;
20
  }
21
 
22
+ :root {
23
+ --accent: #1a9850;
24
+ --accent-soft: #e8f5ec;
25
+ --border: #e2e2e0;
26
+ --muted: #6b6b68;
27
+ --surface: #fafaf9;
28
+ }
29
+
30
+ .card {
31
+ background: var(--surface);
32
+ border: 1px solid var(--border);
33
+ border-radius: 14px;
34
+ padding: 20px 24px;
35
+ margin-top: 24px;
36
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
37
+ }
38
+
39
+ .cardHeader {
40
  display: flex;
41
+ align-items: center;
42
+ justify-content: space-between;
43
+ gap: 12px;
44
  flex-wrap: wrap;
45
  }
46
 
47
+ .cardHeader h3 {
48
+ margin: 0;
49
+ font-size: 17px;
50
  }
51
 
52
+ .status {
53
+ font-size: 13px;
54
+ padding: 4px 12px;
55
+ border-radius: 999px;
56
+ border: 1px solid transparent;
57
+ overflow-wrap: anywhere;
58
  }
59
 
60
+ .status:empty {
61
+ display: none;
62
+ }
63
+
64
+ .status.ok {
65
+ background: var(--accent-soft);
66
+ color: #146c3a;
67
+ border-color: #bfe3cb;
68
+ }
69
+
70
+ .status.loading,
71
+ .status.custom {
72
+ background: #f1f1ef;
73
+ color: var(--muted);
74
+ border-color: var(--border);
75
+ }
76
+
77
+ .status.error {
78
+ background: #fdecea;
79
+ color: #a52a1f;
80
+ border-color: #f5c6c0;
81
+ }
82
+
83
+ .field {
84
+ display: flex;
85
+ flex-direction: column;
86
+ gap: 6px;
87
+ min-width: 0;
88
+ }
89
+
90
+ .field label,
91
+ .sliderHeader label {
92
+ font-size: 13px;
93
+ font-weight: 600;
94
+ color: #333;
95
+ }
96
+
97
+ .hint {
98
+ font-weight: 400;
99
+ color: var(--muted);
100
+ margin-left: 4px;
101
+ }
102
+
103
+ .card select,
104
+ .card input[type="text"],
105
+ .card input[type="number"] {
106
+ font: inherit;
107
+ font-size: 14px;
108
+ height: 38px;
109
+ padding: 0 12px;
110
+ border: 1px solid #cfcfcc;
111
+ border-radius: 8px;
112
+ background: white;
113
+ width: 100%;
114
+ box-sizing: border-box;
115
+ transition: border-color 0.15s, box-shadow 0.15s;
116
+ }
117
+
118
+ .card select:focus,
119
+ .card input[type="text"]:focus,
120
+ .card input[type="number"]:focus {
121
+ outline: none;
122
+ border-color: var(--accent);
123
+ box-shadow: 0 0 0 3px rgba(26, 152, 80, 0.15);
124
+ }
125
+
126
+ .sources {
127
+ display: grid;
128
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1.4fr);
129
+ gap: 16px;
130
+ align-items: end;
131
+ margin-top: 18px;
132
+ }
133
+
134
+ .divider {
135
+ color: var(--muted);
136
+ font-size: 13px;
137
+ height: 38px;
138
+ display: flex;
139
+ align-items: center;
140
+ }
141
+
142
+ .inputGroup {
143
+ display: flex;
144
+ }
145
+
146
+ .inputGroup input[type="text"] {
147
+ border-radius: 8px 0 0 8px;
148
+ }
149
+
150
+ .inputGroup button {
151
+ font: inherit;
152
+ font-size: 14px;
153
+ font-weight: 600;
154
+ height: 38px;
155
+ padding: 0 18px;
156
+ border: 1px solid var(--accent);
157
+ border-radius: 0 8px 8px 0;
158
+ background: var(--accent);
159
+ color: white;
160
+ cursor: pointer;
161
+ margin-left: -1px;
162
+ }
163
+
164
+ .inputGroup button:hover {
165
+ background: #137a40;
166
+ }
167
+
168
+ .sectionLabel {
169
+ margin: 22px 0 10px;
170
+ font-size: 12px;
171
+ letter-spacing: 0.06em;
172
+ text-transform: uppercase;
173
+ color: var(--muted);
174
+ }
175
+
176
+ .archGrid {
177
+ display: grid;
178
+ grid-template-columns: repeat(4, minmax(0, 1fr));
179
+ gap: 16px;
180
+ }
181
+
182
+ #archSummary {
183
+ display: flex;
184
+ flex-wrap: wrap;
185
+ gap: 8px;
186
+ margin-top: 12px;
187
+ }
188
+
189
+ .chip {
190
+ font-size: 12px;
191
+ padding: 3px 10px;
192
+ border-radius: 999px;
193
+ background: white;
194
+ border: 1px solid var(--border);
195
+ color: #444;
196
+ }
197
+
198
+ @media (max-width: 700px) {
199
+ .sources {
200
+ grid-template-columns: 1fr;
201
+ }
202
+
203
+ .divider {
204
+ height: auto;
205
+ }
206
+
207
+ .archGrid {
208
+ grid-template-columns: repeat(2, minmax(0, 1fr));
209
+ }
210
  }
211
 
212
  /* Input-token slider */
213
  #ninWrapper {
214
  width: 100%;
215
+ margin-top: 22px;
216
+ padding-top: 18px;
217
+ border-top: 1px solid var(--border);
218
+ }
219
+
220
+ .sliderHeader {
221
+ display: flex;
222
+ align-items: center;
223
+ gap: 10px;
224
+ }
225
+
226
+ .badge {
227
+ font-size: 13px;
228
+ font-weight: 600;
229
+ font-variant-numeric: tabular-nums;
230
+ padding: 2px 10px;
231
+ border-radius: 6px;
232
+ background: var(--accent-soft);
233
+ color: #146c3a;
234
  }
235
 
236
  #ninSlider {
237
  width: 100%;
238
  display: block;
239
+ margin-top: 10px;
240
+ accent-color: var(--accent);
241
  }
242
 
243
  #ninLabels {
244
  display: flex;
245
  justify-content: space-between;
246
  font-size: 12px;
247
+ color: var(--muted);
248
  margin-top: 3px;
249
  }
250
 
 
314
  <tbody id="thetaBody"></tbody>
315
  </table>
316
 
317
+ <section class="card" id="controls">
318
 
319
+ <div class="cardHeader">
320
+ <h3>Model</h3>
321
+ <span id="status" class="status"></span>
322
+ </div>
 
323
 
324
+ <div class="sources">
 
 
325
 
326
+ <div class="field">
327
+ <label for="preset">Validated models</label>
328
+ <select id="preset" onchange="loadPreset()"></select>
329
+ </div>
330
 
331
+ <div class="divider"><span>or</span></div>
332
 
333
+ <div class="field">
334
+ <label for="repo">Any public Hugging Face model <span class="hint">dense, &lt; 20B</span></label>
335
+ <div class="inputGroup">
336
+ <input id="repo" type="text" placeholder="tiiuae/falcon-rw-7b" spellcheck="false">
337
+ <button onclick="loadModel()">Load</button>
338
+ </div>
339
+ </div>
340
 
341
+ </div>
342
 
343
+ <h4 class="sectionLabel">Architecture</h4>
344
 
345
+ <div class="archGrid">
346
 
347
+ <div class="field">
348
+ <label for="d">Hidden size <i>d</i></label>
349
+ <input id="d" type="number" min="1" value="4096">
350
+ </div>
351
 
352
+ <div class="field">
353
+ <label for="L">Layers <i>L</i></label>
354
+ <input id="L" type="number" min="1" value="36">
355
+ </div>
356
 
357
+ <div class="field">
358
+ <label for="nq">Query heads <i>n</i><sub>q</sub></label>
359
+ <input id="nq" type="number" min="1" value="64">
360
+ </div>
361
 
362
+ <div class="field">
363
+ <label for="nkv">KV heads <i>n</i><sub>kv</sub></label>
364
+ <input id="nkv" type="number" min="1" value="64">
365
+ </div>
366
 
367
  </div>
368
 
369
+ <div id="archSummary"></div>
370
 
371
  <div id="ninWrapper">
372
 
373
+ <div class="sliderHeader">
374
+ <label for="ninSlider">Input tokens τ<sub>in</sub></label>
375
+ <span id="ninValue" class="badge">64</span>
376
+ </div>
377
 
378
  <input
379
  id="ninSlider"
 
395
 
396
  </div>
397
 
398
+ </section>
399
+
400
  <div id="plots">
401
 
402
  <div id="plotPanel">