File size: 6,021 Bytes
d9a3537 | 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 | # Reproducing the eval.md numbers
Step-by-step guide to reproduce the FID / CLIP Score numbers in `eval.md` from scratch. Everything below was run in a throwaway virtualenv outside this repo; none of it is required to just *use* PixelModel (see `README.md` for that) — it's only needed if you want to re-run or extend the evaluation.
Uses Python 3.13 (CPU-only; no GPU required, but slower). Total download size is roughly 1.5–2GB (torch, torchvision, CLIP weights, Inception weights) — pick an install location with that much free space.
## 1. Create an isolated venv
```bash
python3.13 -m venv eval-venv
# Windows:
eval-venv\Scripts\activate
# macOS/Linux:
source eval-venv/bin/activate
```
## 2. Install dependencies
CPU-only torch keeps the install small (~130MB vs several GB for a CUDA build):
```bash
pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
pip install transformers scipy huggingface_hub "datasets<3" pyarrow pytorch-fid
```
`datasets<3` is pinned because `pytorch-fid==0.3.0`'s dependency chain and the CLIP scoring code below weren't tested against `datasets` 3.x.
## 3. Fetch a real COCO caption/image sample
This streams only the first N rows from a public 30K-pair COCO val2014 dataset — it does **not** download the full ~5GB dataset.
```python
# fetch_coco_sample.py
import os, json, time
from datasets import load_dataset
N = 40
OUT_DIR = "coco_sample"
os.makedirs(OUT_DIR, exist_ok=True)
ds = load_dataset("sayakpaul/coco-30-val-2014", split="train", streaming=True)
meta = []
for i, row in enumerate(ds):
if i >= N:
break
img = row["image"]
caption = row["caption"]
fname = f"real_{i:03d}.jpg"
img.convert("RGB").save(os.path.join(OUT_DIR, fname), format="JPEG", quality=90)
meta.append({"file": fname, "caption": caption})
with open(os.path.join(OUT_DIR, "captions.json"), "w") as f:
json.dump(meta, f, indent=2)
```
Run it: `python fetch_coco_sample.py` — takes ~10 seconds, ~3.5MB on disk. Raise `N` for a less noisy (but slower, and eventually much larger) eval — real COCO FID evals typically use N=30,000.
## 4. Generate PixelModel outputs for those captions
Run from inside this repo (needs `model.py` and `model.png` on the path):
```python
# generate_outputs.py
import os, json
import numpy as np
from PIL import Image
import torch
from model import load_model, forward # from this repo
META_PATH = "coco_sample/captions.json"
OUT_DIR = "gen_sample"
os.makedirs(OUT_DIR, exist_ok=True)
with open(META_PATH) as f:
meta = json.load(f)
pixels = load_model("model.png")
for i, row in enumerate(meta):
with torch.no_grad():
result = forward(pixels, row["caption"])
arr = (result.numpy() * 255).clip(0, 255).astype(np.uint8)
Image.fromarray(arr, mode="RGB").save(os.path.join(OUT_DIR, f"gen_{i:03d}.png"))
```
## 5. Compute FID
`pytorch-fid==0.3.0` calls `scipy.linalg.sqrtm(..., disp=False)`, which recent scipy versions (1.14+) no longer accept — this reimplements the Fréchet distance calculation without that removed kwarg instead of pinning an old scipy:
```python
# compute_fid.py
import numpy as np
from scipy import linalg
from pytorch_fid.fid_score import compute_statistics_of_path
from pytorch_fid.inception import InceptionV3
def calculate_frechet_distance(mu1, sigma1, mu2, sigma2):
diff = mu1 - mu2
covmean = linalg.sqrtm(sigma1.dot(sigma2))
if np.iscomplexobj(covmean):
covmean = covmean.real
return diff.dot(diff) + np.trace(sigma1) + np.trace(sigma2) - 2 * np.trace(covmean)
dims = 2048
model = InceptionV3([InceptionV3.BLOCK_INDEX_BY_DIM[dims]]).to("cpu")
# NOTE: real images are JPEGs of varying resolution; pytorch-fid's default
# dataloader batches images without resizing first, which crashes on a batch
# of mismatched sizes — use batch_size=1 to sidestep that.
m1, s1 = compute_statistics_of_path("coco_sample", model, 1, dims, "cpu", 0)
m2, s2 = compute_statistics_of_path("gen_sample", model, 1, dims, "cpu", 0)
fid = calculate_frechet_distance(m1, s1, m2, s2)
print(f"FID: {fid:.4f}")
```
Expect a `LinAlgWarning: Matrix is singular` at low sample counts (n < 2048) — this is expected, not a bug; see the caveat in `eval.md`.
## 6. Compute CLIP Score
```python
# compute_clip_score.py
import json
import torch
from PIL import Image
from transformers import CLIPModel, CLIPProcessor
with open("coco_sample/captions.json") as f:
meta = json.load(f)
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
model.eval()
scores = []
for i, row in enumerate(meta):
img = Image.open(f"gen_sample/gen_{i:03d}.png").convert("RGB")
inputs = processor(text=[row["caption"]], images=img, return_tensors="pt", padding=True, truncation=True)
with torch.no_grad():
out = model(**inputs)
img_emb = out.image_embeds / out.image_embeds.norm(dim=-1, keepdim=True)
txt_emb = out.text_embeds / out.text_embeds.norm(dim=-1, keepdim=True)
cos_sim = (img_emb * txt_emb).sum(dim=-1).item()
scores.append(max(0.0, 100 * cos_sim))
print(f"Mean CLIP Score (n={len(scores)}): {sum(scores)/len(scores):.4f}")
```
First run downloads `openai/clip-vit-base-patch32` (~600MB) from the Hugging Face Hub.
## Notes / gotchas hit while building this
- **Disk location matters.** All of the above (venv, pip cache, HF cache, downloaded datasets) should point at a drive with several GB free — `torch`+`torchvision`+`transformers`+CLIP+Inception weights add up to ~1.5–2GB. Set `PIP_CACHE_DIR`, `HF_HOME`, and `TORCH_HOME` env vars if your default drive is tight on space.
- **Sample size is the biggest caveat.** n=40 is fast and cheap but gives a noisy FID (singular covariance matrix) and a noisy CLIP Score mean. Scaling toward the standard n=30,000 COCO FID protocol means re-running steps 3–6 with a bigger `N`, which will take proportionally longer and use proportionally more disk/bandwidth.
|