Download code/eval_llm_agg_fewshot.py from TabuLM-Research/tabulm: direct link, hf CLI and curl.
- Browser
- Download file 5.41 kB
-
https://huggingface.co/TabuLM-Research/tabulm/resolve/main/code/eval_llm_agg_fewshot.py
- Command line
-
hf download hf://TabuLM-Research/tabulm/code/eval_llm_agg_fewshot.py
-
curl -L -o eval_llm_agg_fewshot.py https://huggingface.co/TabuLM-Research/tabulm/resolve/main/code/eval_llm_agg_fewshot.py
5.41 kB
| #!/usr/bin/env python3 | |
| """3-shot GPT-4o experiment on aggregation items only. | |
| Tests whether few-shot prompting closes the aggregation gap vs TabuLM. | |
| Usage: | |
| python eval_llm_agg_fewshot.py --api-key sk-... | |
| """ | |
| import argparse | |
| import csv | |
| import json | |
| import os | |
| import random | |
| import sys | |
| import time | |
| from typing import List, Optional | |
| import requests | |
| DATA_DIR = '/shared/scratch/0/tmp/v_ireddi_rakshitha_results/tabulm/data' | |
| CODE_DIR = '/shared/scratch/0/tmp/v_ireddi_rakshitha_results/tabulm/code' | |
| TABQA_FILE = os.path.join(DATA_DIR, 'tabqa_kin.json') | |
| CSV_DIR = os.path.join(DATA_DIR, 'tables') | |
| OPENAI_URL = "https://api.openai.com/v1/chat/completions" | |
| sys.path.insert(0, CODE_DIR) | |
| from tabular_serializer import serialize_csv, TableCell | |
| from eval_llm_baseline import csv_to_markdown, find_gold_cell, em_match | |
| FEW_SHOT_EXAMPLES = [ | |
| { | |
| "table": "| Inzego | Umubare_w_Abaturage | Umubare_w_Inzu |\n|---|---|---|\n| Gasabo | 542000 | 89000 |\n| Kicukiro | 312000 | 54000 |\n| Nyarugenge | 274000 | 48000 |", | |
| "question": "Ni iki gifite Umubare_w_Abaturage nyinshi?", | |
| "answer": "Gasabo", | |
| "explanation": "Gasabo has the highest Umubare_w_Abaturage (542000).", | |
| }, | |
| { | |
| "table": "| Igihingwa | Umusaruro_Toni | Akarere |\n|---|---|---|\n| Ibigori | 45200 | Musanze |\n| Ibishyimbo | 12300 | Musanze |\n| Ibirayi | 89000 | Musanze |", | |
| "question": "Ni iki gifite Umusaruro_Toni nke cyane?", | |
| "answer": "Ibishyimbo", | |
| "explanation": "Ibishyimbo has the lowest Umusaruro_Toni (12300).", | |
| }, | |
| { | |
| "table": "| Umwaka | Umusaruro_Miliyari_Frw | Impinduka_% |\n|---|---|---|\n| 2020 | 1100.2 | +3.1 |\n| 2021 | 1189.5 | +8.1 |\n| 2022 | 1209.3 | +1.7 |", | |
| "question": "Ni iki gifite Impinduka_% ntoya muri bose?", | |
| "answer": "2022", | |
| "explanation": "2022 has the lowest Impinduka_% (+1.7).", | |
| }, | |
| ] | |
| def make_fewshot_prompt(table_md: str, question: str) -> str: | |
| examples = "" | |
| for ex in FEW_SHOT_EXAMPLES: | |
| examples += ( | |
| f"Table:\n{ex['table']}\n\n" | |
| f"Question: {ex['question']}\n" | |
| f"Answer: {ex['answer']}\n\n" | |
| ) | |
| return ( | |
| f"You are answering questions about data tables written in Kinyarwanda.\n" | |
| f"The answer is always the NAME of an entity from the first column — NOT a number.\n" | |
| f"Identify which entity has the highest or lowest value in the relevant column.\n\n" | |
| f"Examples:\n\n{examples}" | |
| f"Now answer the following:\n\n" | |
| f"Table:\n{table_md}\n\n" | |
| f"Question: {question}\n" | |
| f"Answer:" | |
| ) | |
| def call_openai(prompt: str, api_key: str, retries: int = 3) -> Optional[str]: | |
| for attempt in range(retries): | |
| try: | |
| headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} | |
| payload = {"model": "gpt-4o", "max_tokens": 64, "temperature": 0, | |
| "messages": [{"role": "user", "content": prompt}]} | |
| r = requests.post(OPENAI_URL, headers=headers, json=payload, timeout=30) | |
| r.raise_for_status() | |
| return r.json()["choices"][0]["message"]["content"].strip() | |
| except Exception as e: | |
| if attempt < retries - 1: | |
| time.sleep(2 ** attempt) | |
| else: | |
| print(f" [API error] {e}") | |
| return None | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument('--api-key', required=True) | |
| parser.add_argument('--out', default=os.path.join(DATA_DIR, 'llm_agg_fewshot_results.json')) | |
| args = parser.parse_args() | |
| random.seed(42) | |
| with open(TABQA_FILE) as f: | |
| all_items = json.load(f) | |
| random.shuffle(all_items) | |
| dev_items = [x for x in all_items[int(0.8 * len(all_items)):] | |
| if x.get('answer_type') == 'aggregation'] | |
| print(f'[FewShot-Agg] {len(dev_items)} aggregation dev items') | |
| correct, total, skipped = 0, 0, 0 | |
| records = [] | |
| for i, item in enumerate(dev_items): | |
| csv_path = os.path.join(CSV_DIR, item['table_file']) | |
| if not os.path.exists(csv_path): | |
| skipped += 1; continue | |
| cells = serialize_csv(csv_path) | |
| if not cells: | |
| skipped += 1; continue | |
| gold_rc = find_gold_cell(cells, item['answer'], item['question']) | |
| if gold_rc is None: | |
| skipped += 1; continue | |
| table_md = csv_to_markdown(csv_path) | |
| if not table_md: | |
| skipped += 1; continue | |
| prompt = make_fewshot_prompt(table_md, item['question']) | |
| pred = call_openai(prompt, args.api_key) | |
| if pred is None: | |
| skipped += 1; continue | |
| hit = int(em_match(pred, item['answer'])) | |
| correct += hit; total += 1 | |
| records.append({'question': item['question'], 'gold': item['answer'], | |
| 'pred': pred, 'hit': hit}) | |
| print(f' [{i+1}/{len(dev_items)}] gold={item["answer"]!r:20s} pred={pred!r:20s} hit={hit}') | |
| em = correct / total if total else 0.0 | |
| print(f'\n[FewShot-Agg] EM = {em:.4f} ({correct}/{total}, {skipped} skipped)') | |
| with open(args.out, 'w') as f: | |
| json.dump({'em': round(em, 4), 'correct': correct, 'total': total, | |
| 'skipped': skipped, 'records': records}, f, indent=2, ensure_ascii=False) | |
| print(f'[FewShot-Agg] Saved to {args.out}') | |
| if __name__ == '__main__': | |
| main() | |