Instructions to use xju-arlab/vbench-model with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use xju-arlab/vbench-model with PEFT:
Task type is invalid.
- Notebooks
- Google Colab
- Kaggle
Download code/vbench_prompts_compile/scripts/annotate_gold.py from xju-arlab/vbench-model: direct link, hf CLI and curl.
- Browser
- Download file 9.78 kB
-
https://huggingface.co/xju-arlab/vbench-model/resolve/main/code/vbench_prompts_compile/scripts/annotate_gold.py
- Command line
-
hf download hf://xju-arlab/vbench-model/code/vbench_prompts_compile/scripts/annotate_gold.py
-
curl -L -o annotate_gold.py https://huggingface.co/xju-arlab/vbench-model/resolve/main/code/vbench_prompts_compile/scripts/annotate_gold.py
9.78 kB
| #!/usr/bin/env python3 | |
| """Annotate prompts with DeepSeek (two passes + arbitration) into LLM-labelled gold sets. | |
| Scope of this entry point: text-only tasks (spatial, action, objects). Scene items | |
| need frames and real captions and are produced by the caption/vision pipeline. | |
| Every provider call is charged to ``output/annotation/BUDGET-annotate-<name>.json`` | |
| before it is sent; ``--budget`` is a hard cap for the run. | |
| Usage:: | |
| # MovieGen long-prompt pilot (decision B1: existing long prompts first) | |
| uv run --no-sync python scripts/annotate_gold.py --name longprompt-pilot \\ | |
| --source moviegen --buckets 50-99,100-200 --budget 300 | |
| # full run over a bucket mix | |
| uv run --no-sync python scripts/annotate_gold.py --name gold-v1 \\ | |
| --source moviegen+seed --buckets 1-14,15-49,50-99,100-200 --limit-per-task 120 --budget 4000 | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| from collections import Counter, defaultdict | |
| import json | |
| from pathlib import Path | |
| import sys | |
| from typing import Any, Iterable, Sequence | |
| ROOT = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(ROOT / "src")) | |
| from vbench_prompts_compile import annotate as A # noqa: E402 | |
| from vbench_prompts_compile import records as R # noqa: E402 | |
| from vbench_prompts_compile import sources as S # noqa: E402 | |
| from vbench_prompts_compile.llm import ChatClient, RequestBudget, load_tokens # noqa: E402 | |
| def build_moviegen_items(tasks: Sequence[str], buckets: Sequence[str], limit_per_task: int | None, seed: int) -> list[A.Item]: | |
| import random | |
| refs = [ref for ref in S.load_moviegen_prompts() if R.length_bucket(ref.word_count) in set(buckets)] | |
| rng = random.Random(seed) | |
| rng.shuffle(refs) | |
| items: list[A.Item] = [] | |
| for task in tasks: | |
| for index, ref in enumerate(refs): | |
| if limit_per_task is not None and index >= limit_per_task: | |
| break | |
| items.append( | |
| A.Item( | |
| item_id=f"{task}-moviegen-{ref.sha256[:12]}", | |
| task=task, | |
| prompt=ref.text, | |
| group_id=f"moviegen:{ref.sha256[:12]}", | |
| bucket=R.length_bucket(ref.word_count), | |
| source="moviegen", | |
| source_id=f"{ref.source_file}:{ref.line}", | |
| meta={"word_count": ref.word_count, "licence": "CC-BY-NC-4.0"}, | |
| ) | |
| ) | |
| return items | |
| def build_seed_items(tasks: Sequence[str], buckets: Sequence[str], limit_per_task: int | None, seed: int) -> list[A.Item]: | |
| """Seeds from the existing weak corpora (VG templates, K400 templates, fixtures).""" | |
| import random | |
| rng = random.Random(seed + 7) | |
| pools: dict[str, list[A.Item]] = defaultdict(list) | |
| vg = [row for row in R.read_jsonl(ROOT / "data" / "processed" / "formal-0001" / "candidates" / "spatial.jsonl")] | |
| rng.shuffle(vg) | |
| for row in vg[: (limit_per_task or 200)]: | |
| for task in ("spatial", "objects"): | |
| if task not in tasks or task not in ("spatial", "objects"): | |
| continue | |
| prompt = row["input"]["prompt"] | |
| if R.length_bucket(R.word_count(prompt)) not in set(buckets): | |
| continue | |
| pools[task].append( | |
| A.Item( | |
| item_id=f"{task}-vg-{row['source_id']}", | |
| task=task, | |
| prompt=prompt, | |
| group_id=row["group_id"], | |
| bucket=R.length_bucket(R.word_count(prompt)), | |
| source="visual_genome_prompt", | |
| source_id=row["source_id"], | |
| meta={"licence": "CC-BY-4.0"}, | |
| ) | |
| ) | |
| k400 = R.read_jsonl(ROOT / "data" / "processed" / "formal-0001" / "candidates" / "action.jsonl") | |
| rng.shuffle(k400) | |
| for row in k400[: (limit_per_task or 200)]: | |
| if "action" not in tasks: | |
| continue | |
| prompt = row["input"]["prompt"] | |
| if R.length_bucket(R.word_count(prompt)) not in set(buckets): | |
| continue | |
| pools["action"].append( | |
| A.Item( | |
| item_id=f"action-k400-{row['source_id']}", | |
| task="action", | |
| prompt=prompt, | |
| group_id=row["group_id"], | |
| bucket=R.length_bucket(R.word_count(prompt)), | |
| source="k400_prompt", | |
| source_id=row["source_id"], | |
| ) | |
| ) | |
| items: list[A.Item] = [] | |
| for task in tasks: | |
| items.extend(pools.get(task, [])[: (limit_per_task or len(pools.get(task, [])))]) | |
| return items | |
| def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: | |
| parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) | |
| parser.add_argument("--name", required=True) | |
| parser.add_argument("--tasks", default="spatial,action,objects") | |
| parser.add_argument("--source", default="moviegen", help="moviegen | seed | moviegen+seed") | |
| parser.add_argument("--buckets", default="1-14,15-49,50-99,100-200") | |
| parser.add_argument("--limit-per-task", type=int, default=None) | |
| parser.add_argument("--budget", type=int, default=200, help="hard cap of provider requests for this run") | |
| parser.add_argument("--provider", default="deepseek") | |
| parser.add_argument("--model", default=None) | |
| parser.add_argument("--token-file", default=str(ROOT / "token.txt")) | |
| parser.add_argument("--seed", type=int, default=20260919) | |
| parser.add_argument("--max-tokens", type=int, default=384) | |
| parser.add_argument("--dry-run", action="store_true") | |
| return parser.parse_args(argv) | |
| def main(argv: Sequence[str] | None = None) -> int: | |
| args = parse_args(argv) | |
| tasks = [t.strip() for t in args.tasks.split(",") if t.strip()] | |
| buckets = [b.strip() for b in args.buckets.split(",") if b.strip()] | |
| items: list[A.Item] = [] | |
| if "moviegen" in args.source: | |
| items += build_moviegen_items(tasks, buckets, args.limit_per_task, args.seed) | |
| if "seed" in args.source: | |
| items += build_seed_items(tasks, buckets, args.limit_per_task, args.seed) | |
| if not items: | |
| print("no items selected", file=sys.stderr) | |
| return 2 | |
| out_dir = ROOT / "data" / "gold" / args.name | |
| out_dir.mkdir(parents=True, exist_ok=True) | |
| R.write_jsonl(out_dir / "items.jsonl", [item.as_dict() for item in items]) | |
| print(json.dumps({"items": len(items), "by_task": dict(Counter(i.task for i in items)), "by_bucket": dict(Counter(i.bucket for i in items))}, ensure_ascii=False)) | |
| if args.dry_run: | |
| return 0 | |
| tokens = load_tokens(args.token_file) | |
| client = ChatClient(provider=args.provider, api_key=tokens.get(args.provider), model=args.model) | |
| budget = RequestBudget(ROOT / "output" / "annotation", f"annotate-{args.name}", authorized_total=args.budget) | |
| vocab = S.load_k400() | |
| resolver = vocab.resolve | |
| gold: list[dict[str, Any]] = [] | |
| quarantine: list[dict[str, Any]] = [] | |
| reports: list[dict[str, Any]] = [] | |
| for item in items: | |
| if budget.remaining() <= 0: | |
| print("budget exhausted; stopping", file=sys.stderr) | |
| break | |
| result = A.annotate_item( | |
| client, | |
| item, | |
| action_resolver=resolver, | |
| max_tokens=args.max_tokens, | |
| charge=lambda entry: budget.charge(entry, provider=args.provider), | |
| ) | |
| entry = { | |
| "item_id": item.item_id, | |
| "task": item.task, | |
| "bucket": item.bucket, | |
| "status": result.status, | |
| "agreement": result.agreement, | |
| "errors": result.errors, | |
| "target": result.target, | |
| "usage": result.usage, | |
| } | |
| reports.append(entry) | |
| if result.target is not None: | |
| gold.append(R.add_length_meta(A.result_to_record(result, item, action_resolver=resolver))) | |
| else: | |
| quarantine.append({**entry, "prompt": item.prompt, "passes": result.passes}) | |
| budget.append({"kind": "result", "item_id": item.item_id, "status": result.status, "agreement": result.agreement, "errors": result.errors}) | |
| R.write_jsonl(out_dir / "gold.jsonl", gold) | |
| R.write_jsonl(out_dir / "quarantine.jsonl", quarantine) | |
| status_counts = Counter(entry["status"] for entry in reports) | |
| report = { | |
| "name": args.name, | |
| "source": args.source, | |
| "buckets": buckets, | |
| "tasks": tasks, | |
| "items": len(items), | |
| "processed": len(reports), | |
| "status_counts": dict(status_counts), | |
| "agreement_rate": (sum(1 for entry in reports if entry["agreement"]) / len(reports)) if reports else 0.0, | |
| "by_task": {task: dict(Counter(entry["status"] for entry in reports if entry["task"] == task)) for task in tasks}, | |
| "by_bucket": {bucket: dict(Counter(entry["status"] for entry in reports if entry["bucket"] == bucket)) for bucket in buckets}, | |
| "error_counts": dict(Counter(error for entry in reports for error in entry["errors"])), | |
| "target_kinds": { | |
| "action_other": sum(1 for record in gold if record["task"] == "action" and R.ACTION_OTHER in record["target"]["actions"]), | |
| "empty_results": sum(1 for record in gold if isinstance(record["target"], dict) and any(isinstance(v, list) and not v for v in record["target"].values())), | |
| }, | |
| "budget": budget.summary(), | |
| "quality": A.ANNOTATION_QUALITY, | |
| "note": "LLM labels (two passes + arbitration), not human gold", | |
| } | |
| (out_dir / "report.json").write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") | |
| print(json.dumps(report, ensure_ascii=False, indent=2)) | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |