gagan1985's picture
Upload folder using huggingface_hub
20d532b verified
Raw
History Blame Contribute Delete
4.1 kB
"""Evaluate the fine-tuned TinyBERT model on a held-out test set.
Standalone script — only needs `transformers`+`torch`. Expects a JSONL file
where each line is `{"messages": [system, user, assistant]}` (same chat
format used by the training data on GitHub), with the assistant content
being the gold JSON string. See:
https://github.com/innerkorehq/indian-address-parser
Usage:
python evaluate_tinybert.py test.jsonl
python evaluate_tinybert.py test.jsonl --n 100
"""
from __future__ import annotations
import argparse
import json
from collections import defaultdict
from inference_tinybert import ALL_FIELDS, extract_fields, load_model, MAX_LENGTH
def predict(model, tokenizer, raw_address: str) -> dict:
import torch
enc = tokenizer(
raw_address, return_tensors="pt", return_offsets_mapping=True,
truncation=True, max_length=MAX_LENGTH,
)
offsets = enc.pop("offset_mapping")[0].tolist()
with torch.no_grad():
out = model(**enc)
pred_ids = out.logits[0].argmax(-1).tolist()
return extract_fields(raw_address, offsets, pred_ids)
def compute_metrics(pairs: list[tuple[dict, dict]]) -> dict:
n_total = len(pairs)
field_correct = defaultdict(int)
field_present_gold = defaultdict(int)
field_recalled = defaultdict(int)
overall_exact = 0
for gold, pred in pairs:
all_match = True
for field in ALL_FIELDS:
g = (gold.get(field) or "").strip().lower()
p = (pred.get(field) or "").strip().lower()
if g:
field_present_gold[field] += 1
if g == p:
field_recalled[field] += 1
else:
all_match = False
if g == p:
field_correct[field] += 1
if all_match:
overall_exact += 1
accs = {f: (field_correct[f] / n_total if n_total else 0) for f in ALL_FIELDS}
return {
"n_total": n_total,
"overall_exact": overall_exact,
"field_correct": field_correct,
"field_present_gold": field_present_gold,
"field_recalled": field_recalled,
"field_accuracy": accs,
"mean_field_accuracy": sum(accs.values()) / len(accs),
}
def print_report(m: dict):
n_total = m["n_total"]
print(f"\nResults")
print(f"Samples evaluated: {n_total}")
print(f"Overall exact match (all present fields): {m['overall_exact']}/{n_total} ({100*m['overall_exact']/max(1,n_total):.1f}%)\n")
print(f"{'Field':20s} {'Accuracy':>10s} {'Recall':>10s} {'Gold presence':>14s}")
print("-" * 60)
for field in ALL_FIELDS:
acc = m["field_accuracy"][field]
n_gold = m["field_present_gold"][field]
rec = m["field_recalled"][field] / n_gold if n_gold else float("nan")
pres = n_gold / n_total if n_total else 0
rec_str = f"{100*rec:.1f}%" if n_gold else " n/a"
print(f"{field:20s} {100*acc:>9.1f}% {rec_str:>10s} {100*pres:>13.1f}%")
print(f"\n{'Mean field accuracy':20s} {100*m['mean_field_accuracy']:>9.1f}%")
def main():
p = argparse.ArgumentParser()
p.add_argument("test_file", help="JSONL file with {'messages': [system, user, assistant]} per line")
p.add_argument("--model", default=".", help="Model dir or HF repo id (default: current directory)")
p.add_argument("--n", type=int, default=None)
args = p.parse_args()
with open(args.test_file, encoding="utf-8") as f:
samples = [json.loads(line) for line in f]
if args.n:
samples = samples[: args.n]
model, tokenizer = load_model(args.model)
pairs = []
for i, sample in enumerate(samples):
msgs = sample["messages"]
raw_address = msgs[1]["content"].replace("Parse this address:\n", "", 1)
gold = json.loads(msgs[2]["content"])
pred = predict(model, tokenizer, raw_address)
pairs.append((gold, pred))
if (i + 1) % 25 == 0:
print(f" ...{i + 1}/{len(samples)} evaluated")
print_report(compute_metrics(pairs))
if __name__ == "__main__":
main()