"""Multimodal train / eval records from public Hugging Face datasets only (no synthetic media). image : A-OKVQA, POPE (from base.pt) + ScienceQA-IMG, AI2D, VQAv2-small (yes/no -> noul, how-many -> score, other -> choice) audio : ESC-50 (from the audio part) + Clotho-AQA (yes/no), UrbanSound8K (fold 10 held out), Speech Commands v0.02 video : TempCompass (multi-choice / yes-no / caption matching, split by video), MV-Bench-mini, UCF101-subset, Kinetics-mini Features are cached at the grid the LM consumes (image 8x8 -> 64 tokens, video frame 4x4 -> 16 tokens); a few eval items per task keep their raw media for end-to-end latency and demo cards. Builders take `jev` and return record lists. """ import glob, io, os, random, re, subprocess, tarfile, zipfile import numpy as np, torch from datasets import Audio, load_dataset from huggingface_hub import hf_hub_download, snapshot_download from mmjev import Seg N_RAW = 12 IMG_GRID, FRAME_GRID = 8, 4 def _rec(task, mod, split, state, qs, ys): qs = qs if isinstance(qs, list) else [qs] ys = ys if isinstance(ys, list) else [ys] from mmjev import options_of tg = [[1.0 if i == y else 0.0 for i in range(len(options_of(q)[0]))] for q, y in zip(qs, ys)] return dict(task=task, modality=mod, split=split, state=state, qs=qs, ys=ys, targets=tg, raw=None) def _img_feats(jev, ims, grid=IMG_GRID): g = jev.vision_tower_features(ims).float() return torch.nn.functional.avg_pool2d(g, g.shape[-1] // grid).half().cpu() def _encode_images(jev, recs): """Replace PIL images in record states by cached grids; keep raw for the first N_RAW eval items of each task.""" cnt = {} for r in recs: if r["split"] == "eval": cnt[r["task"]] = cnt.get(r["task"], 0) + 1 if cnt[r["task"]] <= N_RAW: r["raw"] = [Seg(s.kind, s.data) for s in r["state"]] for s in range(0, len(recs), 32): chunk = recs[s:s + 32] f = _img_feats(jev, [r["state"][0].data.convert("RGB") for r in chunk]) for r, x in zip(chunk, f): r["state"][0] = Seg("image", x) return recs def _letters(opts): return {o: "" for o in opts} if len(set(opts)) == len(opts) else {f"{chr(65 + i)}. {o}": "" for i, o in enumerate(opts)} # ------------------------------------------------------------------------------------------ image def scienceqa(jev, n_tr=1500, n_ev=500): out = [] for split, hf, n in (("train", "train", n_tr), ("eval", "test", n_ev)): ds = load_dataset("lmms-lab/ScienceQA-IMG", split=hf).shuffle(seed=0) k = 0 for ex in ds: if k >= n: break if ex["image"] is None: continue opts = eval(ex["choices"]) if isinstance(ex["choices"], str) else list(ex["choices"]) q = ex["question"] + (f"\nHint: {ex['hint']}" if ex.get("hint") else "") crit = _letters([str(o) for o in opts]) out.append(_rec("scienceqa_img", "image", split, [Seg("image", ex["image"])], {"type": "choice", "instructions": q, "criteria": crit}, int(ex["answer"]))); k += 1 return _encode_images(jev, out) def ai2d(jev, n_ev=600): ds = load_dataset("lmms-lab-encoder/ai2d", split="test").shuffle(seed=0) out = [] for i, ex in enumerate(ds): opts = eval(ex["options"]) if isinstance(ex["options"], str) else list(ex["options"]) split = "eval" if i < n_ev else "train" out.append(_rec("ai2d", "image", split, [Seg("image", ex["image"])], {"type": "choice", "instructions": ex["question"], "criteria": _letters([str(o) for o in opts])}, int(ex["answer"]))) return _encode_images(jev, out) def vqav2(jev, n=6000, eval_frac=0.15, seed=0): rng = random.Random(seed) ds = load_dataset("merve/vqav2-small", split="validation").shuffle(seed=seed).select(range(n)) qtype = lambda q: " ".join(q.lower().split()[:3]) pool = {} for ex in ds: pool.setdefault(qtype(ex["question"]), []).append(ex["multiple_choice_answer"]) out = [] for i, ex in enumerate(ds): split = "eval" if i < n * eval_frac else "train" q, a = ex["question"], str(ex["multiple_choice_answer"]).strip().lower() st = [Seg("image", ex["image"])] if a in ("yes", "no"): out.append(_rec("vqav2_yesno", "image", split, st, {"type": "noul", "instructions": q}, int(a == "yes"))) elif q.lower().startswith("how many") and a.isdigit() and int(a) <= 10: out.append(_rec("vqav2_count", "image", split, st, {"type": "score", "instructions": q, "criteria": [str(k) for k in range(11)]}, int(a))) else: cands = [x for x in set(pool[qtype(q)]) if x != a] if len(cands) < 3: continue opts = [a] + rng.sample(cands, 3); rng.shuffle(opts) out.append(_rec("vqav2_other", "image", split, st, {"type": "choice", "instructions": q, "criteria": {o: "" for o in opts}}, opts.index(a))) return _encode_images(jev, out) # ------------------------------------------------------------------------------------------ audio def _decode(a, sr=16000): import soundfile as sf, librosa y, s = sf.read(io.BytesIO(a["bytes"]), dtype="float32", always_2d=False) if y.ndim > 1: y = y.mean(1) return librosa.resample(y, orig_sr=s, target_sr=sr) if s != sr else y def _encode_audio(jev, recs): cnt = {} for r in recs: if r["split"] == "eval": cnt[r["task"]] = cnt.get(r["task"], 0) + 1 if cnt[r["task"]] <= N_RAW: r["raw"] = [Seg("audio", r["state"][0].data)] for s in range(0, len(recs), 32): chunk = recs[s:s + 32] feats = jev.audio_tower_features([r["state"][0].data[:16000 * 30] for r in chunk]) for r, f in zip(chunk, feats): r["state"][0] = Seg("audio", f.cpu()) return recs def clotho_aqa(jev, n_tr=1500, n_ev=400): out = [] for split, pat, n in (("train", "clotho_aqa_val_filtered-*", n_tr), ("eval", "clotho_aqa_test_filtered-*", n_ev)): files = f"hf://datasets/lmms-lab-audio/ClothoAQA/clotho_aqa/{pat}.parquet" ds = load_dataset("parquet", data_files=files, split="train") acol = next(c for c in ds.column_names if "audio" in c.lower() or c in ("context",)) ds = ds.cast_column(acol, Audio(decode=False)).shuffle(seed=0) qcol = next(c for c in ds.column_names if "question" in c.lower()) anscol = next(c for c in ds.column_names if c.lower() in ("answer", "answers", "label")) k = 0 for ex in ds: if k >= n: break a = str(ex[anscol]).strip().lower() if a not in ("yes", "no"): continue out.append(_rec("clotho_aqa", "audio", split, [Seg("audio", _decode(ex[acol]))], {"type": "noul", "instructions": ex[qcol]}, int(a == "yes"))); k += 1 return _encode_audio(jev, out) def urbansound(jev, shards=6, n_tr=1500, n_ev=400): files = sorted(f for f in [s.rfilename for s in __import__("huggingface_hub").HfApi().dataset_info("danavery/urbansound8K").siblings] if f.endswith(".parquet"))[:shards] ds = load_dataset("parquet", data_files=[f"hf://datasets/danavery/urbansound8K/{f}" for f in files], split="train") ds = ds.cast_column("audio", Audio(decode=False)).shuffle(seed=0) classes = sorted(set(ds["class"])) crit = {c.replace("_", " "): "" for c in classes} out, k = [], {"train": 0, "eval": 0} for ex in ds: split = "eval" if str(ex["fold"]) == "10" else "train" if k[split] >= (n_ev if split == "eval" else n_tr): continue out.append(_rec("urbansound8k", "audio", split, [Seg("audio", _decode(ex["audio"]))], {"type": "choice", "instructions": "Which sound is in the recording?", "criteria": crit}, classes.index(ex["class"]))); k[split] += 1 return _encode_audio(jev, out) def speech_commands(jev, n_tr=1500, n_ev=400): out = [] for split, pat, n in (("train", "data/train/train-00000-of-*.parquet", n_tr), ("eval", "data/test/test-00000-of-*.parquet", n_ev)): ds = load_dataset("parquet", data_files=f"hf://datasets/pollen-robotics/speech-commands-v0.02/{pat}", split="train") ds = ds.cast_column("audio", Audio(decode=False)).shuffle(seed=0) words = sorted(set(ds["label"]) - {"_background_noise_", "_silence_", "_unknown_"}) crit = {w: "" for w in words} k = 0 for ex in ds: if k >= n: break if ex["label"] not in crit: continue out.append(_rec("speech_commands", "audio", split, [Seg("audio", _decode(ex["audio"]))], {"type": "choice", "instructions": "Which word is spoken in the recording?", "criteria": crit}, words.index(ex["label"]))); k += 1 return _encode_audio(jev, out) # ------------------------------------------------------------------------------------------ video def _frames(path, n=8): import cv2 from PIL import Image cap = cv2.VideoCapture(path) tot = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) or 0 fps = cap.get(cv2.CAP_PROP_FPS) or 25.0 if tot <= 0: return None, None idx = np.linspace(0, tot - 1, n).round().astype(int) out = [] for i in idx: cap.set(cv2.CAP_PROP_POS_FRAMES, int(i)) ok, fr = cap.read() if ok: out.append(Image.fromarray(fr[:, :, ::-1])) cap.release() if len(out) < 2: return None, None eff_fps = (len(out) - 1) / max((idx[-1] - idx[0]) / fps, 1e-3) return out, eff_fps def _video_rec(jev, task, split, path, qs, ys, keep_raw): fr, fps = _frames(path) if fr is None: return None r = _rec(task, "video", split, [Seg("video", _img_feats(jev, fr, FRAME_GRID), None, fps)], qs, ys) if keep_raw: r["raw"] = [Seg("video", fr, None, fps)] return r def _parse_mc(text, kind): """TempCompass question text -> (instructions, options).""" lines = text.split("\n") if kind == "mc": opts = [re.sub(r"^[A-Z]\.\s*", "", l).strip() for l in lines[1:] if re.match(r"^[A-Z]\.", l)] return lines[0], opts opts = [re.sub(r"^Option \d+:\s*", "", l).strip() for l in lines[1:] if l.startswith("Option")] return lines[0], opts def tempcompass(jev, eval_frac=0.25): root = "/content/pipe/raw/tempcompass" if not os.path.exists(root): z = hf_hub_download("lmms-eval/TempCompass", "tempcompass_videos.zip", repo_type="dataset") zipfile.ZipFile(z).extractall(root) vids = {os.path.splitext(os.path.basename(p))[0]: p for p in glob.glob(f"{root}/**/*.mp4", recursive=True)} per = {} for cfg, kind in (("multi-choice", "mc"), ("caption_matching", "cm"), ("yes_no", "yn")): for ex in load_dataset("lmms-eval/TempCompass", cfg, split="test"): vid = str(ex["video_id"]) if vid not in vids: continue if kind == "yn": a = str(ex["answer"]).strip().lower() if a not in ("yes", "no"): continue per.setdefault(vid, []).append(({"type": "noul", "instructions": ex["question"]}, int(a == "yes"))) else: ins, opts = _parse_mc(ex["question"], kind) ans = re.sub(r"^([A-Z]\.|Option \d+:)\s*", "", str(ex["answer"])).strip() if ans not in opts or len(set(opts)) != len(opts): continue per.setdefault(vid, []).append(({"type": "choice", "instructions": ins, "criteria": {o: "" for o in opts}}, opts.index(ans))) keys = sorted(per); random.Random(0).shuffle(keys) n_ev = int(len(keys) * eval_frac) out = [] for i, vid in enumerate(keys): split = "eval" if i < n_ev else "train" qa = per[vid] for s in range(0, len(qa), 4): # up to 4 questions per state / call chunk = qa[s:s + 4] r = _video_rec(jev, "tempcompass", split, vids[vid], [q for q, _ in chunk], [y for _, y in chunk], keep_raw=split == "eval" and i < N_RAW) if r: out.append(r) return out def mvbench_mini(jev, eval_frac=0.25): import csv root = snapshot_download("firstep-ai/MV-Bench-mini", repo_type="dataset") rows = list(csv.DictReader(open(f"{root}/resampled_data.csv", encoding="utf-8-sig"))) random.Random(0).shuffle(rows) out = [] for i, r in enumerate(rows): path = os.path.join(root, r["relpath"]) if not os.path.exists(path): continue opts = [str(o) for o in eval(r["candidates"])] if r["answer"] not in opts or len(set(opts)) != len(opts): continue split = "eval" if i < len(rows) * eval_frac else "train" rec = _video_rec(jev, "mvbench_mini", split, path, {"type": "choice", "instructions": r["question"], "criteria": {o: "" for o in opts}}, opts.index(r["answer"]), keep_raw=split == "eval" and i < N_RAW) if rec: out.append(rec) return out def ucf101_subset(jev): root = "/content/pipe/raw/ucf" if not os.path.exists(root): tarfile.open(hf_hub_download("sayakpaul/ucf101-subset", "UCF101_subset.tar.gz", repo_type="dataset")).extractall(root) paths = glob.glob(f"{root}/**/*.avi", recursive=True) cls = lambda p: os.path.basename(os.path.dirname(p)) classes = sorted(set(cls(p) for p in paths)) crit = {re.sub(r"(?