File size: 10,984 Bytes
cd24b4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b91ce50
cd24b4a
 
 
 
 
 
 
 
 
 
 
 
463a482
b91ce50
cd24b4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26a7256
cd24b4a
26a7256
 
cd24b4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26a7256
 
cd24b4a
 
 
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
# SynthScope capabilities (orientation for an agent building a new detector)

SynthScope is a **reproduction + benchmarking library for AI-generated *image and video* detection**. It
hosts many published detectors behind one interface, reproduces each on its own benchmark, and
serves a web UI + a Python/CLI API. Read this before adding a method, then follow
[`docs/ADD_DETECTOR.md`](ADD_DETECTOR.md) for the exact checklist.

> Environment: everything runs in the **`SynthScope` conda env** β€”
> `/home/ssalehi/miniconda3/bin/conda run -n SynthScope <cmd>`. Config is **Hydra** (`conf/`).

---

## 1. What's already integrated (17 detectors)

`synthscope.list_models()` / `from synthscope.api import list_specs`. Each is an `SynthScope<Name>Model` with a
matching `conf/model/<modality>/<key>.yaml`, a `trainer.py._build_model` branch, an
`interface/backend/registry.py` `ModelSpec`, a per-model `README.md`, and a `tests/test_<k>_smoke.py`.

| key | modality | predict_kind | weights | interp map | method |
|---|---|---|---|---|---|
| `clip` | image | ce | synthscope | – | UnivFD / CLIP linear probe |
| `cnn` | image | ce | synthscope | – | CNNDetection (ResNet-50) |
| `dinov2`,`dinov3`,`adapted_dino` | image | ce | synthscope | – | DINO probes / adapter-tuned |
| `npr` | image | bce_npr | official | npr_residual | Neighboring-Pixel Relationships |
| `safe` | image | ce | synthscope | safe_hf | High-freq DWT artifacts |
| `aide` | image | ce | official | aide_dct | ConvNeXt-XXL + DCT/SRM (native-res 5-view) |
| `fat_former` | image | ce | official | – | Forgery-aware adaptive transformer (CLIP) |
| `spai` | image | bce | synthscope* | spai_patch | Spectral, any-resolution (MFM ViT-B/16) |
| `co_spy` | image | bce | synthscope* | – | Semantic + artifact fusion (SigLIP + SD-VAE) |
| `pgc` | image | bce | official | pgc_peak | LoRA-DINOv2 + YCbCr residual, peak-guided |
| `cure` | image | ce | official | – | Frozen PE-Core L/14 + Q/V LoRA + source-response subspace (mixing-ratio regression) |
| `vib_net` | image | vib | synthscope | – | Variational information bottleneck (FLAVA) |
| `d3` | video | video | training-free | – | Disentangled detail dynamics (X-CLIP) |
| `restrav` | video | video | synthscope | – | Representation-trajectory geometry (DINOv2) |
| `stall` | video | video | training-free | – | Spatio-temporal whitened log-likelihoods (DINOv3) |

`*` served via official weights remapped into the SynthScope class. **`predict_kind`** sets the score
convention the engine applies to `forward()` logits: `ce`β†’`softmax[:,1]`, `bce`β†’`sigmoid`,
`bce_npr` (NPR residual transform then sigmoid), `vib` (tuple out), `video` (clip in).

---

## 2. The detector contract (what a new method must implement)

`models/<modality>/<key>/<key>.py`:
```python
class SynthScope<Name>Model(nn.Module):
    def __init__(self, config: DictConfig): ...        # build arch from config.ModelConfig
    def forward(self, images) -> logits: ...           # RAW logits, not probs; model-specific
                                                       # preprocessing (residual/DWT/DCT) goes INSIDE forward
    def compute_loss(self, batch) -> {"loss", "outputs"}  # outputs = [N,2] real/fake logits, used by training
```
Repo rule: **all imports + helper classes at module top level** (no nested defs/imports). Vendor the
official arch pieces verbatim where possible and document deviations in the per-model README.

To wire it in: `conf/model/<modality>/<key>.yaml` (`model_type: <key>`), a branch in
`trainer.py._build_model` (dispatches on `model_type`), and a `ModelSpec` in the interface registry
(see `ADD_DETECTOR.md`). The web engine (`interface/backend/engine.py`) lazy-imports by `model_type`.

---

## 3. Training infrastructure

