summaryrefslogtreecommitdiff
path: root/worldalign/evaluate_prefix.py
diff options
context:
space:
mode:
Diffstat (limited to 'worldalign/evaluate_prefix.py')
-rw-r--r--worldalign/evaluate_prefix.py93
1 files changed, 93 insertions, 0 deletions
diff --git a/worldalign/evaluate_prefix.py b/worldalign/evaluate_prefix.py
new file mode 100644
index 0000000..2d53a51
--- /dev/null
+++ b/worldalign/evaluate_prefix.py
@@ -0,0 +1,93 @@
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+
+import torch
+from transformers import AutoModelForCausalLM, AutoTokenizer
+
+from .common import dtype_for_device, read_json, write_json
+from .evaluate import unigram_f1
+from .io import load_feature_pair, select_rows
+from .models import load_prefix
+
+
+def parse_args() -> argparse.Namespace:
+ p = argparse.ArgumentParser()
+ p.add_argument("--manifest", default="artifacts/manifest.json")
+ p.add_argument("--vision", default="artifacts/vision.pt")
+ p.add_argument("--text", default="artifacts/text.pt")
+ p.add_argument("--prefix", default="artifacts/prefix.pt")
+ p.add_argument("--split", choices=["val", "test"], default="val")
+ p.add_argument("--device", default="cuda:1")
+ p.add_argument("--samples", type=int, default=100)
+ p.add_argument("--max-new-tokens", type=int, default=32)
+ p.add_argument("--output", default="artifacts/prefix_evaluation.json")
+ return p.parse_args()
+
+
+def main() -> None:
+ args = parse_args()
+ manifest = read_json(args.manifest)
+ _, text, _, tlookup = load_feature_pair(args.vision, args.text)
+ rows = manifest[args.split][: args.samples]
+ semantic = select_rows(text["features"], tlookup, rows)
+ caption_lookup = {
+ int(row): caps for row, caps in zip(text["rows"], text["all_captions"])
+ }
+ prefix, prefix_state = load_prefix(args.prefix, args.device)
+ tokenizer = AutoTokenizer.from_pretrained(text["model"])
+ if tokenizer.pad_token_id is None:
+ tokenizer.pad_token = tokenizer.eos_token
+ dtype = dtype_for_device(args.device)
+ lm = AutoModelForCausalLM.from_pretrained(
+ text["model"], torch_dtype=dtype
+ ).to(args.device)
+ lm.eval()
+
+ generated = []
+ with torch.inference_mode():
+ for chunk in semantic.split(16):
+ embeds = prefix(chunk.to(args.device)).to(dtype)
+ attention = torch.ones(
+ embeds.shape[:2], dtype=torch.long, device=args.device
+ )
+ output = lm.generate(
+ inputs_embeds=embeds,
+ attention_mask=attention,
+ max_new_tokens=args.max_new_tokens,
+ do_sample=False,
+ eos_token_id=tokenizer.eos_token_id,
+ pad_token_id=tokenizer.pad_token_id,
+ )
+ generated.extend(tokenizer.batch_decode(output, skip_special_tokens=True))
+
+ records = []
+ for row, caption in zip(rows, generated):
+ references = caption_lookup[int(row)]
+ records.append(
+ {
+ "row": int(row),
+ "generated": caption,
+ "references": references,
+ "unigram_f1": unigram_f1(caption, references),
+ }
+ )
+ result = {
+ "split": args.split,
+ "samples": len(records),
+ "mean_best_reference_unigram_f1": sum(
+ r["unigram_f1"] for r in records
+ )
+ / max(len(records), 1),
+ "examples": records[:25],
+ "training": prefix_state["training"],
+ }
+ Path(args.output).parent.mkdir(parents=True, exist_ok=True)
+ write_json(args.output, result)
+ print(json.dumps(result, indent=2, ensure_ascii=False))
+
+
+if __name__ == "__main__":
+ main()