Spaces:
Running on Zero
Running on Zero
File size: 10,361 Bytes
8e5e386 6de8374 8e5e386 32c217c 8e5e386 32c217c 8e5e386 6de8374 8e5e386 32c217c 6de8374 32c217c 788dbf2 0e398c2 32c217c 8e5e386 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | """Build examples/examples.json for the demo.
Sources
- HotpotQA / MuSiQue / NQ samples produced by the upstream dataset providers
(`lensvlm.dataset_providers`, 3K-12K token contexts with distractors and
character-level evidence spans), exported to /tmp/exprep/*.json.
- The official `examples/hotpotqa_demo.json` from apple-aiml-research/ml-lensvlm.
- Two public-domain texts from Project Gutenberg: the U.S. Constitution (#5)
and "A Scandal in Bohemia" from The Adventures of Sherlock Holmes (#1661).
Evidence spans for the non-benchmark documents are located by exact phrase
match; the app renders them with `render_pages(..., evidence_spans=...)` to
mark the gold pages at the selected compression.
Usage: python scripts/build_examples.py [candidates|final]
`candidates` writes every candidate; `final` keeps only the ids listed in
examples/selection.json (the candidates that were answered correctly, from
the expanded pages, on the hosted Space).
"""
import json
import os
import re
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PREP = "/tmp/exprep"
OUT = os.path.join(ROOT, "examples", "examples.json")
def sentence_span(text, phrase):
"""(start, end) of the sentence containing `phrase`."""
i = text.index(phrase)
start = max(text.rfind(". ", 0, i) + 2, text.rfind("\n", 0, i) + 1, 0)
end = text.find(". ", i + len(phrase))
end = len(text) if end == -1 else end + 1
return [start, end]
def phrase_span(text, phrase):
i = text.index(phrase)
return [i, i + len(phrase)]
def benchmark(name, idx, compression, label, phrases=None):
"""`phrases` overrides the provider's evidence spans with the sentences
containing them (NQ marks the whole Wikipedia page as evidence)."""
s = json.load(open(os.path.join(PREP, f"{name}_validation.json")))[idx]
spans = ([sentence_span(s["context"], p) for p in phrases] if phrases
else [list(sp) for sp in s["evidence_spans"]])
return dict(
id=s["id"],
label=label,
source={"hotpotqa": "HotpotQA (distractor)", "musique": "MuSiQue",
"nq": "Natural Questions"}[name],
question=s["question"],
context=s["context"],
compression=compression,
answers=list(dict.fromkeys(s["answers"] or [s["answer"]])),
match_all=False,
evidence_spans=spans,
)
def official_demo():
d = json.load(open("/tmp/ml-lensvlm/examples/hotpotqa_demo.json"))
ctx = d["context"]
return dict(
id="hotpotqa_demo",
label="Official demo: Kiss and Tell → government post (HotpotQA, 10×)",
source="HotpotQA (official LensVLM demo sample)",
question=d["question"],
context=ctx,
compression="10x",
answers=d["answers"],
match_all=False,
evidence_spans=[
sentence_span(ctx, "Kiss and Tell is a 1945 American comedy film starring"),
sentence_span(ctx, "also served as Chief of Protocol of the United States"),
],
)
def constitution(question, answers, phrases, compression, label, match_all=False):
raw = open(os.path.join(PREP, "constitution.txt"), encoding="utf-8").read()
ctx = raw[raw.index("THE CONSTITUTION OF THE UNITED STATES OF AMERICA, 1787"):raw.index("*** END")].strip()
return dict(
id="constitution_" + re.sub(r"\W+", "_", answers[0].split("|")[0].lower()),
label=label,
source="U.S. Constitution (Project Gutenberg eBook #5, public domain)",
question=question,
context=ctx,
compression=compression,
answers=answers,
match_all=match_all,
evidence_spans=[phrase_span(ctx, p) for p in phrases],
)
def holmes(question, answers, phrases, compression, label, match_all=False):
lines = open(os.path.join(PREP, "holmes.txt"), encoding="utf-8").read().split("\n")
ctx = "\n".join(lines[54:1135])
ctx = ctx[:ctx.index("II. THE RED-HEADED LEAGUE")].strip()
assert ctx.startswith("I. A SCANDAL IN BOHEMIA") and ctx.endswith("_the_ woman."), ctx[-50:]
return dict(
id="holmes_" + re.sub(r"\W+", "_", answers[0].split("|")[0].lower()),
label=label,
source="A Scandal in Bohemia, A. Conan Doyle (Project Gutenberg eBook #1661, public domain)",
question=question,
context=ctx,
compression=compression,
answers=answers,
match_all=match_all,
evidence_spans=[phrase_span(ctx, p) for p in phrases],
)
def candidates():
return [
official_demo(),
benchmark("hotpotqa", 5, "10x", "HotpotQA: Danny DeVito film scored by Kool Kojak (10×)"),
benchmark("hotpotqa", 8, "10x", "HotpotQA: band named by drawing from a hat (10×)"),
benchmark("hotpotqa", 13, "15x", "HotpotQA: port city north of Lingnan Fine Arts Museum (15×)"),
benchmark("hotpotqa", 2, "15x", "HotpotQA: Art Laboe & Scout Tufankjian's ancestry (15×)"),
benchmark("musique", 0, "10x", "MuSiQue: Thelma's actress in A League of Their Own (10×)"),
benchmark("musique", 7, "15x", "MuSiQue: spouse of The Mighty B's creator (15×)"),
benchmark("musique", 26, "10x", "MuSiQue: mother of Julia Murdock Smith's father (10×)"),
benchmark("nq", 21, "15x", "Natural Questions: who wrote City of New Orleans (15×)",
["is a folk song written by Steve Goodman"]),
benchmark("nq", 17, "15x", "Natural Questions: when UK national service ended (15×)",
["call - ups formally ended on 31 December 1960"]),
constitution(
"Which body has the power to declare war, and who is Commander in Chief of the Army and Navy?",
["Congress", "President"],
["To declare War", "The President shall be Commander in Chief"],
"10x", "U.S. Constitution: war powers vs. Commander in Chief (10×)", match_all=True,
),
constitution(
"Who attested the signatures on the Constitution, and in what role?",
["William Jackson", "Secretary"],
["William Jackson, Secretary"],
"10x", "U.S. Constitution: who attested the signatures (10×)", match_all=True,
),
holmes(
"Under what name did the King of Bohemia first introduce himself to Holmes?",
["Von Kramm"],
["You may address me as the Count Von Kramm"],
"10x", "Sherlock Holmes: the King's alias (full story, 10×)",
),
holmes(
"Where exactly had Irene Adler hidden the photograph in her sitting-room?",
["bell-pull"],
["photograph is in a recess behind a sliding panel just above the right\nbell-pull"],
"10x", "Sherlock Holmes: where the photograph was hidden (full story, 10×)",
),
holmes(
"In which church were Irene Adler and Godfrey Norton married?",
["St. Monica"],
["the Church of St. Monica in the\nEdgeware Road"],
"15x", "Sherlock Holmes: the wedding church (full story, 15×)",
),
# Second screening round.
benchmark("hotpotqa", 9, "10x", "HotpotQA: birth year of the singer of “Tunak” (10×)"),
benchmark("hotpotqa", 27, "10x", "HotpotQA: birth year of Lisa Simpson's creator (10×)"),
benchmark("hotpotqa", 37, "10x", "HotpotQA: film by Joby Harold scored by Samuel Sim (10×)"),
benchmark("hotpotqa", 26, "15x", "HotpotQA: home city of Ruddles Brewery's owner (15×)"),
benchmark("musique", 12, "10x", "MuSiQue: father of Crockett County's namesake (10×)"),
benchmark("musique", 15, "10x", "MuSiQue: birthplace of the president born on July 4 (10×)"),
benchmark("musique", 36, "15x", "MuSiQue: where the first female Justice studied (15×)"),
constitution(
"What minimum ages does the Constitution set for a Representative, a Senator and the President?",
["twenty five|25", "thirty Years|30", "thirty five|35"],
["Age of twenty five Years", "Age of\nthirty Years", "Age of thirty five Years"],
"10x", "U.S. Constitution: minimum ages for Congress and President (10×)", match_all=True,
),
constitution(
"How old must a person be to be eligible to the Office of President, and how long must they have been a resident of the United States?",
["thirty five|35", "fourteen|14"],
["Age of thirty five Years,\nand been fourteen Years a Resident"],
"10x", "U.S. Constitution: eligibility for President (10×)", match_all=True,
),
holmes(
"How much money did the King leave with Holmes for present expenses?",
["three hundred pounds|300", "seven hundred|700"],
["three hundred pounds in gold and seven hundred in notes"],
# at 10x the first page pick changed between deployments of identical code (page 9, later 19);
# 5x read the evidence page on both boots checked since
"5x", "Sherlock Holmes: the King's expense money (full story, 5×)", match_all=True,
),
holmes(
"In what disguise did Holmes get himself into Briony Lodge?",
["clergyman"],
["Nonconformist clergyman"],
"10x", "Sherlock Holmes: Holmes's disguise (full story, 10×)",
),
holmes(
"Who was Irene Adler's frequent male visitor, and where was he from?",
["Godfrey Norton", "Inner Temple"],
["Mr. Godfrey\nNorton, of the Inner Temple"],
"10x", "Sherlock Holmes: Irene Adler's visitor (full story, 10×)", match_all=True,
),
]
if __name__ == "__main__":
mode = sys.argv[1] if len(sys.argv) > 1 else "candidates"
exs = candidates()
if mode == "final":
keep = json.load(open(os.path.join(ROOT, "examples", "selection.json")))["keep"]
by_id = {e["id"]: e for e in exs}
exs = [by_id[k] for k in keep]
os.makedirs(os.path.dirname(OUT), exist_ok=True)
json.dump(exs, open(OUT, "w"), ensure_ascii=False, indent=1)
for e in exs:
print(f"{e['id']:32s} {e['compression']:4s} chars={len(e['context']):6d} spans={len(e['evidence_spans'])} {e['question'][:70]}")
print(len(exs), "examples ->", OUT)
|