- **`trainer.py`** (`SynthScopeModelTrainer`) + **`main.py`** entrypoint; Hydra config in `conf/config.yaml`
  with groups `model/`, `dataset/`, `trainer/`. Multi-GPU via πŸ€— `accelerate` (`accelerate_config.yaml`).
- **`dataset.py`** β€” image/video dataset loaders; `_decode_rgb` feeds uint8 `[3,H,W]` tensors;
  `sample_video_frames(..., edge_crop=)` for video.
- **`transformations.py`** β€” composable named transforms / augmentations: `resize_224`,
  `resize_short_{256,384,448}`, `center_crop_{224,256,384,448}`, `random_crop_*`, `pad_random_crop_224`
  (PGC), `gaussian_blur`, `jpeg_compression`, `random_horizontal/vertical_flip`, `random_rotation_180`,
  `color_jitter_safe`, `to_tensor`/`to_dtype`. **Eval-transform fidelity is critical** β€” artifact/
  low-level detectors (SAFE/AIDE/SPAI/PGC/NPR) are wrecked by a wrong resize; match the official eval
  exactly (see `repro/investigate/pgc_gap_investigation.md` and the SAFE crop-not-resize lesson).
- **`metrics.py`** β€” torchmetrics binary `Accuracy` (real/fake), `AveragePrecision` (AP),
  `AUROC`. Threshold-free AP/AUROC are the headline metrics (esp. video, where per-gen real:fake
  ratios make mAP degenerate β€” prefer AUROC there).

---

## 4. Evaluation / benchmarking

- **Image**: `benchmark/bench_eval.py --model <k> --benchmark {forensynths|ufd|pergen|pool} --data <root> [--ckpt <pth>] [--native-res] --tag <t>`. Auto-discovers `<gen>/{0_real,1_fake}` (or `{ai,nature}`, shared-reals) layouts; prints per-generator acc/AP/AUROC + mean; writes `repro/bench_results/<tag>.json`.
- **Video**: `benchmark/bench_eval_video.py --model {d3|restrav|stall} --dataset NAME` (auto-download) or `--benchmark {genvideo|fakeonly|pergen} --data DIR`.
- `--ckpt` overrides the registry checkpoint (for official-vs-SynthScope side-by-side).
- `benchmark/aggregate_results.py`, `leaderboard.py`, `evaluate.py`, `schema.json` aggregate runs.
- Results live in `repro/`: `RESULTS_SUMMARY.md` (read first), `AGGREGATED_BENCHMARK.md`,
  `BENCHMARK_COVERAGE_MATRIX.md`, `REPRODUCED_MODELS_BENCHMARKS.md`, `repro/bench_results/*.json`,
  and per-method deep-dives in `repro/investigate/`.

---

## 5. Datasets (explorer registry: `interface/backend/datasets.py`, `DATASET_DEFS`)

Known image sets: `cnndetection` (ForenSynths/ProGAN), `genimage`, `genimage_full` (8-gen),
`universialfakedetection` (Ojha UFD), `aigibench`, `synthbuster`, `ditfake`, `diffusionforensics`,
`diffusion1kstep`, `aigcdetect`, `commgen15`, `realchain`, `evalgen`, `synthwildx`, `wildrf`, `bfree_online`, `rrdataset`, `cospy`/`cospy_subset`, `spai_ldm`, `freqnetdetection`.
Video: `genvideo_val`, `genvideo`, `videophy`, `vidprom`, `restrav_data`, `evalcrafter`, `genvidbench`, `videofeedback`, `comgenvid`.

Each `DatasetDef` lists candidate roots; **`_resolve_root` is node-portable** β€” it remaps the configured
`/hddstore` path onto whatever scratch mount exists (`/hddstore`↔`/ssdstore`↔`/nvmestore`), overridable
with `SYNTHSCOPE_DATA_PREFIXES`. Labels are inferred from path tokens (`0_real`/`nature`β†’real,
`1_fake`/`ai`β†’fake). **Not every dataset is physically present on every node** β€” data is node-local
scratch; check what's staged (e.g. CNNDetection, GenImage, GenImage_full are on cn029 `/ssdstore`).

---

## 6. The `synthscope` package (Python + CLI)

