oracle7-vision / eval /eval_chartqa.py
psiloceyeben's picture
Upload folder using huggingface_hub
271c183 verified
Raw History Blame Contribute Delete
3.34 kB
#!/usr/bin/env python3
"""oracle7-vision on ChartQA (HuggingFaceM4/ChartQA). Development = the VALIDATION split (every rule was written and
tuned there); held-out = the TEST split (2,500 questions: 1,250 human-written, 1,250 machine-generated), scored ONCE.
Metric: ChartQA relaxed accuracy (numbers within 5%, else exact match); abstentions count as wrong. Reports accuracy
overall, per split half, per answering rule, coverage, and accuracy on answered questions.
usage: eval_chartqa.py val|test [limit] [offset]"""
import collections
import io
import json
import os
import sys
import time
import numpy as np
import pyarrow.parquet as pq
from PIL import Image
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(HERE)
sys.path.insert(0, ROOT)
from vision import chartqa as C # noqa: E402
from vision import glyphs as G # noqa: E402
def main():
split = sys.argv[1]
limit = int(sys.argv[2]) if len(sys.argv) > 2 else None
offset = int(sys.argv[3]) if len(sys.argv) > 3 else 0
t = pq.read_table(os.path.join(ROOT, "data", "chartqa_%s.parquet" % split))
d = t.slice(offset).to_pydict()
n = len(d["query"]) if limit is None else min(limit, len(d["query"]))
fonts = G.discover_fonts([os.path.join(ROOT, "fonts")])
store = G.GlyphStore([f for f in fonts if not G.is_heldout_family(f[1])])
cache = {}
right = answered = 0
by_rule, by_half = collections.defaultdict(lambda: [0, 0]), collections.defaultdict(lambda: [0, 0])
samples = []
t0 = time.time()
for i in range(n):
raw = d["image"][i]["bytes"]
key = hash(raw)
if key not in cache:
gray = np.asarray(Image.open(io.BytesIO(raw)).convert("L"))
cache = {key: G.read_image(store, gray)} # questions on one chart are adjacent
ans, rule, ev = C.answer(d["query"][i], cache[key])
ok = C.relaxed_match(ans, d["label"][i])
right += ok
answered += ans is not None
by_rule[rule][0] += ok
by_rule[rule][1] += 1
half = "human" if d["human_or_machine"][i] == 0 else "machine"
by_half[half][0] += ok
by_half[half][1] += 1
if len(samples) < 25:
samples.append({"q": d["query"][i], "gold": d["label"][i], "answer": ans, "rule": rule, "ok": ok})
if (i + 1) % 250 == 0:
print(" %d/%d acc %.3f (%.0fs)" % (i + 1, n, right / (i + 1), time.time() - t0), flush=True)
res = {"split": split, "n": n, "relaxed_accuracy": round(right / n, 4), "coverage": round(answered / n, 4),
"accuracy_on_answered": round(right / max(1, answered), 4),
"by_half": {k: {"acc": round(v[0] / v[1], 4), "n": v[1]} for k, v in by_half.items()},
"by_rule": {k: {"acc": round(v[0] / v[1], 4), "n": v[1]} for k, v in sorted(by_rule.items())},
"seconds": round(time.time() - t0), "samples": samples}
print(json.dumps({k: v for k, v in res.items() if k != "samples"}, indent=1))
os.makedirs(os.path.join(ROOT, "results"), exist_ok=True)
tag = split if (limit is None and offset == 0) else "%s_%d_%d" % (split, offset, n)
json.dump(res, open(os.path.join(ROOT, "results", "chartqa_%s.json" % tag), "w"), indent=1)
if __name__ == "__main__":
main()