| |
| """Tests for reader evaluation metrics. Pure python, no data, no GPU.""" |
| import sys |
| from pathlib import Path |
| sys.path.insert(0, str(Path(__file__).resolve().parents[1])) |
| from src.evaluate_reader import ece, exact_match, token_f1 |
|
|
| PASS = FAIL = 0 |
| def chk(c, l, d=""): |
| global PASS, FAIL |
| if c: PASS += 1; print(f" ok {l}") |
| else: FAIL += 1; print(f" FAIL {l} {d}") |
|
|
| print("[token F1]") |
| chk(token_f1("30 to 55 mph", "30 to 55 mph") == 1.0, "identical -> 1.0") |
| chk(token_f1("30 to 55 mph.", "30 to 55 mph") == 1.0, "punctuation normalised") |
| chk(token_f1("Eagles fly 30 to 55 mph", "30 to 55 mph") > 0.6, "superset scores high", |
| token_f1("Eagles fly 30 to 55 mph", "30 to 55 mph")) |
| chk(token_f1("blue whales", "30 to 55 mph") == 0.0, "no overlap -> 0.0") |
| chk(token_f1("", "answer") == 0.0, "empty prediction") |
| chk(token_f1("", "") == 1.0, "both empty is trivially equal") |
| f = token_f1("the bald eagle", "bald eagle is the national bird") |
| chk(0 < f < 1, "partial overlap between 0 and 1", f) |
|
|
| print("\n[exact match]") |
| chk(exact_match("1782", "1782.") == 1.0, "punctuation-insensitive") |
| chk(exact_match("THE Bald Eagle", "the bald eagle") == 1.0, "case-insensitive") |
| chk(exact_match("bald eagle", "the bald eagle") == 0.0, "extra token breaks EM") |
|
|
| print("\n[unicode / indic]") |
| chk(token_f1("७३.३ डिग्री", "७३.३ डिग्री") == 1.0, "devanagari identical") |
| chk(token_f1("மணிக்கு 30 மைல்", "30 மைல்") > 0.5, "tamil partial overlap") |
|
|
| print("\n[ECE]") |
| |
| conf = [0.05]*100 + [0.95]*100 |
| corr = [False]*95 + [True]*5 + [False]*5 + [True]*95 |
| chk(ece(conf, corr) < 0.02, "perfect calibration -> ~0", ece(conf, corr)) |
| |
| chk(ece([0.95]*100, [True]*50 + [False]*50) > 0.4, "overconfidence detected", |
| ece([0.95]*100, [True]*50 + [False]*50)) |
| chk(ece([], []) == 0.0, "empty input") |
|
|
| print("\n[unanswerable queries always count as errors]") |
| |
| def is_correct(answerable, f1): return bool(answerable and f1 >= 0.50) |
| chk(is_correct(True, 0.9), "answerable + good span -> correct") |
| chk(not is_correct(True, 0.2), "answerable + bad span -> wrong") |
| chk(not is_correct(False, 1.0), "UNANSWERABLE -> wrong even with a perfect span") |
|
|
| print(f"\n{'='*50}\n {PASS} passed, {FAIL} failed\n{'='*50}") |
| sys.exit(1 if FAIL else 0) |
|
|