Spaces:
Paused
Paused
| import json | |
| import os | |
| from pathlib import Path | |
| from typing import Dict, Iterable, List, Tuple | |
| def _read_jsonl(path: Path) -> List[Dict[str, str]]: | |
| rows: List[Dict[str, str]] = [] | |
| with path.open("r", encoding="utf-8") as f: | |
| for line in f: | |
| line = line.strip() | |
| if not line: | |
| continue | |
| rows.append(json.loads(line)) | |
| return rows | |
| def _normalize(s: str) -> str: | |
| return " ".join((s or "").strip().lower().split()) | |
| def _token_f1(pred: str, gold: str) -> float: | |
| """ | |
| Lightweight similarity: token-level F1 (good enough for Buffett-style free text). | |
| """ | |
| p = _normalize(pred).split() | |
| g = _normalize(gold).split() | |
| if not p and not g: | |
| return 1.0 | |
| if not p or not g: | |
| return 0.0 | |
| from collections import Counter | |
| pc = Counter(p) | |
| gc = Counter(g) | |
| common = pc & gc | |
| tp = sum(common.values()) | |
| precision = tp / max(1, len(p)) | |
| recall = tp / max(1, len(g)) | |
| if precision + recall == 0: | |
| return 0.0 | |
| return 2 * precision * recall / (precision + recall) | |
| def _exact_match(pred: str, gold: str) -> float: | |
| return 1.0 if _normalize(pred) == _normalize(gold) else 0.0 | |
| def _iter_examples(rows: List[Dict[str, str]]) -> Iterable[Tuple[str, str]]: | |
| for obj in rows: | |
| # each line is a single-key dict: {prompt: answer} | |
| if not obj: | |
| continue | |
| [(prompt, answer)] = list(obj.items()) | |
| yield prompt, answer | |
| def main(): | |
| """ | |
| Evaluate adapter vs base on the bundled example sets. | |
| Requirements: | |
| - GPU recommended | |
| - HF_TOKEN set if BASE is gated (Llama 3.1) | |
| Usage: | |
| HF_TOKEN=... python scripts/eval_demo.py | |
| """ | |
| # Import after env is set. | |
| import extract | |
| root = Path(__file__).resolve().parents[1] | |
| exdir = root / "example_data" | |
| # Map example file -> adapter key (matches app.py hidden "Model" input) | |
| suites = [ | |
| ("buffett_example.jsonl", "buffett", _token_f1), | |
| ("buffett_ma_example.jsonl", "buffett_ma", _token_f1), | |
| ("ner_example.jsonl", "ner", _exact_match), | |
| ("xbrl_term_example.jsonl", "xbrl_term", _exact_match), | |
| ] | |
| # Allow small/quick eval | |
| limit = int(os.getenv("EVAL_LIMIT", "25")) | |
| print(f"Base model: {extract.BASE_MODEL_ID}") | |
| print(f"Limit per suite: {limit}") | |
| print("") | |
| for filename, adapter_key, metric in suites: | |
| path = exdir / filename | |
| if not path.exists(): | |
| print(f"[skip] missing {filename}") | |
| continue | |
| rows = _read_jsonl(path) | |
| examples = list(_iter_examples(rows))[:limit] | |
| base_scores: List[float] = [] | |
| ft_scores: List[float] = [] | |
| for prompt, gold in examples: | |
| base = extract._generate(prompt, adapter_key=None, max_new_tokens=220) | |
| ft = extract._generate(prompt, adapter_key=adapter_key, max_new_tokens=220) | |
| base_scores.append(metric(base, gold)) | |
| ft_scores.append(metric(ft, gold)) | |
| base_avg = sum(base_scores) / max(1, len(base_scores)) | |
| ft_avg = sum(ft_scores) / max(1, len(ft_scores)) | |
| print(f"{filename} (adapter={adapter_key})") | |
| print(f" base avg: {base_avg:.3f}") | |
| print(f" ft avg: {ft_avg:.3f}") | |
| print(f" delta : {ft_avg - base_avg:+.3f}") | |
| print("") | |
| if __name__ == "__main__": | |
| main() | |