dacorvo HF Staff commited on
Commit
09eb6df
·
verified ·
1 Parent(s): 88f54b7

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +10 -4
  2. app.js +237 -0
  3. index.html +113 -17
README.md CHANGED
@@ -1,10 +1,16 @@
1
  ---
2
- title: Torch Neuronx Model Support
3
- emoji: 🐠
4
  colorFrom: yellow
5
- colorTo: indigo
6
  sdk: static
7
  pinned: false
 
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
1
  ---
2
+ title: torch-neuronx model support
3
+ emoji: ✅
4
  colorFrom: yellow
5
+ colorTo: gray
6
  sdk: static
7
  pinned: false
8
+ short_description: Which Hub models run on AWS Trainium with torch-neuronx
9
+ datasets:
10
+ - aws-neuron/torch-neuronx-model-support
11
  ---
12
 
13
+ Lists the models of the
14
+ [aws-neuron/torch-neuronx-model-support](https://huggingface.co/datasets/aws-neuron/torch-neuronx-model-support)
15
+ dataset, by library and `(neuronx-cc, library version)` pair, in the order the
16
+ sweep selected them or by their trending score or downloads on the Hub.
app.js ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Lists the models of the aws-neuron/torch-neuronx-model-support dataset, one
2
+ // tab per library, ordered and filtered by each model's live Hub stats.
3
+ import { parquetReadObjects } from "https://cdn.jsdelivr.net/npm/hyparquet@1.31.2/src/index.js";
4
+
5
+ const HUB = "https://huggingface.co";
6
+ const DATASET = "aws-neuron/torch-neuronx-model-support";
7
+ const NEFF_CACHE = "hf://buckets/aws-neuron/torch-neuronx-neff-cache";
8
+ const LIBRARIES = ["transformers", "diffusers", "sentence-transformers"];
9
+ const ORDERS = ["selected", "trending", "downloads"];
10
+ const COLUMNS = ["model_id", "runner", "tp", "result", "wall (s)", "Memory (GB)", "notes"];
11
+ const STATS_QUERY = new URLSearchParams([
12
+ ["expand[]", "trendingScore"], ["expand[]", "downloads"], ["expand[]", "pipeline_tag"],
13
+ ]);
14
+ const NO_STATS = { trending: 0, downloads: 0, task: "unknown" };
15
+
16
+ // An element with attributes and children. Strings become text nodes, never HTML.
17
+ function h(tag, attrs = {}, ...children) {
18
+ const el = document.createElement(tag);
19
+ for (const [key, value] of Object.entries(attrs)) {
20
+ if (key.startsWith("on")) el.addEventListener(key.slice(2), value);
21
+ else if (value === true) el.setAttribute(key, "");
22
+ else if (value !== false && value != null) el.setAttribute(key, value);
23
+ }
24
+ el.append(...children.flat(Infinity).filter((c) => c != null && c !== false));
25
+ return el;
26
+ }
27
+
28
+ const encodePath = (path) => path.split("/").map(encodeURIComponent).join("/");
29
+
30
+ async function fetchOk(url) {
31
+ const response = await fetch(url);
32
+ if (!response.ok) throw new Error(`${url}: HTTP ${response.status}`);
33
+ return response;
34
+ }
35
+
36
+ // Promises by key, dropped on failure so that the next render retries.
37
+ const cache = new Map();
38
+ function cached(key, load) {
39
+ if (!cache.has(key)) cache.set(key, load().catch((e) => { cache.delete(key); throw e; }));
40
+ return cache.get(key);
41
+ }
42
+
43
+ // The dataset's files at its latest commit: one per (neuronx-cc, library version)
44
+ // pair, and one selection per library.
45
+ async function loadIndex() {
46
+ const info = await (await fetchOk(`${HUB}/api/datasets/${DATASET}`)).json();
47
+ const pairs = [];
48
+ const selections = new Map();
49
+ for (const { rfilename: path } of info.siblings) {
50
+ const pair = path.match(/^data\/neuronxcc-([^/]+)\/(.+)-([^-/]+)\.parquet$/);
51
+ if (pair) pairs.push({ sdk: pair[1], library: pair[2], version: pair[3], path });
52
+ const selection = path.match(/^selection\/(.+)\.csv$/);
53
+ if (selection) selections.set(selection[1], path);
54
+ }
55
+ return { sha: info.sha, pairs, selections };
56
+ }
57
+
58
+ const index = await loadIndex().catch((e) => e);
59
+ const fileUrl = (path) => `${HUB}/datasets/${DATASET}/resolve/${index.sha}/${encodePath(path)}`;
60
+
61
+ const loadRuns = (path) => cached(path, async () =>
62
+ parquetReadObjects({ file: await (await fetchOk(fileUrl(path))).arrayBuffer() }));
63
+
64
+ // Each model's position in a selection, best-ranked first.
65
+ const loadSelection = (path) => cached(path, async () => {
66
+ const [header, ...lines] = (await (await fetchOk(fileUrl(path))).text()).trim().split("\n");
67
+ const column = header.split(",").indexOf("model_id");
68
+ return new Map(lines.map((line, rank) => [line.split(",")[column], rank]));
69
+ });
70
+
71
+ // A model's trending score, downloads and task on the Hub today.
72
+ const loadStats = (id) => cached(`stats:${id}`, async () => {
73
+ const response = await fetch(`${HUB}/api/models/${encodePath(id)}?${STATS_QUERY}`);
74
+ if (response.status === 404) return NO_STATS;
75
+ if (!response.ok) throw new Error(`${id}: HTTP ${response.status}`);
76
+ const model = await response.json();
77
+ return {
78
+ trending: model.trendingScore ?? 0,
79
+ downloads: model.downloads ?? 0,
80
+ task: model.pipeline_tag ?? "unknown",
81
+ };
82
+ }).catch(() => NO_STATS);
83
+
84
+ // Newest first. A version that is not a release, such as a git commit pin,
85
+ // comes before the releases.
86
+ function newestFirst(a, b) {
87
+ const release = (v) => v.match(/^\d+(?:\.\d+)+/)?.[0].split(".").map(Number);
88
+ const [ra, rb] = [release(a), release(b)];
89
+ if (!ra || !rb) return Boolean(ra) - Boolean(rb) || a.localeCompare(b);
90
+ for (let i = 0; i < Math.max(ra.length, rb.length); i++) {
91
+ const d = (rb[i] ?? 0) - (ra[i] ?? 0);
92
+ if (d) return d;
93
+ }
94
+ // A pre-release, such as 5.21.0rc1, is older than its release.
95
+ const pre = (v) => /^\d+(?:\.\d+)+(?:a|b|rc|\.?dev)/.test(v);
96
+ return pre(a) - pre(b);
97
+ }
98
+
99
+ const badge = (result) => `${result === "PASS" ? "✅" : "❌"} ${result}`;
100
+ const memory = (run) => run.hbm_peak_gb == null ? ""
101
+ : `${run.hbm_peak_gb.toFixed(2)} / ${Number(run.hbm_capacity_gb.toFixed(1))}`;
102
+ const day = (date) => new Date(date).toISOString().slice(0, 10);
103
+
104
+ const tabs = document.getElementById("tabs");
105
+ const panel = document.getElementById("panel");
106
+ const dialog = document.getElementById("run");
107
+ const state = new Map(LIBRARIES.map((library) =>
108
+ [library, { order: ORDERS[0], sdk: null, version: null, hiddenTasks: new Set() }]));
109
+ let active = LIBRARIES[0];
110
+ let rendering = 0;
111
+
112
+ function renderTabs() {
113
+ tabs.replaceChildren(...LIBRARIES.map((library) => h("button", {
114
+ role: "tab",
115
+ "aria-selected": String(library === active),
116
+ onclick: () => { active = library; renderTabs(); renderPanel(); },
117
+ }, library)));
118
+ }
119
+
120
+ async function renderPanel() {
121
+ const render = ++rendering;
122
+ if (index instanceof Error) {
123
+ panel.replaceChildren(h("p", { class: "status" }, `Could not read the dataset: ${index.message}`));
124
+ return;
125
+ }
126
+ const pairs = index.pairs.filter((p) => p.library === active);
127
+ if (!pairs.length) {
128
+ panel.replaceChildren(h("p", { class: "status" }, "No records yet."));
129
+ return;
130
+ }
131
+ const s = state.get(active);
132
+ const sdks = [...new Set(pairs.map((p) => p.sdk))].sort(newestFirst);
133
+ if (!sdks.includes(s.sdk)) s.sdk = sdks[0];
134
+ const versions = pairs.filter((p) => p.sdk === s.sdk).map((p) => p.version).sort(newestFirst);
135
+ if (!versions.includes(s.version)) s.version = versions[0];
136
+
137
+ const select = (label, key, options, onchange) => h("label", {}, label, h("select", {
138
+ onchange: (e) => { s[key] = e.target.value; onchange?.(); renderPanel(); },
139
+ }, options.map((o) => h("option", { value: o, selected: o === s[key] }, o))));
140
+ const tasks = h("div", { class: "tasks", role: "group", "aria-label": "Tasks" }, h("span", {}, "Tasks"));
141
+ const results = h("div", { class: "results" }, h("p", { class: "status" }, "Loading…"));
142
+ panel.replaceChildren(
143
+ h("div", { class: "controls" },
144
+ select("Order", "order", ORDERS),
145
+ select("SDK (neuronx-cc)", "sdk", sdks, () => { s.version = null; }),
146
+ select(active, "version", versions)),
147
+ tasks,
148
+ results);
149
+
150
+ let runs, rank, stats;
151
+ try {
152
+ const selection = index.selections.get(active);
153
+ [runs, rank] = await Promise.all([
154
+ loadRuns(pairs.find((p) => p.sdk === s.sdk && p.version === s.version).path),
155
+ selection ? loadSelection(selection) : new Map(),
156
+ ]);
157
+ stats = new Map(await Promise.all(runs.map(async (run) => [run.model_id, await loadStats(run.model_id)])));
158
+ } catch (e) {
159
+ if (render === rendering) results.replaceChildren(h("p", { class: "status" }, `Could not load the results: ${e.message}`));
160
+ return;
161
+ }
162
+ if (render !== rendering) return;
163
+
164
+ const task = (run) => stats.get(run.model_id).task;
165
+ const renderResults = () => {
166
+ const order = {
167
+ selected: (a, b) => (rank.get(a.model_id) ?? Infinity) - (rank.get(b.model_id) ?? Infinity),
168
+ trending: (a, b) => stats.get(b.model_id).trending - stats.get(a.model_id).trending
169
+ || stats.get(b.model_id).downloads - stats.get(a.model_id).downloads,
170
+ downloads: (a, b) => stats.get(b.model_id).downloads - stats.get(a.model_id).downloads,
171
+ }[s.order];
172
+ const shown = runs.filter((run) => !s.hiddenTasks.has(task(run)))
173
+ .sort((a, b) => order(a, b) || a.model_id.localeCompare(b.model_id) || a.tp - b.tp);
174
+ const counts = new Map();
175
+ for (const run of shown) counts.set(run.result, (counts.get(run.result) ?? 0) + 1);
176
+ const failures = [...counts].filter(([result]) => result !== "PASS").sort(([a], [b]) => a.localeCompare(b));
177
+ results.replaceChildren(
178
+ h("p", { class: "summary" },
179
+ h("strong", {}, `${counts.get("PASS") ?? 0} of ${shown.length}`), " models pass",
180
+ failures.map(([result, n]) => ` · ${badge(result)}: ${n}`)),
181
+ h("div", { class: "scroll" }, h("table", {},
182
+ h("thead", {}, h("tr", {}, COLUMNS.map((c) => h("th", {}, c)))),
183
+ h("tbody", {}, shown.map((run) => h("tr", {
184
+ tabindex: 0,
185
+ onclick: () => showRun(run),
186
+ onkeydown: (e) => { if (e.key === "Enter") showRun(run); },
187
+ },
188
+ h("td", {}, h("code", {}, run.model_id)),
189
+ h("td", {}, run.runner),
190
+ h("td", { class: "num" }, run.tp),
191
+ h("td", { class: "result" }, badge(run.result)),
192
+ h("td", { class: "num" }, run.wall_s ?? ""),
193
+ h("td", { class: "num" }, memory(run)),
194
+ h("td", { class: "notes" }, run.notes ?? "")))))));
195
+ };
196
+ tasks.append(...[...new Set(runs.map(task))].sort().map((t) => h("label", {},
197
+ h("input", {
198
+ type: "checkbox",
199
+ checked: !s.hiddenTasks.has(t),
200
+ onchange: (e) => { if (e.target.checked) s.hiddenTasks.delete(t); else s.hiddenTasks.add(t); renderResults(); },
201
+ }),
202
+ t)));
203
+ renderResults();
204
+ }
205
+
206
+ let scriptUrl;
207
+ function showRun(run) {
208
+ URL.revokeObjectURL(scriptUrl);
209
+ scriptUrl = URL.createObjectURL(new Blob([run.run_py], { type: "text/x-python" }));
210
+ const pip = run.run_py.match(/^# (pip install .*)$/m)?.[1];
211
+ const stack = [
212
+ ["neuronx-cc", run.neuronx_cc], ["torch-neuronx", run.torch_neuronx], ["torch", run.torch],
213
+ ["nki", run.nki], ["Python", run.python], ["Neuron driver", run.neuron_driver],
214
+ ].filter(([, version]) => version);
215
+ dialog.replaceChildren(h("div", { class: "dialog" },
216
+ h("form", { method: "dialog" }, h("button", { class: "close", "aria-label": "Close" }, "×")),
217
+ h("h2", {}, h("a", { href: `${HUB}/${run.model_id}`, target: "_blank", rel: "noopener" }, run.model_id)),
218
+ h("p", {}, `${badge(run.result)} · TP=${run.tp} · ${run.runner} · ${run.library} ${run.library_version}`
219
+ + ` · tested ${day(run.tested_at)}`),
220
+ h("h3", {}, "Run it"),
221
+ h("p", {}, "On a Trainium2 (trn2) instance with this Neuron stack, for example in the AWS Neuron"
222
+ + " Deep Learning Container:"),
223
+ h("dl", {}, stack.map(([name, version]) => [h("dt", {}, name), h("dd", {}, h("code", {}, version))])),
224
+ h("ol", {},
225
+ pip && h("li", {}, "Install the packages the script adds:", h("pre", {}, pip)),
226
+ h("li", {}, "Optionally, skip compilation with the graphs this compiler already built:",
227
+ h("pre", {}, `hf buckets sync ${NEFF_CACHE}/neuronxcc-${run.neuronx_cc} /tmp/neff_cache`)),
228
+ h("li", {}, "Download ", h("a", { href: scriptUrl, download: "run.py" }, "run.py"), " and run it",
229
+ run.tp > 1 ? ". It launches its own torch.distributed ranks:" : ":", h("pre", {}, "python3 run.py"))),
230
+ run.error && [h("h3", {}, "Error"), h("pre", { class: "error" }, run.error)],
231
+ h("details", {}, h("summary", {}, "run.py"), h("pre", {}, run.run_py))));
232
+ dialog.showModal();
233
+ }
234
+ dialog.addEventListener("click", (e) => { if (e.target === dialog) dialog.close(); });
235
+
236
+ renderTabs();
237
+ renderPanel();
index.html CHANGED
@@ -1,19 +1,115 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
  <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>torch-neuronx model support</title>
7
+ <style>
8
+ :root {
9
+ color-scheme: light dark;
10
+ --bg: #ffffff;
11
+ --fg: #1f2328;
12
+ --muted: #59636e;
13
+ --border: #d1d9e0;
14
+ --stripe: #f6f8fa;
15
+ --hover: #ddeeff;
16
+ --accent: #0969da;
17
+ --code: #eff1f3;
18
+ }
19
+ @media (prefers-color-scheme: dark) {
20
+ :root {
21
+ --bg: #0d1117;
22
+ --fg: #e6edf3;
23
+ --muted: #9198a1;
24
+ --border: #3d444d;
25
+ --stripe: #151b23;
26
+ --hover: #1c2b41;
27
+ --accent: #4493f8;
28
+ --code: #262c36;
29
+ }
30
+ }
31
+ body {
32
+ margin: 0 auto;
33
+ max-width: 1280px;
34
+ padding: 16px;
35
+ background: var(--bg);
36
+ color: var(--fg);
37
+ font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;
38
+ }
39
+ a { color: var(--accent); }
40
+ code, pre { font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; font-size: 12px; }
41
+ h1 { font-size: 24px; margin: 8px 0; }
42
+ .intro { color: var(--muted); max-width: 90ch; }
43
+ [role=tablist] { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin: 16px 0; overflow-x: auto; }
44
+ [role=tab] {
45
+ background: none;
46
+ border: 1px solid transparent;
47
+ border-bottom: none;
48
+ border-radius: 6px 6px 0 0;
49
+ margin-bottom: -1px;
50
+ padding: 8px 16px;
51
+ color: var(--muted);
52
+ font: inherit;
53
+ cursor: pointer;
54
+ white-space: nowrap;
55
+ }
56
+ [role=tab][aria-selected=true] { background: var(--bg); border-color: var(--border); color: var(--fg); font-weight: 600; }
57
+ .controls { display: flex; flex-wrap: wrap; gap: 12px 24px; }
58
+ .controls label { display: flex; flex-direction: column; gap: 4px; color: var(--muted); font-size: 12px; }
59
+ select {
60
+ font: inherit;
61
+ padding: 4px 8px;
62
+ border: 1px solid var(--border);
63
+ border-radius: 6px;
64
+ background: var(--bg);
65
+ color: var(--fg);
66
+ }
67
+ .tasks { display: flex; flex-wrap: wrap; gap: 4px 16px; align-items: center; margin: 12px 0; }
68
+ .tasks > span { color: var(--muted); font-size: 12px; }
69
+ .tasks label { display: inline-flex; gap: 6px; align-items: center; cursor: pointer; }
70
+ .status { color: var(--muted); }
71
+ .scroll { overflow-x: auto; }
72
+ table { border-collapse: collapse; width: 100%; min-width: 960px; }
73
+ th, td { border: 1px solid var(--border); padding: 6px 12px; text-align: left; vertical-align: top; }
74
+ th { font-weight: 600; white-space: nowrap; }
75
+ tbody tr { cursor: pointer; }
76
+ tbody tr:nth-child(even) { background: var(--stripe); }
77
+ tbody tr:hover, tbody tr:focus-visible { background: var(--hover); outline: none; }
78
+ td.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
79
+ td.result { white-space: nowrap; }
80
+ td.notes { word-break: break-word; }
81
+ dialog {
82
+ width: min(900px, calc(100vw - 32px));
83
+ max-height: calc(100vh - 64px);
84
+ padding: 0;
85
+ border: 1px solid var(--border);
86
+ border-radius: 12px;
87
+ background: var(--bg);
88
+ color: var(--fg);
89
+ }
90
+ dialog::backdrop { background: rgb(0 0 0 / 0.5); }
91
+ .dialog { padding: 16px 24px; }
92
+ .dialog h2 { margin: 0 32px 4px 0; font-size: 18px; overflow-wrap: anywhere; }
93
+ .close { float: right; border: none; background: none; color: var(--muted); font-size: 24px; line-height: 1; cursor: pointer; }
94
+ pre { background: var(--code); padding: 8px 12px; border-radius: 6px; overflow-x: auto; }
95
+ pre.error { white-space: pre-wrap; overflow-wrap: anywhere; }
96
+ dl { display: grid; grid-template-columns: max-content 1fr; gap: 2px 16px; margin: 8px 0; }
97
+ dt { color: var(--muted); }
98
+ dd { margin: 0; overflow-wrap: anywhere; }
99
+ summary { cursor: pointer; color: var(--accent); margin: 12px 0 4px; }
100
+ </style>
101
+ <script type="module" src="app.js"></script>
102
+ </head>
103
+ <body>
104
+ <h1>torch-neuronx model support</h1>
105
+ <p class="intro">
106
+ Which Hugging Face Hub models run unmodified on AWS Trainium through the native PyTorch backend
107
+ <a href="https://github.com/aws-neuron/torch-neuronx">torch-neuronx</a>. Select a model to see how
108
+ to run it. The <a href="https://huggingface.co/datasets/aws-neuron/torch-neuronx-model-support">dataset
109
+ card</a> describes each test and what each result means.
110
+ </p>
111
+ <nav id="tabs" role="tablist"></nav>
112
+ <main id="panel"><p class="status">Loading…</p></main>
113
+ <dialog id="run"></dialog>
114
+ </body>
115
  </html>