File size: 17,148 Bytes
61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 61ffaac 00ee773 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | """Merge benchmark results into REPORT.md (Nirnay models vs Laya measured here, Jev as published).
python bench\\report.py
REPORT_NIR="nirnay-v3,nirnay-v2,nirnay-v1" python bench\\report.py (first model = the headline column)
Models without a results file are left out. The multilingual section appears once
results/suites_multi__*.json exist (MASSIVE, 51 languages; SIB-200, 205 languages).
"""
import json
import os
import statistics
import time
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(HERE)
RES = os.path.join(HERE, "results")
OUT = os.path.join(ROOT, os.environ.get("REPORT_OUT", "REPORT.md"))
# Published Jev figures (TypeSafe's closed API; not runnable here). Source: Laya repo
# research/scripts/bench_apps.py, which cites AbdelStark/jev-benchmarks v0.1.0 (n=100 each)
# and nibzard/decision-model-benchmark.
JEV = {"jev.ag_news": 0.910, "jev.banking77_full": 0.870, "jev.emotion": 0.480, "extra.sms_spam": 0.930}
JEV_NOTE = {"jev.banking77_full": "72 labels, n=100; independent run 0.763",
"extra.sms_spam": "independent run, different sample"}
LAYA = [("laya", "laya (en)"), ("laya-multilingual", "laya-ml"), ("laya-typed-decisions", "laya-td")]
MODELS = { # key -> (column label, result-file tag, description)
"nirnay-v3": ("Nirnay v3", "v3", "bge-base-en-v1.5, deep fusion; 324k questions: v2's mix plus request routing, short-message "
"spam, request safety (NVIDIA Aegis 2.0), urgency and more phrasings of every question "
"template; warm-started from v2 for 1.25 more epochs on both CPU sockets."),
"nirnay-multilingual": ("Nirnay multilingual", "ml", "multilingual-e5-base (100 languages), deep fusion; ~380k questions: "
"v3's mix plus MTOP intents (6 languages), PAWS-X, NLI in 26 "
"languages, toxicity in 14, language identification, multilingual "
"reviews and tweets; 1.2 epochs."),
"nirnay-v31": ("Nirnay v3.1", "v31", "v3 with content-only safety wording (v3's AI-assistant / policy phrasings made it "
"call harmful prompts jailbreaks); same recipe: warm-started from v2, 1.25 epochs."),
"nirnay-v31-gpu": ("Nirnay v3.1-GPU", "v31gpu", "v3.1's data and architecture, warm-started from v2 for 2 epochs on a "
"free Kaggle T4 GPU (train/train_gpu.py; frozen lower layers computed live)."),
"nirnay-v2": ("Nirnay v2", "v2", "bge-base-en-v1.5, deep fusion (top 4 encoder layers trained), 294k questions, 1.6 epochs "
"(overnight, both CPU sockets)."),
"nirnay-v1": ("Nirnay v1 (fast)", "v1", "bge-small-en-v1.5, same architecture, 221k questions, 1 epoch."),
}
ORDER = ["jev.ag_news", "jev.emotion", "jev.banking77_full", "app.support_triage", "app.email_spam", "app.phishing",
"app.guardrails_jailbreak", "app.moderation_toxicity", "app.rag_relevance", "app.model_routing_domain",
"extra.sst5", "extra.sms_spam"]
TIE = 0.005
def jload(name):
p = os.path.join(RES, name)
return json.load(open(p, encoding="utf-8")) if os.path.exists(p) else None
def fmt(x, nd=3):
return "—" if x is None else ("%.*f" % (nd, x))
def acc(d, s):
return ((d or {}).get("suites", {}).get(s) or {}).get("accuracy")
def tally(mine, theirs):
w = t = l = 0
for a, b in zip(mine, theirs):
if a is None or b is None:
continue
if a > b + TIE:
w += 1
elif a < b - TIE:
l += 1
else:
t += 1
return w, t, l
def multilingual_section(keys):
"""MASSIVE + SIB-200 per model; per-language MASSIVE table; per-language wins vs Laya-multilingual."""
res = {k: jload("suites_multi__nirnay__%s.json" % k) for k in keys}
res = {k: v for k, v in res.items() if v}
laya = jload("suites_multi__laya__laya-multilingual.json")
if not res: # nothing of ours measured yet: no section (a Laya-only table would compare nothing)
return []
cols = [(k, MODELS[k][0]) for k in res] + ([("laya-ml", "Laya multilingual")] if laya else [])
data = dict(res, **({"laya-ml": laya} if laya else {}))
def per_lang(d, prefix):
return {s.split(".", 1)[1]: m["accuracy"] for s, m in (d or {}).get("suites", {}).items() if s.startswith(prefix + ".")}
L = ["## Multilingual (held out of training for every model)", "",
"* **MASSIVE** intent detection (mteb/amazon_massive_intent, test split): 51 languages, all 60 intents as options, "
"80 requests per language, the question in English.",
"* **SIB-200** topic classification (Davlan/sib200, test split): 205 languages and scripts, 7 topics, the same 24 "
"FLORES sentences in every language.", "",
"| benchmark | " + " | ".join(l for _, l in cols) + " |", "|---|" + "---|" * len(cols)]
for prefix, name in (("massive", "MASSIVE, mean of 51 languages"), ("sib200", "SIB-200, mean of 205 languages")):
row = []
for k, _ in cols:
xs = list(per_lang(data[k], prefix).values())
row.append(fmt(statistics.mean(xs)) if xs else "—")
L.append("| %s | %s |" % (name, " | ".join(row)))
for prefix, name in (("massive", "MASSIVE, worst language"), ("sib200", "SIB-200, worst language")):
row = []
for k, _ in cols:
pl = per_lang(data[k], prefix)
row.append(("%s (%s)" % (fmt(min(pl.values())), min(pl, key=pl.get))) if pl else "—")
L.append("| %s | %s |" % (name, " | ".join(row)))
L.append("")
if laya and "nirnay-multilingual" in res:
for prefix, name in (("massive", "MASSIVE"), ("sib200", "SIB-200")):
a, b = per_lang(res["nirnay-multilingual"], prefix), per_lang(laya, prefix)
common = sorted(set(a) & set(b))
w, t, l = tally([a[x] for x in common], [b[x] for x in common])
L.append("%s, per language, Nirnay multilingual vs Laya multilingual: **%d wins, %d ties, %d losses** "
"(of %d languages)." % (name, w, t, l, len(common)))
L.append("")
langs = sorted(set().union(*[per_lang(data[k], "massive") for k, _ in cols]))
if langs:
L += ["### MASSIVE by language", "", "| language | " + " | ".join(l for _, l in cols) + " |",
"|---|" + "---|" * len(cols)]
for lg in langs:
L.append("| %s | %s |" % (lg, " | ".join(fmt(per_lang(data[k], "massive").get(lg)) for k, _ in cols)))
L.append("")
return L
def main():
want = [k.strip() for k in os.environ.get("REPORT_NIR", "nirnay-v31,nirnay-v3,nirnay-multilingual,nirnay-v2,nirnay-v1").split(",")
if k.strip()]
nir = {k: jload("suites__nirnay__%s.json" % k) for k in want}
NIR = [(k, MODELS[k][0]) for k in want if nir.get(k)]
head = NIR[0][0]
lv = ((jload("laya_latency.json") or {}).get("meta") or {}).get("laya", "?")
lay = {k: jload("suites__laya__%s.json" % k) for k, _ in LAYA}
L = ["# Nirnay vs Laya vs Jev — measured on this machine", "",
"Generated %s. Hardware: HP Z840, 2x Xeon E5-2630 v3 (16 cores / 32 threads, AVX2, no AVX-512), "
"128 GB DDR4, CPU only (Quadro K2200 unused)." % time.strftime("%Y-%m-%d %H:%M"), "",
"Laya was installed from PyPI (`laya` %s) with its three published checkpoints and run through its own API "
"with its shipped calibration. Jev is a closed, paid cloud API: it cannot run on this machine, so its column "
"is the third-party published figure where one exists." % lv, ""]
L += ["* **%s** (`models\\%s`): %s" % (MODELS[k][0], k, MODELS[k][2]) for k, _ in NIR] + [""]
# ------------------------------------------------------------- accuracy
L += ["## Accuracy (same 400 cases per task, same questions, seed 13)", "",
"| task | " + " | ".join(l for _, l in NIR) + " | " + " | ".join(l for _, l in LAYA) +
" | best Laya | Jev (published) | Laya trained on it? |",
"|---|" + "---|" * (len(NIR) + len(LAYA) + 3)]
heads, bests, ens = [], [], []
for s in ORDER:
ls = [acc(lay[k], s) for k, _ in LAYA]
best = max([x for x in ls if x is not None], default=None)
meta = ((nir[head] or {}).get("suites", {}).get(s) or {}).get("meta") or {}
heads.append(acc(nir[head], s))
bests.append(best)
ens.append(ls[0])
jt = JEV.get(s)
cells = ["**%s**" % fmt(acc(nir[k], s)) if k == head else fmt(acc(nir[k], s)) for k, _ in NIR]
L.append("| %s | %s | %s | %s | %s | %s |" % (
s, " | ".join(cells), " | ".join(fmt(x) for x in ls), fmt(best),
(fmt(jt) + (" (%s)" % JEV_NOTE[s] if s in JEV_NOTE else "")) if jt else "—",
"yes" if meta.get("laya_in_training") else "no (held out)"))
hl = MODELS[head][0]
w, t, l = tally(heads, bests)
w2, t2, l2 = tally(heads, ens)
L += ["", "%s vs the best of Laya's three checkpoints (picked per task, after the fact): "
"**%d wins, %d ties (±%.3f), %d losses**." % (hl, w, t, TIE, l),
"%s vs Laya's default English checkpoint: **%d wins, %d ties, %d losses**." % (hl, w2, t2, l2)]
for k, lab in NIR[1:]:
w3, t3, l3 = tally(heads, [acc(nir[k], s) for s in ORDER])
L.append("%s vs %s: %d better, %d same, %d worse." % (hl, lab, w3, t3, l3))
L.append("")
# ------------------------------------------------------------- calibration
L += ["## Calibration (ECE, lower is better)", "",
"| task | " + " | ".join(l for _, l in NIR) + " | best Laya |", "|---|" + "---|" * (len(NIR) + 1)]
for s in ORDER:
le = [((lay[k] or {}).get("suites", {}).get(s) or {}).get("ece") for k, _ in LAYA]
le = [x for x in le if x is not None]
L.append("| %s | %s | %s |" % (s, " | ".join(fmt(((nir[k] or {}).get("suites", {}).get(s) or {}).get("ece")) for k, _ in NIR),
fmt(min(le) if le else None)))
L.append("")
# ------------------------------------------------------------- robustness
flip = lambda d: [v.get("order_flip_rate") for v in (d or {}).get("suites", {}).values() if v.get("order_flip_rate") is not None]
avg = lambda xs: (sum(xs) / len(xs)) if xs else None
cols = [l for _, l in NIR] + [l for _, l in LAYA]
nm = " | ".join("not measured" for _ in LAYA)
L += ["## Robustness and determinism", "", "| | " + " | ".join(cols) + " |", "|---|" + "---|" * len(cols)]
L.append("| option-order flip rate (choice tasks, mean) | %s | %s |" % (
" | ".join(fmt(avg(flip(nir[k]))) for k, _ in NIR), " | ".join(fmt(avg(flip(lay[k]))) for k, _ in LAYA)))
L.append("| identical answers on repeat call | %s | %s |" % (
" | ".join(fmt((nir[k] or {}).get("determinism_repeat_identical")) for k, _ in NIR),
" | ".join(fmt((lay[k] or {}).get("determinism_repeat_identical")) for k, _ in LAYA)))
dets = [jload("nirnay_determinism_%s.json" % MODELS[k][1]) for k, _ in NIR]
L.append("| byte-identical answers across 1/4/8/16 threads | %s | %s |" % (
" | ".join(fmt((d or {}).get("identical_rate")) for d in dets), nm))
L.append("| decisions changed across 1/4/8/16 threads | %s | %s |" % (
" | ".join(("%s (max prob. diff %s)" % (d.get("answer_changes"), d.get("max_probability_diff"))) if d else "—"
for d in dets), nm))
cs = jload("cold_start.json") or {}
L.append("| cold start: new process -> first answer (s) | %s | %s |" % (
" | ".join(fmt((cs.get("nirnay:" + k) or {}).get("first_answer_s"), 1) for k, _ in NIR),
" | ".join(fmt((cs.get("laya:" + k) or {}).get("first_answer_s"), 1) for k, _ in LAYA)))
L.append("")
# ------------------------------------------------------------- latency
ll, lo = jload("laya_latency.json"), jload("laya_latency_onnx.json")
if ll and lo: # Laya's best of eager torch (8 / 16 threads) and its own ONNX Runtime backend
for mname, ent in lo.get("models", {}).items():
ll["models"].setdefault(mname, {"by_threads": {}})["by_threads"]["onnx"] = ent["by_threads"].get("0", {})
nl = {k: jload("nirnay_latency_%s.json" % MODELS[k][1]) for k, _ in NIR}
L += ["## Latency on this CPU (Laya's own workload: a support ticket, mixed choice / yes-no questions)", "",
"p50 ms, warm, best setting for each model (Laya: eager torch at 8/16 threads or its ONNX Runtime backend).", "",
"| questions per call | " + " | ".join(l for _, l in NIR) + " | " + " | ".join(l for _, l in LAYA) +
" | " + " | ".join("%s speed-up vs fastest Laya" % l for _, l in NIR) + " |",
"|---|" + "---|" * (2 * len(NIR) + len(LAYA))]
for n in (1, 5, 10, 50):
key = "%d_questions" % n
def best_l(m):
bt = ((ll or {}).get("models", {}).get(m) or {}).get("by_threads", {})
xs = [v[key]["p50_ms"] for v in bt.values() if key in v]
return min(xs) if xs else None
def best_n(d):
bt = (d or {}).get("by_threads", {})
xs = [v[key]["warm"]["p50_ms"] for v in bt.values() if key in v]
return min(xs) if xs else None
ls = [best_l(m) for m, _ in LAYA]
fastest = min([x for x in ls if x], default=None)
ns = [best_n(nl[k]) for k, _ in NIR]
L.append("| %d | %s | %s | %s |" % (
n, " | ".join(("**%s**" % fmt(x, 1)) if k == head else fmt(x, 1) for x, (k, _) in zip(ns, NIR)),
" | ".join(fmt(x, 1) for x in ls),
" | ".join(("%.1fx" % (fastest / x)) if (fastest and x) else "—" for x in ns)))
L.append("")
L.append("Batch throughput (10 questions per state): " + ", ".join(
"%s %s decisions/s" % (lab, (nl[k] or {}).get("throughput_10q", {}).get("decisions_per_s")) for k, lab in NIR) + ".")
L.append("")
disk = lambda name: round(sum(os.path.getsize(os.path.join(ROOT, "models", name, f))
for f in os.listdir(os.path.join(ROOT, "models", name))) / 2**20) \
if os.path.isdir(os.path.join(ROOT, "models", name)) else None
L += ["## Footprint", "", "| | " + " | ".join(cols) + " |", "|---|" + "---|" * len(cols),
"| weights on disk (MB) | %s | 804 | 614 | 804 |" % " | ".join(str(disk(k)) for k, _ in NIR),
"| RAM added by loading (MB) | %s | %s |" % (
" | ".join(fmt((nl[k] or {}).get("rss_delta_mb"), 0) for k, _ in NIR),
" | ".join(fmt(((ll or {}).get("models", {}).get(m) or {}).get("rss_delta_mb"), 0) for m, _ in LAYA)), ""]
# ------------------------------------------------------------- few-shot
L += ["## Few-shot adaptation (`learn()`, no training, CPU)", "",
"Labelled examples per label from the TRAIN split; evaluated on the same 400 TEST cases. Laya cannot do this "
"without RL fine-tuning on a GPU; its zero-shot score is the reference.", "",
"| model / task | k = 0 | 2 | 4 | 8 | 16 | learn() time at 16 | Laya (best ckpt) | Jev (published) |",
"|---|---|---|---|---|---|---|---|---|"]
for k, lab in NIR:
fs = jload("fewshot_%s.json" % MODELS[k][1]) or {}
for task, suite in (("banking77", "jev.banking77_full"), ("emotion", "jev.emotion")):
r = fs.get(task) or {}
if not r:
continue
ls = [acc(lay[x], suite) for x, _ in LAYA]
L.append("| %s %s | %s | %s s | %s | %s |" % (
lab, task, " | ".join(fmt((r.get(str(q)) or {}).get("accuracy")) for q in (0, 2, 4, 8, 16)),
fmt((r.get("16") or {}).get("learn_seconds"), 0), fmt(max([x for x in ls if x is not None], default=None)),
fmt(JEV.get(suite))))
L.append("")
# ------------------------------------------------------------- multilingual
L += multilingual_section([k for k in want if k in MODELS])
# ------------------------------------------------------------- ablations
ab = [("nirnay-bges-b2", "v1-small: frozen encoder + 2 fusion blocks"),
("nirnay-v1", "v1: deep fusion (bge-small)"),
("nirnay-bges-deep-debias", "v1 + opt-in debias"),
("nirnay-v2", "v2: deep fusion (bge-base, more data)"),
("nirnay-v3", "v3: v2 + routing, short messages, safety, urgency, phrasings"),
("nirnay-v31", "v3.1: v3 with content-only safety wording")]
abd = [(lbl, jload("suites__nirnay__%s.json" % n)) for n, lbl in ab]
abd = [(lbl, d) for lbl, d in abd if d]
L += ["## Ablations (same suites)", "", "| task | " + " | ".join(l for l, _ in abd) + " |", "|---|" + "---|" * len(abd)]
for s in ORDER:
L.append("| %s | %s |" % (s, " | ".join(fmt(acc(d, s)) for _, d in abd)))
L.append("")
open(OUT, "w", encoding="utf-8").write("\n".join(L) + "\n")
print("wrote", OUT)
if __name__ == "__main__":
main()
|