anima_closeout v15: the val bed was reading the eval cache wrong in TWO ways. (1) text embeddings live in the AR-bucket dir (ar_frames_{ar}x{f}, dataset.py:485), not the size-bucket dir (cache_{ar}x{w}x{h}x{f}, :247) — hence 'found []'. (2) far worse: SizeBucketDataset SHUFFLES its metadata (:241-242) before caching latents while TE is cached unshuffled one level up, so pairing by row order marries each latent to someone else's caption and still yields plausible numbers. The bed now joins on image_spec — the key the fork itself joins on (:312) — and iterates in sorted spec order for run-to-run reproducibility. PROVEN on a synthetic cache written by the fork's OWN ParquetCache with the real shuffle: 24/24 rows correctly paired, while the old order-based read mismatched 23/24.
Browse files- colab/anima_closeout.ipynb +59 -29
colab/anima_closeout.ipynb
CHANGED
|
@@ -954,19 +954,22 @@
|
|
| 954 |
"DEV = \"cuda\"\n",
|
| 955 |
"\n",
|
| 956 |
"# -- read the fork's eval cache -------------------------------------------\n",
|
| 957 |
-
"#
|
| 958 |
-
"#
|
| 959 |
-
"#
|
| 960 |
-
"#
|
| 961 |
-
"#
|
| 962 |
-
"#
|
| 963 |
-
"#
|
| 964 |
-
"#
|
|
|
|
|
|
|
|
|
|
| 965 |
"cache_root = CFG[\"eval_cache_dir\"]\n",
|
| 966 |
-
"
|
| 967 |
-
"
|
| 968 |
-
"
|
| 969 |
-
"assert
|
| 970 |
"\n",
|
| 971 |
"def read_cache(d):\n",
|
| 972 |
" shards = sorted(p for p in os.listdir(d)\n",
|
|
@@ -981,29 +984,56 @@
|
|
| 981 |
" return torch.frombuffer(bytearray(raw),\n",
|
| 982 |
" dtype=getattr(torch, dt)).reshape(shape)\n",
|
| 983 |
"\n",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 984 |
"LAT = [\"latents\"]\n",
|
| 985 |
"TE = [\"prompt_embeds\", \"attn_mask\", \"t5_input_ids\", \"t5_attn_mask\"]\n",
|
| 986 |
"rows = []\n",
|
| 987 |
-
"for
|
| 988 |
-
"
|
| 989 |
-
"
|
| 990 |
-
"
|
| 991 |
-
"
|
| 992 |
-
"
|
| 993 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 994 |
" for c in LAT:\n",
|
| 995 |
-
"
|
|
|
|
| 996 |
" for c in TE:\n",
|
| 997 |
-
"
|
| 998 |
-
"
|
| 999 |
-
"
|
| 1000 |
-
" assert
|
| 1001 |
-
" f\"{
|
| 1002 |
-
"
|
| 1003 |
-
"
|
| 1004 |
-
"
|
|
|
|
|
|
|
|
|
|
| 1005 |
" rows.append(r)\n",
|
| 1006 |
-
"print(f\"
|
|
|
|
|
|
|
| 1007 |
" flush=True)\n",
|
| 1008 |
"rows = rows[:int(os.environ.get(\"VB_MAX_ROWS\", \"512\"))]\n",
|
| 1009 |
"assert rows\n",
|
|
|
|
| 954 |
"DEV = \"cuda\"\n",
|
| 955 |
"\n",
|
| 956 |
"# -- read the fork's eval cache -------------------------------------------\n",
|
| 957 |
+
"# LAYOUT (utils/dataset.py) and the trap inside it:\n",
|
| 958 |
+
"# {root}/cache_{ar}x{w}x{h}x{f}/latents/ SizeBucketDataset (:247)\n",
|
| 959 |
+
"# {root}/ar_frames_{ar}x{f}/text_embeddings_N/ ARBucketDataset (:485)\n",
|
| 960 |
+
"# Latents are cached from metadata that SizeBucketDataset SHUFFLES first\n",
|
| 961 |
+
"# (:241-242); text embeddings are cached one level up, at the AR bucket,\n",
|
| 962 |
+
"# unshuffled. So latent row i and TE row i are DIFFERENT images \u2014 pairing\n",
|
| 963 |
+
"# by row order would silently hand every latent someone else's caption and\n",
|
| 964 |
+
"# still produce plausible numbers. Both caches carry image_spec\n",
|
| 965 |
+
"# (parquet_cache.image_specs()), which is the key the fork itself joins on\n",
|
| 966 |
+
"# (dataset.py:312), so we join on image_spec and iterate in sorted spec\n",
|
| 967 |
+
"# order, which also makes the bed reproducible run-to-run.\n",
|
| 968 |
"cache_root = CFG[\"eval_cache_dir\"]\n",
|
| 969 |
+
"size_dirs = sorted(d for d in os.listdir(cache_root)\n",
|
| 970 |
+
" if d.startswith(\"cache_\")\n",
|
| 971 |
+
" and os.path.isdir(os.path.join(cache_root, d)))\n",
|
| 972 |
+
"assert size_dirs, f\"no cache_* size-bucket dirs under {cache_root}\"\n",
|
| 973 |
"\n",
|
| 974 |
"def read_cache(d):\n",
|
| 975 |
" shards = sorted(p for p in os.listdir(d)\n",
|
|
|
|
| 984 |
" return torch.frombuffer(bytearray(raw),\n",
|
| 985 |
" dtype=getattr(torch, dt)).reshape(shape)\n",
|
| 986 |
"\n",
|
| 987 |
+
"def by_spec(tables, what):\n",
|
| 988 |
+
" \"\"\"{image_spec tuple: (table, row)} \u2014 tuples are JSON-encoded by the\n",
|
| 989 |
+
" cache writer (parquet_cache.py:_encode_row), so accept either form.\"\"\"\n",
|
| 990 |
+
" out = {}\n",
|
| 991 |
+
" for t in tables:\n",
|
| 992 |
+
" names = t.schema.names\n",
|
| 993 |
+
" col = (\"image_spec__json\" if \"image_spec__json\" in names else\n",
|
| 994 |
+
" (\"image_spec\" if \"image_spec\" in names else None))\n",
|
| 995 |
+
" assert col, f\"{what} cache carries no image_spec column: {names[:8]}\"\n",
|
| 996 |
+
" for j, v in enumerate(t.column(col).to_pylist()):\n",
|
| 997 |
+
" s = tuple(json.loads(v)) if isinstance(v, str) else tuple(v)\n",
|
| 998 |
+
" out.setdefault(s, (t, j))\n",
|
| 999 |
+
" return out\n",
|
| 1000 |
+
"\n",
|
| 1001 |
"LAT = [\"latents\"]\n",
|
| 1002 |
"TE = [\"prompt_embeds\", \"attn_mask\", \"t5_input_ids\", \"t5_attn_mask\"]\n",
|
| 1003 |
"rows = []\n",
|
| 1004 |
+
"for sd in size_dirs:\n",
|
| 1005 |
+
" parts = sd[len(\"cache_\"):].split(\"x\") # {ar}x{w}x{h}x{frames}\n",
|
| 1006 |
+
" ar_dir = os.path.join(cache_root, f\"ar_frames_{parts[0]}x{parts[-1]}\")\n",
|
| 1007 |
+
" assert os.path.isdir(ar_dir), (\n",
|
| 1008 |
+
" f\"{sd}: expected AR-bucket dir {ar_dir} \u2014 text embeddings are \"\n",
|
| 1009 |
+
" \"cached per AR bucket, not per size bucket\")\n",
|
| 1010 |
+
" te_names = sorted(d for d in os.listdir(ar_dir)\n",
|
| 1011 |
+
" if d.startswith(\"text_embeddings_\"))\n",
|
| 1012 |
+
" assert len(te_names) == 1, \\\n",
|
| 1013 |
+
" f\"{ar_dir}: expected one text_embeddings_* cache, found {te_names}\"\n",
|
| 1014 |
+
" lat = by_spec(read_cache(os.path.join(cache_root, sd, \"latents\")),\n",
|
| 1015 |
+
" \"latents\")\n",
|
| 1016 |
+
" te = by_spec(read_cache(os.path.join(ar_dir, te_names[0])),\n",
|
| 1017 |
+
" \"text embedding\")\n",
|
| 1018 |
" for c in LAT:\n",
|
| 1019 |
+
" _s = next(iter(lat.values()))[0].schema.names\n",
|
| 1020 |
+
" assert c + \"__shape\" in _s, (sd, _s[:8])\n",
|
| 1021 |
" for c in TE:\n",
|
| 1022 |
+
" _s = next(iter(te.values()))[0].schema.names\n",
|
| 1023 |
+
" assert c + \"__shape\" in _s, (ar_dir, _s[:8])\n",
|
| 1024 |
+
" _missing = [s for s in lat if s not in te]\n",
|
| 1025 |
+
" assert not _missing, (\n",
|
| 1026 |
+
" f\"{sd}: {len(_missing)} latents have no text embedding for their \"\n",
|
| 1027 |
+
" f\"image_spec (e.g. {_missing[:1]}) \u2014 caches are out of sync\")\n",
|
| 1028 |
+
" for s in sorted(lat):\n",
|
| 1029 |
+
" lt, lj = lat[s]\n",
|
| 1030 |
+
" tt, tj = te[s]\n",
|
| 1031 |
+
" r = {c: decol(lt, c, lj) for c in LAT}\n",
|
| 1032 |
+
" r.update({c: decol(tt, c, tj) for c in TE})\n",
|
| 1033 |
" rows.append(r)\n",
|
| 1034 |
+
" print(f\" {sd}: {len(lat)} rows joined on image_spec \"\n",
|
| 1035 |
+
" f\"(AR pool {len(te)})\", flush=True)\n",
|
| 1036 |
+
"print(f\"eval cache rows: {len(rows)} from {len(size_dirs)} size bucket(s)\",\n",
|
| 1037 |
" flush=True)\n",
|
| 1038 |
"rows = rows[:int(os.environ.get(\"VB_MAX_ROWS\", \"512\"))]\n",
|
| 1039 |
"assert rows\n",
|