vela-3.0-coreml / conversion /verify_release.py
alexwengg's picture
Vela 3.0 (vela3-fast) Core ML: decision backbone + span_sets adapter, runtime, verification
deaf348 verified
Raw History Blame Contribute Delete
3.39 kB
"""Check release/vela3_coreml.py (fresh import, release folder only) against the fp32 torch release code:
decisions on typed-decisions TEST (choice + noul, data/ref.jsonl), PII spans on the 25 hand-written demo sections, and
unsupported-claim spans on a few context / answer pairs. Also times each Core ML function on the GPU."""
import json, sys, time
import numpy as np
import pyarrow.parquet as pq
sys.path.insert(0, "release")
from vela3_coreml import Vela3CoreML # noqa: E402
m = Vela3CoreML("release")
# 1) decisions
ref = {x["id"]: x for x in map(json.loads, open("data/ref.jsonl"))}
nq = flips = 0; dmax = 0.0; margins = []
for r in pq.read_table("data/typed/all/test-00000-of-00001.parquet").to_pylist():
R = ref[r["id"]]; state, qs = json.loads(r["state"]), json.loads(r["questions"])
qs = {k: q for k, q in qs.items() if q["type"] in ("choice", "noul")}
for k, an in m.decide(state, qs).items():
rb = R["answers"][k]; nq += 1
if an["type"] == "noul":
dmax = max(dmax, abs(an["noul"] - rb["noul"])); flips += (an["noul"] >= 0.5) != (rb["noul"] >= 0.5)
else:
pr = rb["probabilities"]; dmax = max(dmax, max(abs(an["probabilities"][o] - pr[o]) for o in pr)); flips += an["choice"] != rb["choice"]
print(f"decisions: {nq} questions, {flips} flips vs fp32 torch, max |dp| {dmax:.4f}")
# 2) + 3) spans, torch reference computed fresh
sys.path.insert(0, "demo")
from doc import RAW, TAG # noqa: E402
sections = [TAG.sub(r"\2", l) for l in RAW.strip().splitlines() if l.strip() and not l.startswith("#")]
sys.path.insert(0, "."); from span_engine import PIIEngine # noqa: E402
t = PIIEngine("torch").v
key = lambda ss: {(s["label"], s["start"], s["end"]) for s in ss}
pii_diff = [i for i, s in enumerate(sections) if key(m.pii(s)) != key(t.span_answer(s, {"type": "span", "criteria": json.load(open("src/vela3_config.json"))["span"]["router_label_sets"]["pii"]})[0]["spans"])]
n_spans = sum(len(m.pii(s)) for s in sections)
print(f"pii: {len(sections)} sections, {n_spans} spans, sections whose span set differs from torch: {pii_diff}")
pairs = [("Adults may take up to 4 g of paracetamol in 24 hours.", "Adults may take up to 6 g of paracetamol in 24 hours."),
("The Eiffel Tower is 330 m tall and was finished in 1889.", "The Eiffel Tower, finished in 1899, is 330 m tall."),
("Refunds are issued within 5 business days to the original card.", "Refunds are issued within 5 business days to the original card."),
("The meeting moved from Tuesday to Thursday at 3 pm in room B.", "The meeting is now on Thursday at 4 pm in room C.")]
for c, a in pairs:
got = [s["text"] for s in m.unsupported(c, a)]
exp = [s["text"] for s in t.span_answer({"context": c, "answer": a}, {"type": "span", "criteria": ["unsupported"]})[0]["spans"]]
print(f"unsupported: coreml {got} | torch {exp} | {'same' if got == exp else 'DIFF'}")
# 4) latency per Core ML function (GPU), median of 20 after warm-up
for name, enc in (("decision", m.dec_enc), ("span_sets", m.span_enc)):
row = {}
for L in enc.lengths:
ids = list(np.random.randint(5, 49000, size=L - 4))
for _ in range(3): enc(ids)
enc.last_ms.clear(); [enc(ids) for _ in range(20)]
row[f"L{L}"] = round(float(np.median([ms for _, ms in enc.last_ms])), 1)
print(f"latency {name} (ms, CPU_AND_GPU):", row)