Say why a listed model was not tested
Browse files- app.js +59 -25
- index.html +4 -3
app.js
CHANGED
|
@@ -12,6 +12,9 @@ const ORDERS = ["selected", "trending", "downloads"];
|
|
| 12 |
const HUB_SORTS = { trending: "trendingScore", downloads: "downloads" };
|
| 13 |
// How many of the Hub's top models a trending or downloads list shows.
|
| 14 |
const TOP = 100;
|
|
|
|
|
|
|
|
|
|
| 15 |
const COLUMNS = ["model_id", "tp", "result", "wall (s)", "Memory (GB)", "notes"];
|
| 16 |
|
| 17 |
// An element with attributes and children. Strings become text nodes, never HTML.
|
|
@@ -41,19 +44,37 @@ function cached(key, load) {
|
|
| 41 |
return cache.get(key);
|
| 42 |
}
|
| 43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
// The dataset's files at its latest commit: one per (neuronx-cc, library version)
|
| 45 |
-
// pair, and
|
| 46 |
async function loadIndex() {
|
| 47 |
const info = await (await fetchOk(`${HUB}/api/datasets/${DATASET}`)).json();
|
| 48 |
const pairs = [];
|
| 49 |
-
const
|
| 50 |
for (const { rfilename: path } of info.siblings) {
|
| 51 |
const pair = path.match(/^data\/neuronxcc-([^/]+)\/(.+)-([^-/]+)\.parquet$/);
|
| 52 |
if (pair) pairs.push({ sdk: pair[1], library: pair[2], version: pair[3], path });
|
| 53 |
-
const
|
| 54 |
-
if (
|
| 55 |
}
|
| 56 |
-
return { sha: info.sha, pairs,
|
| 57 |
}
|
| 58 |
|
| 59 |
const index = await loadIndex().catch((e) => e);
|
|
@@ -63,11 +84,12 @@ const loadRuns = (path) => cached(path, async () =>
|
|
| 63 |
parquetReadObjects({ file: await (await fetchOk(fileUrl(path))).arrayBuffer() }));
|
| 64 |
|
| 65 |
// Each model's position in a selection, best-ranked first.
|
| 66 |
-
const loadSelection = (path) => cached(path, async () =>
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
|
|
|
| 71 |
|
| 72 |
// A model's task on the Hub, or "unknown" if the Hub no longer has it.
|
| 73 |
const loadTask = (id) => cached(`task:${id}`, async () => {
|
|
@@ -78,14 +100,15 @@ const loadTask = (id) => cached(`task:${id}`, async () => {
|
|
| 78 |
return (await response.json()).pipeline_tag ?? "unknown";
|
| 79 |
}).catch(() => "unknown");
|
| 80 |
|
| 81 |
-
// The Hub's top models of a library and task, as its model list shows them
|
|
|
|
| 82 |
const loadTop = (library, task, order) => cached(`top:${library}:${task}:${order}`, async () => {
|
| 83 |
const sort = HUB_SORTS[order];
|
| 84 |
-
const query = new URLSearchParams({
|
| 85 |
-
|
| 86 |
-
|
| 87 |
const models = await (await fetchOk(`${HUB}/api/models?${query}`)).json();
|
| 88 |
-
return models.map((model) => ({ id: model.id, score: model[sort] ?? 0 }));
|
| 89 |
});
|
| 90 |
|
| 91 |
// Newest first. A version that is not a release, such as a git commit pin,
|
|
@@ -154,12 +177,13 @@ async function renderPanel() {
|
|
| 154 |
tasks,
|
| 155 |
results);
|
| 156 |
|
| 157 |
-
let runs, rank, taskOf;
|
| 158 |
try {
|
| 159 |
-
const selection = index.
|
| 160 |
-
[runs, rank] = await Promise.all([
|
| 161 |
loadRuns(pairs.find((p) => p.sdk === s.sdk && p.version === s.version).path),
|
| 162 |
selection ? loadSelection(selection) : new Map(),
|
|
|
|
| 163 |
]);
|
| 164 |
taskOf = new Map(await Promise.all(runs.map(async (run) => [run.model_id, await loadTask(run.model_id)])));
|
| 165 |
} catch (e) {
|
|
@@ -178,13 +202,23 @@ async function renderPanel() {
|
|
| 178 |
if (s.order === "selected") {
|
| 179 |
return runs.filter((run) => rank.has(run.model_id) && ticked.includes(taskOf.get(run.model_id)))
|
| 180 |
.sort((a, b) => rank.get(a.model_id) - rank.get(b.model_id) || a.tp - b.tp)
|
| 181 |
-
.map((run) =>
|
| 182 |
}
|
| 183 |
const lists = await Promise.all(ticked.map((t) => loadTop(active, t, s.order)));
|
| 184 |
return [...new Map(lists.flat().map((model) => [model.id, model])).values()]
|
| 185 |
.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
|
| 186 |
.slice(0, TOP)
|
| 187 |
-
.flatMap((
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
}
|
| 189 |
|
| 190 |
const renderResults = async () => {
|
|
@@ -204,7 +238,7 @@ async function renderPanel() {
|
|
| 204 |
: "The Hub lists no model for the ticked tasks."));
|
| 205 |
return;
|
| 206 |
}
|
| 207 |
-
const tested = shown.map((
|
| 208 |
const counts = new Map();
|
| 209 |
for (const run of tested) counts.set(run.result, (counts.get(run.result) ?? 0) + 1);
|
| 210 |
const failures = [...counts].filter(([result]) => result !== "PASS").sort(([a], [b]) => a.localeCompare(b));
|
|
@@ -217,20 +251,20 @@ async function renderPanel() {
|
|
| 217 |
: `None of these ${untested} models was tested on this pair.`),
|
| 218 |
h("div", { class: "scroll" }, h("table", {},
|
| 219 |
h("thead", {}, h("tr", {}, COLUMNS.map((c) => h("th", {}, c)))),
|
| 220 |
-
h("tbody", {}, shown.map((
|
| 221 |
tabindex: 0,
|
| 222 |
onclick: () => showRun(run),
|
| 223 |
onkeydown: (e) => { if (e.key === "Enter") showRun(run); },
|
| 224 |
},
|
| 225 |
-
h("td", {}, h("code", {}, id)),
|
| 226 |
h("td", { class: "num" }, run.tp),
|
| 227 |
h("td", { class: "result" }, badge(run.result)),
|
| 228 |
h("td", { class: "num" }, run.wall_s ?? ""),
|
| 229 |
h("td", { class: "num" }, memory(run)),
|
| 230 |
h("td", { class: "notes" }, run.notes ?? ""))
|
| 231 |
: h("tr", { class: "untested" },
|
| 232 |
-
h("td", {}, h("code", {}, id)), h("td"), h("td", { class: "result" }, "not tested"),
|
| 233 |
-
h("td"), h("td"), h("td")))))));
|
| 234 |
};
|
| 235 |
tasks.append(...taskList.map((t) => h("label", {},
|
| 236 |
h("input", {
|
|
|
|
| 12 |
const HUB_SORTS = { trending: "trendingScore", downloads: "downloads" };
|
| 13 |
// How many of the Hub's top models a trending or downloads list shows.
|
| 14 |
const TOP = 100;
|
| 15 |
+
// The test instance: 4 Trainium2 devices of 96 GiB of HBM each.
|
| 16 |
+
const DEVICES = 4;
|
| 17 |
+
const DEVICE_HBM = 96 * 1024 ** 3;
|
| 18 |
const COLUMNS = ["model_id", "tp", "result", "wall (s)", "Memory (GB)", "notes"];
|
| 19 |
|
| 20 |
// An element with attributes and children. Strings become text nodes, never HTML.
|
|
|
|
| 44 |
return cache.get(key);
|
| 45 |
}
|
| 46 |
|
| 47 |
+
// The rows of a CSV file as objects keyed by its header. Fields may be quoted.
|
| 48 |
+
function parseCsv(text) {
|
| 49 |
+
const rows = [[]];
|
| 50 |
+
let field = "";
|
| 51 |
+
let quoted = false;
|
| 52 |
+
for (let i = 0; i < text.length; i++) {
|
| 53 |
+
const c = text[i];
|
| 54 |
+
if (quoted && c === '"' && text[i + 1] === '"') field += text[i++];
|
| 55 |
+
else if (c === '"') quoted = !quoted;
|
| 56 |
+
else if (quoted || (c !== "," && c !== "\n" && c !== "\r")) field += c;
|
| 57 |
+
else if (c === ",") { rows.at(-1).push(field); field = ""; }
|
| 58 |
+
else if (c === "\n") { rows.at(-1).push(field); field = ""; rows.push([]); }
|
| 59 |
+
}
|
| 60 |
+
if (field || rows.at(-1).length) rows.at(-1).push(field);
|
| 61 |
+
const [header, ...lines] = rows.filter((row) => row.length);
|
| 62 |
+
return lines.map((line) => Object.fromEntries(header.map((key, i) => [key, line[i]])));
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
// The dataset's files at its latest commit: one per (neuronx-cc, library version)
|
| 66 |
+
// pair, and the selection and excluded list of each library.
|
| 67 |
async function loadIndex() {
|
| 68 |
const info = await (await fetchOk(`${HUB}/api/datasets/${DATASET}`)).json();
|
| 69 |
const pairs = [];
|
| 70 |
+
const lists = { selection: new Map(), excluded: new Map() };
|
| 71 |
for (const { rfilename: path } of info.siblings) {
|
| 72 |
const pair = path.match(/^data\/neuronxcc-([^/]+)\/(.+)-([^-/]+)\.parquet$/);
|
| 73 |
if (pair) pairs.push({ sdk: pair[1], library: pair[2], version: pair[3], path });
|
| 74 |
+
const list = path.match(/^(selection|excluded)\/(.+)\.csv$/);
|
| 75 |
+
if (list) lists[list[1]].set(list[2], path);
|
| 76 |
}
|
| 77 |
+
return { sha: info.sha, pairs, ...lists };
|
| 78 |
}
|
| 79 |
|
| 80 |
const index = await loadIndex().catch((e) => e);
|
|
|
|
| 84 |
parquetReadObjects({ file: await (await fetchOk(fileUrl(path))).arrayBuffer() }));
|
| 85 |
|
| 86 |
// Each model's position in a selection, best-ranked first.
|
| 87 |
+
const loadSelection = (path) => cached(path, async () =>
|
| 88 |
+
new Map(parseCsv(await (await fetchOk(fileUrl(path))).text()).map((row, rank) => [row.model_id, rank])));
|
| 89 |
+
|
| 90 |
+
// Each left-out model's row of an excluded list.
|
| 91 |
+
const loadExcluded = (path) => cached(path, async () =>
|
| 92 |
+
new Map(parseCsv(await (await fetchOk(fileUrl(path))).text()).map((row) => [row.model_id, row])));
|
| 93 |
|
| 94 |
// A model's task on the Hub, or "unknown" if the Hub no longer has it.
|
| 95 |
const loadTask = (id) => cached(`task:${id}`, async () => {
|
|
|
|
| 100 |
return (await response.json()).pipeline_tag ?? "unknown";
|
| 101 |
}).catch(() => "unknown");
|
| 102 |
|
| 103 |
+
// The Hub's top models of a library and task, as its model list shows them,
|
| 104 |
+
// with their parameter count when the Hub knows it.
|
| 105 |
const loadTop = (library, task, order) => cached(`top:${library}:${task}:${order}`, async () => {
|
| 106 |
const sort = HUB_SORTS[order];
|
| 107 |
+
const query = new URLSearchParams({ library, pipeline_tag: task, sort, direction: "-1", limit: TOP });
|
| 108 |
+
query.append("expand[]", sort);
|
| 109 |
+
query.append("expand[]", "safetensors");
|
| 110 |
const models = await (await fetchOk(`${HUB}/api/models?${query}`)).json();
|
| 111 |
+
return models.map((model) => ({ id: model.id, score: model[sort] ?? 0, params: model.safetensors?.total }));
|
| 112 |
});
|
| 113 |
|
| 114 |
// Newest first. A version that is not a release, such as a git commit pin,
|
|
|
|
| 177 |
tasks,
|
| 178 |
results);
|
| 179 |
|
| 180 |
+
let runs, rank, exclusions, taskOf;
|
| 181 |
try {
|
| 182 |
+
const [selection, excluded] = [index.selection.get(active), index.excluded.get(active)];
|
| 183 |
+
[runs, rank, exclusions] = await Promise.all([
|
| 184 |
loadRuns(pairs.find((p) => p.sdk === s.sdk && p.version === s.version).path),
|
| 185 |
selection ? loadSelection(selection) : new Map(),
|
| 186 |
+
excluded ? loadExcluded(excluded) : new Map(),
|
| 187 |
]);
|
| 188 |
taskOf = new Map(await Promise.all(runs.map(async (run) => [run.model_id, await loadTask(run.model_id)])));
|
| 189 |
} catch (e) {
|
|
|
|
| 202 |
if (s.order === "selected") {
|
| 203 |
return runs.filter((run) => rank.has(run.model_id) && ticked.includes(taskOf.get(run.model_id)))
|
| 204 |
.sort((a, b) => rank.get(a.model_id) - rank.get(b.model_id) || a.tp - b.tp)
|
| 205 |
+
.map((run) => ({ id: run.model_id, run }));
|
| 206 |
}
|
| 207 |
const lists = await Promise.all(ticked.map((t) => loadTop(active, t, s.order)));
|
| 208 |
return [...new Map(lists.flat().map((model) => [model.id, model])).values()]
|
| 209 |
.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
|
| 210 |
.slice(0, TOP)
|
| 211 |
+
.flatMap((model) => (runsOf.get(model.id) ?? [null]).map((run) => ({ ...model, run })));
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
// Why a model was not tested: weights larger than the test instance's HBM, or
|
| 215 |
+
// the reason the selection left it out, with the value its rule found.
|
| 216 |
+
function untestedReason({ id, params }) {
|
| 217 |
+
if (params * 2 > DEVICES * DEVICE_HBM) return `Requires more than ${DEVICES} Trainium2 devices`;
|
| 218 |
+
const exclusion = exclusions.get(id);
|
| 219 |
+
if (!exclusion) return "";
|
| 220 |
+
return exclusion.matched_pattern.includes("=") ? `${exclusion.reason} (${exclusion.matched_pattern})`
|
| 221 |
+
: exclusion.reason;
|
| 222 |
}
|
| 223 |
|
| 224 |
const renderResults = async () => {
|
|
|
|
| 238 |
: "The Hub lists no model for the ticked tasks."));
|
| 239 |
return;
|
| 240 |
}
|
| 241 |
+
const tested = shown.map((entry) => entry.run).filter(Boolean);
|
| 242 |
const counts = new Map();
|
| 243 |
for (const run of tested) counts.set(run.result, (counts.get(run.result) ?? 0) + 1);
|
| 244 |
const failures = [...counts].filter(([result]) => result !== "PASS").sort(([a], [b]) => a.localeCompare(b));
|
|
|
|
| 251 |
: `None of these ${untested} models was tested on this pair.`),
|
| 252 |
h("div", { class: "scroll" }, h("table", {},
|
| 253 |
h("thead", {}, h("tr", {}, COLUMNS.map((c) => h("th", {}, c)))),
|
| 254 |
+
h("tbody", {}, shown.map(({ run, ...model }) => run ? h("tr", {
|
| 255 |
tabindex: 0,
|
| 256 |
onclick: () => showRun(run),
|
| 257 |
onkeydown: (e) => { if (e.key === "Enter") showRun(run); },
|
| 258 |
},
|
| 259 |
+
h("td", {}, h("code", {}, model.id)),
|
| 260 |
h("td", { class: "num" }, run.tp),
|
| 261 |
h("td", { class: "result" }, badge(run.result)),
|
| 262 |
h("td", { class: "num" }, run.wall_s ?? ""),
|
| 263 |
h("td", { class: "num" }, memory(run)),
|
| 264 |
h("td", { class: "notes" }, run.notes ?? ""))
|
| 265 |
: h("tr", { class: "untested" },
|
| 266 |
+
h("td", {}, h("code", {}, model.id)), h("td"), h("td", { class: "result" }, "not tested"),
|
| 267 |
+
h("td"), h("td"), h("td", { class: "notes" }, untestedReason(model))))))));
|
| 268 |
};
|
| 269 |
tasks.append(...taskList.map((t) => h("label", {},
|
| 270 |
h("input", {
|
index.html
CHANGED
|
@@ -105,9 +105,10 @@ summary { cursor: pointer; color: var(--accent); margin: 12px 0 4px; }
|
|
| 105 |
<h1>torch-neuronx model support</h1>
|
| 106 |
<p class="intro">
|
| 107 |
Which Hugging Face Hub models run unmodified on AWS Trainium through the native PyTorch backend
|
| 108 |
-
<a href="https://github.com/aws-neuron/torch-neuronx">torch-neuronx</a>.
|
| 109 |
-
|
| 110 |
-
card</a>
|
|
|
|
| 111 |
</p>
|
| 112 |
<nav id="tabs" role="tablist"></nav>
|
| 113 |
<main id="panel"><p class="status">Loading…</p></main>
|
|
|
|
| 105 |
<h1>torch-neuronx model support</h1>
|
| 106 |
<p class="intro">
|
| 107 |
Which Hugging Face Hub models run unmodified on AWS Trainium through the native PyTorch backend
|
| 108 |
+
<a href="https://github.com/aws-neuron/torch-neuronx">torch-neuronx</a>. For now, only models that
|
| 109 |
+
fit on 4 Trainium2 devices (TP=16) are tested. Select a model to see how to run it. The
|
| 110 |
+
<a href="https://huggingface.co/datasets/aws-neuron/torch-neuronx-model-support">dataset card</a>
|
| 111 |
+
describes each test and what each result means.
|
| 112 |
</p>
|
| 113 |
<nav id="tabs" role="tablist"></nav>
|
| 114 |
<main id="panel"><p class="status">Loading…</p></main>
|