```python
import synthscope
det = synthscope.load("pgc")                  # or load(key, checkpoint=PATH, device=...)
det.predict("img.png")                  # -> {fake_probability, verdict, confidence, interpret_map, ...}
synthscope.list_models(); synthscope.available_models()   # registered / weights-present
```
CLI: `synthscope list`, `synthscope detect <file> --model <k>`, `synthscope pull <k>` (fetch weights from the HF model
zoo), `synthscope push` (maintainer). Weights resolve under **`$SYNTHSCOPE_CKPT_DIR`** (default
`/hddstore/ssalehi/SynthScope/checkpoints`; override per node) or are auto-downloaded.

---

## 7. Web interface (`interface/`, FastAPI + uvicorn)

`bash interface/run_backend.sh` (`SYNTHSCOPE_PORT`, `SYNTHSCOPE_HOST`). Serves `frontend/dist` if built, else the
no-build `static/index.html`. Endpoints (`interface/backend/app.py`): `/api/models`, `/api/predict`
(image/video, lazy model load + LRU), `/api/datasets[.../sample]`, `/api/predict_dataset`,
`/api/score_batch`, `/api/leaderboard`, `/api/generator_benchmarks`, and a **SOTA-vs-detector** demo
(`/api/sota/{generators,real_image,compare}`) that generates an image from a prompt (`generators.py`,
API-keyed SOTA generators incl. local Ideogram) and scores real vs generated across detectors.
On-cluster launch scripts: `repro/webui_cn029_serve.sbatch` (GPU on free cees node, checkpoints staged
to `/ssdstore`), `repro/webui_cn023_cpu.sbatch`. Reach it via SSH tunnel through the `ivi-h1` login host.

### Interpretability maps
The engine returns a per-detector artifact map for the 5 native-signal detectors (`spec.interpret`):
`safe_hf` (DWT high-freq band), `npr_residual` (pixel residual), `pgc_peak` (peak-guided residual
score), `aide_dct` (native-res DCT patch energy), `spai_patch` (per-patch spectral fake-evidence). Each
yields **`interpret_map`** (bare jet heatmap) **+ `interpret_overlay`** (heatmap blended on the input,
transform-aligned). A new method exposes one by setting `interpret="<kind>"` on its `ModelSpec` and
adding a branch in `engine._interpret_map` that produces a 2-D saliency tensor.

---

## 8. New detectors & repo hygiene

New detectors are integrated **on a `detector/<key>` branch and pushed for review β€” never auto-merged to `main`**
(see [ADD_DETECTOR.md](ADD_DETECTOR.md)). Never commit weights (`*.pth`), `.env`, or caches.

---

## 9. Cluster cheat-sheet (IvI SLURM)

- **cn023** (`quva`/`quvausers`, A6000) holds all detector checkpoints + most datasets on node-local
  **`/hddstore`**; GPUs are often booked for days. `SYNTHSCOPE_CKPT_DIR=/hddstore/ssalehi/SynthScope/checkpoints`.
- **cees** (`ceesusers`: cn029 etc., a5000, free) and **cees6000** (cn030/31, rtx6000) use node-local
  `/ssdstore` / `/nvmestore`. To run there, stage checkpoints/data from cn023 (`rsync -aL`, dereference
  the symlinks) β€” note `pam_slurm_adopt` only allows ssh to a node where you hold an active job.
- `/ivi/zfs/...` is the shared home (24 GiB user quota β€” don't park big checkpoints there);
  HF cache is shared at `/ivi/.../SynthScope/.cache_shared/hf` (`HF_HOME`). DINOv3 weights gated, under
  `dinov3_checkpoints/` (`DINO_V3_WEIGHTS`); SPAI MFM backbone via `SYNTHSCOPE_MFM_CKPT`.

---

## 10. Starting a new method β€” TL;DR

1. Confirm it passes both rules (public repo + strong results). Read `ADD_DETECTOR.md`.
2. `git checkout -b detector/<key>`; add the 5 files (model, conf, trainer dispatch, registry ModelSpec, README) + smoke test.
3. **Match the official eval transform exactly** in the `ModelSpec` (resize vs crop, norm) β€” this is the #1 silent-failure source.
4. Reproduce on the paper's benchmark with official weights via `bench_eval.py --ckpt`; fix `remap_<k>` for key mismatches; record per-gen AP/AUROC in the Paper/SynthScope_official/SynthScope_train schema.
5. (Optional) add an interpretability map. Push the branch; do **not** merge to `main`.