Spaces:
Sleeping
Sleeping
Update SynthScope Space from the GitHub repository
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- LICENSES.md +2 -0
- conf/dataset/image/arforensics.yaml +2 -0
- conf/dataset/image/mirage_test.yaml +16 -0
- conf/model/image/omniaid.yaml +18 -0
- conf/model/image/omniaid_genimage.yaml +18 -0
- conf/model/image/omniaid_v1.yaml +18 -0
- conf/trainer/image/d3qe.yaml +24 -0
- conf/trainer/image/npr.yaml +13 -5
- conf/trainer/image/pgc.yaml +17 -6
- dataset.py +102 -11
- deploy/hf-space/fetch_weights.py +2 -2
- docs/BENCHMARKS.md +1 -0
- docs/CAPABILITIES.md +1 -0
- docs/WILD_BENCHMARKS.md +1 -0
- experiments/image/d3qe.sh +34 -0
- experiments/image/npr.sh +28 -14
- experiments/image/pgc.sh +25 -18
- interface/backend/build_repro_full.py +24 -7
- interface/backend/datasets.py +3 -0
- interface/backend/engine.py +3 -0
- interface/backend/registry.py +23 -0
- interface/backend/repro_full.json +160 -18
- models/image/d3qe/README.md +21 -4
- models/image/d3qe/d3qe.py +5 -3
- models/image/dda/dda.py +4 -3
- models/image/npr/README.md +28 -3
- models/image/npr/npr.py +7 -0
- models/image/omniaid/README.md +88 -0
- models/image/omniaid/__init__.py +0 -0
- models/image/omniaid/official_arch.py +681 -0
- models/image/omniaid/omniaid.py +46 -0
- models/image/pgc/README.md +38 -7
- pyproject.toml +1 -0
- repro/MASTER_RESULTS.jsonl +39 -0
- repro/RESULTS_SUMMARY.md +23 -7
- repro/bench_results/d3qe_trained_arforensics.json +67 -0
- repro/bench_results/d3qe_trained_cnndetection.json +67 -0
- repro/bench_results/d3qe_trained_ep3_arforensics.json +67 -0
- repro/bench_results/d3qe_trained_genimage_full.json +60 -0
- repro/bench_results/npr_official3090_forensynths8.json +74 -0
- repro/bench_results/npr_official3090_ufd.json +74 -0
- repro/bench_results/npr_official_nprpth_forensynths8.json +74 -0
- repro/bench_results/npr_official_nprpth_ufd.json +74 -0
- repro/bench_results/npr_officialcode_rerun_forensynths8.json +74 -0
- repro/bench_results/npr_officialcode_rerun_ufd.json +74 -0
- repro/bench_results/npr_trained_best_forensynths8.json +74 -0
- repro/bench_results/npr_trained_best_ufd.json +74 -0
- repro/bench_results/npr_trained_last_forensynths8.json +74 -0
- repro/bench_results/npr_trained_last_ufd.json +74 -0
- repro/bench_results/omniaid_bfree_online.json +25 -0
LICENSES.md
CHANGED
|
@@ -33,6 +33,7 @@ third-party components, and every **pretrained checkpoint / backbone** SynthScop
|
|
| 33 |
| `cure` | CuRe (arXiv 2609.14316) | manic-cui/CuRe | Apache-2.0 (repo `LICENSE`) | **official** 25 MB adapter (`cure_adapter.pt`, loads as `cure.pth`) | Apache-2.0 (shipped in the repo; verify before redistributing) |
|
| 34 |
| `d3qe` | D3QE (ICCV'25) | Zhangyr2022/D3QE | MIT (repo `LICENSE`); `official/` vendors its network code — see the file headers | **official** checkpoint (`model_epoch_best.pth` from HF `Yanran21/D3QE`, loads as `d3qe.pth`) | MIT (HF model card license tag) |
|
| 35 |
| `dda` | DDA (NeurIPS'25) | roy-ch/Dual-Data-Alignment | Apache-2.0 (repo `LICENSE`); `official_arch.py` vendors its `lora.py` — see its header | **official** checkpoint (`DDA_ckpt.pth` from HF `Junwei-Xi/Dual-Data-Alignment`, loads as `dda.pth`) | Apache-2.0 (HF model card license tag) |
|
|
|
|
| 36 |
| `dinov2` | DINOv2 probe | facebookresearch/dinov2 | Apache-2.0 (verify) | DINOv2 backbone | Apache-2.0 (verify) |
|
| 37 |
| `dinov3` | DINOv3 probe | facebookresearch/dinov3 | DINOv3 License (gated) | DINOv3 backbone | **gated** — DINOv3 License |
|
| 38 |
| `adapted_dino` | Adapter-tuned DINOv2 (SynthScope) | this repo | Apache-2.0 | — (SynthScope-trained) | n/a |
|
|
@@ -45,6 +46,7 @@ third-party components, and every **pretrained checkpoint / backbone** SynthScop
|
|
| 45 |
| Backbone | Used by | License |
|
| 46 |
| --- | --- | --- |
|
| 47 |
| OpenAI CLIP (ViT-L/14) | `clip`, `fat_former`, `d3`, `d3qe` | MIT (verify) |
|
|
|
|
| 48 |
| OpenCLIP (ConvNeXt-XXL, LAION) | `aide` | code MIT; weights per LAION / model card (verify) |
|
| 49 |
| DINOv2 (ViT-L/14, ViT-S/14) | `dinov2`, `adapted_dino`, `restrav`, `dda` | Apache-2.0 (verify) |
|
| 50 |
| DINOv3 (ViT-L/16) | `dinov3`, `stall` | **gated** — DINOv3 License (non-commercial; accept upstream) |
|
|
|
|
| 33 |
| `cure` | CuRe (arXiv 2609.14316) | manic-cui/CuRe | Apache-2.0 (repo `LICENSE`) | **official** 25 MB adapter (`cure_adapter.pt`, loads as `cure.pth`) | Apache-2.0 (shipped in the repo; verify before redistributing) |
|
| 34 |
| `d3qe` | D3QE (ICCV'25) | Zhangyr2022/D3QE | MIT (repo `LICENSE`); `official/` vendors its network code — see the file headers | **official** checkpoint (`model_epoch_best.pth` from HF `Yanran21/D3QE`, loads as `d3qe.pth`) | MIT (HF model card license tag) |
|
| 35 |
| `dda` | DDA (NeurIPS'25) | roy-ch/Dual-Data-Alignment | Apache-2.0 (repo `LICENSE`); `official_arch.py` vendors its `lora.py` — see its header | **official** checkpoint (`DDA_ckpt.pth` from HF `Junwei-Xi/Dual-Data-Alignment`, loads as `dda.pth`) | Apache-2.0 (HF model card license tag) |
|
| 36 |
+
| `omniaid`, `omniaid_v1`, `omniaid_genimage` | OmniAID (arXiv 2511.08423) | yunncheng/OmniAID | MIT (README badge; no LICENSE file in the repo); `official_arch.py` vendors `models/OmniAID.py` — see its header | **official** checkpoints (`ckpt/checkpoint_omniaid_{v2,v1,genimage_sd14}.pth` from HF `Yunncheng/OmniAID`, loaded as `<key>.pth`) | MIT (HF model card license tag) |
|
| 37 |
| `dinov2` | DINOv2 probe | facebookresearch/dinov2 | Apache-2.0 (verify) | DINOv2 backbone | Apache-2.0 (verify) |
|
| 38 |
| `dinov3` | DINOv3 probe | facebookresearch/dinov3 | DINOv3 License (gated) | DINOv3 backbone | **gated** — DINOv3 License |
|
| 39 |
| `adapted_dino` | Adapter-tuned DINOv2 (SynthScope) | this repo | Apache-2.0 | — (SynthScope-trained) | n/a |
|
|
|
|
| 46 |
| Backbone | Used by | License |
|
| 47 |
| --- | --- | --- |
|
| 48 |
| OpenAI CLIP (ViT-L/14) | `clip`, `fat_former`, `d3`, `d3qe` | MIT (verify) |
|
| 49 |
+
| OpenAI CLIP ViT-L/14@336 (HF `openai/clip-vit-large-patch14-336`) | `omniaid`, `omniaid_v1`, `omniaid_genimage` | MIT |
|
| 50 |
| OpenCLIP (ConvNeXt-XXL, LAION) | `aide` | code MIT; weights per LAION / model card (verify) |
|
| 51 |
| DINOv2 (ViT-L/14, ViT-S/14) | `dinov2`, `adapted_dino`, `restrav`, `dda` | Apache-2.0 (verify) |
|
| 52 |
| DINOv3 (ViT-L/16) | `dinov3`, `stall` | **gated** — DINOv3 License (non-commercial; accept upstream) |
|
conf/dataset/image/arforensics.yaml
CHANGED
|
@@ -3,6 +3,8 @@ name: arforensics
|
|
| 3 |
# Open-MAGVIT2), 6000 fakes + 6000 ImageNet reals each. (HF Yanran21/ARForensics, corrected test release, 21 GB, MIT)
|
| 4 |
root_dir: /hddstore/ssalehi/SynthScope/ARForensics
|
| 5 |
hf_repo_id: Yanran21/ARForensics
|
|
|
|
|
|
|
| 6 |
test_batch_size: 512
|
| 7 |
num_workers: 8
|
| 8 |
persistent_workers: true
|
|
|
|
| 3 |
# Open-MAGVIT2), 6000 fakes + 6000 ImageNet reals each. (HF Yanran21/ARForensics, corrected test release, 21 GB, MIT)
|
| 4 |
root_dir: /hddstore/ssalehi/SynthScope/ARForensics
|
| 5 |
hf_repo_id: Yanran21/ARForensics
|
| 6 |
+
train_batch_size: 32 # D3QE's training data (train/val splits) is fetched only when a train_transform_list is set
|
| 7 |
+
val_batch_size: 64
|
| 8 |
test_batch_size: 512
|
| 9 |
num_workers: 8
|
| 10 |
persistent_workers: true
|
conf/dataset/image/mirage_test.yaml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: mirage_test
|
| 2 |
+
# Mirage-Test (OmniAID, arXiv 2511.08423) — real/fake images from recent realism-tuned generators in five categories
|
| 3 |
+
# (Human, Animal, Object, Scene, Anime; Anime is fakes only and is paired with the other categories' reals).
|
| 4 |
+
# (HF Yunncheng/Mirage-Test, MIT)
|
| 5 |
+
root_dir: /hddstore/ssalehi/SynthScope/MirageTest
|
| 6 |
+
hf_repo_id: Yunncheng/Mirage-Test
|
| 7 |
+
test_batch_size: 512
|
| 8 |
+
num_workers: 8
|
| 9 |
+
persistent_workers: true
|
| 10 |
+
pin_memory: true
|
| 11 |
+
prefetch: true
|
| 12 |
+
eval_transform_list:
|
| 13 |
+
- resize_short_256
|
| 14 |
+
- center_crop_224
|
| 15 |
+
- to_dtype
|
| 16 |
+
- normalize_clip
|
conf/model/image/omniaid.yaml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- /model/default
|
| 3 |
+
- _self_
|
| 4 |
+
|
| 5 |
+
# OmniAID v2 (recommended; Mirage-Train, more robust augmentation). Expert configuration of the released checkpoint_omniaid_v2.pth (clean_test.py settings);
|
| 6 |
+
# read from the checkpoint's shapes (experts, rank, router width; top-k from the official config).
|
| 7 |
+
model_type: omniaid
|
| 8 |
+
CLIP_path: openai/clip-vit-large-patch14-336
|
| 9 |
+
num_experts: 6
|
| 10 |
+
rank_per_expert: 1
|
| 11 |
+
moe_router_hidden_dim: 256
|
| 12 |
+
moe_top_k: 2
|
| 13 |
+
moe_lambda_orth: 0.001
|
| 14 |
+
moe_lambda_balance: 0.001
|
| 15 |
+
moe_lambda_gating_cls: 0.1
|
| 16 |
+
dropout: 0.0
|
| 17 |
+
gradient_checkpointing_enable: false
|
| 18 |
+
is_hybrid: true
|
conf/model/image/omniaid_genimage.yaml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- /model/default
|
| 3 |
+
- _self_
|
| 4 |
+
|
| 5 |
+
# OmniAID GenImage-SD v1.4 (the paper's GenImage/Mirage/AIGCDetect tables). Expert configuration of the released checkpoint_omniaid_genimage_sd14.pth (config_omniaid_genimage_paper.json);
|
| 6 |
+
# read from the checkpoint's shapes (experts, rank, router width; top-k from the official config).
|
| 7 |
+
model_type: omniaid
|
| 8 |
+
CLIP_path: openai/clip-vit-large-patch14-336
|
| 9 |
+
num_experts: 3
|
| 10 |
+
rank_per_expert: 4
|
| 11 |
+
moe_router_hidden_dim: 64
|
| 12 |
+
moe_top_k: 1
|
| 13 |
+
moe_lambda_orth: 0.01
|
| 14 |
+
moe_lambda_balance: 0.1
|
| 15 |
+
moe_lambda_gating_cls: 0.1
|
| 16 |
+
dropout: 0.0
|
| 17 |
+
gradient_checkpointing_enable: false
|
| 18 |
+
is_hybrid: true
|
conf/model/image/omniaid_v1.yaml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- /model/default
|
| 3 |
+
- _self_
|
| 4 |
+
|
| 5 |
+
# OmniAID v1 (Mirage-Train, the paper's OmniAID-Mirage). Expert configuration of the released checkpoint_omniaid_v1.pth (config_omniaid_mirage_paper.json);
|
| 6 |
+
# read from the checkpoint's shapes (experts, rank, router width; top-k from the official config).
|
| 7 |
+
model_type: omniaid
|
| 8 |
+
CLIP_path: openai/clip-vit-large-patch14-336
|
| 9 |
+
num_experts: 6
|
| 10 |
+
rank_per_expert: 8
|
| 11 |
+
moe_router_hidden_dim: 256
|
| 12 |
+
moe_top_k: 2
|
| 13 |
+
moe_lambda_orth: 0.001
|
| 14 |
+
moe_lambda_balance: 0.001
|
| 15 |
+
moe_lambda_gating_cls: 0.1
|
| 16 |
+
dropout: 0.0
|
| 17 |
+
gradient_checkpointing_enable: false
|
| 18 |
+
is_hybrid: true
|
conf/trainer/image/d3qe.yaml
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# D3QE's official training recipe (train.py + options.TrainOptions defaults; train.sh only sets the augmentation):
|
| 2 |
+
# AdamW lr 8e-5, betas (0.9, 0.999), weight decay 0.01, batch 32 on one GPU, 10 epochs, no warmup. Validation after
|
| 3 |
+
# every epoch keeps the best-accuracy checkpoint; EarlyStopping (patience 5, delta 0.001, then 0.002 after a drop)
|
| 4 |
+
# divides the learning rate by 10, stopping below 1e-6. The released checkpoint has total_steps 62500 = 10 epochs.
|
| 5 |
+
epochs: 10
|
| 6 |
+
lr: 0.00008
|
| 7 |
+
weight_decay: 0.01
|
| 8 |
+
val_interval: 1
|
| 9 |
+
test_interval: 1
|
| 10 |
+
save_interval: 1
|
| 11 |
+
save: true
|
| 12 |
+
save_policy: max
|
| 13 |
+
save_path: /ssdstore/ssalehi/SynthScope/models/d3qe
|
| 14 |
+
seed: 42
|
| 15 |
+
warmup:
|
| 16 |
+
mode: "off" # quoted: a bare off is YAML boolean False, which the trainer reads as "on"
|
| 17 |
+
warmup_ratio: 0.0
|
| 18 |
+
lr_scheduler: linear
|
| 19 |
+
plateau:
|
| 20 |
+
patience: 5
|
| 21 |
+
delta: 0.001
|
| 22 |
+
delta_after_drop: 0.002
|
| 23 |
+
factor: 10
|
| 24 |
+
min_lr: 0.000001
|
conf/trainer/image/npr.yaml
CHANGED
|
@@ -2,17 +2,25 @@ defaults:
|
|
| 2 |
- /trainer/default
|
| 3 |
- _self_
|
| 4 |
|
|
|
|
|
|
|
|
|
|
| 5 |
trainer_type: npr
|
| 6 |
-
epochs:
|
| 7 |
lr: 2.0e-4
|
| 8 |
-
weight_decay: 0.0
|
| 9 |
label_smoothing: 0.0
|
| 10 |
val_interval: 1
|
| 11 |
test_interval: 1
|
| 12 |
save_interval: 1
|
| 13 |
-
save:
|
| 14 |
save_policy: max
|
|
|
|
|
|
|
| 15 |
warmup:
|
| 16 |
-
mode: off
|
| 17 |
-
warmup_ratio: 0.
|
| 18 |
lr_scheduler: linear
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
- /trainer/default
|
| 3 |
- _self_
|
| 4 |
|
| 5 |
+
# NPR's official training recipe (README: train.py --classes car,cat,chair,horse --batch_size 32 --delr_freq 10
|
| 6 |
+
# --lr 0.0002 --niter 50; networks/trainer.py: Adam, betas (0.9, 0.999), no weight decay): 4-class ProGAN, 50 epochs,
|
| 7 |
+
# learning rate x0.9 after epochs 10, 20, 30, 40; the released checkpoint is the last epoch's model.
|
| 8 |
trainer_type: npr
|
| 9 |
+
epochs: 50
|
| 10 |
lr: 2.0e-4
|
| 11 |
+
weight_decay: 0.0 # AdamW with weight decay 0 == the official Adam
|
| 12 |
label_smoothing: 0.0
|
| 13 |
val_interval: 1
|
| 14 |
test_interval: 1
|
| 15 |
save_interval: 1
|
| 16 |
+
save: true
|
| 17 |
save_policy: max
|
| 18 |
+
save_path: /ssdstore/ssalehi/SynthScope/models/npr
|
| 19 |
+
seed: 100 # the official seed_torch(100)
|
| 20 |
warmup:
|
| 21 |
+
mode: "off" # quoted: a bare off is YAML boolean False, which the trainer reads as "on"
|
| 22 |
+
warmup_ratio: 0.0
|
| 23 |
lr_scheduler: linear
|
| 24 |
+
lr_step:
|
| 25 |
+
every: 10
|
| 26 |
+
factor: 0.9
|
conf/trainer/image/pgc.yaml
CHANGED
|
@@ -2,11 +2,15 @@ defaults:
|
|
| 2 |
- /trainer/default
|
| 3 |
- _self_
|
| 4 |
|
| 5 |
-
# PGC
|
| 6 |
-
#
|
| 7 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
trainer_type: pgc
|
| 9 |
-
epochs:
|
| 10 |
lr: 5.0e-5
|
| 11 |
weight_decay: 0.05
|
| 12 |
label_smoothing: 0.1
|
|
@@ -15,7 +19,14 @@ test_interval: 1
|
|
| 15 |
save_interval: 1
|
| 16 |
save: true
|
| 17 |
save_policy: max
|
|
|
|
|
|
|
| 18 |
warmup:
|
| 19 |
-
mode:
|
| 20 |
-
warmup_ratio: 0.
|
| 21 |
lr_scheduler: cosine
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
- /trainer/default
|
| 3 |
- _self_
|
| 4 |
|
| 5 |
+
# PGC's official training recipe (xiaoyu6868/PGC train.py + engine/trainer.py, README 3.2): AdamW lr 5e-5, weight
|
| 6 |
+
# decay 0.05, betas (0.9, 0.999), label smoothing 0.1 (in the model's compute_loss), micro-batch 32 x 4 accumulation
|
| 7 |
+
# = 128, gradient-norm clipping 1.0, seed 42. The released ProGAN checkpoint is only ~400 steps in (the authors:
|
| 8 |
+
# DINOv2's prior overfits ProGAN within the first epoch), so the run is a fixed budget of micro-batches with a
|
| 9 |
+
# checkpoint every 100, and the early curve is evaluated. Official cosine schedule: T_max = 100 epochs of
|
| 10 |
+
# micro-batches but stepped once per optimizer step, so over the first 2000 micro-batches the LR stays at 5e-5
|
| 11 |
+
# (>99.99%); it is run here at constant 5e-5.
|
| 12 |
trainer_type: pgc
|
| 13 |
+
epochs: 1
|
| 14 |
lr: 5.0e-5
|
| 15 |
weight_decay: 0.05
|
| 16 |
label_smoothing: 0.1
|
|
|
|
| 19 |
save_interval: 1
|
| 20 |
save: true
|
| 21 |
save_policy: max
|
| 22 |
+
save_path: /ssdstore/ssalehi/SynthScope/models/pgc
|
| 23 |
+
seed: 42
|
| 24 |
warmup:
|
| 25 |
+
mode: "off" # quoted: a bare off is YAML boolean False
|
| 26 |
+
warmup_ratio: 0.0
|
| 27 |
lr_scheduler: cosine
|
| 28 |
+
step_budget:
|
| 29 |
+
max_steps: 2000 # micro-batches
|
| 30 |
+
save_every: 100
|
| 31 |
+
grad_accum_steps: 4
|
| 32 |
+
max_grad_norm: 1.0
|
dataset.py
CHANGED
|
@@ -903,7 +903,8 @@ class CNNDetectionDataset(Dataset):
|
|
| 903 |
"test": ("cnn_synth_testset", "test"),
|
| 904 |
}
|
| 905 |
|
| 906 |
-
def __init__(self, root_dir: str | Path, split: Literal["train", "val", "test"], transform: AIGITransformations | None = None, prefetch: bool = False
|
|
|
|
| 907 |
super().__init__()
|
| 908 |
self.root_dir = Path(root_dir)
|
| 909 |
self.split = split
|
|
@@ -914,6 +915,12 @@ class CNNDetectionDataset(Dataset):
|
|
| 914 |
print(f"Downloading {self.split} split from Google Drive...")
|
| 915 |
self._download_data()
|
| 916 |
self.images, self.labels, self.folder_names = self._load_data()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 917 |
self.unique_folder_names = sorted(set(self.folder_names)) or [self.split]
|
| 918 |
|
| 919 |
@classmethod
|
|
@@ -1081,8 +1088,10 @@ class CNNDetectionDatasetModule:
|
|
| 1081 |
self.train_transform = AIGITransformations(train_list) if len(train_list) > 0 else None
|
| 1082 |
self.eval_transform = AIGITransformations(eval_list) if len(eval_list) > 0 else None
|
| 1083 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1084 |
-
|
| 1085 |
-
|
|
|
|
|
|
|
| 1086 |
self.test_dataset = CNNDetectionDataset(config.DatasetConfig.root_dir, "test", self.eval_transform, prefetch=prefetch)
|
| 1087 |
|
| 1088 |
def train_dataloader(self):
|
|
@@ -2626,9 +2635,12 @@ class ARForensicsDataset(_TestOnlyImageDataset):
|
|
| 2626 |
Janus-Pro, RAR, Switti, Open-MAGVIT2), 6000 fakes and 6000 ImageNet reals each, laid out as
|
| 2627 |
``ARForensics/test/<generator>/{0_real,1_fake}``. Sourced from the HF dataset ``Yanran21/ARForensics`` (MIT): the
|
| 2628 |
corrected test release (MAR replaced by Switti) in four independent zips, ARForensics_updated_test_part00{1..4}.zip
|
| 2629 |
-
(21 GB); a generator can span two zips, so all four are extracted into one folder.
|
|
|
|
|
|
|
| 2630 |
hf_repo_id = "Yanran21/ARForensics"
|
| 2631 |
-
|
|
|
|
| 2632 |
|
| 2633 |
@classmethod
|
| 2634 |
def prepare_split(cls, root_dir, split="test", force=False):
|
|
@@ -2636,20 +2648,70 @@ class ARForensicsDataset(_TestOnlyImageDataset):
|
|
| 2636 |
if not force and _has_images(dest):
|
| 2637 |
return
|
| 2638 |
from huggingface_hub import snapshot_download
|
| 2639 |
-
|
| 2640 |
-
|
| 2641 |
-
|
| 2642 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2643 |
_safe_extract_zip(archive, staging)
|
| 2644 |
-
inner = staging / "ARForensics" /
|
| 2645 |
if not inner.is_dir():
|
| 2646 |
-
raise RuntimeError(f"ARForensics: expected ARForensics/
|
| 2647 |
if dest.exists():
|
| 2648 |
shutil.rmtree(dest)
|
| 2649 |
shutil.move(str(inner), str(dest))
|
| 2650 |
shutil.rmtree(dl, ignore_errors=True); shutil.rmtree(staging, ignore_errors=True)
|
| 2651 |
|
| 2652 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2653 |
class _TestOnlyImageModule:
|
| 2654 |
"""Shared loader for the eval-only image benchmarks (test split only)."""
|
| 2655 |
DATASET_CLASS = _TestOnlyImageDataset
|
|
@@ -2724,8 +2786,36 @@ class RRDatasetModule(_TestOnlyImageModule):
|
|
| 2724 |
|
| 2725 |
|
| 2726 |
class ARForensicsDatasetModule(_TestOnlyImageModule):
|
|
|
|
|
|
|
| 2727 |
DATASET_CLASS = ARForensicsDataset
|
| 2728 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2729 |
|
| 2730 |
class VideoFeedbackDataset(VideoFolderDataset):
|
| 2731 |
"""VideoFeedback (TIGER-Lab) — used by STALL. 11 generators vs DiDeMo+Panda70M real, laid out
|
|
@@ -2954,6 +3044,7 @@ SynthScopeModuleDict = {
|
|
| 2954 |
"bfree_online": BFreeOnlineDatasetModule,
|
| 2955 |
"rrdataset": RRDatasetModule,
|
| 2956 |
"arforensics": ARForensicsDatasetModule,
|
|
|
|
| 2957 |
# Video benchmarks. EvalCrafter/GenVidBench/VideoFeedback/ComGenVid have dedicated classes that
|
| 2958 |
# download + extract their HF archives into the real/fake layout; the rest use the generic
|
| 2959 |
# VideoFolderDatasetModule.
|
|
|
|
| 903 |
"test": ("cnn_synth_testset", "test"),
|
| 904 |
}
|
| 905 |
|
| 906 |
+
def __init__(self, root_dir: str | Path, split: Literal["train", "val", "test"], transform: AIGITransformations | None = None, prefetch: bool = False,
|
| 907 |
+
classes: list[str] | None = None):
|
| 908 |
super().__init__()
|
| 909 |
self.root_dir = Path(root_dir)
|
| 910 |
self.split = split
|
|
|
|
| 915 |
print(f"Downloading {self.split} split from Google Drive...")
|
| 916 |
self._download_data()
|
| 917 |
self.images, self.labels, self.folder_names = self._load_data()
|
| 918 |
+
if classes: # e.g. the 4-class ProGAN setting (car, cat, chair, horse) of CNNDetection/NPR/FatFormer
|
| 919 |
+
keep = [i for i, f in enumerate(self.folder_names) if f in set(classes)]
|
| 920 |
+
if not keep:
|
| 921 |
+
raise ValueError(f"CNNDetection {split}: none of the classes {list(classes)} found")
|
| 922 |
+
self.images = [self.images[i] for i in keep]; self.labels = [self.labels[i] for i in keep]
|
| 923 |
+
self.folder_names = [self.folder_names[i] for i in keep]
|
| 924 |
self.unique_folder_names = sorted(set(self.folder_names)) or [self.split]
|
| 925 |
|
| 926 |
@classmethod
|
|
|
|
| 1088 |
self.train_transform = AIGITransformations(train_list) if len(train_list) > 0 else None
|
| 1089 |
self.eval_transform = AIGITransformations(eval_list) if len(eval_list) > 0 else None
|
| 1090 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1091 |
+
classes = config.DatasetConfig.get("train_classes", None) # ProGAN classes for train/val (None: all 20)
|
| 1092 |
+
classes = list(classes) if classes else None
|
| 1093 |
+
self.train_dataset = CNNDetectionDataset(config.DatasetConfig.root_dir, "train", self.train_transform, prefetch=prefetch, classes=classes)
|
| 1094 |
+
self.val_dataset = CNNDetectionDataset(config.DatasetConfig.root_dir, "val", self.eval_transform, prefetch=prefetch, classes=classes)
|
| 1095 |
self.test_dataset = CNNDetectionDataset(config.DatasetConfig.root_dir, "test", self.eval_transform, prefetch=prefetch)
|
| 1096 |
|
| 1097 |
def train_dataloader(self):
|
|
|
|
| 2635 |
Janus-Pro, RAR, Switti, Open-MAGVIT2), 6000 fakes and 6000 ImageNet reals each, laid out as
|
| 2636 |
``ARForensics/test/<generator>/{0_real,1_fake}``. Sourced from the HF dataset ``Yanran21/ARForensics`` (MIT): the
|
| 2637 |
corrected test release (MAR replaced by Switti) in four independent zips, ARForensics_updated_test_part00{1..4}.zip
|
| 2638 |
+
(21 GB); a generator can span two zips, so all four are extracted into one folder. D3QE's training data is in the
|
| 2639 |
+
same repo: ``train`` (100k LlamaGen fakes + 100k ImageNet reals; one zip split into 7 parts, 35 GB) and ``val``
|
| 2640 |
+
(10k + 10k, 3.7 GB), each laid out as ``ARForensics/<split>/{0_real,1_fake}``."""
|
| 2641 |
hf_repo_id = "Yanran21/ARForensics"
|
| 2642 |
+
hf_patterns = {"test": "ARForensics_updated_test_part*.zip", "val": "val/ARForensics_val.zip",
|
| 2643 |
+
"train": "train/ARForensics_train_split.zip.a?"}
|
| 2644 |
|
| 2645 |
@classmethod
|
| 2646 |
def prepare_split(cls, root_dir, split="test", force=False):
|
|
|
|
| 2648 |
if not force and _has_images(dest):
|
| 2649 |
return
|
| 2650 |
from huggingface_hub import snapshot_download
|
| 2651 |
+
pattern = cls.hf_patterns[split]
|
| 2652 |
+
dl = root_dir / f"_downloads_{split}"; dl.mkdir(parents=True, exist_ok=True)
|
| 2653 |
+
snapshot_download(repo_id=cls.hf_repo_id, repo_type="dataset", local_dir=str(dl), allow_patterns=[pattern])
|
| 2654 |
+
archives = sorted(dl.glob(pattern))
|
| 2655 |
+
if split == "train": # a single zip split into parts: join them first
|
| 2656 |
+
joined = dl / "train_joined.zip"
|
| 2657 |
+
with open(joined, "wb") as out:
|
| 2658 |
+
for part in archives:
|
| 2659 |
+
with open(part, "rb") as f:
|
| 2660 |
+
shutil.copyfileobj(f, out, 1 << 24)
|
| 2661 |
+
part.unlink()
|
| 2662 |
+
archives = [joined]
|
| 2663 |
+
staging = root_dir / f"_staging_{split}"
|
| 2664 |
+
for archive in archives:
|
| 2665 |
_safe_extract_zip(archive, staging)
|
| 2666 |
+
inner = staging / "ARForensics" / split
|
| 2667 |
if not inner.is_dir():
|
| 2668 |
+
raise RuntimeError(f"ARForensics: expected ARForensics/{split}/ inside the archives, found {list(staging.iterdir())}")
|
| 2669 |
if dest.exists():
|
| 2670 |
shutil.rmtree(dest)
|
| 2671 |
shutil.move(str(inner), str(dest))
|
| 2672 |
shutil.rmtree(dl, ignore_errors=True); shutil.rmtree(staging, ignore_errors=True)
|
| 2673 |
|
| 2674 |
|
| 2675 |
+
class MirageTestDataset(_TestOnlyImageDataset):
|
| 2676 |
+
"""Mirage-Test (OmniAID, arXiv 2511.08423) — real and fake images from recent generators tuned for realism, in five
|
| 2677 |
+
content categories laid out as ``<category>/{0_real,1_fake}`` (Human, Animal, Object, Scene, Anime). Sourced from the
|
| 2678 |
+
HF dataset ``Yunncheng/Mirage-Test`` (MIT). Anime ships fakes only ("due to copyright considerations") and the paper
|
| 2679 |
+
scores it by its fake accuracy, so the loader pairs Anime with the reals of the other four categories: its
|
| 2680 |
+
``fake_acc`` is the paper's Anime number, and the other categories' balanced accuracy is the paper's "Overall"."""
|
| 2681 |
+
hf_repo_id = "Yunncheng/Mirage-Test"
|
| 2682 |
+
categories = ("Human", "Animal", "Object", "Scene", "Anime")
|
| 2683 |
+
|
| 2684 |
+
@classmethod
|
| 2685 |
+
def prepare_split(cls, root_dir, split="test", force=False):
|
| 2686 |
+
root_dir = Path(root_dir); dest = root_dir / split
|
| 2687 |
+
if not force and _has_images(dest):
|
| 2688 |
+
return
|
| 2689 |
+
from huggingface_hub import snapshot_download
|
| 2690 |
+
dl = root_dir / "_downloads"; dl.mkdir(parents=True, exist_ok=True)
|
| 2691 |
+
snapshot_download(repo_id=cls.hf_repo_id, repo_type="dataset", local_dir=str(dl),
|
| 2692 |
+
allow_patterns=[f"{c}/*" for c in cls.categories])
|
| 2693 |
+
if dest.exists():
|
| 2694 |
+
shutil.rmtree(dest)
|
| 2695 |
+
dest.mkdir(parents=True)
|
| 2696 |
+
for c in cls.categories:
|
| 2697 |
+
shutil.move(str(dl / c), str(dest / c))
|
| 2698 |
+
reals = sorted(p for c in cls.categories if c != "Anime"
|
| 2699 |
+
for p in (dest / c / "0_real").rglob("*") if p.suffix.lower() in IMAGE_EXTENSIONS)
|
| 2700 |
+
_link_images(reals, dest / "Anime" / "0_real")
|
| 2701 |
+
shutil.rmtree(dl, ignore_errors=True)
|
| 2702 |
+
|
| 2703 |
+
def _load_data(self):
|
| 2704 |
+
# Fakes are nested one level deeper, by generator (``Human/1_fake/<generator>/*.jpg``): the label comes from the
|
| 2705 |
+
# 0_real/1_fake ancestor and the group from the category folder.
|
| 2706 |
+
images, labels, folder_names = [], [], []
|
| 2707 |
+
for filepath in _iter_images_follow(self.destination_dir):
|
| 2708 |
+
parts = filepath.relative_to(self.destination_dir).parts
|
| 2709 |
+
images.append(filepath)
|
| 2710 |
+
labels.append(1 if "1_fake" in parts else 0)
|
| 2711 |
+
folder_names.append(parts[0])
|
| 2712 |
+
return images, labels, folder_names
|
| 2713 |
+
|
| 2714 |
+
|
| 2715 |
class _TestOnlyImageModule:
|
| 2716 |
"""Shared loader for the eval-only image benchmarks (test split only)."""
|
| 2717 |
DATASET_CLASS = _TestOnlyImageDataset
|
|
|
|
| 2786 |
|
| 2787 |
|
| 2788 |
class ARForensicsDatasetModule(_TestOnlyImageModule):
|
| 2789 |
+
"""The test split for benchmarking; with a ``train_transform_list`` in the config also the train and val splits
|
| 2790 |
+
(D3QE's training data), validated with ``val_transform_list`` (default: ``eval_transform_list``)."""
|
| 2791 |
DATASET_CLASS = ARForensicsDataset
|
| 2792 |
|
| 2793 |
+
def __init__(self, config):
|
| 2794 |
+
super().__init__(config)
|
| 2795 |
+
cfg = config.DatasetConfig
|
| 2796 |
+
train_list = list(cfg.get("train_transform_list", []))
|
| 2797 |
+
self.train_dataset = self.val_dataset = None
|
| 2798 |
+
if train_list:
|
| 2799 |
+
prefetch = bool(cfg.get("prefetch", False))
|
| 2800 |
+
val_list = list(cfg.get("val_transform_list", cfg.get("eval_transform_list", [])))
|
| 2801 |
+
self.train_dataset = ARForensicsDataset(cfg.root_dir, "train", AIGITransformations(train_list), prefetch=prefetch)
|
| 2802 |
+
self.val_dataset = ARForensicsDataset(cfg.root_dir, "val", AIGITransformations(val_list), prefetch=prefetch)
|
| 2803 |
+
|
| 2804 |
+
def _loader(self, dataset, batch_size, shuffle):
|
| 2805 |
+
cfg = self.config.DatasetConfig
|
| 2806 |
+
return DataLoader(dataset, batch_size=batch_size, shuffle=shuffle, num_workers=cfg.num_workers,
|
| 2807 |
+
persistent_workers=cfg.persistent_workers, pin_memory=cfg.pin_memory)
|
| 2808 |
+
|
| 2809 |
+
def train_dataloader(self):
|
| 2810 |
+
return None if self.train_dataset is None else self._loader(self.train_dataset, self.config.DatasetConfig.train_batch_size, True)
|
| 2811 |
+
|
| 2812 |
+
def val_dataloader(self):
|
| 2813 |
+
return None if self.val_dataset is None else self._loader(self.val_dataset, self.config.DatasetConfig.val_batch_size, False)
|
| 2814 |
+
|
| 2815 |
+
|
| 2816 |
+
class MirageTestDatasetModule(_TestOnlyImageModule):
|
| 2817 |
+
DATASET_CLASS = MirageTestDataset
|
| 2818 |
+
|
| 2819 |
|
| 2820 |
class VideoFeedbackDataset(VideoFolderDataset):
|
| 2821 |
"""VideoFeedback (TIGER-Lab) — used by STALL. 11 generators vs DiDeMo+Panda70M real, laid out
|
|
|
|
| 3044 |
"bfree_online": BFreeOnlineDatasetModule,
|
| 3045 |
"rrdataset": RRDatasetModule,
|
| 3046 |
"arforensics": ARForensicsDatasetModule,
|
| 3047 |
+
"mirage_test": MirageTestDatasetModule,
|
| 3048 |
# Video benchmarks. EvalCrafter/GenVidBench/VideoFeedback/ComGenVid have dedicated classes that
|
| 3049 |
# download + extract their HF archives into the real/fake layout; the rest use the generic
|
| 3050 |
# VideoFolderDatasetModule.
|
deploy/hf-space/fetch_weights.py
CHANGED
|
@@ -15,8 +15,8 @@ sys.path.insert(0, str(ROOT))
|
|
| 15 |
|
| 16 |
# Detectors whose upstream licenses allow a free public demo. NPR's repository has no license; its first author gave
|
| 17 |
# written permission (2026-10-11) to use NPR.pth in this free, non-commercial demo with credit (see LICENSES.md).
|
| 18 |
-
DEFAULT = ["clip", "cnn", "npr", "safe", "aide", "co_spy", "fat_former", "pgc", "cure", "dda", "d3qe", "
|
| 19 |
-
"dinov2", "adapted_dino", "restrav"]
|
| 20 |
|
| 21 |
|
| 22 |
def main() -> None:
|
|
|
|
| 15 |
|
| 16 |
# Detectors whose upstream licenses allow a free public demo. NPR's repository has no license; its first author gave
|
| 17 |
# written permission (2026-10-11) to use NPR.pth in this free, non-commercial demo with credit (see LICENSES.md).
|
| 18 |
+
DEFAULT = ["clip", "cnn", "npr", "safe", "aide", "co_spy", "fat_former", "pgc", "cure", "dda", "d3qe", "omniaid",
|
| 19 |
+
"vib_net", "dinov2", "adapted_dino", "restrav"]
|
| 20 |
|
| 21 |
|
| 22 |
def main() -> None:
|
docs/BENCHMARKS.md
CHANGED
|
@@ -86,6 +86,7 @@ have no automatic source: get them from their official sites and place them in t
|
|
| 86 |
| `bfree_online` | BFree-Online (B-Free's viral images; used by DDA and CuRe): real and fake images that went viral, several web-scraped copies of each (about 1400 in the paper). Only URLs are released; the loader keeps an image only if its md5 matches, so dead links and placeholders are skipped (`<root>/missing.txt`; in October 2026, 807 of the 1111 listed were still online: 269 real, 538 fake). One group, `test/viral` | `viral_images_dataset/BFree_viral_images.csv` in `grip-unina/B-Free` (nonprofit use only) |
|
| 87 |
| `rrdataset` | RRDataset (ICCV 2025; used by CuRe): three scenarios, `original`, `transfer` (shared over online platforms) and `redigital` (re-digitized), about 8500 real and 8500 fake each. Only the test archive is used | Zenodo record `14963880` (`RRDataset_test.tar.gz`, 20 GB, CC BY 4.0) |
|
| 88 |
| `arforensics` | ARForensics (ICCV 2025; used by D3QE): images from 7 visual autoregressive generators, LlamaGen, VAR, Infinity, Janus-Pro, RAR, Switti and Open-MAGVIT2, 6000 fakes and 6000 ImageNet reals each. The corrected test release (MAR replaced by Switti) | HF `Yanran21/ARForensics` (`ARForensics_updated_test_part00{1..4}.zip`, 21 GB, MIT) |
|
|
|
|
| 89 |
| `diffusionforensics`, `diffusion1kstep`, `freqnetdetection` | DiffusionForensics, Diffusion-1kStep, FreqNet sets | see the yamls |
|
| 90 |
| `genvideo` | GenVideo-Val (DeMamba; D3 Table 1, STALL): 10 generators vs 10,000 MSR-VTT reals, shared reals hard-linked into every generator | ModelScope `cccnju/Gen-Video` (`GenVideo-Val.zip`, 14 GB) |
|
| 91 |
| `videophy` | VideoPhy test set (D3 Table 3): 3360 clips from 10 generators, released as URLs; dead links are skipped (both CogVideoX sets were offline in October 2026). Fake-only, so paired with GenVideo-Val's MSR-VTT reals, as D3 does | `videophysics/videophy_test_public` (MIT) + ModelScope for the reals |
|
|
|
|
| 86 |
| `bfree_online` | BFree-Online (B-Free's viral images; used by DDA and CuRe): real and fake images that went viral, several web-scraped copies of each (about 1400 in the paper). Only URLs are released; the loader keeps an image only if its md5 matches, so dead links and placeholders are skipped (`<root>/missing.txt`; in October 2026, 807 of the 1111 listed were still online: 269 real, 538 fake). One group, `test/viral` | `viral_images_dataset/BFree_viral_images.csv` in `grip-unina/B-Free` (nonprofit use only) |
|
| 87 |
| `rrdataset` | RRDataset (ICCV 2025; used by CuRe): three scenarios, `original`, `transfer` (shared over online platforms) and `redigital` (re-digitized), about 8500 real and 8500 fake each. Only the test archive is used | Zenodo record `14963880` (`RRDataset_test.tar.gz`, 20 GB, CC BY 4.0) |
|
| 88 |
| `arforensics` | ARForensics (ICCV 2025; used by D3QE): images from 7 visual autoregressive generators, LlamaGen, VAR, Infinity, Janus-Pro, RAR, Switti and Open-MAGVIT2, 6000 fakes and 6000 ImageNet reals each. The corrected test release (MAR replaced by Switti) | HF `Yanran21/ARForensics` (`ARForensics_updated_test_part00{1..4}.zip`, 21 GB, MIT) |
|
| 89 |
+
| `mirage_test` | Mirage-Test (OmniAID, arXiv 2511.08423): real and fake images from recent generators tuned for realism, in five categories: Human, Animal, Object, Scene and Anime. Anime is fakes only, so the loader pairs it with the other categories' reals; its fake accuracy is the paper's Anime number | HF `Yunncheng/Mirage-Test` (49k files, MIT) |
|
| 90 |
| `diffusionforensics`, `diffusion1kstep`, `freqnetdetection` | DiffusionForensics, Diffusion-1kStep, FreqNet sets | see the yamls |
|
| 91 |
| `genvideo` | GenVideo-Val (DeMamba; D3 Table 1, STALL): 10 generators vs 10,000 MSR-VTT reals, shared reals hard-linked into every generator | ModelScope `cccnju/Gen-Video` (`GenVideo-Val.zip`, 14 GB) |
|
| 92 |
| `videophy` | VideoPhy test set (D3 Table 3): 3360 clips from 10 generators, released as URLs; dead links are skipped (both CogVideoX sets were offline in October 2026). Fake-only, so paired with GenVideo-Val's MSR-VTT reals, as D3 does | `videophysics/videophy_test_public` (MIT) + ModelScope for the reals |
|
docs/CAPABILITIES.md
CHANGED
|
@@ -30,6 +30,7 @@ matching `conf/model/<modality>/<key>.yaml`, a `trainer.py._build_model` branch,
|
|
| 30 |
| `pgc` | image | bce | official | pgc_peak | LoRA-DINOv2 + YCbCr residual, peak-guided |
|
| 31 |
| `cure` | image | ce | official | – | Frozen PE-Core L/14 + Q/V LoRA + source-response subspace (mixing-ratio regression) |
|
| 32 |
| `d3qe` | image | bce | official | – | LlamaGen VQ quantization error + codebook-aware transformer + CLIP ViT-L/14 (autoregressive images) |
|
|
|
|
| 33 |
| `dda` | image | bce | official | – | DINOv2 ViT-L/14 + LoRA, pixel- and frequency-aligned training data (center-crop 336) |
|
| 34 |
| `vib_net` | image | vib | synthscope | – | Variational information bottleneck (FLAVA) |
|
| 35 |
| `d3` | video | video | training-free | – | Disentangled detail dynamics (X-CLIP) |
|
|
|
|
| 30 |
| `pgc` | image | bce | official | pgc_peak | LoRA-DINOv2 + YCbCr residual, peak-guided |
|
| 31 |
| `cure` | image | ce | official | – | Frozen PE-Core L/14 + Q/V LoRA + source-response subspace (mixing-ratio regression) |
|
| 32 |
| `d3qe` | image | bce | official | – | LlamaGen VQ quantization error + codebook-aware transformer + CLIP ViT-L/14 (autoregressive images) |
|
| 33 |
+
| `omniaid` (+ `omniaid_v1`, `omniaid_genimage`) | image | ce | official | – | Hybrid MoE on CLIP ViT-L/14@336: routed semantic experts + an always-on artifact expert |
|
| 34 |
| `dda` | image | bce | official | – | DINOv2 ViT-L/14 + LoRA, pixel- and frequency-aligned training data (center-crop 336) |
|
| 35 |
| `vib_net` | image | vib | synthscope | – | Variational information bottleneck (FLAVA) |
|
| 36 |
| `d3` | video | video | training-free | – | Disentangled detail dynamics (X-CLIP) |
|
docs/WILD_BENCHMARKS.md
CHANGED
|
@@ -14,6 +14,7 @@ Each number comes from `repro/bench_results/<model>_<dataset>.json`; regenerate
|
|
| 14 |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
| 15 |
| CuRe | **79.7 / 85.5** | **98.1 / 99.7** | **92.7 / 96.5** | **95.9 / 99.1** | **91.6 / 98.1** | **82.0 / 88.9** | 90.0 |
|
| 16 |
| DDA | 67.2 / 75.0 | **97.9 / 99.8** | **90.8 / 96.4** | **90.2 / 96.1** | **95.0 / 98.9** | **76.0 / 81.7** | 86.2 |
|
|
|
|
| 17 |
| SPAI | 61.6 / 69.0 | **93.8 / 98.7** | **76.2 / 84.6** | **80.6 / 88.4** | 59.8 / 60.2 | 64.8 / 72.0 | 72.8 |
|
| 18 |
| Co-Spy | 71.0 / 76.5 | **96.7 / 99.5** | 64.3 / 69.5 | 61.6 / 71.2 | 56.3 / 73.3 | 66.8 / 73.9 | 69.4 |
|
| 19 |
| VIB-Net | 65.8 / 74.3 | **94.8 / 99.7** | 58.5 / 73.6 | 56.1 / 71.3 | 51.9 / 53.6 | 60.0 / 66.4 | 64.5 |
|
|
|
|
| 14 |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
| 15 |
| CuRe | **79.7 / 85.5** | **98.1 / 99.7** | **92.7 / 96.5** | **95.9 / 99.1** | **91.6 / 98.1** | **82.0 / 88.9** | 90.0 |
|
| 16 |
| DDA | 67.2 / 75.0 | **97.9 / 99.8** | **90.8 / 96.4** | **90.2 / 96.1** | **95.0 / 98.9** | **76.0 / 81.7** | 86.2 |
|
| 17 |
+
| OmniAID (v2) | 61.7 / 83.6 | 74.1 / 93.1 | **86.0 / 95.3** | **88.4 / 96.9** | 70.3 / 87.2 | 71.8 / 82.3 | 75.4 |
|
| 18 |
| SPAI | 61.6 / 69.0 | **93.8 / 98.7** | **76.2 / 84.6** | **80.6 / 88.4** | 59.8 / 60.2 | 64.8 / 72.0 | 72.8 |
|
| 19 |
| Co-Spy | 71.0 / 76.5 | **96.7 / 99.5** | 64.3 / 69.5 | 61.6 / 71.2 | 56.3 / 73.3 | 66.8 / 73.9 | 69.4 |
|
| 20 |
| VIB-Net | 65.8 / 74.3 | **94.8 / 99.7** | 58.5 / 73.6 | 56.1 / 71.3 | 51.9 / 53.6 | 60.0 / 66.4 | 64.5 |
|
experiments/image/d3qe.sh
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# D3QE (ICCV 2025): the official training recipe, reproduced in SynthScope.
|
| 3 |
+
# data ARForensics train (100k LlamaGen vs 100k ImageNet) and val (10k + 10k); downloaded on first use
|
| 4 |
+
# model frozen LlamaGen VQ-16 + frozen CLIP ViT-L/14; only the D3AT transformer and the heads are trained
|
| 5 |
+
# train short side 256 -> CNNDetection blur/JPEG (p 0.1 each) -> RandomCrop 256 -> flip -> [0, 1], no normalization
|
| 6 |
+
# val short side 256 -> the same blur/JPEG at fixed settings (sigma 1.5, PIL q65) -> CenterCrop 256
|
| 7 |
+
# optimizer AdamW lr 8e-5, weight decay 0.01, batch 32 on ONE GPU (BatchNorm in the heads), 10 epochs,
|
| 8 |
+
# best-validation-accuracy checkpoint, learning rate /10 after 5 epochs without improvement
|
| 9 |
+
# Evaluate the result with: python benchmark/bench_eval.py --model d3qe --ckpt <save_path>/model_best.pth --dataset arforensics
|
| 10 |
+
export TRITON_CACHE_DIR=${TRITON_CACHE_DIR:-/nvmestore/ssalehi/.triton}
|
| 11 |
+
export HF_HOME=${HF_HOME:-/nvmestore/ssalehi/.cache/huggingface}
|
| 12 |
+
export TORCH_HOME=${TORCH_HOME:-/nvmestore/ssalehi/.cache/torch}
|
| 13 |
+
export CUDA_VISIBLE_DEVICES=${CUDA_VISIBLE_DEVICES:-0}
|
| 14 |
+
export OMP_NUM_THREADS=1
|
| 15 |
+
DATA=${DATA:-/nvmestore/ssalehi/SynthScope/ARForensics}
|
| 16 |
+
SAVE=${SAVE:-/nvmestore/ssalehi/SynthScope/models/d3qe}
|
| 17 |
+
|
| 18 |
+
# Plain single-process run (no `accelerate launch`): the repo's accelerate_config.yaml uses DeepSpeed with gradient
|
| 19 |
+
# clipping 1.0, which the official recipe does not have.
|
| 20 |
+
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope python main.py \
|
| 21 |
+
trainer@TrainerConfig=image/d3qe \
|
| 22 |
+
model@ModelConfig=image/d3qe \
|
| 23 |
+
dataset@DatasetConfig=image/arforensics \
|
| 24 |
+
DatasetConfig.root_dir=$DATA \
|
| 25 |
+
DatasetConfig.train_batch_size=32 \
|
| 26 |
+
DatasetConfig.val_batch_size=64 \
|
| 27 |
+
"+DatasetConfig.train_transform_list=[resize_short_256,blur_jpeg_cnndet,random_crop_256,random_horizontal_flip,to_dtype]" \
|
| 28 |
+
"+DatasetConfig.val_transform_list=[resize_short_256,blur_jpeg_cnndet_val,center_crop_256,to_dtype]" \
|
| 29 |
+
"DatasetConfig.eval_transform_list=[resize_short_256,center_crop_256,to_dtype]" \
|
| 30 |
+
TrainerConfig.save_path=$SAVE \
|
| 31 |
+
"metrics=[real_accuracy,fake_accuracy]" \
|
| 32 |
+
logger.name=d3qe \
|
| 33 |
+
logger.mode=offline \
|
| 34 |
+
"logger.tags=[SynthScope,D3QE,arforensics]"
|
experiments/image/npr.sh
CHANGED
|
@@ -1,20 +1,34 @@
|
|
| 1 |
#!/bin/bash
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
export OMP_NUM_THREADS=1
|
|
|
|
|
|
|
| 7 |
|
| 8 |
-
#
|
| 9 |
-
|
|
|
|
| 10 |
trainer@TrainerConfig=image/npr \
|
| 11 |
model@ModelConfig=image/npr \
|
| 12 |
-
dataset@DatasetConfig=image/
|
| 13 |
-
DatasetConfig.
|
| 14 |
-
DatasetConfig.
|
| 15 |
-
DatasetConfig.
|
| 16 |
-
DatasetConfig.
|
| 17 |
-
DatasetConfig.
|
|
|
|
|
|
|
|
|
|
| 18 |
logger.name=npr \
|
| 19 |
-
logger.mode=
|
| 20 |
-
logger.tags=[SynthScope,NPR,
|
|
|
|
| 1 |
#!/bin/bash
|
| 2 |
+
# NPR (CVPR 2024): the official training recipe, reproduced in SynthScope.
|
| 3 |
+
# data ForenSynths / CNNDetection ProGAN train + val, 4-class setting (car, cat, chair, horse)
|
| 4 |
+
# train Resize((256, 256)) -> RandomCrop 224 -> flip -> ImageNet normalization (no blur/JPEG: the official
|
| 5 |
+
# data_augment call is commented out); NPR's neighboring-pixel residual is computed inside the model
|
| 6 |
+
# val Resize((256, 256)) -> CenterCrop 224 -> ImageNet normalization
|
| 7 |
+
# optimizer Adam lr 2e-4 (AdamW, weight decay 0), batch 32 on ONE GPU, 50 epochs, lr x0.9 every 10 epochs
|
| 8 |
+
# checkpoint model_last.pth (the official code releases the last epoch); model_best.pth is kept for reference
|
| 9 |
+
# Evaluate with: python benchmark/bench_eval.py --model npr --ckpt <save_path>/model_last.pth --benchmark forensynths --data <CNNDetection/test>
|
| 10 |
+
export TRITON_CACHE_DIR=${TRITON_CACHE_DIR:-/nvmestore/ssalehi/.triton}
|
| 11 |
+
export HF_HOME=${HF_HOME:-/nvmestore/ssalehi/.cache/huggingface}
|
| 12 |
+
export TORCH_HOME=${TORCH_HOME:-/nvmestore/ssalehi/.cache/torch}
|
| 13 |
+
export CUDA_VISIBLE_DEVICES=${CUDA_VISIBLE_DEVICES:-0}
|
| 14 |
export OMP_NUM_THREADS=1
|
| 15 |
+
DATA=${DATA:-/nvmestore/ssalehi/SynthScope/CNNDetection}
|
| 16 |
+
SAVE=${SAVE:-/nvmestore/ssalehi/SynthScope/models/npr}
|
| 17 |
|
| 18 |
+
# Plain single-process run (no `accelerate launch`): the repo's accelerate_config.yaml uses DeepSpeed with gradient
|
| 19 |
+
# clipping 1.0, which the official recipe does not have.
|
| 20 |
+
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope python main.py \
|
| 21 |
trainer@TrainerConfig=image/npr \
|
| 22 |
model@ModelConfig=image/npr \
|
| 23 |
+
dataset@DatasetConfig=image/cnndetection \
|
| 24 |
+
DatasetConfig.root_dir=$DATA \
|
| 25 |
+
"+DatasetConfig.train_classes=[car,cat,chair,horse]" \
|
| 26 |
+
DatasetConfig.train_batch_size=32 \
|
| 27 |
+
DatasetConfig.val_batch_size=64 \
|
| 28 |
+
"DatasetConfig.train_transform_list=[resize_256,random_crop_224,random_horizontal_flip,to_dtype,normalize_dinov2]" \
|
| 29 |
+
"DatasetConfig.eval_transform_list=[resize_256,center_crop_224,to_dtype,normalize_dinov2]" \
|
| 30 |
+
TrainerConfig.save_path=$SAVE \
|
| 31 |
+
"metrics=[real_accuracy,fake_accuracy]" \
|
| 32 |
logger.name=npr \
|
| 33 |
+
logger.mode=offline \
|
| 34 |
+
"logger.tags=[SynthScope,NPR,progan4]"
|
experiments/image/pgc.sh
CHANGED
|
@@ -1,27 +1,34 @@
|
|
| 1 |
#!/bin/bash
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
export OMP_NUM_THREADS=1
|
|
|
|
|
|
|
| 7 |
|
| 8 |
-
#
|
| 9 |
-
#
|
| 10 |
-
|
| 11 |
-
# residual is resolution-sensitive, so we use CROP-only transforms (no resize) to preserve the
|
| 12 |
-
# per-pixel quantization pattern. Trained here on ProGAN (CNNDetection); swap the dataset for
|
| 13 |
-
# GenImage to match the paper's SDv1.4 setting.
|
| 14 |
-
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py \
|
| 15 |
trainer@TrainerConfig=image/pgc \
|
| 16 |
model@ModelConfig=image/pgc \
|
| 17 |
dataset@DatasetConfig=image/cnndetection \
|
| 18 |
-
|
| 19 |
DatasetConfig.train_batch_size=32 \
|
| 20 |
DatasetConfig.val_batch_size=64 \
|
| 21 |
-
DatasetConfig.
|
| 22 |
-
DatasetConfig.
|
| 23 |
-
|
| 24 |
-
metrics=[real_accuracy,fake_accuracy
|
| 25 |
logger.name=pgc \
|
| 26 |
-
logger.mode=
|
| 27 |
-
logger.tags=[SynthScope,PGC,
|
|
|
|
| 1 |
#!/bin/bash
|
| 2 |
+
# PGC (Zhou et al., 2026): the official ProGAN training recipe, reproduced in SynthScope.
|
| 3 |
+
# data ForenSynths / CNNDetection ProGAN train, all 20 classes (720k images = the UniversalFakeDetect train set)
|
| 4 |
+
# train flip -> ColorJitter(0.15, 0.15, 0.15, 0.05) p0.2 -> pad + RandomCrop 224 (no resize: the YCbCr residual
|
| 5 |
+
# is resolution-sensitive) -> ImageNet normalization; the residual is computed inside the model
|
| 6 |
+
# optimizer AdamW lr 5e-5 (constant over this budget), weight decay 0.05, label smoothing 0.1, batch 32 x 4
|
| 7 |
+
# accumulation on ONE GPU, gradient clipping 1.0, seed 42
|
| 8 |
+
# budget 2000 micro-batches with a checkpoint every 100 (model_step<N>.pth): the released checkpoint is ~400 steps
|
| 9 |
+
# in and transfer to unseen generators erodes within the first epoch, so the early curve is evaluated
|
| 10 |
+
# Evaluate: python benchmark/bench_eval.py --model pgc --ckpt <save_path>/model_step<N>.pth --benchmark forensynths --data <CNNDetection/test>
|
| 11 |
+
export TRITON_CACHE_DIR=${TRITON_CACHE_DIR:-/nvmestore/ssalehi/.triton}
|
| 12 |
+
export HF_HOME=${HF_HOME:-/nvmestore/ssalehi/.cache/huggingface}
|
| 13 |
+
export TORCH_HOME=${TORCH_HOME:-/nvmestore/ssalehi/.cache/torch}
|
| 14 |
+
export CUDA_VISIBLE_DEVICES=${CUDA_VISIBLE_DEVICES:-0}
|
| 15 |
export OMP_NUM_THREADS=1
|
| 16 |
+
DATA=${DATA:-/nvmestore/ssalehi/SynthScope/CNNDetection}
|
| 17 |
+
SAVE=${SAVE:-/nvmestore/ssalehi/SynthScope/models/pgc}
|
| 18 |
|
| 19 |
+
# Plain single-process run (no `accelerate launch`): gradient accumulation and clipping are done by the trainer's
|
| 20 |
+
# step budget exactly as in the official code.
|
| 21 |
+
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope python main.py \
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
trainer@TrainerConfig=image/pgc \
|
| 23 |
model@ModelConfig=image/pgc \
|
| 24 |
dataset@DatasetConfig=image/cnndetection \
|
| 25 |
+
DatasetConfig.root_dir=$DATA \
|
| 26 |
DatasetConfig.train_batch_size=32 \
|
| 27 |
DatasetConfig.val_batch_size=64 \
|
| 28 |
+
"DatasetConfig.train_transform_list=[random_horizontal_flip,color_jitter_pgc,pad_random_crop_224,to_dtype,normalize_dinov2]" \
|
| 29 |
+
"DatasetConfig.eval_transform_list=[center_crop_224,to_dtype,normalize_dinov2]" \
|
| 30 |
+
TrainerConfig.save_path=$SAVE \
|
| 31 |
+
"metrics=[real_accuracy,fake_accuracy]" \
|
| 32 |
logger.name=pgc \
|
| 33 |
+
logger.mode=offline \
|
| 34 |
+
"logger.tags=[SynthScope,PGC,progan20]"
|
interface/backend/build_repro_full.py
CHANGED
|
@@ -50,21 +50,37 @@ VAL = {
|
|
| 50 |
("dda", "RealChain"): {"official": {"map": 76.46, "acc": 67.23, "auroc": 74.97}},
|
| 51 |
("dda", "RRDataset"): {"official": {"map": 85.18, "acc": 75.99, "auroc": 81.74}},
|
| 52 |
# D3QE: only the official checkpoint is evaluated (no SynthScope-trained number). acc = plain = balanced (balanced subsets).
|
| 53 |
-
|
| 54 |
-
("d3qe", "
|
| 55 |
-
("d3qe", "
|
|
|
|
| 56 |
("d3qe", "SynthWildX"): {"official": {"map": 49.3, "acc": 49.58, "auroc": 45.47}},
|
| 57 |
("d3qe", "WildRF"): {"official": {"map": 63.65, "acc": 53.03, "auroc": 63.89}},
|
| 58 |
("d3qe", "BFree-Online"): {"official": {"map": 68.77, "acc": 47.03, "auroc": 64.0}},
|
| 59 |
("d3qe", "RealChain"): {"official": {"map": 47.24, "acc": 49.49, "auroc": 43.49}},
|
| 60 |
("d3qe", "EvalGEN"): {"official": {"map": 91.48, "acc": 62.21, "auroc": 83.94}},
|
| 61 |
("d3qe", "RRDataset"): {"official": {"map": 59.25, "acc": 54.95, "auroc": 55.11}},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
("fat_former", "4-class"): {"official": {"map": 99.7, "acc": 98.39}},
|
| 63 |
("fat_former", "Diffusion-model"): {"official": {"map": 98.6, "acc": 93.72}},
|
| 64 |
-
|
|
|
|
| 65 |
("npr", "Table 2"): {"official": {"map": 96.49, "acc": 94.81}},
|
| 66 |
("npr", "Table 3"): {"official": {"map": 99.4, "acc": 95.03}},
|
| 67 |
-
("npr", "Table 4"): {"official": {"map": 96.69, "acc": 93.94}},
|
| 68 |
("npr", "Table 5"): {"official": {"map": 83.47, "acc": 79.13}},
|
| 69 |
# SAFE now SERVES the SynthScope-trained checkpoint (retrain v2 / model_38), which reproduces the paper.
|
| 70 |
("safe", "ForenSynths"): {"official": {"map": 98.78, "acc": 96.2}, "synthscope": {"map": 97.79, "acc": 95.1}},
|
|
@@ -74,8 +90,9 @@ VAL = {
|
|
| 74 |
("safe", "DiTFake"): {"synthscope": {"map": 99.96, "acc": 99.2}},
|
| 75 |
("safe", "DiffusionForensics"): {"synthscope": {"map": 99.34, "acc": 91.3}},
|
| 76 |
("pgc", "GenImage"): {"official": {"map": 100.0, "acc": 99.49}, "synthscope": {"map": 97.62, "acc": 78.6}},
|
| 77 |
-
|
| 78 |
-
("pgc", "
|
|
|
|
| 79 |
("pgc", "AIGIBench"): {"official": {"map": 95.36, "acc": 88.33}, "synthscope": {"map": 86.88}},
|
| 80 |
("pgc", "CommGen15"): {"official": {"map": 99.26, "acc": 97.39}, "synthscope": {"map": 98.61, "acc": 66.66}},
|
| 81 |
# SPAI now SERVES official weights remapped into the SynthScope class, scored at NATIVE resolution
|
|
|
|
| 50 |
("dda", "RealChain"): {"official": {"map": 76.46, "acc": 67.23, "auroc": 74.97}},
|
| 51 |
("dda", "RRDataset"): {"official": {"map": 85.18, "acc": 75.99, "auroc": 81.74}},
|
| 52 |
# D3QE: only the official checkpoint is evaluated (no SynthScope-trained number). acc = plain = balanced (balanced subsets).
|
| 53 |
+
# synthscope = trained in SynthScope with the official recipe (experiments/image/d3qe.sh; job 265064, eval 265826).
|
| 54 |
+
("d3qe", "ARForensics"): {"official": {"map": 92.22, "acc": 81.74, "auroc": 91.91}, "synthscope": {"map": 91.73, "acc": 79.83, "auroc": 91.49}},
|
| 55 |
+
("d3qe", "ForenSynths"): {"official": {"map": 92.23, "acc": 83.45, "auroc": 90.65}, "synthscope": {"map": 91.35, "acc": 82.14, "auroc": 90.29}},
|
| 56 |
+
("d3qe", "GenImage"): {"official": {"map": 90.94, "acc": 79.44, "auroc": 90.49}, "synthscope": {"map": 90.11, "acc": 77.01, "auroc": 89.63}},
|
| 57 |
("d3qe", "SynthWildX"): {"official": {"map": 49.3, "acc": 49.58, "auroc": 45.47}},
|
| 58 |
("d3qe", "WildRF"): {"official": {"map": 63.65, "acc": 53.03, "auroc": 63.89}},
|
| 59 |
("d3qe", "BFree-Online"): {"official": {"map": 68.77, "acc": 47.03, "auroc": 64.0}},
|
| 60 |
("d3qe", "RealChain"): {"official": {"map": 47.24, "acc": 49.49, "auroc": 43.49}},
|
| 61 |
("d3qe", "EvalGEN"): {"official": {"map": 91.48, "acc": 62.21, "auroc": 83.94}},
|
| 62 |
("d3qe", "RRDataset"): {"official": {"map": 59.25, "acc": 54.95, "auroc": 55.11}},
|
| 63 |
+
# OmniAID: official checkpoints only. Mirage-Test acc = the paper's metric (4 balanced categories + Anime fake accuracy).
|
| 64 |
+
("omniaid", "GenImage (8 subsets) — GenImage-SD1.4"): {"official": {"map": 99.02, "acc": 95.82, "auroc": 99.16}},
|
| 65 |
+
("omniaid", "AIGCDetectBenchmark (17) — GenImage-SD1.4"): {"official": {"map": 97.88, "acc": 88.87, "auroc": 98.2}},
|
| 66 |
+
("omniaid", "Mirage-Test (5 categories) — GenImage-SD1.4"): {"official": {"acc": 51.1}},
|
| 67 |
+
("omniaid", "GenImage (8 subsets) — Mirage-Train v1"): {"official": {"map": 99.2, "acc": 97.16, "auroc": 99.12}},
|
| 68 |
+
("omniaid", "AIGCDetectBenchmark (17) — Mirage-Train v1"): {"official": {"map": 98.93, "acc": 92.88, "auroc": 98.99}},
|
| 69 |
+
("omniaid", "Mirage-Test (5 categories) — Mirage-Train v1"): {"official": {"acc": 88.39}},
|
| 70 |
+
("omniaid", "Mirage-Test (v2"): {"official": {"map": 96.48, "acc": 78.46, "auroc": 96.65}},
|
| 71 |
+
("omniaid", "SynthWildX (v2"): {"official": {"map": 95.52, "acc": 86.02, "auroc": 95.31}},
|
| 72 |
+
("omniaid", "WildRF (v2"): {"official": {"map": 97.28, "acc": 88.36, "auroc": 96.91}},
|
| 73 |
+
("omniaid", "BFree-Online (v2"): {"official": {"map": 91.7, "acc": 70.26, "auroc": 87.18}},
|
| 74 |
+
("omniaid", "RealChain (v2"): {"official": {"map": 84.02, "acc": 61.71, "auroc": 83.63}},
|
| 75 |
+
("omniaid", "EvalGEN (v2"): {"official": {"map": 96.46, "acc": 74.14, "auroc": 93.08}},
|
| 76 |
+
("omniaid", "RRDataset (v2"): {"official": {"map": 84.19, "acc": 71.75, "auroc": 82.32}},
|
| 77 |
("fat_former", "4-class"): {"official": {"map": 99.7, "acc": 98.39}},
|
| 78 |
("fat_former", "Diffusion-model"): {"official": {"map": 98.6, "acc": 93.72}},
|
| 79 |
+
# synthscope = trained in SynthScope with the official recipe (experiments/image/npr.sh; job 265720, eval 265808).
|
| 80 |
+
("npr", "Table 1"): {"official": {"map": 94.22, "acc": 92.30}, "synthscope": {"map": 93.83, "acc": 84.09, "auroc": 94.94}},
|
| 81 |
("npr", "Table 2"): {"official": {"map": 96.49, "acc": 94.81}},
|
| 82 |
("npr", "Table 3"): {"official": {"map": 99.4, "acc": 95.03}},
|
| 83 |
+
("npr", "Table 4"): {"official": {"map": 96.69, "acc": 93.94}, "synthscope": {"map": 97.49, "acc": 88.91, "auroc": 97.23}},
|
| 84 |
("npr", "Table 5"): {"official": {"map": 83.47, "acc": 79.13}},
|
| 85 |
# SAFE now SERVES the SynthScope-trained checkpoint (retrain v2 / model_38), which reproduces the paper.
|
| 86 |
("safe", "ForenSynths"): {"official": {"map": 98.78, "acc": 96.2}, "synthscope": {"map": 97.79, "acc": 95.1}},
|
|
|
|
| 90 |
("safe", "DiTFake"): {"synthscope": {"map": 99.96, "acc": 99.2}},
|
| 91 |
("safe", "DiffusionForensics"): {"synthscope": {"map": 99.34, "acc": 91.3}},
|
| 92 |
("pgc", "GenImage"): {"official": {"map": 100.0, "acc": 99.49}, "synthscope": {"map": 97.62, "acc": 78.6}},
|
| 93 |
+
# synthscope UFD / ForenSynths = the official recipe in SynthScope, step 500 (experiments/image/pgc.sh; jobs 266276, 266282, 266660).
|
| 94 |
+
("pgc", "UniversalFakeDetect"): {"official": {"map": 99.34, "acc": 94.16}, "synthscope": {"map": 98.99, "acc": 91.96, "auroc": 98.92}},
|
| 95 |
+
("pgc", "ForenSynths"): {"official": {"map": 96.25, "acc": 83.73}, "synthscope": {"map": 97.41, "acc": 85.89, "auroc": 97.63}},
|
| 96 |
("pgc", "AIGIBench"): {"official": {"map": 95.36, "acc": 88.33}, "synthscope": {"map": 86.88}},
|
| 97 |
("pgc", "CommGen15"): {"official": {"map": 99.26, "acc": 97.39}, "synthscope": {"map": 98.61, "acc": 66.66}},
|
| 98 |
# SPAI now SERVES official weights remapped into the SynthScope class, scored at NATIVE resolution
|
interface/backend/datasets.py
CHANGED
|
@@ -120,6 +120,9 @@ DATASET_DEFS: list[DatasetDef] = [
|
|
| 120 |
DatasetDef("rrdataset", "RRDataset (original / transfer / redigital)", "image",
|
| 121 |
["/hddstore/ssalehi/SynthScope/RRDataset"], ["test"],
|
| 122 |
"Real vs AI images as created, after sharing online, and after re-digitization (ICCV 2025)."),
|
|
|
|
|
|
|
|
|
|
| 123 |
DatasetDef("arforensics", "ARForensics (7 autoregressive generators)", "image",
|
| 124 |
["/hddstore/ssalehi/SynthScope/ARForensics"], ["test"],
|
| 125 |
"LlamaGen, VAR, Infinity, Janus-Pro, RAR, Switti and Open-MAGVIT2 vs ImageNet reals, 6000 each (D3QE, ICCV 2025)."),
|
|
|
|
| 120 |
DatasetDef("rrdataset", "RRDataset (original / transfer / redigital)", "image",
|
| 121 |
["/hddstore/ssalehi/SynthScope/RRDataset"], ["test"],
|
| 122 |
"Real vs AI images as created, after sharing online, and after re-digitization (ICCV 2025)."),
|
| 123 |
+
DatasetDef("mirage_test", "Mirage-Test (human, animal, object, scene, anime)", "image",
|
| 124 |
+
["/hddstore/ssalehi/SynthScope/MirageTest"], ["test"],
|
| 125 |
+
"Recent generators tuned for realism, in five content categories (OmniAID); Anime is fakes only."),
|
| 126 |
DatasetDef("arforensics", "ARForensics (7 autoregressive generators)", "image",
|
| 127 |
["/hddstore/ssalehi/SynthScope/ARForensics"], ["test"],
|
| 128 |
"LlamaGen, VAR, Infinity, Janus-Pro, RAR, Switti and Open-MAGVIT2 vs ImageNet reals, 6000 each (D3QE, ICCV 2025)."),
|
interface/backend/engine.py
CHANGED
|
@@ -107,6 +107,9 @@ def _build_model(cfg):
|
|
| 107 |
if model_type == "dda":
|
| 108 |
from models.image.dda.dda import SynthScopeDDAModel
|
| 109 |
return SynthScopeDDAModel(cfg)
|
|
|
|
|
|
|
|
|
|
| 110 |
if model_type == "d3qe":
|
| 111 |
from models.image.d3qe.d3qe import SynthScopeD3QEModel
|
| 112 |
return SynthScopeD3QEModel(cfg)
|
|
|
|
| 107 |
if model_type == "dda":
|
| 108 |
from models.image.dda.dda import SynthScopeDDAModel
|
| 109 |
return SynthScopeDDAModel(cfg)
|
| 110 |
+
if model_type == "omniaid":
|
| 111 |
+
from models.image.omniaid.omniaid import SynthScopeOmniAIDModel
|
| 112 |
+
return SynthScopeOmniAIDModel(cfg)
|
| 113 |
if model_type == "d3qe":
|
| 114 |
from models.image.d3qe.d3qe import SynthScopeD3QEModel
|
| 115 |
return SynthScopeD3QEModel(cfg)
|
interface/backend/registry.py
CHANGED
|
@@ -347,6 +347,26 @@ _add(ModelSpec(
|
|
| 347 |
"tokenizer, read by a small transformer, fused with frozen CLIP ViT-L/14 features. Trained on LlamaGen vs "
|
| 348 |
"ImageNet only. Uses the OFFICIAL released checkpoint.",
|
| 349 |
))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 350 |
_add(ModelSpec(
|
| 351 |
key="npr", name="NPR", paper="Tan et al., CVPR'24",
|
| 352 |
modality="image", predict_kind="bce_npr", checkpoint="npr.pth",
|
|
@@ -417,6 +437,9 @@ INPUT_NOTES: dict[str, tuple[str, str]] = {
|
|
| 417 |
"images are padded with black; the rest of a larger image is not seen."),
|
| 418 |
"d3qe": ("Resized to 256, center crop", "The shorter side is scaled to 256 and the central 256×256 square is used; "
|
| 419 |
"its CLIP branch looks at the central 224×224 of that square."),
|
|
|
|
|
|
|
|
|
|
| 420 |
"cure": ("Paper-specific pipeline", "As in the paper: PNG images are re-saved as JPEG, the shorter side is scaled to "
|
| 421 |
"390 and center-cropped, resized to 336, then JPEG-compressed again (quality 70)."),
|
| 422 |
}
|
|
|
|
| 347 |
"tokenizer, read by a small transformer, fused with frozen CLIP ViT-L/14 features. Trained on LlamaGen vs "
|
| 348 |
"ImageNet only. Uses the OFFICIAL released checkpoint.",
|
| 349 |
))
|
| 350 |
+
# OmniAID's official eval preprocessing: v2 resizes straight to 336 (data/datasets.py); the v1-era releases used a
|
| 351 |
+
# double resize (512x512, then 336x336) - the commented-out pipeline that the official code says to use for v1 weights.
|
| 352 |
+
omniaid_v1_transform = T.Compose([T.Resize((512, 512)), T.Resize((336, 336)), T.ToTensor(), _norm(CLIP_MEAN, CLIP_STD)])
|
| 353 |
+
for _key, _name, _ckpt, _tf, _note in [
|
| 354 |
+
("omniaid", "OmniAID", "omniaid.pth", tf_square_resize(336, None, CLIP_MEAN, CLIP_STD),
|
| 355 |
+
"the recommended v2 release, trained on Mirage-Train (the authors' modern in-the-wild dataset)"),
|
| 356 |
+
("omniaid_v1", "OmniAID (v1)", "omniaid_v1.pth", omniaid_v1_transform,
|
| 357 |
+
"the v1 release trained on Mirage-Train, the paper's OmniAID-Mirage"),
|
| 358 |
+
("omniaid_genimage", "OmniAID (GenImage)", "omniaid_genimage.pth", omniaid_v1_transform,
|
| 359 |
+
"the release trained on GenImage SD v1.4 only, for the paper's academic GenImage protocol"),
|
| 360 |
+
]:
|
| 361 |
+
_add(ModelSpec(
|
| 362 |
+
key=_key, name=_name, paper="Guo et al., arXiv 2511.08423",
|
| 363 |
+
modality="image", predict_kind="ce", checkpoint=_ckpt, ckpt_kind="official_omniaid", weights="official",
|
| 364 |
+
# <key>.pth = the official ckpt/checkpoint_omniaid_*.pth unchanged (HF Yunncheng/OmniAID); full model, strict load.
|
| 365 |
+
overrides=[f"model@ModelConfig=image/{_key}"],
|
| 366 |
+
transform=_tf,
|
| 367 |
+
notes="Hybrid mixture of experts on CLIP ViT-L/14@336: a router picks semantic experts (human, animal, object, "
|
| 368 |
+
"scene, ...) and an always-on artifact expert sees every image. Uses the OFFICIAL checkpoint: " + _note + ".",
|
| 369 |
+
))
|
| 370 |
_add(ModelSpec(
|
| 371 |
key="npr", name="NPR", paper="Tan et al., CVPR'24",
|
| 372 |
modality="image", predict_kind="bce_npr", checkpoint="npr.pth",
|
|
|
|
| 437 |
"images are padded with black; the rest of a larger image is not seen."),
|
| 438 |
"d3qe": ("Resized to 256, center crop", "The shorter side is scaled to 256 and the central 256×256 square is used; "
|
| 439 |
"its CLIP branch looks at the central 224×224 of that square."),
|
| 440 |
+
"omniaid": ("Squashed to 336×336", "The whole image is stretched to a 336×336 square (the aspect ratio changes)."),
|
| 441 |
+
"omniaid_v1": ("Squashed to 512, then 336", "The image is stretched to 512×512 and then to 336×336, as in the v1 release."),
|
| 442 |
+
"omniaid_genimage": ("Squashed to 512, then 336", "The image is stretched to 512×512 and then to 336×336, as in the v1-era releases."),
|
| 443 |
"cure": ("Paper-specific pipeline", "As in the paper: PNG images are re-saved as JPEG, the shorter side is scaled to "
|
| 444 |
"390 and center-cropped, resized to 336, then JPEG-compressed again (quality 70)."),
|
| 445 |
}
|
interface/backend/repro_full.json
CHANGED
|
@@ -286,8 +286,8 @@
|
|
| 286 |
"benchmark": "ARForensics (7 autoregressive generators, full test set)",
|
| 287 |
"metric": "accuracy / AP (mean over generators)",
|
| 288 |
"paper": "acc=82.11 / map=92.07",
|
| 289 |
-
"ours": "mAP=92.22 / acc=81.74 / AUROC=91.91
|
| 290 |
-
"ours_synthscope": "",
|
| 291 |
"ours_official": "mAP=92.22 / acc=81.74 / AUROC=91.91",
|
| 292 |
"status": "official",
|
| 293 |
"note": "Official checkpoint through SynthScope: 81.74 acc / 92.22 AP (AUROC 91.91) on all 84,000 images; LlamaGen 97.16/99.58, VAR 84.46/95.16, Infinity 63.06/80.26, Janus-Pro 92.39/97.85, RAR 91.70/97.88, Switti 74.23/88.71, Open-MAGVIT2 69.21/86.12, every generator within 1.1 pt acc (tag d3qe_arforensics, job 264795)."
|
|
@@ -296,20 +296,20 @@
|
|
| 296 |
"benchmark": "ForenSynths (paper's 7 GANs, cross-paradigm)",
|
| 297 |
"metric": "accuracy / AP (mean over generators)",
|
| 298 |
"paper": "acc=83.73 / map=92.23",
|
| 299 |
-
"ours": "mAP=92.23 / acc=83.45 / AUROC=90.65
|
| 300 |
-
"ours_synthscope": "",
|
| 301 |
"ours_official": "mAP=92.23 / acc=83.45 / AUROC=90.65",
|
| 302 |
-
"status": "
|
| 303 |
"note": "Official checkpoint through SynthScope (bench_eval --dataset cnndetection --select the 7 GANs): 83.45 acc / 92.23 AP (AUROC 90.65); ProGAN 95.10, StyleGAN 76.21, StyleGAN2 75.08, BigGAN 86.07, CycleGAN 83.27, StarGAN 73.64, GauGAN 94.76, every GAN within 1.5 pt (tag d3qe_cnndetection, job 264795)."
|
| 304 |
},
|
| 305 |
{
|
| 306 |
"benchmark": "GenImage (paper's 6 diffusion subsets, cross-paradigm)",
|
| 307 |
"metric": "accuracy / AP (mean over generators)",
|
| 308 |
"paper": "acc=78.61 / map=89.6",
|
| 309 |
-
"ours": "mAP=90.94 / acc=79.44 / AUROC=90.49
|
| 310 |
-
"ours_synthscope": "",
|
| 311 |
"ours_official": "mAP=90.94 / acc=79.44 / AUROC=90.49",
|
| 312 |
-
"status": "
|
| 313 |
"note": "Official checkpoint through SynthScope (bench_eval --dataset genimage_full --select the 6 subsets): 79.44 acc / 90.94 AP (AUROC 90.49), the mean within 0.9 pt; per subset the gaps are larger (ADM 63.67, -6.8; GLIDE 91.13; Midjourney 63.80; SD-v1.4 82.21; SD-v1.5 85.10; Wukong 90.73, +6.3), so the paper's GenImage test sample probably differs from ours (tag d3qe_genimage_full, job 264795)."
|
| 314 |
},
|
| 315 |
{
|
|
@@ -542,8 +542,8 @@
|
|
| 542 |
"benchmark": "Table 1 \u2014 ForenSynths / Cross-GAN-Sources (8 GANs) \u2014 HEADLINE",
|
| 543 |
"metric": "AP (and acc@0.5)",
|
| 544 |
"paper": "map=96.1 / acc=92.5",
|
| 545 |
-
"ours": "mAP=94.22 / acc=92.3
|
| 546 |
-
"ours_synthscope": "",
|
| 547 |
"ours_official": "mAP=94.22 / acc=92.3",
|
| 548 |
"status": "official",
|
| 549 |
"note": "Our npr_official_forensynths.json logs the full 13-gen mean 79.45 mAP; the 8-GAN subset {progan,cyclegan,biggan,gaugan,stargan,stylegan,stylegan2,deepfake} computes to 92.81 mAP / 91.83 acc."
|
|
@@ -572,8 +572,8 @@
|
|
| 572 |
"benchmark": "Table 4 \u2014 UniversalFakeDetect / Ojha",
|
| 573 |
"metric": "AP (and acc@0.5)",
|
| 574 |
"paper": "acc=95.2",
|
| 575 |
-
"ours": "mAP=96.69 / acc=93.94
|
| 576 |
-
"ours_synthscope": "",
|
| 577 |
"ours_official": "mAP=96.69 / acc=93.94",
|
| 578 |
"status": "official",
|
| 579 |
"note": "n/a \u2014 96.69 mAP / 93.94 acc, close to paper acc 95.2."
|
|
@@ -589,6 +589,148 @@
|
|
| 589 |
"note": "n/a \u2014 83.47 mAP / 79.13 acc \u2248 paper acc 80.1."
|
| 590 |
}
|
| 591 |
],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 592 |
"pgc": [
|
| 593 |
{
|
| 594 |
"benchmark": "GenImage",
|
|
@@ -604,21 +746,21 @@
|
|
| 604 |
"benchmark": "UniversalFakeDetect (UFD)",
|
| 605 |
"metric": "AP",
|
| 606 |
"paper": "map=98.0",
|
| 607 |
-
"ours": "SynthScope mAP=99.
|
| 608 |
-
"ours_synthscope": "mAP=99.
|
| 609 |
"ours_official": "mAP=99.34 / acc=94.16",
|
| 610 |
"status": "reproduced",
|
| 611 |
-
"note": "Reproduced (official
|
| 612 |
},
|
| 613 |
{
|
| 614 |
"benchmark": "ForenSynths (CNNDetection test)",
|
| 615 |
"metric": "AP",
|
| 616 |
"paper": "\u2014",
|
| 617 |
-
"ours": "SynthScope mAP=89.
|
| 618 |
-
"ours_synthscope": "mAP=89.
|
| 619 |
"ours_official": "mAP=96.25 / acc=83.73",
|
| 620 |
"status": "reference",
|
| 621 |
-
"note": "
|
| 622 |
},
|
| 623 |
{
|
| 624 |
"benchmark": "AIGIBench (AIGI)",
|
|
|
|
| 286 |
"benchmark": "ARForensics (7 autoregressive generators, full test set)",
|
| 287 |
"metric": "accuracy / AP (mean over generators)",
|
| 288 |
"paper": "acc=82.11 / map=92.07",
|
| 289 |
+
"ours": "SynthScope mAP=91.73 / acc=79.83 / AUROC=91.49 \u00b7 official mAP=92.22 / acc=81.74 / AUROC=91.91",
|
| 290 |
+
"ours_synthscope": "mAP=91.73 / acc=79.83 / AUROC=91.49",
|
| 291 |
"ours_official": "mAP=92.22 / acc=81.74 / AUROC=91.91",
|
| 292 |
"status": "official",
|
| 293 |
"note": "Official checkpoint through SynthScope: 81.74 acc / 92.22 AP (AUROC 91.91) on all 84,000 images; LlamaGen 97.16/99.58, VAR 84.46/95.16, Infinity 63.06/80.26, Janus-Pro 92.39/97.85, RAR 91.70/97.88, Switti 74.23/88.71, Open-MAGVIT2 69.21/86.12, every generator within 1.1 pt acc (tag d3qe_arforensics, job 264795)."
|
|
|
|
| 296 |
"benchmark": "ForenSynths (paper's 7 GANs, cross-paradigm)",
|
| 297 |
"metric": "accuracy / AP (mean over generators)",
|
| 298 |
"paper": "acc=83.73 / map=92.23",
|
| 299 |
+
"ours": "SynthScope mAP=91.35 / acc=82.14 / AUROC=90.29 \u00b7 official mAP=92.23 / acc=83.45 / AUROC=90.65",
|
| 300 |
+
"ours_synthscope": "mAP=91.35 / acc=82.14 / AUROC=90.29",
|
| 301 |
"ours_official": "mAP=92.23 / acc=83.45 / AUROC=90.65",
|
| 302 |
+
"status": "reproduced",
|
| 303 |
"note": "Official checkpoint through SynthScope (bench_eval --dataset cnndetection --select the 7 GANs): 83.45 acc / 92.23 AP (AUROC 90.65); ProGAN 95.10, StyleGAN 76.21, StyleGAN2 75.08, BigGAN 86.07, CycleGAN 83.27, StarGAN 73.64, GauGAN 94.76, every GAN within 1.5 pt (tag d3qe_cnndetection, job 264795)."
|
| 304 |
},
|
| 305 |
{
|
| 306 |
"benchmark": "GenImage (paper's 6 diffusion subsets, cross-paradigm)",
|
| 307 |
"metric": "accuracy / AP (mean over generators)",
|
| 308 |
"paper": "acc=78.61 / map=89.6",
|
| 309 |
+
"ours": "SynthScope mAP=90.11 / acc=77.01 / AUROC=89.63 \u00b7 official mAP=90.94 / acc=79.44 / AUROC=90.49",
|
| 310 |
+
"ours_synthscope": "mAP=90.11 / acc=77.01 / AUROC=89.63",
|
| 311 |
"ours_official": "mAP=90.94 / acc=79.44 / AUROC=90.49",
|
| 312 |
+
"status": "reproduced",
|
| 313 |
"note": "Official checkpoint through SynthScope (bench_eval --dataset genimage_full --select the 6 subsets): 79.44 acc / 90.94 AP (AUROC 90.49), the mean within 0.9 pt; per subset the gaps are larger (ADM 63.67, -6.8; GLIDE 91.13; Midjourney 63.80; SD-v1.4 82.21; SD-v1.5 85.10; Wukong 90.73, +6.3), so the paper's GenImage test sample probably differs from ours (tag d3qe_genimage_full, job 264795)."
|
| 314 |
},
|
| 315 |
{
|
|
|
|
| 542 |
"benchmark": "Table 1 \u2014 ForenSynths / Cross-GAN-Sources (8 GANs) \u2014 HEADLINE",
|
| 543 |
"metric": "AP (and acc@0.5)",
|
| 544 |
"paper": "map=96.1 / acc=92.5",
|
| 545 |
+
"ours": "SynthScope mAP=93.83 / acc=84.09 / AUROC=94.94 \u00b7 official mAP=94.22 / acc=92.3",
|
| 546 |
+
"ours_synthscope": "mAP=93.83 / acc=84.09 / AUROC=94.94",
|
| 547 |
"ours_official": "mAP=94.22 / acc=92.3",
|
| 548 |
"status": "official",
|
| 549 |
"note": "Our npr_official_forensynths.json logs the full 13-gen mean 79.45 mAP; the 8-GAN subset {progan,cyclegan,biggan,gaugan,stargan,stylegan,stylegan2,deepfake} computes to 92.81 mAP / 91.83 acc."
|
|
|
|
| 572 |
"benchmark": "Table 4 \u2014 UniversalFakeDetect / Ojha",
|
| 573 |
"metric": "AP (and acc@0.5)",
|
| 574 |
"paper": "acc=95.2",
|
| 575 |
+
"ours": "SynthScope mAP=97.49 / acc=88.91 / AUROC=97.23 \u00b7 official mAP=96.69 / acc=93.94",
|
| 576 |
+
"ours_synthscope": "mAP=97.49 / acc=88.91 / AUROC=97.23",
|
| 577 |
"ours_official": "mAP=96.69 / acc=93.94",
|
| 578 |
"status": "official",
|
| 579 |
"note": "n/a \u2014 96.69 mAP / 93.94 acc, close to paper acc 95.2."
|
|
|
|
| 589 |
"note": "n/a \u2014 83.47 mAP / 79.13 acc \u2248 paper acc 80.1."
|
| 590 |
}
|
| 591 |
],
|
| 592 |
+
"omniaid": [
|
| 593 |
+
{
|
| 594 |
+
"benchmark": "GenImage (8 subsets) \u2014 GenImage-SD1.4 release",
|
| 595 |
+
"metric": "accuracy (mean over subsets)",
|
| 596 |
+
"paper": "acc=95.94",
|
| 597 |
+
"ours": "mAP=99.02 / acc=95.82 / AUROC=99.16 (official wts)",
|
| 598 |
+
"ours_synthscope": "",
|
| 599 |
+
"ours_official": "mAP=99.02 / acc=95.82 / AUROC=99.16",
|
| 600 |
+
"status": "official",
|
| 601 |
+
"note": "omniaid_genimage through SynthScope: 95.82 (mAP 99.02), every subset within 1.5 pt; v1-era 512->336 preprocessing (tag omniaid_genimage_genimage_full, job 265856)."
|
| 602 |
+
},
|
| 603 |
+
{
|
| 604 |
+
"benchmark": "AIGCDetectBenchmark (17) \u2014 GenImage-SD1.4 release",
|
| 605 |
+
"metric": "accuracy (mean over subsets)",
|
| 606 |
+
"paper": "acc=88.87",
|
| 607 |
+
"ours": "mAP=97.88 / acc=88.87 / AUROC=98.2 (official wts)",
|
| 608 |
+
"ours_synthscope": "",
|
| 609 |
+
"ours_official": "mAP=97.88 / acc=88.87 / AUROC=98.2",
|
| 610 |
+
"status": "official",
|
| 611 |
+
"note": "omniaid_genimage through SynthScope: 88.87 (mAP 97.88), exactly the paper's number (tag omniaid_genimage_aigcdetect, job 265856)."
|
| 612 |
+
},
|
| 613 |
+
{
|
| 614 |
+
"benchmark": "Mirage-Test (5 categories) \u2014 GenImage-SD1.4 release",
|
| 615 |
+
"metric": "accuracy (mean of Human/Animal/Object/Scene balanced + Anime fake accuracy)",
|
| 616 |
+
"paper": "acc=51.1",
|
| 617 |
+
"ours": "acc=51.1 (official wts)",
|
| 618 |
+
"ours_synthscope": "",
|
| 619 |
+
"ours_official": "acc=51.1",
|
| 620 |
+
"status": "official",
|
| 621 |
+
"note": "omniaid_genimage through SynthScope: 51.10, every category equal to the paper (Scene 46.52) (tag omniaid_genimage_mirage_test, job 265935)."
|
| 622 |
+
},
|
| 623 |
+
{
|
| 624 |
+
"benchmark": "GenImage (8 subsets) \u2014 Mirage-Train v1 release (OmniAID-Mirage)",
|
| 625 |
+
"metric": "accuracy (mean over subsets)",
|
| 626 |
+
"paper": "acc=97.24",
|
| 627 |
+
"ours": "mAP=99.2 / acc=97.16 / AUROC=99.12 (official wts)",
|
| 628 |
+
"ours_synthscope": "",
|
| 629 |
+
"ours_official": "mAP=99.2 / acc=97.16 / AUROC=99.12",
|
| 630 |
+
"status": "official",
|
| 631 |
+
"note": "omniaid_v1 through SynthScope: 97.16 (mAP 99.20), every subset within 0.6 pt (tag omniaid_v1_genimage_full, job 265857)."
|
| 632 |
+
},
|
| 633 |
+
{
|
| 634 |
+
"benchmark": "AIGCDetectBenchmark (17) \u2014 Mirage-Train v1 release (OmniAID-Mirage)",
|
| 635 |
+
"metric": "accuracy (mean over subsets)",
|
| 636 |
+
"paper": "acc=92.88",
|
| 637 |
+
"ours": "mAP=98.93 / acc=92.88 / AUROC=98.99 (official wts)",
|
| 638 |
+
"ours_synthscope": "",
|
| 639 |
+
"ours_official": "mAP=98.93 / acc=92.88 / AUROC=98.99",
|
| 640 |
+
"status": "official",
|
| 641 |
+
"note": "omniaid_v1 through SynthScope: 92.88 (mAP 98.93), exactly the paper's number (tag omniaid_v1_aigcdetect, job 265857)."
|
| 642 |
+
},
|
| 643 |
+
{
|
| 644 |
+
"benchmark": "Mirage-Test (5 categories) \u2014 Mirage-Train v1 release (OmniAID-Mirage)",
|
| 645 |
+
"metric": "accuracy (mean of Human/Animal/Object/Scene balanced + Anime fake accuracy)",
|
| 646 |
+
"paper": "acc=88.39",
|
| 647 |
+
"ours": "acc=88.39 (official wts)",
|
| 648 |
+
"ours_synthscope": "",
|
| 649 |
+
"ours_official": "acc=88.39",
|
| 650 |
+
"status": "official",
|
| 651 |
+
"note": "omniaid_v1 through SynthScope: 88.39, every category equal to the paper (tag omniaid_v1_mirage_test, job 265936)."
|
| 652 |
+
},
|
| 653 |
+
{
|
| 654 |
+
"benchmark": "Chameleon / DRCT-2M",
|
| 655 |
+
"metric": "accuracy",
|
| 656 |
+
"paper": "acc=77.35",
|
| 657 |
+
"ours": "",
|
| 658 |
+
"ours_synthscope": "",
|
| 659 |
+
"ours_official": "",
|
| 660 |
+
"status": "missing",
|
| 661 |
+
"note": "Chameleon is released by request only; DRCT-2M is not in SynthScope yet."
|
| 662 |
+
},
|
| 663 |
+
{
|
| 664 |
+
"benchmark": "Mirage-Test (v2 release, served)",
|
| 665 |
+
"metric": "balanced accuracy",
|
| 666 |
+
"paper": "\u2014",
|
| 667 |
+
"ours": "mAP=96.48 / acc=78.46 / AUROC=96.65 (official wts)",
|
| 668 |
+
"ours_synthscope": "",
|
| 669 |
+
"ours_official": "mAP=96.48 / acc=78.46 / AUROC=96.65",
|
| 670 |
+
"status": "reference",
|
| 671 |
+
"note": "omniaid (v2) through SynthScope: 78.46 in the paper's metric (balanced acc 80.37, mAP 96.48) (tag omniaid_mirage_test, job 265937)."
|
| 672 |
+
},
|
| 673 |
+
{
|
| 674 |
+
"benchmark": "SynthWildX (v2 release, served)",
|
| 675 |
+
"metric": "balanced accuracy",
|
| 676 |
+
"paper": "\u2014",
|
| 677 |
+
"ours": "mAP=95.52 / acc=86.02 / AUROC=95.31 (official wts)",
|
| 678 |
+
"ours_synthscope": "",
|
| 679 |
+
"ours_official": "mAP=95.52 / acc=86.02 / AUROC=95.31",
|
| 680 |
+
"status": "reference",
|
| 681 |
+
"note": "omniaid (v2) through SynthScope: 86.02 (mAP 95.52, AUROC 95.31) (tag omniaid_synthwildx, job 265858)."
|
| 682 |
+
},
|
| 683 |
+
{
|
| 684 |
+
"benchmark": "WildRF (v2 release, served)",
|
| 685 |
+
"metric": "balanced accuracy",
|
| 686 |
+
"paper": "\u2014",
|
| 687 |
+
"ours": "mAP=97.28 / acc=88.36 / AUROC=96.91 (official wts)",
|
| 688 |
+
"ours_synthscope": "",
|
| 689 |
+
"ours_official": "mAP=97.28 / acc=88.36 / AUROC=96.91",
|
| 690 |
+
"status": "reference",
|
| 691 |
+
"note": "omniaid (v2) through SynthScope: 88.36 (mAP 97.28, AUROC 96.91) (tag omniaid_wildrf, job 265858)."
|
| 692 |
+
},
|
| 693 |
+
{
|
| 694 |
+
"benchmark": "BFree-Online (v2 release, served)",
|
| 695 |
+
"metric": "balanced accuracy",
|
| 696 |
+
"paper": "\u2014",
|
| 697 |
+
"ours": "mAP=91.7 / acc=70.26 / AUROC=87.18 (official wts)",
|
| 698 |
+
"ours_synthscope": "",
|
| 699 |
+
"ours_official": "mAP=91.7 / acc=70.26 / AUROC=87.18",
|
| 700 |
+
"status": "reference",
|
| 701 |
+
"note": "omniaid (v2) through SynthScope: 70.26 (mAP 91.70, AUROC 87.18) (tag omniaid_bfree_online, job 265858)."
|
| 702 |
+
},
|
| 703 |
+
{
|
| 704 |
+
"benchmark": "RealChain (v2 release, served)",
|
| 705 |
+
"metric": "balanced accuracy",
|
| 706 |
+
"paper": "\u2014",
|
| 707 |
+
"ours": "mAP=84.02 / acc=61.71 / AUROC=83.63 (official wts)",
|
| 708 |
+
"ours_synthscope": "",
|
| 709 |
+
"ours_official": "mAP=84.02 / acc=61.71 / AUROC=83.63",
|
| 710 |
+
"status": "reference",
|
| 711 |
+
"note": "omniaid (v2) through SynthScope: 61.71 (mAP 84.02, AUROC 83.63) (tag omniaid_realchain, job 265858)."
|
| 712 |
+
},
|
| 713 |
+
{
|
| 714 |
+
"benchmark": "EvalGEN (v2 release, served)",
|
| 715 |
+
"metric": "balanced accuracy",
|
| 716 |
+
"paper": "\u2014",
|
| 717 |
+
"ours": "mAP=96.46 / acc=74.14 / AUROC=93.08 (official wts)",
|
| 718 |
+
"ours_synthscope": "",
|
| 719 |
+
"ours_official": "mAP=96.46 / acc=74.14 / AUROC=93.08",
|
| 720 |
+
"status": "reference",
|
| 721 |
+
"note": "omniaid (v2) through SynthScope: 74.14 (mAP 96.46, AUROC 93.08) (tag omniaid_evalgen, job 265858)."
|
| 722 |
+
},
|
| 723 |
+
{
|
| 724 |
+
"benchmark": "RRDataset (v2 release, served)",
|
| 725 |
+
"metric": "balanced accuracy",
|
| 726 |
+
"paper": "\u2014",
|
| 727 |
+
"ours": "mAP=84.19 / acc=71.75 / AUROC=82.32 (official wts)",
|
| 728 |
+
"ours_synthscope": "",
|
| 729 |
+
"ours_official": "mAP=84.19 / acc=71.75 / AUROC=82.32",
|
| 730 |
+
"status": "reference",
|
| 731 |
+
"note": "omniaid (v2) through SynthScope: 71.75 (mAP 84.19, AUROC 82.32) (tag omniaid_rrdataset, job 265858)."
|
| 732 |
+
}
|
| 733 |
+
],
|
| 734 |
"pgc": [
|
| 735 |
{
|
| 736 |
"benchmark": "GenImage",
|
|
|
|
| 746 |
"benchmark": "UniversalFakeDetect (UFD)",
|
| 747 |
"metric": "AP",
|
| 748 |
"paper": "map=98.0",
|
| 749 |
+
"ours": "SynthScope mAP=98.99 / acc=91.96 / AUROC=98.92 \u00b7 official mAP=99.34 / acc=94.16",
|
| 750 |
+
"ours_synthscope": "mAP=98.99 / acc=91.96 / AUROC=98.92",
|
| 751 |
"ours_official": "mAP=99.34 / acc=94.16",
|
| 752 |
"status": "reproduced",
|
| 753 |
+
"note": "Reproduced (official weights 99.34; the official recipe trained in SynthScope, step 500: 98.99) >= paper 98.0."
|
| 754 |
},
|
| 755 |
{
|
| 756 |
"benchmark": "ForenSynths (CNNDetection test)",
|
| 757 |
"metric": "AP",
|
| 758 |
"paper": "\u2014",
|
| 759 |
+
"ours": "SynthScope mAP=97.41 / acc=85.89 / AUROC=97.63 \u00b7 official mAP=96.25 / acc=83.73",
|
| 760 |
+
"ours_synthscope": "mAP=97.41 / acc=85.89 / AUROC=97.63",
|
| 761 |
"ours_official": "mAP=96.25 / acc=83.73",
|
| 762 |
"status": "reference",
|
| 763 |
+
"note": "Not a paper number. The official recipe trained in SynthScope (step 500) gives FS-8 97.41 mAP, above the official checkpoint (96.41 on the same evaluation); the earlier 89.03 retrain overshot the early transfer peak."
|
| 764 |
},
|
| 765 |
{
|
| 766 |
"benchmark": "AIGIBench (AIGI)",
|
models/image/d3qe/README.md
CHANGED
|
@@ -32,7 +32,7 @@ is downloaded to `~/.cache/clip` (or `$SYNTHSCOPE_CLIP_DIR`) and the LlamaGen VQ
|
|
| 32 |
| `official/` = the official `networks/D3QE.py`, `vq_model.py` and `clip/model.py`, **byte-identical bodies** | `SynthScopeD3QEModel` subclasses the official `D3QE`, so the checkpoint loads key-for-key; MIT attribution in the headers. |
|
| 33 |
| `official/clip/clip.py` reduced to the official `load()` path D3QE uses (CPU, non-JIT) | The text tokenizer is not needed; `from pkg_resources import packaging`, which current setuptools no longer provides, became `import packaging.version`. |
|
| 34 |
| `pretrained_prefixes = ("clip_model.", "vq_model.")` | The checkpoint does not contain the frozen CLIP and VQ weights (the official `eval.py` falls back to `strict=False`). The engine accepts missing keys only under these prefixes; every trained tensor still loads strictly. |
|
| 35 |
-
| `compute_loss` = BCE on the logit, labels passed to `forward` | The official objective; the forward pass updates the codebook statistics during training as in the official code.
|
| 36 |
|
| 37 |
Two properties of the released code are kept on purpose, because they decide the released scores:
|
| 38 |
* the VQ tokenizer sees the [0, 1] image: `forward` builds a [-1, 1] copy but encodes the unnormalized one;
|
|
@@ -55,9 +55,9 @@ accuracy equals SynthScope's balanced accuracy.
|
|
| 55 |
|
| 56 |
| Benchmark | Paper (acc / AP) | SynthScope_official (acc / AP) | SynthScope_train | Status |
|
| 57 |
|---|---|---|---|---|
|
| 58 |
-
| **ARForensics** (7 AR generators, all 84,000 test images) | 82.11 / 92.07 | **81.74 / 92.22** (AUROC 91.91) |
|
| 59 |
-
| **ForenSynths** (the paper's 7 GANs) | 83.73 / 92.23 | **83.45 / 92.23** (AUROC 90.65) |
|
| 60 |
-
| **GenImage** (the paper's 6 subsets) | 78.61 / 89.60 | **79.44 / 90.94** (AUROC 90.49) |
|
| 61 |
|
| 62 |
Per generator (SynthScope vs paper, accuracy):
|
| 63 |
|
|
@@ -78,6 +78,23 @@ BFree-Online 47.03, RealChain 49.49, EvalGEN 62.21 (balanced; AUROC 83.94), RRDa
|
|
| 78 |
Runs: jobs 264795 (ARForensics, ForenSynths, GenImage) and 264796 (in-the-wild sets); results in
|
| 79 |
`repro/bench_results/d3qe_*.json`.
|
| 80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
## Usage
|
| 82 |
|
| 83 |
```bash
|
|
|
|
| 32 |
| `official/` = the official `networks/D3QE.py`, `vq_model.py` and `clip/model.py`, **byte-identical bodies** | `SynthScopeD3QEModel` subclasses the official `D3QE`, so the checkpoint loads key-for-key; MIT attribution in the headers. |
|
| 33 |
| `official/clip/clip.py` reduced to the official `load()` path D3QE uses (CPU, non-JIT) | The text tokenizer is not needed; `from pkg_resources import packaging`, which current setuptools no longer provides, became `import packaging.version`. |
|
| 34 |
| `pretrained_prefixes = ("clip_model.", "vq_model.")` | The checkpoint does not contain the frozen CLIP and VQ weights (the official `eval.py` falls back to `strict=False`). The engine accepts missing keys only under these prefixes; every trained tensor still loads strictly. |
|
| 35 |
+
| `compute_loss` = BCE on the logit, labels passed to `forward` | The official objective; the forward pass updates the codebook statistics during training as in the official code. The full official recipe is `experiments/image/d3qe.sh` (see *Training in SynthScope* below). |
|
| 36 |
|
| 37 |
Two properties of the released code are kept on purpose, because they decide the released scores:
|
| 38 |
* the VQ tokenizer sees the [0, 1] image: `forward` builds a [-1, 1] copy but encodes the unnormalized one;
|
|
|
|
| 55 |
|
| 56 |
| Benchmark | Paper (acc / AP) | SynthScope_official (acc / AP) | SynthScope_train | Status |
|
| 57 |
|---|---|---|---|---|
|
| 58 |
+
| **ARForensics** (7 AR generators, all 84,000 test images) | 82.11 / 92.07 | **81.74 / 92.22** (AUROC 91.91) | 79.83 / 91.73 | official **reproduced**; trained: AP matches, acc 2.3 below |
|
| 59 |
+
| **ForenSynths** (the paper's 7 GANs) | 83.73 / 92.23 | **83.45 / 92.23** (AUROC 90.65) | **82.14 / 91.35** | **reproduced** (official and trained) |
|
| 60 |
+
| **GenImage** (the paper's 6 subsets) | 78.61 / 89.60 | **79.44 / 90.94** (AUROC 90.49) | **77.01 / 90.11** | **reproduced** on the mean (official and trained); per-subset gaps up to 6.8 pt |
|
| 61 |
|
| 62 |
Per generator (SynthScope vs paper, accuracy):
|
| 63 |
|
|
|
|
| 78 |
Runs: jobs 264795 (ARForensics, ForenSynths, GenImage) and 264796 (in-the-wild sets); results in
|
| 79 |
`repro/bench_results/d3qe_*.json`.
|
| 80 |
|
| 81 |
+
## Training in SynthScope
|
| 82 |
+
|
| 83 |
+
`experiments/image/d3qe.sh` runs the official recipe (`train.py` and its `TrainOptions` defaults) through SynthScope's
|
| 84 |
+
trainer: ARForensics train (100k LlamaGen vs 100k ImageNet) and val; short side 256, then the CNNDetection blur/JPEG
|
| 85 |
+
augmentation (p 0.1 each; `CNNDetAugment`, identical to the official `data_augment` over 300 seeded draws), RandomCrop
|
| 86 |
+
256, flip, no normalization; AdamW lr 8e-5, weight decay 0.01, batch 32 on one GPU, 10 epochs; the best-validation
|
| 87 |
+
checkpoint is kept and the learning rate is divided by 10 after 5 epochs without improvement (trainer `plateau`). It runs
|
| 88 |
+
as a plain single process, not through the repo's DeepSpeed accelerate config, whose gradient clipping the official
|
| 89 |
+
recipe does not have.
|
| 90 |
+
|
| 91 |
+
Our run (job 265064, 15 h on one A5000) improved its validation accuracy every epoch (88.5 → 94.8), so, as in the
|
| 92 |
+
released checkpoint (62,500 steps), the last epoch is the best and the learning rate never dropped. Against the paper it
|
| 93 |
+
gives 79.83 / 91.73 (ARForensics), 82.14 / 91.35 (ForenSynths-7) and 77.01 / 90.11 (GenImage-6): AP reproduces everywhere
|
| 94 |
+
(within 0.9 pt), accuracy is 1.6 to 2.3 pt lower. Per generator the trained model matches the official one closely
|
| 95 |
+
(LlamaGen 97.49 vs 97.16) but calls a few more unseen-generator fakes real (real accuracy 97.4 vs 96.4), which moves the
|
| 96 |
+
fixed-threshold accuracy, not the ranking. Results: `repro/bench_results/d3qe_trained_*.json` (job 265826).
|
| 97 |
+
|
| 98 |
## Usage
|
| 99 |
|
| 100 |
```bash
|
models/image/d3qe/d3qe.py
CHANGED
|
@@ -26,6 +26,7 @@ Input contract: ``forward`` takes the tensor of the official test transform —
|
|
| 26 |
import os
|
| 27 |
from pathlib import Path
|
| 28 |
|
|
|
|
| 29 |
import torch.nn as nn
|
| 30 |
from omegaconf import DictConfig
|
| 31 |
|
|
@@ -58,6 +59,7 @@ class SynthScopeD3QEModel(D3QE):
|
|
| 58 |
# Official objective (networks/trainer.py): BCE on the single logit; passing the labels lets the forward pass
|
| 59 |
# update the codebook-frequency statistics during training, as in the official code.
|
| 60 |
images, labels, _folder_ids = batch
|
| 61 |
-
|
| 62 |
-
loss = self.criterion(
|
| 63 |
-
|
|
|
|
|
|
| 26 |
import os
|
| 27 |
from pathlib import Path
|
| 28 |
|
| 29 |
+
import torch
|
| 30 |
import torch.nn as nn
|
| 31 |
from omegaconf import DictConfig
|
| 32 |
|
|
|
|
| 59 |
# Official objective (networks/trainer.py): BCE on the single logit; passing the labels lets the forward pass
|
| 60 |
# update the codebook-frequency statistics during training, as in the official code.
|
| 61 |
images, labels, _folder_ids = batch
|
| 62 |
+
logits = self(images, labels).squeeze(1)
|
| 63 |
+
loss = self.criterion(logits, labels.float())
|
| 64 |
+
# Two-column [-logit, logit] so the trainer's metrics (argmax / softmax[:, 1]) read the single logit right.
|
| 65 |
+
return {"loss": loss, "outputs": torch.stack([-logits, logits], dim=1)}
|
models/image/dda/dda.py
CHANGED
|
@@ -62,6 +62,7 @@ class SynthScopeDDAModel(nn.Module):
|
|
| 62 |
|
| 63 |
def compute_loss(self, batch):
|
| 64 |
images, labels, _folder_ids = batch
|
| 65 |
-
|
| 66 |
-
loss = self.criterion(
|
| 67 |
-
|
|
|
|
|
|
| 62 |
|
| 63 |
def compute_loss(self, batch):
|
| 64 |
images, labels, _folder_ids = batch
|
| 65 |
+
logits = self.forward(images).squeeze(1)
|
| 66 |
+
loss = self.criterion(logits, labels.float())
|
| 67 |
+
# Two-column [-logit, logit] so the trainer's metrics (argmax / softmax[:, 1]) read the single logit right.
|
| 68 |
+
return {"loss": loss, "outputs": torch.stack([-logits, logits], dim=1)}
|
models/image/npr/README.md
CHANGED
|
@@ -69,18 +69,43 @@ The paper reports both accuracy and average precision, with the main result show
|
|
| 69 |
|
| 70 |
All benchmarks the paper reports, with our numbers (verified, traced to `repro/bench_results/` + `repro/benchmark_specs/`).
|
| 71 |
|
| 72 |
-
Values are `mAP / acc`. **SynthScope_official** = the authors' released weights run through the SynthScope class / eval. **SynthScope_train** = the SynthScope class trained from scratch by us
|
| 73 |
|
| 74 |
| Benchmark | Paper | SynthScope_official | SynthScope_train | Status |
|
| 75 |
|---|---|---|---|---|
|
| 76 |
-
| Table 1 — ForenSynths / Cross-GAN (8) | 96.1 / 92.5 | 94.22 / 92.3 |
|
| 77 |
| Table 2 — GANGen-Detection (9) | 96.6 / 93.2 | 96.49 / 94.81 | — | official |
|
| 78 |
| Table 3 — DiffusionForensics / DIRE | 99.8 / 95.3 | 99.4 / 95.03 | — | official |
|
| 79 |
-
| Table 4 — UniversalFakeDetect / Ojha | acc 95.2 | 96.69 / 93.94 |
|
| 80 |
| Table 5 — Diffusion1kStep | acc 80.1 | 83.47 / 79.13 | — | official |
|
| 81 |
|
| 82 |
**reproduced** = SynthScope-trained checkpoint matches the paper · **official** = official weights loaded into the SynthScope class match (the SynthScope-trained number is shown alongside where we trained from scratch) · **below** = gap remains · **reference** = paper reports no comparable number · **missing** = not yet evaluated.
|
| 83 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
## Model details
|
| 85 |
|
| 86 |
The SynthScope implementation lives in:
|
|
|
|
| 69 |
|
| 70 |
All benchmarks the paper reports, with our numbers (verified, traced to `repro/bench_results/` + `repro/benchmark_specs/`).
|
| 71 |
|
| 72 |
+
Values are `mAP / acc`. **SynthScope_official** = the authors' released weights run through the SynthScope class / eval. **SynthScope_train** = the SynthScope class trained from scratch by us with the official recipe (`experiments/image/npr.sh`; see *Training in SynthScope* below), evaluated so far on Tables 1 and 4.
|
| 73 |
|
| 74 |
| Benchmark | Paper | SynthScope_official | SynthScope_train | Status |
|
| 75 |
|---|---|---|---|---|
|
| 76 |
+
| Table 1 — ForenSynths / Cross-GAN (8) | 96.1 / 92.5 | 94.22 / 92.3 | 93.83 / 84.09 | official; trained: AP close, acc below (as the official training code here) |
|
| 77 |
| Table 2 — GANGen-Detection (9) | 96.6 / 93.2 | 96.49 / 94.81 | — | official |
|
| 78 |
| Table 3 — DiffusionForensics / DIRE | 99.8 / 95.3 | 99.4 / 95.03 | — | official |
|
| 79 |
+
| Table 4 — UniversalFakeDetect / Ojha | acc 95.2 | 96.69 / 93.94 | 97.49 / 88.91 | official; trained: AP matches, acc below |
|
| 80 |
| Table 5 — Diffusion1kStep | acc 80.1 | 83.47 / 79.13 | — | official |
|
| 81 |
|
| 82 |
**reproduced** = SynthScope-trained checkpoint matches the paper · **official** = official weights loaded into the SynthScope class match (the SynthScope-trained number is shown alongside where we trained from scratch) · **below** = gap remains · **reference** = paper reports no comparable number · **missing** = not yet evaluated.
|
| 83 |
|
| 84 |
+
## Training in SynthScope
|
| 85 |
+
|
| 86 |
+
`experiments/image/npr.sh` runs the official recipe (the README's `train.py` command and `networks/trainer.py`): ProGAN
|
| 87 |
+
4-class (car, cat, chair, horse; 144,024 images), `Resize((256, 256))` → RandomCrop 224 → flip → ImageNet normalization
|
| 88 |
+
(no blur/JPEG: the official `data_augment` call is commented out), Adam lr 2e-4 without weight decay, batch 32 on one
|
| 89 |
+
GPU, 50 epochs, learning rate ×0.9 after epochs 10, 20, 30 and 40, last-epoch checkpoint, seed 100. The SynthScope NPR
|
| 90 |
+
uses the official convolution initialization for this.
|
| 91 |
+
|
| 92 |
+
To tell our pipeline apart from run-to-run variance, we also ran the **official `train.py`, unmodified**, on the same
|
| 93 |
+
data and machine, and scored every checkpoint with the same `bench_eval` command (`mAP / acc`):
|
| 94 |
+
|
| 95 |
+
| Checkpoint | Table 1 — ForenSynths (8 GANs) | Table 4 — UniversalFakeDetect |
|
| 96 |
+
|---|---|---|
|
| 97 |
+
| official `NPR.pth` (served as `npr.pth`) | 94.22 / 92.30 | 97.16 / 94.57 |
|
| 98 |
+
| official `model_epoch_last_3090.pth` | 92.81 / 91.83 | 96.69 / 93.94 |
|
| 99 |
+
| official `train.py` rerun here | 92.72 / 81.47 | 96.56 / 87.61 |
|
| 100 |
+
| **SynthScope recipe (ours)** | **93.83 / 84.09** | **97.49 / 88.91** |
|
| 101 |
+
|
| 102 |
+
Our recipe matches the official code's own rerun on every number (it is slightly better), so the SynthScope pipeline is
|
| 103 |
+
faithful; neither run reaches the released checkpoints' accuracy. AP — how well real and fake are ranked — reproduces;
|
| 104 |
+
the accuracy gap is a threshold shift (our models call more unseen-generator fakes real, e.g. CycleGAN fake recall 48 vs
|
| 105 |
+
96 for `NPR.pth`), which the released checkpoints did not show and which this environment (PyTorch 2.11 instead of 1.8,
|
| 106 |
+
another GPU) did not reproduce. Runs: training job 265720, official-code rerun 265815, evaluation 265808/265814/266028
|
| 107 |
+
(`repro/bench_results/npr_{official_nprpth,official3090,officialcode_rerun,trained_last,trained_best}_*.json`).
|
| 108 |
+
|
| 109 |
## Model details
|
| 110 |
|
| 111 |
The SynthScope implementation lives in:
|
models/image/npr/npr.py
CHANGED
|
@@ -39,6 +39,13 @@ class NPR(nn.Module):
|
|
| 39 |
)
|
| 40 |
self.head = nn.Linear(layer2_ch, 1)
|
| 41 |
self.criterion = torch.nn.BCEWithLogitsLoss()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
|
| 44 |
def NPR_transform(self, x: torch.Tensor) -> torch.Tensor:
|
|
|
|
| 39 |
)
|
| 40 |
self.head = nn.Linear(layer2_ch, 1)
|
| 41 |
self.criterion = torch.nn.BCEWithLogitsLoss()
|
| 42 |
+
# Official initialization (networks/resnet.py ResNet.__init__); only matters when training from scratch.
|
| 43 |
+
for m in self.modules():
|
| 44 |
+
if isinstance(m, nn.Conv2d):
|
| 45 |
+
nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")
|
| 46 |
+
elif isinstance(m, nn.BatchNorm2d):
|
| 47 |
+
nn.init.constant_(m.weight, 1)
|
| 48 |
+
nn.init.constant_(m.bias, 0)
|
| 49 |
|
| 50 |
|
| 51 |
def NPR_transform(self, x: torch.Tensor) -> torch.Tensor:
|
models/image/omniaid/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# OmniAID (`omniaid`, `omniaid_v1`, `omniaid_genimage`)
|
| 2 |
+
|
| 3 |
+
**OmniAID: Decoupling Semantics and Artifacts for Universal AI-Generated Image Detection in the Wild** —
|
| 4 |
+
[paper (arXiv 2511.08423)](https://arxiv.org/abs/2511.08423) · [official code](https://github.com/yunncheng/OmniAID) ·
|
| 5 |
+
[Mirage-Test](https://huggingface.co/datasets/Yunncheng/Mirage-Test)
|
| 6 |
+
|
| 7 |
+
A detector trained on one kind of content (say, landscapes) often fails on another (faces), because it mixes up *what*
|
| 8 |
+
is shown with *how* it was made. OmniAID splits these apart with a hybrid mixture of experts on CLIP ViT-L/14@336:
|
| 9 |
+
semantic experts (human, animal, object, scene, ...) are routed per image, and an artifact expert is always on. The
|
| 10 |
+
authors also built **Mirage**, a train/test set of images from recent generators tuned for realism.
|
| 11 |
+
|
| 12 |
+
## Pipeline (official, reproduced exactly)
|
| 13 |
+
|
| 14 |
+
```
|
| 15 |
+
image ─ Resize((336, 336)) [v2] | Resize((512, 512)) → Resize((336, 336)) [v1-era releases]
|
| 16 |
+
─ ToTensor → CLIP normalization
|
| 17 |
+
├ frozen CLIP vision tower → pooler output → router → top-k semantic experts
|
| 18 |
+
└ MoE ViT-L/14: every attention projection = shared main weights (SVD) + the routed semantic experts
|
| 19 |
+
+ the always-on artifact expert
|
| 20 |
+
→ ln_post(CLS) → Linear(1024, 2) → logits; P(fake) = softmax(logits)[:, 1] (the official `prob`)
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
Three official releases (Hugging Face `Yunncheng/OmniAID`, MIT), each a full model loaded with `strict=True` as in the
|
| 24 |
+
official eval:
|
| 25 |
+
|
| 26 |
+
| key | release | trained on | experts × rank, router, top-k | preprocessing |
|
| 27 |
+
|---|---|---|---|---|
|
| 28 |
+
| `omniaid` | `checkpoint_omniaid_v2.pth` (recommended; served on the demo) | Mirage-Train | 6 × 1, 256, top-2 | single resize to 336 |
|
| 29 |
+
| `omniaid_v1` | `checkpoint_omniaid_v1.pth` (the paper's OmniAID-Mirage) | Mirage-Train | 6 × 8, 256, top-2 | 512 → 336 |
|
| 30 |
+
| `omniaid_genimage` | `checkpoint_omniaid_genimage_sd14.pth` (the paper's academic tables) | GenImage SD v1.4 | 3 × 4, 64, top-1 | 512 → 336 |
|
| 31 |
+
|
| 32 |
+
The expert counts, ranks and router widths were read from each checkpoint's tensor shapes (the README's
|
| 33 |
+
`config_omniaid_v1/v2.json` are not in the repo); top-k comes from the official configs and `reward/clean_test.py`.
|
| 34 |
+
The v1-era double resize is the pipeline the official code says to use for v1 weights; for the GenImage release it is
|
| 35 |
+
confirmed by the results below (it reproduces the paper).
|
| 36 |
+
|
| 37 |
+
## SynthScope adaptations
|
| 38 |
+
|
| 39 |
+
| choice | reason |
|
| 40 |
+
|---|---|
|
| 41 |
+
| `official_arch.py` = the official `models/OmniAID.py`, **byte-identical body** | `SynthScopeOmniAIDModel` subclasses the official `OmniAID`, so the checkpoints load key-for-key; MIT attribution in the header. |
|
| 42 |
+
| `forward` returns the 2-class logits (`cls`) | SynthScope's `ce` convention gives softmax[:, 1], the official `prob`. |
|
| 43 |
+
| one registry key per release | Each release has its own expert configuration and preprocessing. |
|
| 44 |
+
| `compute_loss` = cross-entropy on the logits | **The official recipe trains in two stages** (each semantic expert with hard routing, then the router with the artifact expert fixed) with orthogonality, load-balancing and gating losses; that recipe is not reproduced here. |
|
| 45 |
+
|
| 46 |
+
## Fidelity check (identical to the official model)
|
| 47 |
+
|
| 48 |
+
The official `OmniAID` (built from the release's config and loaded `strict=True`, as `main_finetune.py` does) and
|
| 49 |
+
SynthScope's class, for all three releases on an A5000: **every state-dict tensor identical** (2804 / 2804 / 1940, max
|
| 50 |
+
|diff| 0.0), the registry transforms equal the official v2 / v1 pipelines pixel for pixel, and **max |logit diff| 0.0
|
| 51 |
+
with 0 verdict flips** on 24 GenImage images (ADM, BigGAN, Midjourney, Wukong; real and fake). Job 265851.
|
| 52 |
+
|
| 53 |
+
## Results
|
| 54 |
+
|
| 55 |
+
Paper metric: accuracy at 0.5 per subset, mean over subsets (Tables 2, 3, 6). Every subset used here is balanced except
|
| 56 |
+
Mirage-Test's Anime (fakes only), so accuracy equals SynthScope's balanced accuracy.
|
| 57 |
+
|
| 58 |
+
| Benchmark | Release | Paper | SynthScope_official | SynthScope_train | Status |
|
| 59 |
+
|---|---|---|---|---|---|
|
| 60 |
+
| **GenImage** (8 subsets) | `omniaid_genimage` | 95.94 | **95.82** (AP 99.02) | — | **reproduced** (every subset within 1.5 pt) |
|
| 61 |
+
| **AIGCDetectBenchmark** (17) | `omniaid_genimage` | 88.87 | **88.87** (AP 97.88) | — | **reproduced** exactly |
|
| 62 |
+
| **Mirage-Test** (5 categories) | `omniaid_genimage` | 51.10 | **51.10** | — | **reproduced** exactly (every category) |
|
| 63 |
+
| **GenImage** (8 subsets) | `omniaid_v1` | 97.24 | **97.16** (AP 99.20) | — | **reproduced** (every subset within 0.6 pt) |
|
| 64 |
+
| **AIGCDetectBenchmark** (17) | `omniaid_v1` | 92.88 | **92.88** (AP 98.93) | — | **reproduced** exactly |
|
| 65 |
+
| **Mirage-Test** (5 categories) | `omniaid_v1` | 88.39 | **88.39** | — | **reproduced** exactly |
|
| 66 |
+
| Chameleon / DRCT-2M | `omniaid_genimage` | 77.35 / 88.21 | — | — | missing (Chameleon is by request; DRCT-2M is not in SynthScope) |
|
| 67 |
+
|
| 68 |
+
GenImage per subset (SynthScope vs paper): `omniaid_genimage` Midjourney 87.20 vs 85.7 · SD v1.4 98.92 vs 98.9 · SD v1.5
|
| 69 |
+
98.77 vs 98.8 · ADM 90.40 vs 91.4 · GLIDE 98.97 vs 98.7 · Wukong 97.50 vs 98.1 · VQDM 96.07 vs 97.3 · BigGAN 98.70 vs
|
| 70 |
+
98.7; `omniaid_v1` Midjourney 98.20 vs 98.0 · SD v1.4 98.69 vs 98.7 · SD v1.5 98.63 vs 98.4 · ADM 88.90 vs 89.5 · GLIDE
|
| 71 |
+
98.20 vs 98.3 · Wukong 98.43 vs 98.6 · VQDM 98.40 vs 98.4 · BigGAN 97.87 vs 98.1.
|
| 72 |
+
|
| 73 |
+
**Mirage-Test protocol.** The paper's mean averages the balanced accuracy ("Overall") of Human, Animal, Object and Scene with the fake accuracy of Anime, which has no reals; the loader pairs Anime with the other categories' reals, and SynthScope reports the same mean. `omniaid_genimage`: Human 59.38, Animal 55.90, Object 49.02, Scene 46.52, Anime 44.67 (paper Table 3: 59.38, 55.90, 49.02, 46.53, 44.67); `omniaid_v1`: Human 93.69, Animal 81.37, Object 84.92, Scene 87.07, Anime 94.92 (paper: 93.69, 81.37, 84.92, 87.07, 94.92). The recommended v2 release (`omniaid`, not in the paper) gives 78.46 (AP 96.48): Human 88.82, Animal 77.20, Object 75.09, Scene 70.89, Anime 80.28.
|
| 74 |
+
|
| 75 |
+
**In the wild (`omniaid`, v2; new measurements, not in the paper)** — balanced accuracy / AP: SynthWildX 86.02 / 95.52,
|
| 76 |
+
WildRF 88.36 / 97.28, BFree-Online 70.26 / 91.70, RealChain 61.71 / 84.02, EvalGEN 74.14 / 96.46, RRDataset 71.75 /
|
| 77 |
+
84.19. See [docs/WILD_BENCHMARKS.md](../../../docs/WILD_BENCHMARKS.md).
|
| 78 |
+
|
| 79 |
+
Runs: jobs 265856 (`omniaid_genimage`), 265857 (`omniaid_v1`), 265858 (`omniaid`), 265935-265937 (Mirage-Test); results in
|
| 80 |
+
`repro/bench_results/omniaid*.json`.
|
| 81 |
+
|
| 82 |
+
## Usage
|
| 83 |
+
|
| 84 |
+
```bash
|
| 85 |
+
synthscope detect photo.png --model omniaid # v2; needs omniaid.pth in $SYNTHSCOPE_CKPT_DIR + HF access for CLIP
|
| 86 |
+
python benchmark/bench_eval.py --model omniaid_genimage --dataset genimage_full --root /data/GenImage_full
|
| 87 |
+
python benchmark/bench_eval.py --model omniaid_v1 --dataset mirage_test --root /data/MirageTest
|
| 88 |
+
```
|
models/image/omniaid/__init__.py
ADDED
|
File without changes
|
models/image/omniaid/official_arch.py
ADDED
|
@@ -0,0 +1,681 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Vendored from github.com/yunncheng/OmniAID (MIT License per its README and Hugging Face model card), file models/OmniAID.py.
|
| 2 |
+
# Unchanged below this header, so the released checkpoints load key-for-key (strict=True, as the official eval does).
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
import torch.nn.functional as F
|
| 6 |
+
from typing import Optional, Tuple, Dict
|
| 7 |
+
from transformers import CLIPModel, CLIPVisionConfig
|
| 8 |
+
from transformers.models.clip.modeling_clip import CLIPVisionEmbeddings, CLIPMLP
|
| 9 |
+
from torch.utils.checkpoint import checkpoint
|
| 10 |
+
|
| 11 |
+
class OmniAID(nn.Module):
|
| 12 |
+
def __init__(self, config=None):
|
| 13 |
+
super(OmniAID, self).__init__()
|
| 14 |
+
self.config = config
|
| 15 |
+
self.num_experts = config.num_experts
|
| 16 |
+
self.rank_per_expert = config.rank_per_expert
|
| 17 |
+
|
| 18 |
+
self.moe_lambda_orth = config.moe_lambda_orth
|
| 19 |
+
self.moe_lambda_balance = config.moe_lambda_balance
|
| 20 |
+
self.moe_lambda_gating_cls = config.moe_lambda_gating_cls
|
| 21 |
+
self.svd_dropout = config.dropout
|
| 22 |
+
|
| 23 |
+
self.moe_router_hidden_dim = config.moe_router_hidden_dim
|
| 24 |
+
self.top_k = config.moe_top_k
|
| 25 |
+
|
| 26 |
+
self.gradient_checkpointing = config.gradient_checkpointing_enable
|
| 27 |
+
|
| 28 |
+
self.gating_criterion = torch.nn.CrossEntropyLoss()
|
| 29 |
+
|
| 30 |
+
self.training_mode = "standard" # Default runtime mode, can be changed by set_training_mode
|
| 31 |
+
self.active_expert_idx = None
|
| 32 |
+
|
| 33 |
+
# This property is fixed after initialization
|
| 34 |
+
self.is_hybrid = config.is_hybrid
|
| 35 |
+
|
| 36 |
+
if self.is_hybrid:
|
| 37 |
+
print("Initializing in HYBRID MoE architectural mode.")
|
| 38 |
+
# Use the last expert as the artifact expert
|
| 39 |
+
self.artifact_expert_idx = config.num_experts - 1
|
| 40 |
+
self.num_semantic_experts = config.num_experts - 1
|
| 41 |
+
if self.num_semantic_experts <= 0:
|
| 42 |
+
raise ValueError("num_experts must be at least 2 for the Hybrid MoE model (1 fixed, >=1 semantic).")
|
| 43 |
+
gating_num_experts = self.num_semantic_experts
|
| 44 |
+
else:
|
| 45 |
+
print("Initializing in STANDARD MoE architectural mode.")
|
| 46 |
+
self.artifact_expert_idx = -1 # A value that will never match an expert index
|
| 47 |
+
gating_num_experts = self.num_experts
|
| 48 |
+
|
| 49 |
+
pretrained_path = config.CLIP_path
|
| 50 |
+
|
| 51 |
+
# Initialize an independent Vision Model as the feature extractor. This model will be used exclusively to generate features for the router.
|
| 52 |
+
self.feature_extractor = CLIPModel.from_pretrained(pretrained_path).vision_model
|
| 53 |
+
self.feature_extractor.eval()
|
| 54 |
+
|
| 55 |
+
vision_config = self.feature_extractor.config
|
| 56 |
+
self.hidden_size = vision_config.hidden_size
|
| 57 |
+
|
| 58 |
+
# Calculate rank allocation
|
| 59 |
+
total_rank = vision_config.hidden_size
|
| 60 |
+
residual_rank = self.rank_per_expert
|
| 61 |
+
|
| 62 |
+
if residual_rank >= total_rank:
|
| 63 |
+
raise ValueError(
|
| 64 |
+
f"The total rank for experts ({residual_rank}) must be less than the total rank ({total_rank}). "
|
| 65 |
+
f"Please reduce num_experts ({self.num_experts}) or rank_per_expert ({self.rank_per_expert})."
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
r_main = total_rank - residual_rank
|
| 69 |
+
print(f"Rank allocation: total_rank={total_rank}, r_main={r_main}, num_experts={self.num_experts}, rank_per_expert={self.rank_per_expert}")
|
| 70 |
+
|
| 71 |
+
# Build the MoE backbone network
|
| 72 |
+
self.embeddings = CLIPVisionEmbeddings(vision_config)
|
| 73 |
+
self.ln_pre = nn.LayerNorm(vision_config.hidden_size)
|
| 74 |
+
self.encoder_layers = nn.ModuleList([
|
| 75 |
+
ViTMoELayer(vision_config, self.num_experts, r_main, self.rank_per_expert, self.artifact_expert_idx, self.svd_dropout)
|
| 76 |
+
for _ in range(vision_config.num_hidden_layers)
|
| 77 |
+
])
|
| 78 |
+
self.ln_post = nn.LayerNorm(self.hidden_size, eps=vision_config.layer_norm_eps)
|
| 79 |
+
self.gating_network = GatingNetwork(
|
| 80 |
+
input_dim=self.hidden_size,
|
| 81 |
+
num_experts=gating_num_experts,
|
| 82 |
+
hidden_dim=self.moe_router_hidden_dim,
|
| 83 |
+
top_k=self.top_k
|
| 84 |
+
)
|
| 85 |
+
self.head = nn.Linear(self.hidden_size, 2)
|
| 86 |
+
|
| 87 |
+
self.load_and_replace_from_pretrained(self.feature_extractor)
|
| 88 |
+
|
| 89 |
+
def load_and_replace_from_pretrained(self, pretrained_model):
|
| 90 |
+
self.embeddings.load_state_dict(pretrained_model.embeddings.state_dict())
|
| 91 |
+
self.ln_pre.load_state_dict(pretrained_model.pre_layrnorm.state_dict())
|
| 92 |
+
self.ln_post.load_state_dict(pretrained_model.post_layernorm.state_dict())
|
| 93 |
+
|
| 94 |
+
for moe_layer, pretrained_layer in zip(self.encoder_layers, pretrained_model.encoder.layers):
|
| 95 |
+
for proj_name in ['q_proj', 'k_proj', 'v_proj', 'out_proj']:
|
| 96 |
+
self._replace_linear_with_svd_moe(
|
| 97 |
+
getattr(pretrained_layer.self_attn, proj_name),
|
| 98 |
+
getattr(moe_layer.self_attn, proj_name)
|
| 99 |
+
)
|
| 100 |
+
moe_layer.layer_norm1.load_state_dict(pretrained_layer.layer_norm1.state_dict())
|
| 101 |
+
moe_layer.mlp.load_state_dict(pretrained_layer.mlp.state_dict())
|
| 102 |
+
moe_layer.layer_norm2.load_state_dict(pretrained_layer.layer_norm2.state_dict())
|
| 103 |
+
|
| 104 |
+
# Freeze non-MoE parameters
|
| 105 |
+
for name, param in self.named_parameters():
|
| 106 |
+
if 'experts' not in name and 'gating' not in name and 'head' not in name:
|
| 107 |
+
param.requires_grad = False
|
| 108 |
+
|
| 109 |
+
def _replace_linear_with_svd_moe(self, original_module: nn.Linear, moe_module):
|
| 110 |
+
original_weight = original_module.weight.data
|
| 111 |
+
moe_module.weight_original_fnorm.data.copy_(torch.norm(original_weight, p='fro'))
|
| 112 |
+
if original_module.bias is not None:
|
| 113 |
+
moe_module.bias.data.copy_(original_module.bias.data)
|
| 114 |
+
|
| 115 |
+
U, S, Vh = torch.linalg.svd(original_weight, full_matrices=False)
|
| 116 |
+
|
| 117 |
+
# Initialize the main (shared) weight matrix
|
| 118 |
+
r = moe_module.r_main
|
| 119 |
+
U_r, S_r, Vh_r = U[:, :r], S[:r], Vh[:r, :]
|
| 120 |
+
moe_module.weight_main.data.copy_(U_r @ torch.diag(S_r) @ Vh_r)
|
| 121 |
+
|
| 122 |
+
moe_module.U_r.data.copy_(U_r)
|
| 123 |
+
moe_module.V_r.data.copy_(Vh_r)
|
| 124 |
+
|
| 125 |
+
# Define the SINGLE, SHARED chunk from ALL remaining singular values
|
| 126 |
+
chunk_start_rank = r
|
| 127 |
+
|
| 128 |
+
# Check if there are any singular values left
|
| 129 |
+
if chunk_start_rank >= len(S):
|
| 130 |
+
# If not, initialize all experts to zero
|
| 131 |
+
for i in range(moe_module.num_experts):
|
| 132 |
+
moe_module.U_experts[i].data.zero_()
|
| 133 |
+
moe_module.S_experts[i].data.zero_()
|
| 134 |
+
moe_module.V_experts[i].data.zero_()
|
| 135 |
+
return
|
| 136 |
+
|
| 137 |
+
U_chunk, S_chunk, Vh_chunk = U[:, chunk_start_rank:], S[chunk_start_rank:], Vh[chunk_start_rank:, :]
|
| 138 |
+
|
| 139 |
+
# The expert's parameter dimension is fixed at rank_per_expert. We must pad the chunk if its actual rank is smaller
|
| 140 |
+
actual_chunk_rank = U_chunk.shape[1]
|
| 141 |
+
|
| 142 |
+
if actual_chunk_rank < moe_module.rank_per_expert:
|
| 143 |
+
pad_rank = moe_module.rank_per_expert - actual_chunk_rank
|
| 144 |
+
U_chunk = F.pad(U_chunk, (0, pad_rank))
|
| 145 |
+
S_chunk = F.pad(S_chunk, (0, pad_rank))
|
| 146 |
+
Vh_chunk = F.pad(Vh_chunk, (0, 0, 0, pad_rank))
|
| 147 |
+
|
| 148 |
+
# Assign the SAME shared chunk to all experts
|
| 149 |
+
for i in range(moe_module.num_experts):
|
| 150 |
+
moe_module.U_experts[i].data.copy_(U_chunk)
|
| 151 |
+
moe_module.S_experts[i].data.copy_(S_chunk)
|
| 152 |
+
moe_module.V_experts[i].data.copy_(Vh_chunk)
|
| 153 |
+
|
| 154 |
+
def set_training_mode(self, mode: str, active_expert_idx: int = None, trainable_expert_indices: list = None):
|
| 155 |
+
"""
|
| 156 |
+
Sets the training mode of the model, allowing specific parts of the parameters to be frozen/unfrozen.
|
| 157 |
+
|
| 158 |
+
Args:
|
| 159 |
+
mode (str): Training mode. Possible values are:
|
| 160 |
+
- 'hard_sampling': Stage 1. Unfreezes one active expert and the head.
|
| 161 |
+
- 'router_training': Stage 2. Unfreezes the router and the head. Optionally unfreezes specified experts.
|
| 162 |
+
- 'standard': End-to-end. Unfreezes the router, head, and all experts.
|
| 163 |
+
active_expert_idx (int, optional): The index of the expert to be trained in 'hard_sampling' mode. Required for this mode.
|
| 164 |
+
trainable_expert_indices (list, optional): A list of expert indices to also unfreeze during 'router_training' mode.
|
| 165 |
+
Allows for joint training of the router and specific experts.
|
| 166 |
+
"""
|
| 167 |
+
if mode not in ['hard_sampling', 'router_training', 'standard']:
|
| 168 |
+
raise ValueError("Training mode must be one of 'hard_sampling', 'router_training', or 'standard'")
|
| 169 |
+
|
| 170 |
+
if mode == 'hard_sampling' and (active_expert_idx is None or not isinstance(active_expert_idx, int) or active_expert_idx < 0):
|
| 171 |
+
raise ValueError("A valid integer active_expert_idx must be provided for 'hard_sampling' mode.")
|
| 172 |
+
|
| 173 |
+
if mode == 'router_training' and trainable_expert_indices is not None and not isinstance(trainable_expert_indices, list):
|
| 174 |
+
raise ValueError("'trainable_expert_indices' must be a list of integers.")
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
# Store the state in the model instance
|
| 178 |
+
self.training_mode = mode
|
| 179 |
+
self.active_expert_idx = active_expert_idx if mode == 'hard_sampling' else None
|
| 180 |
+
|
| 181 |
+
for param in self.parameters():
|
| 182 |
+
param.requires_grad = False
|
| 183 |
+
|
| 184 |
+
if mode == 'standard':
|
| 185 |
+
# In standard MoE training, train the router, all experts, and the final head
|
| 186 |
+
for param in self.gating_network.parameters():
|
| 187 |
+
param.requires_grad = True
|
| 188 |
+
|
| 189 |
+
for param in self.head.parameters():
|
| 190 |
+
param.requires_grad = True
|
| 191 |
+
|
| 192 |
+
for layer in self.encoder_layers:
|
| 193 |
+
for proj_name in ['q_proj', 'k_proj', 'v_proj', 'out_proj']:
|
| 194 |
+
moe_linear_layer = getattr(layer.self_attn, proj_name)
|
| 195 |
+
for param in moe_linear_layer.U_experts:
|
| 196 |
+
param.requires_grad = True
|
| 197 |
+
for param in moe_linear_layer.S_experts:
|
| 198 |
+
param.requires_grad = True
|
| 199 |
+
for param in moe_linear_layer.V_experts:
|
| 200 |
+
param.requires_grad = True
|
| 201 |
+
|
| 202 |
+
elif mode == 'router_training':
|
| 203 |
+
print("Unfreezing: Gating Network and Head.")
|
| 204 |
+
for param in self.gating_network.parameters():
|
| 205 |
+
param.requires_grad = True
|
| 206 |
+
for param in self.head.parameters():
|
| 207 |
+
param.requires_grad = True
|
| 208 |
+
|
| 209 |
+
# Optionally unfreeze specific experts during router training
|
| 210 |
+
if trainable_expert_indices:
|
| 211 |
+
print(f"Additionally unfreezing experts with indices: {trainable_expert_indices}")
|
| 212 |
+
for expert_idx in trainable_expert_indices:
|
| 213 |
+
if not 0 <= expert_idx < self.num_experts:
|
| 214 |
+
raise ValueError(f"trainable_expert_index ({expert_idx}) is out of bounds for num_experts ({self.num_experts}).")
|
| 215 |
+
|
| 216 |
+
for layer in self.encoder_layers:
|
| 217 |
+
for proj_name in ['q_proj', 'k_proj', 'v_proj', 'out_proj']:
|
| 218 |
+
moe_linear_layer = getattr(layer.self_attn, proj_name)
|
| 219 |
+
moe_linear_layer.U_experts[expert_idx].requires_grad = True
|
| 220 |
+
moe_linear_layer.S_experts[expert_idx].requires_grad = True
|
| 221 |
+
moe_linear_layer.V_experts[expert_idx].requires_grad = True
|
| 222 |
+
|
| 223 |
+
elif mode == 'hard_sampling':
|
| 224 |
+
# In hard_sampling stage, unfreeze only the parameters of the single active expert
|
| 225 |
+
if self.active_expert_idx >= self.num_experts:
|
| 226 |
+
raise ValueError(f"active_expert_idx ({self.active_expert_idx}) is out of bounds for num_experts ({self.num_experts}).")
|
| 227 |
+
|
| 228 |
+
for layer in self.encoder_layers:
|
| 229 |
+
for proj_name in ['q_proj', 'k_proj', 'v_proj', 'out_proj']:
|
| 230 |
+
moe_linear_layer = getattr(layer.self_attn, proj_name)
|
| 231 |
+
|
| 232 |
+
# Unfreeze the parameters (U, S, V) for the specified active expert by direct indexing
|
| 233 |
+
moe_linear_layer.U_experts[self.active_expert_idx].requires_grad = True
|
| 234 |
+
moe_linear_layer.S_experts[self.active_expert_idx].requires_grad = True
|
| 235 |
+
moe_linear_layer.V_experts[self.active_expert_idx].requires_grad = True
|
| 236 |
+
|
| 237 |
+
for param in self.head.parameters():
|
| 238 |
+
param.requires_grad = True
|
| 239 |
+
|
| 240 |
+
print(f"Successfully set training mode to '{self.training_mode}'" + (f" (active expert: {self.active_expert_idx})" if self.training_mode == 'hard_sampling' else ""))
|
| 241 |
+
|
| 242 |
+
# print("Currently trainable parameters:")
|
| 243 |
+
# for name, param in self.named_parameters():
|
| 244 |
+
# if param.requires_grad:
|
| 245 |
+
# print(f"- {name}")
|
| 246 |
+
|
| 247 |
+
# For checkpoint
|
| 248 |
+
@staticmethod
|
| 249 |
+
def run_moe_layer(module, hidden_states, gating_outputs):
|
| 250 |
+
return module(hidden_states, gating_outputs=gating_outputs)[0]
|
| 251 |
+
|
| 252 |
+
def forward(self, images) -> dict:
|
| 253 |
+
batch_size = images.size(0)
|
| 254 |
+
|
| 255 |
+
with torch.no_grad():
|
| 256 |
+
# Use the pooler_output as the routing feature
|
| 257 |
+
routing_features = self.feature_extractor(images, output_hidden_states=False).pooler_output
|
| 258 |
+
|
| 259 |
+
hidden_states = self.embeddings(images)
|
| 260 |
+
hidden_states = self.ln_pre(hidden_states)
|
| 261 |
+
|
| 262 |
+
gating_outputs = {}
|
| 263 |
+
|
| 264 |
+
# Stage 1 expert training ('hard_sampling')
|
| 265 |
+
if self.training_mode == 'hard_sampling':
|
| 266 |
+
if self.active_expert_idx is None:
|
| 267 |
+
raise ValueError("In hard_sampling mode, active_expert_idx must be set.")
|
| 268 |
+
|
| 269 |
+
hard_sampling_target = torch.full((batch_size,), self.active_expert_idx, dtype=torch.long, device=hidden_states.device)
|
| 270 |
+
gating_outputs['top_k_indices'] = hard_sampling_target.unsqueeze(1)
|
| 271 |
+
gating_outputs['top_k_gates'] = torch.ones_like(gating_outputs['top_k_indices'], dtype=hidden_states.dtype)
|
| 272 |
+
gating_outputs['balance_loss'] = torch.tensor(0.0, device=hidden_states.device)
|
| 273 |
+
gating_outputs['gating_logits'] = None
|
| 274 |
+
|
| 275 |
+
# Stage 2 routing ('router_training') or inference for a Hybrid model
|
| 276 |
+
elif self.is_hybrid:
|
| 277 |
+
# Create a "pass-through" gating output for the fixed artifact expert (the last expert)
|
| 278 |
+
artifact_expert_indices = torch.full(
|
| 279 |
+
(batch_size, 1),
|
| 280 |
+
self.artifact_expert_idx, # Use the last expert index
|
| 281 |
+
dtype=torch.long,
|
| 282 |
+
device=hidden_states.device
|
| 283 |
+
)
|
| 284 |
+
artifact_expert_gates = torch.ones(
|
| 285 |
+
(batch_size, 1),
|
| 286 |
+
dtype=hidden_states.dtype,
|
| 287 |
+
device=hidden_states.device
|
| 288 |
+
)
|
| 289 |
+
|
| 290 |
+
# Get routing decisions for the SEMANTIC experts (experts 0, 1, ..., N-2)
|
| 291 |
+
semantic_gating_outputs = self.gating_network(routing_features)
|
| 292 |
+
|
| 293 |
+
semantic_expert_indices = semantic_gating_outputs['top_k_indices']
|
| 294 |
+
|
| 295 |
+
# Combine the routed semantic experts and the fixed artifact expert
|
| 296 |
+
gating_outputs['top_k_indices'] = torch.cat([semantic_expert_indices, artifact_expert_indices], dim=1)
|
| 297 |
+
gating_outputs['top_k_gates'] = torch.cat([semantic_gating_outputs['top_k_gates'], artifact_expert_gates], dim=1)
|
| 298 |
+
|
| 299 |
+
gating_outputs['balance_loss'] = semantic_gating_outputs['balance_loss']
|
| 300 |
+
gating_outputs['gating_logits'] = semantic_gating_outputs['gating_logits']
|
| 301 |
+
|
| 302 |
+
# Standard end-to-end training or inference for a Standard MoE model
|
| 303 |
+
else:
|
| 304 |
+
gating_outputs = self.gating_network(routing_features)
|
| 305 |
+
|
| 306 |
+
final_gates = torch.zeros(batch_size, self.num_experts, device=hidden_states.device, dtype=gating_outputs['top_k_gates'].dtype)
|
| 307 |
+
|
| 308 |
+
final_gates.scatter_(-1, gating_outputs['top_k_indices'], gating_outputs['top_k_gates'])
|
| 309 |
+
|
| 310 |
+
for layer_module in self.encoder_layers:
|
| 311 |
+
if self.gradient_checkpointing and self.training:
|
| 312 |
+
hidden_states = checkpoint(
|
| 313 |
+
self.run_moe_layer,
|
| 314 |
+
layer_module,
|
| 315 |
+
hidden_states,
|
| 316 |
+
gating_outputs,
|
| 317 |
+
use_reentrant=False
|
| 318 |
+
)
|
| 319 |
+
else:
|
| 320 |
+
hidden_states = layer_module(
|
| 321 |
+
hidden_states,
|
| 322 |
+
gating_outputs=gating_outputs,
|
| 323 |
+
)[0]
|
| 324 |
+
|
| 325 |
+
pooled_output = self.ln_post(hidden_states[:, 0, :])
|
| 326 |
+
pred = self.head(pooled_output)
|
| 327 |
+
prob = torch.softmax(pred, dim=1)[:, 1]
|
| 328 |
+
|
| 329 |
+
return {
|
| 330 |
+
'cls': pred,
|
| 331 |
+
'prob': prob,
|
| 332 |
+
'balance_loss': gating_outputs['balance_loss'],
|
| 333 |
+
'gating_logits': gating_outputs['gating_logits'],
|
| 334 |
+
'final_gates': final_gates
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
def get_losses(self, pred_dict: dict, labels, expert_domain_labels, criterion) -> dict:
|
| 338 |
+
pred = pred_dict['cls']
|
| 339 |
+
classification_loss = criterion(pred, labels)
|
| 340 |
+
|
| 341 |
+
orth_loss, load_balancing_loss = torch.tensor(0.0, device=pred.device), torch.tensor(0.0, device=pred.device)
|
| 342 |
+
gating_classification_loss = torch.tensor(0.0, device=pred.device)
|
| 343 |
+
|
| 344 |
+
num_moe_layers = sum(1 for module in self.modules() if isinstance(module, SVDMoeLinear))
|
| 345 |
+
|
| 346 |
+
# Orthogonal loss is not calculated during router_training
|
| 347 |
+
if self.training_mode != 'router_training' and num_moe_layers > 0:
|
| 348 |
+
current_orth_loss = 0.0
|
| 349 |
+
for module in self.modules():
|
| 350 |
+
if isinstance(module, SVDMoeLinear):
|
| 351 |
+
if self.training_mode == 'hard_sampling':
|
| 352 |
+
current_orth_loss += module.compute_targeted_orthogonal_loss(self.active_expert_idx)
|
| 353 |
+
elif self.training_mode == 'standard':
|
| 354 |
+
current_orth_loss += module.compute_full_orthogonal_loss()
|
| 355 |
+
orth_loss = current_orth_loss / num_moe_layers
|
| 356 |
+
|
| 357 |
+
# Balance loss is only relevant when the router is active
|
| 358 |
+
if self.training_mode == 'router_training' or self.training_mode == 'standard':
|
| 359 |
+
load_balancing_loss = pred_dict['balance_loss']
|
| 360 |
+
|
| 361 |
+
if self.training_mode == 'router_training' or self.training_mode == 'standard':
|
| 362 |
+
gating_logits = pred_dict['gating_logits']
|
| 363 |
+
|
| 364 |
+
if gating_logits is not None and expert_domain_labels is not None:
|
| 365 |
+
|
| 366 |
+
valid_mask = (expert_domain_labels >= 0) & (expert_domain_labels < self.gating_network.num_experts)
|
| 367 |
+
|
| 368 |
+
filtered_logits = gating_logits[valid_mask]
|
| 369 |
+
filtered_labels = expert_domain_labels[valid_mask]
|
| 370 |
+
|
| 371 |
+
if filtered_logits.shape[0] > 0:
|
| 372 |
+
gating_classification_loss = self.gating_criterion(filtered_logits, filtered_labels)
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
total_loss = classification_loss + \
|
| 376 |
+
self.moe_lambda_orth * orth_loss + \
|
| 377 |
+
self.moe_lambda_balance * load_balancing_loss + \
|
| 378 |
+
self.moe_lambda_gating_cls * gating_classification_loss
|
| 379 |
+
|
| 380 |
+
return {
|
| 381 |
+
'overall_loss': total_loss,
|
| 382 |
+
'classification_loss': classification_loss.detach(),
|
| 383 |
+
'orth_loss': orth_loss.detach(),
|
| 384 |
+
'balance_loss': load_balancing_loss.detach(),
|
| 385 |
+
'gating_cls_loss': gating_classification_loss.detach(),
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
|
| 389 |
+
class ViTMoEAttention(nn.Module):
|
| 390 |
+
def __init__(self, config: CLIPVisionConfig, num_experts: int, r_main: int, rank_per_expert: int, artifact_expert_idx: int, svd_dropout: float):
|
| 391 |
+
super().__init__()
|
| 392 |
+
self.embed_dim = config.hidden_size
|
| 393 |
+
self.num_heads = config.num_attention_heads
|
| 394 |
+
self.head_dim = self.embed_dim // self.num_heads
|
| 395 |
+
if self.head_dim * self.num_heads != self.embed_dim:
|
| 396 |
+
raise ValueError(
|
| 397 |
+
f"embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:"
|
| 398 |
+
f" {self.num_heads})."
|
| 399 |
+
)
|
| 400 |
+
|
| 401 |
+
self.scale = self.head_dim**-0.5
|
| 402 |
+
self.dropout = config.attention_dropout
|
| 403 |
+
|
| 404 |
+
self.q_proj = SVDMoeLinear(self.embed_dim, self.embed_dim, r_main, num_experts, rank_per_expert, artifact_expert_idx, svd_dropout)
|
| 405 |
+
self.k_proj = SVDMoeLinear(self.embed_dim, self.embed_dim, r_main, num_experts, rank_per_expert, artifact_expert_idx, svd_dropout)
|
| 406 |
+
self.v_proj = SVDMoeLinear(self.embed_dim, self.embed_dim, r_main, num_experts, rank_per_expert, artifact_expert_idx, svd_dropout)
|
| 407 |
+
self.out_proj = SVDMoeLinear(self.embed_dim, self.embed_dim, r_main, num_experts, rank_per_expert, artifact_expert_idx, svd_dropout)
|
| 408 |
+
|
| 409 |
+
def _shape(self, tensor: torch.Tensor, seq_len: int, bsz: int):
|
| 410 |
+
return tensor.view(bsz, seq_len, self.num_heads, self.head_dim).transpose(1, 2).contiguous()
|
| 411 |
+
|
| 412 |
+
def forward(
|
| 413 |
+
self,
|
| 414 |
+
hidden_states: torch.Tensor,
|
| 415 |
+
gating_outputs: Dict[str, torch.Tensor],
|
| 416 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 417 |
+
output_attentions: Optional[bool] = False,
|
| 418 |
+
) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
|
| 419 |
+
"""
|
| 420 |
+
Input shape: Batch x Time x Channel
|
| 421 |
+
attention_mask: (batch, 1, a_seq_len, b_seq_len)
|
| 422 |
+
"""
|
| 423 |
+
bsz, tgt_len, embed_dim = hidden_states.size()
|
| 424 |
+
|
| 425 |
+
query_states = self.q_proj(hidden_states, gating_outputs) * self.scale
|
| 426 |
+
key_states = self.k_proj(hidden_states, gating_outputs)
|
| 427 |
+
value_states = self.v_proj(hidden_states, gating_outputs)
|
| 428 |
+
|
| 429 |
+
query_states = self._shape(query_states, tgt_len, bsz)
|
| 430 |
+
key_states = self._shape(key_states, -1, bsz)
|
| 431 |
+
value_states = self._shape(value_states, -1, bsz)
|
| 432 |
+
|
| 433 |
+
proj_shape = (bsz * self.num_heads, -1, self.head_dim)
|
| 434 |
+
query_states = query_states.reshape(*proj_shape)
|
| 435 |
+
key_states = key_states.reshape(*proj_shape)
|
| 436 |
+
value_states = value_states.reshape(*proj_shape)
|
| 437 |
+
|
| 438 |
+
src_len = key_states.size(1)
|
| 439 |
+
attn_weights = torch.bmm(query_states, key_states.transpose(1, 2))
|
| 440 |
+
|
| 441 |
+
if attention_mask is not None:
|
| 442 |
+
if attention_mask.size() != (bsz, 1, tgt_len, src_len):
|
| 443 |
+
raise ValueError(
|
| 444 |
+
f"Attention mask should be of size {(bsz, 1, tgt_len, src_len)}, but is {attention_mask.size()}"
|
| 445 |
+
)
|
| 446 |
+
attn_weights = attn_weights.view(bsz, self.num_heads, tgt_len, src_len)
|
| 447 |
+
attn_weights = attn_weights + attention_mask
|
| 448 |
+
attn_weights = attn_weights.view(bsz * self.num_heads, tgt_len, src_len)
|
| 449 |
+
|
| 450 |
+
attn_weights = nn.functional.softmax(attn_weights, dim=-1)
|
| 451 |
+
|
| 452 |
+
attn_weights_reshaped = None
|
| 453 |
+
if output_attentions:
|
| 454 |
+
attn_weights_reshaped = attn_weights.view(bsz, self.num_heads, tgt_len, src_len)
|
| 455 |
+
attn_weights = attn_weights_reshaped.view(bsz * self.num_heads, tgt_len, src_len)
|
| 456 |
+
|
| 457 |
+
attn_probs = nn.functional.dropout(attn_weights, p=self.dropout, training=self.training)
|
| 458 |
+
|
| 459 |
+
attn_output = torch.bmm(attn_probs, value_states)
|
| 460 |
+
|
| 461 |
+
attn_output = attn_output.view(bsz, self.num_heads, tgt_len, self.head_dim)
|
| 462 |
+
attn_output = attn_output.transpose(1, 2)
|
| 463 |
+
attn_output = attn_output.reshape(bsz, tgt_len, embed_dim)
|
| 464 |
+
|
| 465 |
+
attn_output = self.out_proj(attn_output, gating_outputs)
|
| 466 |
+
|
| 467 |
+
return attn_output, attn_weights_reshaped
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
class ViTMoELayer(nn.Module):
|
| 471 |
+
def __init__(self, config: CLIPVisionConfig, num_experts: int, r_main: int, rank_per_expert: int, artifact_expert_idx: int, svd_dropout: float):
|
| 472 |
+
super().__init__()
|
| 473 |
+
self.self_attn = ViTMoEAttention(config, num_experts, r_main, rank_per_expert, artifact_expert_idx, svd_dropout)
|
| 474 |
+
self.layer_norm1 = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
|
| 475 |
+
self.mlp = CLIPMLP(config)
|
| 476 |
+
self.layer_norm2 = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
|
| 477 |
+
|
| 478 |
+
def forward(self, hidden_states: torch.Tensor, gating_outputs: Dict[str, torch.Tensor], attention_mask: Optional[torch.Tensor] = None):
|
| 479 |
+
residual = hidden_states
|
| 480 |
+
hidden_states = self.layer_norm1(hidden_states)
|
| 481 |
+
|
| 482 |
+
# Pass attention_mask to self_attn
|
| 483 |
+
hidden_states, attn_weights = self.self_attn(
|
| 484 |
+
hidden_states=hidden_states,
|
| 485 |
+
gating_outputs=gating_outputs,
|
| 486 |
+
attention_mask=attention_mask
|
| 487 |
+
)
|
| 488 |
+
|
| 489 |
+
hidden_states = residual + hidden_states
|
| 490 |
+
|
| 491 |
+
residual = hidden_states
|
| 492 |
+
hidden_states = self.layer_norm2(hidden_states)
|
| 493 |
+
hidden_states = self.mlp(hidden_states)
|
| 494 |
+
hidden_states = residual + hidden_states
|
| 495 |
+
|
| 496 |
+
# Maintain the return value format consistent with the official EncoderLayer (tuple)
|
| 497 |
+
return (hidden_states, )
|
| 498 |
+
|
| 499 |
+
|
| 500 |
+
class SVDMoeLinear(nn.Module):
|
| 501 |
+
"""
|
| 502 |
+
SVD orthogonal subspace linear layer supporting multiple experts and dynamic gating.
|
| 503 |
+
"""
|
| 504 |
+
def __init__(self, in_features, out_features, r_main, num_experts, rank_per_expert, artifact_expert_idx, svd_dropout=0.0, bias=True):
|
| 505 |
+
super(SVDMoeLinear, self).__init__()
|
| 506 |
+
self.in_features = in_features
|
| 507 |
+
self.out_features = out_features
|
| 508 |
+
self.r_main = r_main
|
| 509 |
+
self.num_experts = num_experts
|
| 510 |
+
self.rank_per_expert = rank_per_expert
|
| 511 |
+
self.artifact_expert_idx = artifact_expert_idx
|
| 512 |
+
|
| 513 |
+
self.register_buffer('weight_main', torch.zeros(out_features, in_features))
|
| 514 |
+
self.register_buffer('U_r', torch.zeros(out_features, r_main))
|
| 515 |
+
self.register_buffer('V_r', torch.zeros(r_main, in_features))
|
| 516 |
+
|
| 517 |
+
self.U_experts = nn.ParameterList([nn.Parameter(torch.zeros(out_features, rank_per_expert)) for _ in range(num_experts)])
|
| 518 |
+
self.S_experts = nn.ParameterList([nn.Parameter(torch.zeros(rank_per_expert)) for _ in range(num_experts)])
|
| 519 |
+
self.V_experts = nn.ParameterList([nn.Parameter(torch.zeros(rank_per_expert, in_features)) for _ in range(num_experts)])
|
| 520 |
+
|
| 521 |
+
self.register_buffer('weight_original_fnorm', torch.tensor(0.0))
|
| 522 |
+
|
| 523 |
+
if bias:
|
| 524 |
+
self.bias = nn.Parameter(torch.Tensor(out_features))
|
| 525 |
+
nn.init.zeros_(self.bias)
|
| 526 |
+
else:
|
| 527 |
+
self.register_parameter('bias', None)
|
| 528 |
+
|
| 529 |
+
if svd_dropout > 0.0:
|
| 530 |
+
self.dropout = nn.Dropout(p=svd_dropout)
|
| 531 |
+
else:
|
| 532 |
+
self.dropout = nn.Identity()
|
| 533 |
+
|
| 534 |
+
def forward(self, x: torch.Tensor, gating_outputs: Dict[str, torch.Tensor]) -> torch.Tensor:
|
| 535 |
+
output_main = F.linear(x, self.weight_main, None)
|
| 536 |
+
|
| 537 |
+
x = self.dropout(x)
|
| 538 |
+
|
| 539 |
+
top_k_indices = gating_outputs['top_k_indices'] # Shape: [B, k]
|
| 540 |
+
top_k_gates = gating_outputs['top_k_gates'] # Shape: [B, k]
|
| 541 |
+
k = top_k_indices.size(1)
|
| 542 |
+
|
| 543 |
+
expert_output = torch.zeros_like(output_main)
|
| 544 |
+
U_all = torch.stack([p for p in self.U_experts])
|
| 545 |
+
S_all = torch.stack([p for p in self.S_experts])
|
| 546 |
+
V_all = torch.stack([p for p in self.V_experts])
|
| 547 |
+
|
| 548 |
+
# Record the original input dimension (2 or 3)
|
| 549 |
+
original_dim = x.dim()
|
| 550 |
+
|
| 551 |
+
# We use torch.bmm which requires 3D inputs
|
| 552 |
+
# If x is 2D (Batch, In_features), we unsqueeze it to 3D (Batch, 1, In_features)
|
| 553 |
+
if original_dim == 2:
|
| 554 |
+
x = x.unsqueeze(1) # Shape: [B, I] -> [B, 1, I]
|
| 555 |
+
# We do the same for expert_output to match dimensions for accumulation
|
| 556 |
+
expert_output = expert_output.unsqueeze(1) # Shape: [B, O] -> [B, 1, O]
|
| 557 |
+
|
| 558 |
+
# Loop over the k choices
|
| 559 |
+
for i in range(k):
|
| 560 |
+
chosen_expert_indices = top_k_indices[:, i] # Shape: [B]
|
| 561 |
+
gate_values = top_k_gates[:, i].unsqueeze(-1) # Shape: [B, 1]
|
| 562 |
+
|
| 563 |
+
U_batch = U_all[chosen_expert_indices] # Shape: [B, O, r]
|
| 564 |
+
S_batch = S_all[chosen_expert_indices] # Shape: [B, r]
|
| 565 |
+
V_batch = V_all[chosen_expert_indices] # Shape: [B, r, I]
|
| 566 |
+
|
| 567 |
+
# 1. (x @ V.T)
|
| 568 |
+
# Shape: [B, S, I] @ [B, I, r] -> [B, S, r]
|
| 569 |
+
x_v = torch.bmm(x, V_batch.transpose(1, 2))
|
| 570 |
+
|
| 571 |
+
# 2. (x @ V.T) * S
|
| 572 |
+
# This is equivalent to (x @ V.T) @ diag(S)
|
| 573 |
+
# Shape: [B, S, r] * [B, 1, r] (broadcast) -> [B, S, r]
|
| 574 |
+
x_v_s = x_v * S_batch.unsqueeze(1)
|
| 575 |
+
|
| 576 |
+
# 3. ((x @ V.T) * S) @ U.T is equivalent to `output = x @ (U @ diag(S) @ V).T`
|
| 577 |
+
# Shape: [B, S, r] @ [B, r, O] -> [B, S, O]
|
| 578 |
+
current_expert_output = torch.bmm(x_v_s, U_batch.transpose(1, 2))
|
| 579 |
+
|
| 580 |
+
# 4. Apply gate weights
|
| 581 |
+
# Shape: [B, S, O] * [B, 1, 1] (broadcast) -> [B, S, O]
|
| 582 |
+
expert_output += current_expert_output * gate_values.unsqueeze(-1)
|
| 583 |
+
|
| 584 |
+
# If the input was 2D, squeeze the sequence dimension (S=1) back out
|
| 585 |
+
if original_dim == 2:
|
| 586 |
+
expert_output = expert_output.squeeze(1) # Shape: [B, 1, O] -> [B, O]
|
| 587 |
+
|
| 588 |
+
final_output = output_main + expert_output
|
| 589 |
+
if self.bias is not None:
|
| 590 |
+
final_output = final_output + self.bias
|
| 591 |
+
return final_output
|
| 592 |
+
|
| 593 |
+
def _calculate_pairwise_loss(self, base1, base2):
|
| 594 |
+
error = base1.t() @ base2
|
| 595 |
+
return torch.norm(error, p='fro')
|
| 596 |
+
|
| 597 |
+
def compute_targeted_orthogonal_loss(self, active_expert_idx: int) -> torch.Tensor:
|
| 598 |
+
"""
|
| 599 |
+
Efficient orthogonal loss for the hard sampling stage.
|
| 600 |
+
- If the active expert is the artifact expert, computes loss only against the main space.
|
| 601 |
+
- If the active expert is a semantic expert, computes loss against the main space
|
| 602 |
+
and previously trained *semantic* experts, skipping the artifact expert.
|
| 603 |
+
"""
|
| 604 |
+
loss = torch.tensor(0.0, device=self.weight_main.device)
|
| 605 |
+
num_pairs = 0
|
| 606 |
+
|
| 607 |
+
active_U = F.normalize(self.U_experts[active_expert_idx], dim=0)
|
| 608 |
+
active_V_t = F.normalize(self.V_experts[active_expert_idx], dim=1).t()
|
| 609 |
+
|
| 610 |
+
# Loss vs. Main space
|
| 611 |
+
loss += self._calculate_pairwise_loss(self.U_r, active_U)
|
| 612 |
+
loss += self._calculate_pairwise_loss(self.V_r.t(), active_V_t)
|
| 613 |
+
num_pairs += 1
|
| 614 |
+
|
| 615 |
+
if active_expert_idx == self.artifact_expert_idx:
|
| 616 |
+
return loss / num_pairs
|
| 617 |
+
|
| 618 |
+
# Loss vs. previously trained semantic experts
|
| 619 |
+
for i in range(active_expert_idx):
|
| 620 |
+
# Skip comparison with the artifact expert
|
| 621 |
+
if i == self.artifact_expert_idx:
|
| 622 |
+
continue
|
| 623 |
+
|
| 624 |
+
prev_U = F.normalize(self.U_experts[i].detach(), dim=0)
|
| 625 |
+
prev_V_t = F.normalize(self.V_experts[i].detach(), dim=1).t()
|
| 626 |
+
loss += self._calculate_pairwise_loss(prev_U, active_U)
|
| 627 |
+
loss += self._calculate_pairwise_loss(prev_V_t, active_V_t)
|
| 628 |
+
num_pairs += 1
|
| 629 |
+
|
| 630 |
+
return loss / num_pairs if num_pairs > 0 else torch.tensor(0.0, device=self.weight_main.device)
|
| 631 |
+
|
| 632 |
+
def compute_full_orthogonal_loss(self) -> torch.Tensor:
|
| 633 |
+
"""
|
| 634 |
+
Computes the full pairwise orthogonal loss between all components.
|
| 635 |
+
Used for end-to-end finetuning stages.
|
| 636 |
+
"""
|
| 637 |
+
all_U_bases = [self.U_r] + [F.normalize(u, dim=0) for u in self.U_experts]
|
| 638 |
+
all_V_bases_t = [self.V_r.t()] + [F.normalize(v, dim=1).t() for v in self.V_experts]
|
| 639 |
+
|
| 640 |
+
loss = torch.tensor(0.0, device=self.weight_main.device)
|
| 641 |
+
num_pairs = 0
|
| 642 |
+
|
| 643 |
+
# Iterate over all unique pairs of basis matrices
|
| 644 |
+
for i in range(len(all_U_bases)):
|
| 645 |
+
for j in range(i + 1, len(all_U_bases)):
|
| 646 |
+
loss += self._calculate_pairwise_loss(all_U_bases[i], all_U_bases[j])
|
| 647 |
+
loss += self._calculate_pairwise_loss(all_V_bases_t[i], all_V_bases_t[j])
|
| 648 |
+
num_pairs += 1
|
| 649 |
+
|
| 650 |
+
return loss / num_pairs if num_pairs > 0 else torch.tensor(0.0, device=self.weight_main.device)
|
| 651 |
+
|
| 652 |
+
|
| 653 |
+
class GatingNetwork(nn.Module):
|
| 654 |
+
def __init__(self, input_dim: int, num_experts: int, hidden_dim: int = 256, top_k: int = 2):
|
| 655 |
+
super(GatingNetwork, self).__init__()
|
| 656 |
+
self.num_experts = num_experts
|
| 657 |
+
self.top_k = top_k
|
| 658 |
+
self.network = nn.Sequential(
|
| 659 |
+
nn.Linear(input_dim, hidden_dim),
|
| 660 |
+
nn.ReLU(),
|
| 661 |
+
nn.Linear(hidden_dim, num_experts)
|
| 662 |
+
)
|
| 663 |
+
|
| 664 |
+
def forward(self, x: torch.Tensor) -> Dict[str, torch.Tensor]:
|
| 665 |
+
logits = self.network(x) # Shape: [B, N]
|
| 666 |
+
|
| 667 |
+
top_k_logits, top_k_indices = torch.topk(logits, self.top_k, dim=-1) # Shape: [B, k]
|
| 668 |
+
top_k_gates = F.softmax(top_k_logits, dim=-1) # Shape: [B, k]
|
| 669 |
+
|
| 670 |
+
router_probs = F.softmax(logits, dim=-1)
|
| 671 |
+
sparse_mask = torch.zeros_like(logits).scatter_(-1, top_k_indices, 1.0)
|
| 672 |
+
tokens_per_expert = torch.mean(sparse_mask.float(), dim=0)
|
| 673 |
+
router_prob_per_expert = torch.mean(router_probs, dim=0)
|
| 674 |
+
load_balancing_loss = self.num_experts * torch.sum(tokens_per_expert * router_prob_per_expert)
|
| 675 |
+
|
| 676 |
+
return {
|
| 677 |
+
'gating_logits': logits,
|
| 678 |
+
'top_k_indices': top_k_indices, # The indices of the chosen experts
|
| 679 |
+
'top_k_gates': top_k_gates, # The weights for the chosen experts
|
| 680 |
+
'balance_loss': load_balancing_loss
|
| 681 |
+
}
|
models/image/omniaid/omniaid.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""OmniAID (Decoupling Semantics and Artifacts for Universal AI-Generated Image Detection in the Wild, arXiv 2511.08423).
|
| 2 |
+
|
| 3 |
+
Official repo: github.com/yunncheng/OmniAID (MIT). A CLIP ViT-L/14@336 whose attention projections are split by SVD into
|
| 4 |
+
a shared main subspace plus small expert subspaces (a hybrid mixture of experts):
|
| 5 |
+
|
| 6 |
+
route = frozen CLIP vision tower (pooler output) → router → top-k SEMANTIC experts (e.g. human/animal, object/scene)
|
| 7 |
+
tokens = MoE ViT: every layer uses the routed semantic experts + the always-on ARTIFACT expert
|
| 8 |
+
logits = Linear(ln_post(CLS)) → [B, 2]; P(fake) = softmax(logits)[:, 1] (the official ``prob``)
|
| 9 |
+
|
| 10 |
+
``SynthScopeOmniAIDModel`` *is* the official ``OmniAID`` class (vendored unchanged in ``official_arch.py``); the released
|
| 11 |
+
checkpoints hold the full model and load key-for-key with ``strict=True`` as in the official eval. The constructor, as in
|
| 12 |
+
the official code, first builds the experts from the CLIP weights (``openai/clip-vit-large-patch14-336``), which the
|
| 13 |
+
checkpoint then overwrites. Each release has its own expert configuration (number of experts, rank, router width,
|
| 14 |
+
top-k), set per checkpoint in the model configs.
|
| 15 |
+
|
| 16 |
+
Training: the official recipe trains in two stages (each semantic expert with hard routing, then the router with the
|
| 17 |
+
artifact expert fixed) with orthogonality, load-balancing and gating losses; ``compute_loss`` here is only the
|
| 18 |
+
classification loss and does NOT reproduce that recipe.
|
| 19 |
+
"""
|
| 20 |
+
import types
|
| 21 |
+
|
| 22 |
+
import torch.nn as nn
|
| 23 |
+
from omegaconf import DictConfig
|
| 24 |
+
|
| 25 |
+
from .official_arch import OmniAID
|
| 26 |
+
|
| 27 |
+
CONFIG_KEYS = ("CLIP_path", "num_experts", "rank_per_expert", "moe_lambda_orth", "moe_lambda_balance",
|
| 28 |
+
"moe_lambda_gating_cls", "moe_router_hidden_dim", "moe_top_k", "dropout",
|
| 29 |
+
"gradient_checkpointing_enable", "is_hybrid")
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class SynthScopeOmniAIDModel(OmniAID):
|
| 33 |
+
"""The official OmniAID model, configured from SynthScope's ModelConfig."""
|
| 34 |
+
|
| 35 |
+
def __init__(self, config: DictConfig):
|
| 36 |
+
mc = config.ModelConfig
|
| 37 |
+
super().__init__(types.SimpleNamespace(**{k: mc[k] for k in CONFIG_KEYS}))
|
| 38 |
+
self.criterion = nn.CrossEntropyLoss()
|
| 39 |
+
|
| 40 |
+
def forward(self, images):
|
| 41 |
+
return super().forward(images)["cls"]
|
| 42 |
+
|
| 43 |
+
def compute_loss(self, batch):
|
| 44 |
+
images, labels, _folder_ids = batch
|
| 45 |
+
logits = self.forward(images)
|
| 46 |
+
return {"loss": self.criterion(logits, labels), "outputs": logits}
|
models/image/pgc/README.md
CHANGED
|
@@ -58,9 +58,9 @@ The residual is resolution-sensitive, so the experiment uses **crop-only** trans
|
|
| 58 |
## Training setup
|
| 59 |
|
| 60 |
```bash
|
| 61 |
-
conf/model/image/pgc.yaml # model_type: pgc, backbone_name, lora_*, tau_rgb/res
|
| 62 |
-
conf/trainer/image/pgc.yaml #
|
| 63 |
-
experiments/image/pgc.sh #
|
| 64 |
```
|
| 65 |
|
| 66 |
Registered in `trainer.py` under `model_type == "pgc"`. Trainable params: LoRA adapters + residual
|
|
@@ -68,6 +68,37 @@ stream + PGCM + classifier (DINOv2 backbone frozen). The paper's benchmarks are
|
|
| 68 |
AIGI, and UniversalFakeDetect; CommGen15 (the paper's new dataset) is optional — the SynthScope integration
|
| 69 |
trains on the existing ProGAN/GenImage data.
|
| 70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
## Reproduced benchmark results
|
| 72 |
|
| 73 |
All benchmarks the paper reports, with our numbers (verified, traced to `repro/bench_results/` + `repro/benchmark_specs/`).
|
|
@@ -75,14 +106,14 @@ All benchmarks the paper reports, with our numbers (verified, traced to `repro/b
|
|
| 75 |
| Benchmark | Paper | SynthScope_official | SynthScope_train | Status |
|
| 76 |
|---|---|---|---|---|
|
| 77 |
| GenImage | map=100.0 / acc=98.3 | mAP=100.0 / acc=99.49 | mAP=97.62 / acc=78.6 | official |
|
| 78 |
-
| UniversalFakeDetect (UFD) | map=98.0 | mAP=99.34 / acc=94.16 | mAP=99.
|
| 79 |
-
| ForenSynths-8 (CNNDetection test) |
|
| 80 |
| AIGIBench (AIGI) | map=95.0 / acc=86.6 | mAP=95.36 / acc=88.33 | mAP=86.88 | official |
|
| 81 |
| CommGen15 (commercial in-the-wild) | acc=97.9 | mAP=99.26 / acc=97.39 | mAP=98.61 / acc=66.66 | official |
|
| 82 |
|
| 83 |
> **ForenSynths hard generators — reproduced via early-stopping (resolved 2026-06-19).** The released ProGAN checkpoint is trained for only **~400 steps (< 1 epoch)** — confirmed by the authors on our issue. DINOv2's strong priors overfit ProGAN within a few epochs (loss → floor), which keeps easy generators high but **erodes hard-generator transfer**; our earlier epoch-level checkpoints (ep10+) all overshot the peak. Re-running the official `train.py` verbatim with fine-grained eval shows the hard-3 (deepfake/san/seeingdark) **mean AUROC climb to 94.56 at step ~900 ≈ official 94.35**, then decay with longer training. So PGC **does** reproduce from the published recipe+data with early-stopping inside the first epoch. (Served PGC uses the official weights.) See `repro/investigate/pgc_gap_investigation.md`.
|
| 84 |
|
| 85 |
-
> **Eval transform — `PadCenterCrop(224)`, no resize (verified 2026-06-19).** PGC's YCbCr residual is resolution-sensitive, so the eval transform is center-crop-224 with **no resize** (= the official `PadCenterCrop`; byte-identical to the genuine repo's). The earlier `Resize→CenterCrop` registry transform wiped the forensic signal on super-res/low-light generators (san 73.3, seeingdark 72.5 — same eval-transform family as the SAFE/AIDE/SPAI fixes). With the fix, SynthScope `bench_eval`
|
| 86 |
|
| 87 |
**reproduced** = SynthScope-trained checkpoint matches the paper · **official** = official weights loaded into the SynthScope class match (the SynthScope-trained number is shown alongside where we trained from scratch) · **below** = gap remains · **reference** = paper reports no comparable number · **missing** = not yet evaluated.
|
| 88 |
|
|
@@ -92,4 +123,4 @@ All benchmarks the paper reports, with our numbers (verified, traced to `repro/b
|
|
| 92 |
|---|---|
|
| 93 |
| Residual built in-model (not in the dataset transform) | Keeps the SynthScope dataset/transform pipeline standard (3-channel in). Same residual math (BT.601 quantization, Eq. 1) — `_quantization_residual` snaps `round(x01*255)` to the uint8 grid so the residual is **bit-exact** to the official 6-channel transform despite the in-model ImageNet de-normalization (verified zero-diff, `repro/_residual_path_diff.py`). |
|
| 94 |
| DINOv2-Large loaded via HF name | No local-checkpoint requirement; LoRA targets use the HF DINOv2 module names. |
|
| 95 |
-
|
|
|
|
|
| 58 |
## Training setup
|
| 59 |
|
| 60 |
```bash
|
| 61 |
+
conf/model/image/pgc.yaml # model_type: pgc, backbone_name, lora_*, tau_rgb/res, label smoothing 0.1
|
| 62 |
+
conf/trainer/image/pgc.yaml # AdamW 5e-5, wd 0.05, 32 x 4 accumulation, clip 1.0, step budget (checkpoint every 100)
|
| 63 |
+
experiments/image/pgc.sh # the official ProGAN recipe (all 20 classes); see "Training in SynthScope" below
|
| 64 |
```
|
| 65 |
|
| 66 |
Registered in `trainer.py` under `model_type == "pgc"`. Trainable params: LoRA adapters + residual
|
|
|
|
| 68 |
AIGI, and UniversalFakeDetect; CommGen15 (the paper's new dataset) is optional — the SynthScope integration
|
| 69 |
trains on the existing ProGAN/GenImage data.
|
| 70 |
|
| 71 |
+
## Training in SynthScope
|
| 72 |
+
|
| 73 |
+
`experiments/image/pgc.sh` runs the official ProGAN recipe (`train.py` + `engine/trainer.py`, README 3.2) through
|
| 74 |
+
SynthScope's trainer: all 20 ProGAN classes (720k images, = the UniversalFakeDetect train set); flip → ColorJitter
|
| 75 |
+
(0.15, 0.15, 0.15, 0.05) p 0.2 → pad + RandomCrop 224 → ImageNet normalization, residual computed in the model; AdamW
|
| 76 |
+
5e-5, weight decay 0.05, label smoothing 0.1, micro-batch 32 × 4 accumulation, gradient clipping 1.0, seed 42, one GPU.
|
| 77 |
+
The authors' released checkpoint is only **~400 steps** in, because DINOv2's prior overfits ProGAN within the first
|
| 78 |
+
epoch, so the run is a budget of 2000 micro-batches (trainer `step_budget`) with a checkpoint every 100, and the early
|
| 79 |
+
curve is evaluated. (The official cosine schedule spans 100 epochs and is stepped per optimizer step, so the learning
|
| 80 |
+
rate stays at 5e-5 here.) The run takes 38 minutes on one A5000 (job 266276).
|
| 81 |
+
|
| 82 |
+
ForenSynths with the same `bench_eval` command for every checkpoint (job 266282; FS-8 = the paper's 8 GANs, hard-3 =
|
| 83 |
+
deepfake, SAN, SeeingDark):
|
| 84 |
+
|
| 85 |
+
| checkpoint | FS-8 mAP | FS-8 acc | hard-3 AUROC | FS-13 mAP |
|
| 86 |
+
|---|---|---|---|---|
|
| 87 |
+
| official `PGC_train_progan_ckpt.pth` | 96.41 | 76.34 | 90.87 | 93.80 |
|
| 88 |
+
| step 300 | 97.60 | 90.82 | 92.88 | 95.07 |
|
| 89 |
+
| **step 500** | **97.41** | **85.89** | **93.93** | **95.78** |
|
| 90 |
+
| step 900 | 97.82 | 85.71 | 93.88 | 97.12 |
|
| 91 |
+
| step 1300 | 97.63 | 85.61 | 92.76 | 97.03 |
|
| 92 |
+
| step 1700 | 97.21 | 84.06 | 91.03 | 96.69 |
|
| 93 |
+
| step 1900 | — | 74.49 (FS-13) | — | 95.97 |
|
| 94 |
+
|
| 95 |
+
The paper reports no standalone ForenSynths number, so the reference is the official checkpoint: every checkpoint from
|
| 96 |
+
step 300 on matches or exceeds it on ForenSynths-8 mAP (96.41 on this evaluation); transfer to the hard generators peaks around steps 500-900 and then erodes, as the authors
|
| 97 |
+
describe. **Step 500**, the saved checkpoint nearest the authors' ~400 steps (picked by that rule, not by test score),
|
| 98 |
+
is the SynthScope-trained number in the table below; on UniversalFakeDetect, which the paper does report, it gives 98.99 mAP / 91.96 acc (paper
|
| 99 |
+
98.0 — reproduced; official 99.17 / 83.57 on the same run, job 266660). The GenImage, AIGIBench and CommGen15 SynthScope_train entries are
|
| 100 |
+
from an earlier SynthScope recipe (2 GPUs, full cosine over epochs) and have not been rerun with this one.
|
| 101 |
+
|
| 102 |
## Reproduced benchmark results
|
| 103 |
|
| 104 |
All benchmarks the paper reports, with our numbers (verified, traced to `repro/bench_results/` + `repro/benchmark_specs/`).
|
|
|
|
| 106 |
| Benchmark | Paper | SynthScope_official | SynthScope_train | Status |
|
| 107 |
|---|---|---|---|---|
|
| 108 |
| GenImage | map=100.0 / acc=98.3 | mAP=100.0 / acc=99.49 | mAP=97.62 / acc=78.6 | official |
|
| 109 |
+
| UniversalFakeDetect (UFD) | map=98.0 | mAP=99.34 / acc=94.16 | **mAP=98.99 / acc=91.96** | **reproduced** (step 500) |
|
| 110 |
+
| ForenSynths-8 (CNNDetection test) | — (not reported separately; official checkpoint 96.25) | mAP=96.39 / acc=76.29 | **mAP=97.41 / acc=85.89** | matches/exceeds the official checkpoint (step 500) |
|
| 111 |
| AIGIBench (AIGI) | map=95.0 / acc=86.6 | mAP=95.36 / acc=88.33 | mAP=86.88 | official |
|
| 112 |
| CommGen15 (commercial in-the-wild) | acc=97.9 | mAP=99.26 / acc=97.39 | mAP=98.61 / acc=66.66 | official |
|
| 113 |
|
| 114 |
> **ForenSynths hard generators — reproduced via early-stopping (resolved 2026-06-19).** The released ProGAN checkpoint is trained for only **~400 steps (< 1 epoch)** — confirmed by the authors on our issue. DINOv2's strong priors overfit ProGAN within a few epochs (loss → floor), which keeps easy generators high but **erodes hard-generator transfer**; our earlier epoch-level checkpoints (ep10+) all overshot the peak. Re-running the official `train.py` verbatim with fine-grained eval shows the hard-3 (deepfake/san/seeingdark) **mean AUROC climb to 94.56 at step ~900 ≈ official 94.35**, then decay with longer training. So PGC **does** reproduce from the published recipe+data with early-stopping inside the first epoch. (Served PGC uses the official weights.) See `repro/investigate/pgc_gap_investigation.md`.
|
| 115 |
|
| 116 |
+
> **Eval transform — `PadCenterCrop(224)`, no resize (verified 2026-06-19).** PGC's YCbCr residual is resolution-sensitive, so the eval transform is center-crop-224 with **no resize** (= the official `PadCenterCrop`; byte-identical to the genuine repo's). The earlier `Resize→CenterCrop` registry transform wiped the forensic signal on super-res/low-light generators (san 73.3, seeingdark 72.5 — same eval-transform family as the SAFE/AIDE/SPAI fixes). With the fix, SynthScope `bench_eval` gives ForenSynths-8 96.39 mAP with the official weights and 96.05 with the official code retrained (the paper reports no ForenSynths number; 96.25 was the earlier official-weights measurement) and san recovers to 98.65 AUROC. One generator, **seeingdark**, stays ~10 pt below the genuine repo (77.9 vs 87.49) on identical images/weights with now-bit-exact preprocessing — isolated to the model forward, not the transform; it is not part of the reported ForenSynths-8.
|
| 117 |
|
| 118 |
**reproduced** = SynthScope-trained checkpoint matches the paper · **official** = official weights loaded into the SynthScope class match (the SynthScope-trained number is shown alongside where we trained from scratch) · **below** = gap remains · **reference** = paper reports no comparable number · **missing** = not yet evaluated.
|
| 119 |
|
|
|
|
| 123 |
|---|---|
|
| 124 |
| Residual built in-model (not in the dataset transform) | Keeps the SynthScope dataset/transform pipeline standard (3-channel in). Same residual math (BT.601 quantization, Eq. 1) — `_quantization_residual` snaps `round(x01*255)` to the uint8 grid so the residual is **bit-exact** to the official 6-channel transform despite the in-model ImageNet de-normalization (verified zero-diff, `repro/_residual_path_diff.py`). |
|
| 125 |
| DINOv2-Large loaded via HF name | No local-checkpoint requirement; LoRA targets use the HF DINOv2 module names. |
|
| 126 |
+
| Training budget | The official checkpoint is ~400 steps in; `experiments/image/pgc.sh` saves every 100 micro-batches up to 2000 instead of training 100 epochs, which overfits ProGAN. |
|
pyproject.toml
CHANGED
|
@@ -46,6 +46,7 @@ dependencies = [
|
|
| 46 |
"pytorch_wavelets>=1.3",
|
| 47 |
"PyWavelets>=1.9",
|
| 48 |
"opencv-python-headless", # CuRe preprocessing (JPEG round-trips, resizing)
|
|
|
|
| 49 |
"setuptools<81", # pytorch_wavelets (SAFE) still imports pkg_resources, removed in setuptools 81
|
| 50 |
]
|
| 51 |
|
|
|
|
| 46 |
"pytorch_wavelets>=1.3",
|
| 47 |
"PyWavelets>=1.9",
|
| 48 |
"opencv-python-headless", # CuRe preprocessing (JPEG round-trips, resizing)
|
| 49 |
+
"scipy", # CNNDetection-style blur augmentation (transformations.CNNDetAugment)
|
| 50 |
"setuptools<81", # pytorch_wavelets (SAFE) still imports pkg_resources, removed in setuptools 81
|
| 51 |
]
|
| 52 |
|
repro/MASTER_RESULTS.jsonl
CHANGED
|
@@ -296,3 +296,42 @@
|
|
| 296 |
{"date": "2026-10-26T09:48:10", "tag": "d3qe_cnndetection", "model": "d3qe", "ckpt": "registry-default", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 7, "means": {"acc": 83.45, "map": 92.23, "mauroc": 90.65}}
|
| 297 |
{"date": "2026-10-26T10:02:32", "tag": "d3qe_genimage_full", "model": "d3qe", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/GenImage_full/test", "n_generators": 6, "means": {"acc": 79.44, "map": 90.94, "mauroc": 90.49}}
|
| 298 |
{"date": "2026-10-26T10:14:34", "tag": "d3qe_rrdataset", "model": "d3qe", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/RRDataset/test", "n_generators": 3, "means": {"acc": 54.95, "map": 59.25, "mauroc": 55.11}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
{"date": "2026-10-26T09:48:10", "tag": "d3qe_cnndetection", "model": "d3qe", "ckpt": "registry-default", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 7, "means": {"acc": 83.45, "map": 92.23, "mauroc": 90.65}}
|
| 297 |
{"date": "2026-10-26T10:02:32", "tag": "d3qe_genimage_full", "model": "d3qe", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/GenImage_full/test", "n_generators": 6, "means": {"acc": 79.44, "map": 90.94, "mauroc": 90.49}}
|
| 298 |
{"date": "2026-10-26T10:14:34", "tag": "d3qe_rrdataset", "model": "d3qe", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/RRDataset/test", "n_generators": 3, "means": {"acc": 54.95, "map": 59.25, "mauroc": 55.11}}
|
| 299 |
+
{"date": "2026-10-26T21:42:06", "tag": "d3qe_trained_ep3_arforensics", "model": "d3qe", "ckpt": "/ssdstore/ssalehi/SynthScope/models/d3qe_v1/snapshot_ep3_best.pth", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/ARForensics/test", "n_generators": 7, "means": {"acc": 81.11, "map": 90.77, "mauroc": 90.97}}
|
| 300 |
+
{"date": "2026-10-27T06:02:46", "tag": "d3qe_trained_arforensics", "model": "d3qe", "ckpt": "/ssdstore/ssalehi/SynthScope/models/d3qe_v1/model_best.pth", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/ARForensics/test", "n_generators": 7, "means": {"acc": 79.83, "map": 91.73, "mauroc": 91.49}}
|
| 301 |
+
{"date": "2026-10-27T06:28:33", "tag": "d3qe_trained_cnndetection", "model": "d3qe", "ckpt": "/ssdstore/ssalehi/SynthScope/models/d3qe_v1/model_best.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 7, "means": {"acc": 82.14, "map": 91.35, "mauroc": 90.29}}
|
| 302 |
+
{"date": "2026-10-27T06:42:56", "tag": "d3qe_trained_genimage_full", "model": "d3qe", "ckpt": "/ssdstore/ssalehi/SynthScope/models/d3qe_v1/model_best.pth", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/GenImage_full/test", "n_generators": 6, "means": {"acc": 77.01, "map": 90.11, "mauroc": 89.63}}
|
| 303 |
+
{"date": "2026-10-27T02:49:02", "tag": "npr_official_nprpth_forensynths8", "model": "npr", "ckpt": "registry-default", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 8, "means": {"acc": 92.3, "map": 94.22, "mauroc": 96.08}}
|
| 304 |
+
{"date": "2026-10-27T02:49:53", "tag": "npr_official_nprpth_ufd", "model": "npr", "ckpt": "registry-default", "benchmark": "ufd", "data_root": "/ssdstore/ssalehi/SynthScope/universialfakedetection/test", "n_generators": 8, "means": {"acc": 94.57, "map": 97.16, "mauroc": 97.68}}
|
| 305 |
+
{"date": "2026-10-27T02:53:54", "tag": "npr_trained_last_forensynths8", "model": "npr", "ckpt": "/ssdstore/ssalehi/SynthScope/models/npr_v1/model_last.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 8, "means": {"acc": 84.09, "map": 93.83, "mauroc": 94.94}}
|
| 306 |
+
{"date": "2026-10-27T02:54:44", "tag": "npr_trained_last_ufd", "model": "npr", "ckpt": "/ssdstore/ssalehi/SynthScope/models/npr_v1/model_last.pth", "benchmark": "ufd", "data_root": "/ssdstore/ssalehi/SynthScope/universialfakedetection/test", "n_generators": 8, "means": {"acc": 88.91, "map": 97.49, "mauroc": 97.23}}
|
| 307 |
+
{"date": "2026-10-27T02:58:43", "tag": "npr_trained_best_forensynths8", "model": "npr", "ckpt": "/ssdstore/ssalehi/SynthScope/models/npr_v1/model_best.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 8, "means": {"acc": 84.3, "map": 91.81, "mauroc": 93.3}}
|
| 308 |
+
{"date": "2026-10-27T02:59:33", "tag": "npr_trained_best_ufd", "model": "npr", "ckpt": "/ssdstore/ssalehi/SynthScope/models/npr_v1/model_best.pth", "benchmark": "ufd", "data_root": "/ssdstore/ssalehi/SynthScope/universialfakedetection/test", "n_generators": 8, "means": {"acc": 85.34, "map": 95.56, "mauroc": 95.3}}
|
| 309 |
+
{"date": "2026-10-27T03:25:16", "tag": "npr_official3090_forensynths8", "model": "npr", "ckpt": "/ivi/zfs/s0/original_homes/ssalehi/SynthScope_shared/npr_ref/model_epoch_last_3090.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 8, "means": {"acc": 91.83, "map": 92.81, "mauroc": 95.18}}
|
| 310 |
+
{"date": "2026-10-27T03:26:05", "tag": "npr_official3090_ufd", "model": "npr", "ckpt": "/ivi/zfs/s0/original_homes/ssalehi/SynthScope_shared/npr_ref/model_epoch_last_3090.pth", "benchmark": "ufd", "data_root": "/ssdstore/ssalehi/SynthScope/universialfakedetection/test", "n_generators": 8, "means": {"acc": 93.94, "map": 96.69, "mauroc": 97.26}}
|
| 311 |
+
{"date": "2026-10-27T13:08:53", "tag": "npr_officialcode_rerun_forensynths8", "model": "npr", "ckpt": "/ssdstore/ssalehi/SynthScope/models/npr_official_rerun/4class-resnet-car-cat-chair-horse2026_10_27_03_27_09/model_epoch_last.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 8, "means": {"acc": 81.47, "map": 92.72, "mauroc": 93.97}}
|
| 312 |
+
{"date": "2026-10-27T13:09:44", "tag": "npr_officialcode_rerun_ufd", "model": "npr", "ckpt": "/ssdstore/ssalehi/SynthScope/models/npr_official_rerun/4class-resnet-car-cat-chair-horse2026_10_27_03_27_09/model_epoch_last.pth", "benchmark": "ufd", "data_root": "/ssdstore/ssalehi/SynthScope/universialfakedetection/test", "n_generators": 8, "means": {"acc": 87.61, "map": 96.56, "mauroc": 96.43}}
|
| 313 |
+
{"date": "2026-10-27T18:34:34", "tag": "pgc_official_forensynths13", "model": "pgc", "ckpt": "registry-default", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 13, "means": {"acc": 70.58, "map": 93.8, "mauroc": 94.37}}
|
| 314 |
+
{"date": "2026-10-27T19:07:32", "tag": "pgc_trained_step300_forensynths13", "model": "pgc", "ckpt": "/ssdstore/ssalehi/SynthScope/models/pgc_v1/model_step300.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 13, "means": {"acc": 79.88, "map": 95.07, "mauroc": 95.66}}
|
| 315 |
+
{"date": "2026-10-27T19:39:25", "tag": "pgc_trained_step500_forensynths13", "model": "pgc", "ckpt": "/ssdstore/ssalehi/SynthScope/models/pgc_v1/model_step500.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 13, "means": {"acc": 82.98, "map": 95.78, "mauroc": 96.15}}
|
| 316 |
+
{"date": "2026-10-27T20:11:21", "tag": "pgc_trained_step700_forensynths13", "model": "pgc", "ckpt": "/ssdstore/ssalehi/SynthScope/models/pgc_v1/model_step700.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 13, "means": {"acc": 80.35, "map": 96.67, "mauroc": 96.6}}
|
| 317 |
+
{"date": "2026-10-27T20:43:13", "tag": "pgc_trained_step900_forensynths13", "model": "pgc", "ckpt": "/ssdstore/ssalehi/SynthScope/models/pgc_v1/model_step900.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 13, "means": {"acc": 79.92, "map": 97.12, "mauroc": 97.01}}
|
| 318 |
+
{"date": "2026-10-27T21:15:31", "tag": "pgc_trained_step1100_forensynths13", "model": "pgc", "ckpt": "/ssdstore/ssalehi/SynthScope/models/pgc_v1/model_step1100.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 13, "means": {"acc": 75.44, "map": 96.74, "mauroc": 96.5}}
|
| 319 |
+
{"date": "2026-10-27T21:47:29", "tag": "pgc_trained_step1300_forensynths13", "model": "pgc", "ckpt": "/ssdstore/ssalehi/SynthScope/models/pgc_v1/model_step1300.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 13, "means": {"acc": 78.24, "map": 97.03, "mauroc": 96.8}}
|
| 320 |
+
{"date": "2026-10-27T22:19:20", "tag": "pgc_trained_step1500_forensynths13", "model": "pgc", "ckpt": "/ssdstore/ssalehi/SynthScope/models/pgc_v1/model_step1500.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 13, "means": {"acc": 77.2, "map": 96.85, "mauroc": 96.54}}
|
| 321 |
+
{"date": "2026-10-27T22:51:19", "tag": "pgc_trained_step1700_forensynths13", "model": "pgc", "ckpt": "/ssdstore/ssalehi/SynthScope/models/pgc_v1/model_step1700.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 13, "means": {"acc": 74.18, "map": 96.69, "mauroc": 96.32}}
|
| 322 |
+
{"date": "2026-10-27T23:23:08", "tag": "pgc_trained_step1900_forensynths13", "model": "pgc", "ckpt": "/ssdstore/ssalehi/SynthScope/models/pgc_v1/model_step1900.pth", "benchmark": "forensynths", "data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test", "n_generators": 13, "means": {"acc": 74.49, "map": 95.97, "mauroc": 95.35}}
|
| 323 |
+
{"date": "2026-10-28T00:46:03", "tag": "pgc_official_ufd8", "model": "pgc", "ckpt": "registry-default", "benchmark": "ufd", "data_root": "/ssdstore/ssalehi/SynthScope/universialfakedetection/test", "n_generators": 8, "means": {"acc": 83.57, "map": 99.17, "mauroc": 99.2}}
|
| 324 |
+
{"date": "2026-10-28T00:50:55", "tag": "pgc_trained_step500_ufd8", "model": "pgc", "ckpt": "/ssdstore/ssalehi/SynthScope/models/pgc_v1/model_step500.pth", "benchmark": "ufd", "data_root": "/ssdstore/ssalehi/SynthScope/universialfakedetection/test", "n_generators": 8, "means": {"acc": 91.96, "map": 98.99, "mauroc": 98.92}}
|
| 325 |
+
{"date": "2026-10-27T08:37:18", "tag": "omniaid_synthwildx", "model": "omniaid", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/SynthWildX/test", "n_generators": 3, "means": {"acc": 86.02, "map": 95.52, "mauroc": 95.31}}
|
| 326 |
+
{"date": "2026-10-27T08:42:46", "tag": "omniaid_wildrf", "model": "omniaid", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/WildRF/test", "n_generators": 3, "means": {"acc": 88.36, "map": 97.28, "mauroc": 96.91}}
|
| 327 |
+
{"date": "2026-10-27T08:44:32", "tag": "omniaid_bfree_online", "model": "omniaid", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/BFreeOnline/test", "n_generators": 1, "means": {"acc": 70.26, "map": 91.7, "mauroc": 87.18}}
|
| 328 |
+
{"date": "2026-10-27T09:09:34", "tag": "omniaid_realchain", "model": "omniaid", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/RealChain/test", "n_generators": 7, "means": {"acc": 61.71, "map": 84.02, "mauroc": 83.63}}
|
| 329 |
+
{"date": "2026-10-27T09:20:42", "tag": "omniaid_genimage_genimage_full", "model": "omniaid_genimage", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/GenImage_full/test", "n_generators": 8, "means": {"acc": 95.82, "map": 99.02, "mauroc": 99.16}}
|
| 330 |
+
{"date": "2026-10-27T09:22:13", "tag": "omniaid_v1_genimage_full", "model": "omniaid_v1", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/GenImage_full/test", "n_generators": 8, "means": {"acc": 97.16, "map": 99.2, "mauroc": 99.12}}
|
| 331 |
+
{"date": "2026-10-27T11:05:31", "tag": "omniaid_evalgen", "model": "omniaid", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/EvalGEN/test", "n_generators": 5, "means": {"acc": 74.14, "map": 96.46, "mauroc": 93.08}}
|
| 332 |
+
{"date": "2026-10-27T12:23:42", "tag": "omniaid_rrdataset", "model": "omniaid", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/RRDataset/test", "n_generators": 3, "means": {"acc": 71.75, "map": 84.19, "mauroc": 82.32}}
|
| 333 |
+
{"date": "2026-10-27T13:04:46", "tag": "omniaid_genimage_aigcdetect", "model": "omniaid_genimage", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/AIGCDetect/test", "n_generators": 17, "means": {"acc": 88.87, "map": 97.88, "mauroc": 98.2}}
|
| 334 |
+
{"date": "2026-10-27T13:11:33", "tag": "omniaid_v1_aigcdetect", "model": "omniaid_v1", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/AIGCDetect/test", "n_generators": 17, "means": {"acc": 92.88, "map": 98.93, "mauroc": 98.99}}
|
| 335 |
+
{"date": "2026-10-27T15:40:59", "tag": "omniaid_genimage_mirage_test", "model": "omniaid_genimage", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/MirageTest/test", "n_generators": 5, "means": {"acc": 54.66, "map": 50.6, "mauroc": 50.74}}
|
| 336 |
+
{"date": "2026-10-27T15:49:26", "tag": "omniaid_v1_mirage_test", "model": "omniaid_v1", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/MirageTest/test", "n_generators": 5, "means": {"acc": 88.63, "map": 97.04, "mauroc": 97.97}}
|
| 337 |
+
{"date": "2026-10-27T16:20:10", "tag": "omniaid_mirage_test", "model": "omniaid", "ckpt": "registry-default", "benchmark": "pergen", "data_root": "/ssdstore/ssalehi/SynthScope/MirageTest/test", "n_generators": 5, "means": {"acc": 80.37, "map": 96.48, "mauroc": 96.65}}
|
repro/RESULTS_SUMMARY.md
CHANGED
|
@@ -11,17 +11,18 @@
|
|
| 11 |
| Method | Headline benchmark | Paper | SynthScope_official | SynthScope_train | Verdict |
|
| 12 |
|---|---|---:|---:|---:|---|
|
| 13 |
| FatFormer | ForenSynths-8 | 99.7 | 99.70 | — | ✅ official match |
|
| 14 |
-
| NPR | ForenSynths-8 / GANGen | 96.1 / 96.6 | 94.22 / 96.49 | — | ✅ official match |
|
| 15 |
| CLIP/UnivFD | ForenSynths | — | 93.14 | 92.99 | ✅ match |
|
| 16 |
| CNNDetection | ForenSynths | 90.8 | 91.15 | 91.05 | ✅ match |
|
| 17 |
| SAFE | GenImage | 99.3 | 99.32 | 99.51 | ✅ retrain reproduces |
|
| 18 |
| AIDE | GenImage (SD-v1.4) | 86.9 acc | 97.12 | 96.42 | ✅ reproduced (native-res retrain) |
|
| 19 |
| SPAI | GenImage | ~91 AUC | 94.22 | 88.80 | ✅ served native-res (beats vendored 91.33) |
|
| 20 |
-
| PGC | GenImage | 100 AP | 100.0 | 97.62 | ✅
|
| 21 |
| VIB-Net | GenImage (SD-v1.4) | 95.5 AP | *none released* | 83.26 | ⚠️ recipe/arch gap |
|
| 22 |
| Co-Spy | Co-Spy-Bench-110 | 96.0 AP | 86.65 | 96.60 | ✅ beats official |
|
| 23 |
| DDA | AIGCDetect / ForenSynths / EvalGEN / SynthWildX / WildRF | 87.8 / 81.4 / 97.2 / 90.9 / 90.3 acc | 87.84 / 81.43 / 96.86 / 90.80 / 90.20 acc | — | ✅ official match (GenImage 2.3 pt below) |
|
| 24 |
-
| D3QE | ARForensics / ForenSynths-7 / GenImage-6 | 82.11 / 83.73 / 78.61 acc | 81.74 / 83.45 / 79.44 acc |
|
|
|
|
| 25 |
| D3 (video) | GenVideo-Val | 98.5 AP | 97.93 (official code) | 99.14 | ✅ reproduced (D3 protocol; preprocessing matched to the official code) |
|
| 26 |
| ReStraV (video) | GenVidBench | 93.0 | — | 92.70 | ✅ match |
|
| 27 |
| STALL (video) | GenVideo-Val | ~80 AUC | — | 83.5 AUROC | ✅ match |
|
|
@@ -46,12 +47,12 @@ SPAI now SERVES official weights remapped into the SynthScope class at NATIVE re
|
|
| 46 |
| Benchmark | Paper | SynthScope_official | SynthScope_train |
|
| 47 |
|---|---:|---:|---:|
|
| 48 |
| GenImage-8 | 100 AP | 100.0 (99.5) | **97.62 (78.6)** |
|
| 49 |
-
| UniversalFakeDetect-8 |
|
| 50 |
-
| ForenSynths-13 | — | 96.25 (83.7) |
|
| 51 |
| AIGIBench-25 | — | 95.36 (88.3) | 86.88 |
|
| 52 |
| CommGen15-15 | — | 99.26 (97.4) | **98.61 (66.7)** |
|
| 53 |
|
| 54 |
-
*PGC
|
| 55 |
|
| 56 |
### Co-Spy
|
| 57 |
| Benchmark | Paper | SynthScope_official | SynthScope_train |
|
|
@@ -148,7 +149,22 @@ Fidelity: SynthScope's class == the official `D3QE` (871/871 state-dict tensors
|
|
| 148 |
| GenImage, the paper's 6 subsets (`--dataset genimage_full --select ...`) | **79.44** | 90.94 | **78.61 / 89.60** | mean reproduced; per subset up to 6.8 pt off in both directions (ADM 63.7 vs 70.4, Wukong 90.7 vs 84.4), likely a different test sample |
|
| 149 |
| In the wild: SynthWildX / WildRF / BFree-Online / RealChain / EvalGEN / RRDataset | 49.6 / 53.0 / 47.0 / 49.5 / 62.2 / 55.0 | – | — | new measurements: it calls nearly every recent fake real (AR-only training) |
|
| 150 |
|
| 151 |
-
**Verdict: faithful port (identical to the official model); all 3 paper tables reproduced on the mean** (ARForensics and ForenSynths per generator too). Note: in the released code the codebook-frequency bias is zero at inference (`freq_log_counter` is not saved), so the reported scores come from the quantization-error and CLIP branches.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
|
| 153 |
### Video detectors (no official weights released)
|
| 154 |
| Method · Benchmark | Paper | SynthScope_train |
|
|
|
|
| 11 |
| Method | Headline benchmark | Paper | SynthScope_official | SynthScope_train | Verdict |
|
| 12 |
|---|---|---:|---:|---:|---|
|
| 13 |
| FatFormer | ForenSynths-8 | 99.7 | 99.70 | — | ✅ official match |
|
| 14 |
+
| NPR | ForenSynths-8 / GANGen | 96.1 / 96.6 | 94.22 / 96.49 | 93.83 / — | ✅ official match; trained (official recipe): AP close, acc 84.1 — the official train.py rerun here gets 81.5 |
|
| 15 |
| CLIP/UnivFD | ForenSynths | — | 93.14 | 92.99 | ✅ match |
|
| 16 |
| CNNDetection | ForenSynths | 90.8 | 91.15 | 91.05 | ✅ match |
|
| 17 |
| SAFE | GenImage | 99.3 | 99.32 | 99.51 | ✅ retrain reproduces |
|
| 18 |
| AIDE | GenImage (SD-v1.4) | 86.9 acc | 97.12 | 96.42 | ✅ reproduced (native-res retrain) |
|
| 19 |
| SPAI | GenImage | ~91 AUC | 94.22 | 88.80 | ✅ served native-res (beats vendored 91.33) |
|
| 20 |
+
| PGC | GenImage / ForenSynths-8 / UFD | 100 / — / 98.0 AP | 100.0 / 96.41 / 99.17 | 97.62 / **97.41** / **98.99** | ✅ UFD reproduced by the official recipe in SynthScope (step 500); ForenSynths (not in the paper) above the official checkpoint |
|
| 21 |
| VIB-Net | GenImage (SD-v1.4) | 95.5 AP | *none released* | 83.26 | ⚠️ recipe/arch gap |
|
| 22 |
| Co-Spy | Co-Spy-Bench-110 | 96.0 AP | 86.65 | 96.60 | ✅ beats official |
|
| 23 |
| DDA | AIGCDetect / ForenSynths / EvalGEN / SynthWildX / WildRF | 87.8 / 81.4 / 97.2 / 90.9 / 90.3 acc | 87.84 / 81.43 / 96.86 / 90.80 / 90.20 acc | — | ✅ official match (GenImage 2.3 pt below) |
|
| 24 |
+
| D3QE | ARForensics / ForenSynths-7 / GenImage-6 | 82.11 / 83.73 / 78.61 acc | 81.74 / 83.45 / 79.44 acc | 79.83 / 82.14 / 77.01 acc | ✅ official match; trained in SynthScope: AP matches, acc 1.6-2.3 below |
|
| 25 |
+
| OmniAID | GenImage / AIGCDetect / Mirage-Test (GenImage release) | 95.94 / 88.87 / 51.10 acc | 95.82 / 88.87 / 51.10 acc | — | ✅ official match (v1 release too: 97.16 / 92.88 / 88.39 vs 97.24 / 92.88 / 88.39) |
|
| 26 |
| D3 (video) | GenVideo-Val | 98.5 AP | 97.93 (official code) | 99.14 | ✅ reproduced (D3 protocol; preprocessing matched to the official code) |
|
| 27 |
| ReStraV (video) | GenVidBench | 93.0 | — | 92.70 | ✅ match |
|
| 28 |
| STALL (video) | GenVideo-Val | ~80 AUC | — | 83.5 AUROC | ✅ match |
|
|
|
|
| 47 |
| Benchmark | Paper | SynthScope_official | SynthScope_train |
|
| 48 |
|---|---:|---:|---:|
|
| 49 |
| GenImage-8 | 100 AP | 100.0 (99.5) | **97.62 (78.6)** |
|
| 50 |
+
| UniversalFakeDetect-8 | 98.0 AP | 99.34 (94.2) | **98.99 (92.0)** |
|
| 51 |
+
| ForenSynths-13 | — | 96.25 (83.7) | **95.78** (FS-8 97.41 / 85.9) |
|
| 52 |
| AIGIBench-25 | — | 95.36 (88.3) | 86.88 |
|
| 53 |
| CommGen15-15 | — | 99.26 (97.4) | **98.61 (66.7)** |
|
| 54 |
|
| 55 |
+
*PGC training:* the released ProGAN checkpoint is only ~400 steps in (the authors, on our issue), and DINOv2's prior overfits ProGAN within the first epoch, so the earlier epoch-level retrains (GenImage, AIGIBench, CommGen15 rows above, and the old 94.57 / hard-3 75.87 ForenSynths entry) overshot the transfer peak. The official recipe run in SynthScope with a 2000-micro-batch budget (`experiments/image/pgc.sh`, checkpoint every 100) matches or exceeds the official checkpoint at every step from 300 on: step 500 (the saved step nearest ~400) gives FS-8 97.41 mAP and hard-3 AUROC 93.93 (official 96.41 / 90.87 on the same evaluation) and UFD 98.99 (paper 98.0); transfer peaks around steps 500-900 and erodes after. GenImage, AIGIBench and CommGen15 have not been rerun with this recipe. See `repro/investigate/pgc_gap_investigation.md` and `models/image/pgc/README.md`.
|
| 56 |
|
| 57 |
### Co-Spy
|
| 58 |
| Benchmark | Paper | SynthScope_official | SynthScope_train |
|
|
|
|
| 149 |
| GenImage, the paper's 6 subsets (`--dataset genimage_full --select ...`) | **79.44** | 90.94 | **78.61 / 89.60** | mean reproduced; per subset up to 6.8 pt off in both directions (ADM 63.7 vs 70.4, Wukong 90.7 vs 84.4), likely a different test sample |
|
| 150 |
| In the wild: SynthWildX / WildRF / BFree-Online / RealChain / EvalGEN / RRDataset | 49.6 / 53.0 / 47.0 / 49.5 / 62.2 / 55.0 | – | — | new measurements: it calls nearly every recent fake real (AR-only training) |
|
| 151 |
|
| 152 |
+
**Verdict: faithful port (identical to the official model); all 3 paper tables reproduced on the mean** (ARForensics and ForenSynths per generator too). **Trained in SynthScope with the official recipe** (`experiments/image/d3qe.sh`, job 265064): 79.83 / 91.73, 82.14 / 91.35, 77.01 / 90.11 (acc / AP) — AP within 0.9 pt of the paper everywhere, accuracy 1.6-2.3 pt lower (a slightly more conservative threshold, not a weaker ranking). Note: in the released code the codebook-frequency bias is zero at inference (`freq_log_counter` is not saved), so the reported scores come from the quantization-error and CLIP branches.
|
| 153 |
+
|
| 154 |
+
### OmniAID (official checkpoints)
|
| 155 |
+
Fidelity: SynthScope's class == the official `OmniAID` for all three releases (state dicts identical, logits identical on 24 GenImage images, 0 verdict flips; job 265851). Accuracy at 0.5, mean over subsets; Mirage-Test mean = 4 balanced categories + Anime fake accuracy (the paper's Table 3).
|
| 156 |
+
|
| 157 |
+
| Benchmark | release | acc | mAP | Paper | Note |
|
| 158 |
+
|---|---|---:|---:|---|---|
|
| 159 |
+
| GenImage, 8 subsets (`--dataset genimage_full`) | `omniaid_genimage` | **95.82** | 99.02 | **95.94** | **reproduced**, every subset within 1.5 pt; confirms the v1-era 512 -> 336 preprocessing |
|
| 160 |
+
| AIGCDetect, 17 (`--dataset aigcdetect`) | `omniaid_genimage` | **88.87** | 97.88 | **88.87** | **reproduced** exactly |
|
| 161 |
+
| Mirage-Test, 5 categories (`--dataset mirage_test`) | `omniaid_genimage` | **51.10** | — | **51.10** | **reproduced** exactly, every category |
|
| 162 |
+
| GenImage, 8 subsets | `omniaid_v1` | **97.16** | 99.20 | **97.24** | **reproduced**, every subset within 0.6 pt |
|
| 163 |
+
| AIGCDetect, 17 | `omniaid_v1` | **92.88** | 98.93 | **92.88** | **reproduced** exactly |
|
| 164 |
+
| Mirage-Test, 5 categories | `omniaid_v1` | **88.39** | — | **88.39** | **reproduced** exactly |
|
| 165 |
+
| Mirage-Test / SynthWildX / WildRF / BFree-Online / RealChain / EvalGEN / RRDataset | `omniaid` (v2, served) | 78.46 / 86.02 / 88.36 / 70.26 / 61.71 / 74.14 / 71.75 | — | — | new measurements |
|
| 166 |
+
|
| 167 |
+
**Verdict: faithful port; every paper number SynthScope can run is reproduced** (Chameleon and DRCT-2M are not available).
|
| 168 |
|
| 169 |
### Video detectors (no official weights released)
|
| 170 |
| Method · Benchmark | Paper | SynthScope_train |
|
repro/bench_results/d3qe_trained_arforensics.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "d3qe_trained_arforensics",
|
| 3 |
+
"model": "d3qe",
|
| 4 |
+
"ckpt": "/ssdstore/ssalehi/SynthScope/models/d3qe_v1/model_best.pth",
|
| 5 |
+
"benchmark": "pergen",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/ARForensics/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 7,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 79.83,
|
| 11 |
+
"map": 91.73,
|
| 12 |
+
"mauroc": 91.49
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"Infinity": {
|
| 16 |
+
"ap": 78.61,
|
| 17 |
+
"auroc": 79.4,
|
| 18 |
+
"real_acc": 97.38,
|
| 19 |
+
"fake_acc": 20.68,
|
| 20 |
+
"acc": 59.03
|
| 21 |
+
},
|
| 22 |
+
"Janus_Pro": {
|
| 23 |
+
"ap": 97.31,
|
| 24 |
+
"auroc": 97.06,
|
| 25 |
+
"real_acc": 97.6,
|
| 26 |
+
"fake_acc": 82.52,
|
| 27 |
+
"acc": 90.06
|
| 28 |
+
},
|
| 29 |
+
"LlamaGen": {
|
| 30 |
+
"ap": 99.62,
|
| 31 |
+
"auroc": 99.66,
|
| 32 |
+
"real_acc": 97.8,
|
| 33 |
+
"fake_acc": 97.18,
|
| 34 |
+
"acc": 97.49
|
| 35 |
+
},
|
| 36 |
+
"Open_MAGVIT2": {
|
| 37 |
+
"ap": 86.39,
|
| 38 |
+
"auroc": 85.63,
|
| 39 |
+
"real_acc": 97.42,
|
| 40 |
+
"fake_acc": 39.08,
|
| 41 |
+
"acc": 68.25
|
| 42 |
+
},
|
| 43 |
+
"RAR": {
|
| 44 |
+
"ap": 97.71,
|
| 45 |
+
"auroc": 97.67,
|
| 46 |
+
"real_acc": 97.47,
|
| 47 |
+
"fake_acc": 83.62,
|
| 48 |
+
"acc": 90.54
|
| 49 |
+
},
|
| 50 |
+
"Switti": {
|
| 51 |
+
"ap": 87.59,
|
| 52 |
+
"auroc": 86.6,
|
| 53 |
+
"real_acc": 97.4,
|
| 54 |
+
"fake_acc": 44.62,
|
| 55 |
+
"acc": 71.01
|
| 56 |
+
},
|
| 57 |
+
"VAR": {
|
| 58 |
+
"ap": 94.89,
|
| 59 |
+
"auroc": 94.42,
|
| 60 |
+
"real_acc": 97.43,
|
| 61 |
+
"fake_acc": 67.42,
|
| 62 |
+
"acc": 82.43
|
| 63 |
+
}
|
| 64 |
+
},
|
| 65 |
+
"date": "2026-10-27T06:02:46",
|
| 66 |
+
"synthscope_version": "0.1.0"
|
| 67 |
+
}
|
repro/bench_results/d3qe_trained_cnndetection.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "d3qe_trained_cnndetection",
|
| 3 |
+
"model": "d3qe",
|
| 4 |
+
"ckpt": "/ssdstore/ssalehi/SynthScope/models/d3qe_v1/model_best.pth",
|
| 5 |
+
"benchmark": "forensynths",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 7,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 82.14,
|
| 11 |
+
"map": 91.35,
|
| 12 |
+
"mauroc": 90.29
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"biggan": {
|
| 16 |
+
"ap": 94.39,
|
| 17 |
+
"auroc": 93.54,
|
| 18 |
+
"real_acc": 97.6,
|
| 19 |
+
"fake_acc": 69.65,
|
| 20 |
+
"acc": 83.63
|
| 21 |
+
},
|
| 22 |
+
"cyclegan": {
|
| 23 |
+
"ap": 93.08,
|
| 24 |
+
"auroc": 92.91,
|
| 25 |
+
"real_acc": 95.46,
|
| 26 |
+
"fake_acc": 66.92,
|
| 27 |
+
"acc": 81.19
|
| 28 |
+
},
|
| 29 |
+
"gaugan": {
|
| 30 |
+
"ap": 97.36,
|
| 31 |
+
"auroc": 97.86,
|
| 32 |
+
"real_acc": 97.02,
|
| 33 |
+
"fake_acc": 87.44,
|
| 34 |
+
"acc": 92.23
|
| 35 |
+
},
|
| 36 |
+
"progan": {
|
| 37 |
+
"ap": 97.37,
|
| 38 |
+
"auroc": 97.68,
|
| 39 |
+
"real_acc": 94.85,
|
| 40 |
+
"fake_acc": 92.98,
|
| 41 |
+
"acc": 93.91
|
| 42 |
+
},
|
| 43 |
+
"stargan": {
|
| 44 |
+
"ap": 83.68,
|
| 45 |
+
"auroc": 82.03,
|
| 46 |
+
"real_acc": 96.25,
|
| 47 |
+
"fake_acc": 50.18,
|
| 48 |
+
"acc": 73.21
|
| 49 |
+
},
|
| 50 |
+
"stylegan": {
|
| 51 |
+
"ap": 85.88,
|
| 52 |
+
"auroc": 82.53,
|
| 53 |
+
"real_acc": 95.19,
|
| 54 |
+
"fake_acc": 55.68,
|
| 55 |
+
"acc": 75.44
|
| 56 |
+
},
|
| 57 |
+
"stylegan2": {
|
| 58 |
+
"ap": 87.68,
|
| 59 |
+
"auroc": 85.46,
|
| 60 |
+
"real_acc": 95.82,
|
| 61 |
+
"fake_acc": 54.88,
|
| 62 |
+
"acc": 75.35
|
| 63 |
+
}
|
| 64 |
+
},
|
| 65 |
+
"date": "2026-10-27T06:28:33",
|
| 66 |
+
"synthscope_version": "0.1.0"
|
| 67 |
+
}
|
repro/bench_results/d3qe_trained_ep3_arforensics.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "d3qe_trained_ep3_arforensics",
|
| 3 |
+
"model": "d3qe",
|
| 4 |
+
"ckpt": "/ssdstore/ssalehi/SynthScope/models/d3qe_v1/snapshot_ep3_best.pth",
|
| 5 |
+
"benchmark": "pergen",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/ARForensics/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 7,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 81.11,
|
| 11 |
+
"map": 90.77,
|
| 12 |
+
"mauroc": 90.97
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"Infinity": {
|
| 16 |
+
"ap": 79.06,
|
| 17 |
+
"auroc": 80.41,
|
| 18 |
+
"real_acc": 94.67,
|
| 19 |
+
"fake_acc": 33.8,
|
| 20 |
+
"acc": 64.23
|
| 21 |
+
},
|
| 22 |
+
"Janus_Pro": {
|
| 23 |
+
"ap": 96.19,
|
| 24 |
+
"auroc": 96.36,
|
| 25 |
+
"real_acc": 95.4,
|
| 26 |
+
"fake_acc": 85.65,
|
| 27 |
+
"acc": 90.53
|
| 28 |
+
},
|
| 29 |
+
"LlamaGen": {
|
| 30 |
+
"ap": 98.59,
|
| 31 |
+
"auroc": 98.89,
|
| 32 |
+
"real_acc": 95.38,
|
| 33 |
+
"fake_acc": 95.78,
|
| 34 |
+
"acc": 95.58
|
| 35 |
+
},
|
| 36 |
+
"Open_MAGVIT2": {
|
| 37 |
+
"ap": 85.27,
|
| 38 |
+
"auroc": 84.87,
|
| 39 |
+
"real_acc": 95.13,
|
| 40 |
+
"fake_acc": 45.12,
|
| 41 |
+
"acc": 70.13
|
| 42 |
+
},
|
| 43 |
+
"RAR": {
|
| 44 |
+
"ap": 96.75,
|
| 45 |
+
"auroc": 97.04,
|
| 46 |
+
"real_acc": 94.87,
|
| 47 |
+
"fake_acc": 86.83,
|
| 48 |
+
"acc": 90.85
|
| 49 |
+
},
|
| 50 |
+
"Switti": {
|
| 51 |
+
"ap": 86.43,
|
| 52 |
+
"auroc": 86.26,
|
| 53 |
+
"real_acc": 94.68,
|
| 54 |
+
"fake_acc": 53.87,
|
| 55 |
+
"acc": 74.27
|
| 56 |
+
},
|
| 57 |
+
"VAR": {
|
| 58 |
+
"ap": 93.07,
|
| 59 |
+
"auroc": 92.93,
|
| 60 |
+
"real_acc": 95.3,
|
| 61 |
+
"fake_acc": 69.12,
|
| 62 |
+
"acc": 82.21
|
| 63 |
+
}
|
| 64 |
+
},
|
| 65 |
+
"date": "2026-10-26T21:42:06",
|
| 66 |
+
"synthscope_version": "0.1.0"
|
| 67 |
+
}
|
repro/bench_results/d3qe_trained_genimage_full.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "d3qe_trained_genimage_full",
|
| 3 |
+
"model": "d3qe",
|
| 4 |
+
"ckpt": "/ssdstore/ssalehi/SynthScope/models/d3qe_v1/model_best.pth",
|
| 5 |
+
"benchmark": "pergen",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/GenImage_full/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 6,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 77.01,
|
| 11 |
+
"map": 90.11,
|
| 12 |
+
"mauroc": 89.63
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"adm": {
|
| 16 |
+
"ap": 77.51,
|
| 17 |
+
"auroc": 73.53,
|
| 18 |
+
"real_acc": 97.8,
|
| 19 |
+
"fake_acc": 27.53,
|
| 20 |
+
"acc": 62.67
|
| 21 |
+
},
|
| 22 |
+
"glide": {
|
| 23 |
+
"ap": 97.62,
|
| 24 |
+
"auroc": 98.08,
|
| 25 |
+
"real_acc": 97.67,
|
| 26 |
+
"fake_acc": 81.13,
|
| 27 |
+
"acc": 89.4
|
| 28 |
+
},
|
| 29 |
+
"midjourney": {
|
| 30 |
+
"ap": 80.15,
|
| 31 |
+
"auroc": 81.72,
|
| 32 |
+
"real_acc": 97.2,
|
| 33 |
+
"fake_acc": 25.93,
|
| 34 |
+
"acc": 61.57
|
| 35 |
+
},
|
| 36 |
+
"sdv1.4": {
|
| 37 |
+
"ap": 93.68,
|
| 38 |
+
"auroc": 92.81,
|
| 39 |
+
"real_acc": 97.72,
|
| 40 |
+
"fake_acc": 61.12,
|
| 41 |
+
"acc": 79.42
|
| 42 |
+
},
|
| 43 |
+
"sdv1.5": {
|
| 44 |
+
"ap": 94.38,
|
| 45 |
+
"auroc": 94.5,
|
| 46 |
+
"real_acc": 96.2,
|
| 47 |
+
"fake_acc": 67.13,
|
| 48 |
+
"acc": 81.67
|
| 49 |
+
},
|
| 50 |
+
"wukong": {
|
| 51 |
+
"ap": 97.3,
|
| 52 |
+
"auroc": 97.16,
|
| 53 |
+
"real_acc": 97.73,
|
| 54 |
+
"fake_acc": 76.93,
|
| 55 |
+
"acc": 87.33
|
| 56 |
+
}
|
| 57 |
+
},
|
| 58 |
+
"date": "2026-10-27T06:42:56",
|
| 59 |
+
"synthscope_version": "0.1.0"
|
| 60 |
+
}
|
repro/bench_results/npr_official3090_forensynths8.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "npr_official3090_forensynths8",
|
| 3 |
+
"model": "npr",
|
| 4 |
+
"ckpt": "/ivi/zfs/s0/original_homes/ssalehi/SynthScope_shared/npr_ref/model_epoch_last_3090.pth",
|
| 5 |
+
"benchmark": "forensynths",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 8,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 91.83,
|
| 11 |
+
"map": 92.81,
|
| 12 |
+
"mauroc": 95.18
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"biggan": {
|
| 16 |
+
"ap": 85.59,
|
| 17 |
+
"auroc": 90.86,
|
| 18 |
+
"real_acc": 69.75,
|
| 19 |
+
"fake_acc": 98.15,
|
| 20 |
+
"acc": 83.95
|
| 21 |
+
},
|
| 22 |
+
"cyclegan": {
|
| 23 |
+
"ap": 98.12,
|
| 24 |
+
"auroc": 98.86,
|
| 25 |
+
"real_acc": 96.9,
|
| 26 |
+
"fake_acc": 93.49,
|
| 27 |
+
"acc": 95.19
|
| 28 |
+
},
|
| 29 |
+
"deepfake": {
|
| 30 |
+
"ap": 76.06,
|
| 31 |
+
"auroc": 83.04,
|
| 32 |
+
"real_acc": 61.17,
|
| 33 |
+
"fake_acc": 93.25,
|
| 34 |
+
"acc": 77.21
|
| 35 |
+
},
|
| 36 |
+
"gaugan": {
|
| 37 |
+
"ap": 82.97,
|
| 38 |
+
"auroc": 88.99,
|
| 39 |
+
"real_acc": 62.72,
|
| 40 |
+
"fake_acc": 99.16,
|
| 41 |
+
"acc": 80.94
|
| 42 |
+
},
|
| 43 |
+
"progan": {
|
| 44 |
+
"ap": 99.98,
|
| 45 |
+
"auroc": 99.97,
|
| 46 |
+
"real_acc": 99.9,
|
| 47 |
+
"fake_acc": 99.9,
|
| 48 |
+
"acc": 99.9
|
| 49 |
+
},
|
| 50 |
+
"stargan": {
|
| 51 |
+
"ap": 100.0,
|
| 52 |
+
"auroc": 100.0,
|
| 53 |
+
"real_acc": 99.8,
|
| 54 |
+
"fake_acc": 99.75,
|
| 55 |
+
"acc": 99.77
|
| 56 |
+
},
|
| 57 |
+
"stylegan": {
|
| 58 |
+
"ap": 99.78,
|
| 59 |
+
"auroc": 99.77,
|
| 60 |
+
"real_acc": 99.93,
|
| 61 |
+
"fake_acc": 96.19,
|
| 62 |
+
"acc": 98.06
|
| 63 |
+
},
|
| 64 |
+
"stylegan2": {
|
| 65 |
+
"ap": 99.95,
|
| 66 |
+
"auroc": 99.97,
|
| 67 |
+
"real_acc": 99.95,
|
| 68 |
+
"fake_acc": 99.26,
|
| 69 |
+
"acc": 99.61
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"date": "2026-10-27T03:25:16",
|
| 73 |
+
"synthscope_version": "0.1.0"
|
| 74 |
+
}
|
repro/bench_results/npr_official3090_ufd.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "npr_official3090_ufd",
|
| 3 |
+
"model": "npr",
|
| 4 |
+
"ckpt": "/ivi/zfs/s0/original_homes/ssalehi/SynthScope_shared/npr_ref/model_epoch_last_3090.pth",
|
| 5 |
+
"benchmark": "ufd",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/universialfakedetection/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 8,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 93.94,
|
| 11 |
+
"map": 96.69,
|
| 12 |
+
"mauroc": 97.26
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"dalle": {
|
| 16 |
+
"ap": 98.08,
|
| 17 |
+
"auroc": 98.08,
|
| 18 |
+
"real_acc": 98.0,
|
| 19 |
+
"fake_acc": 83.8,
|
| 20 |
+
"acc": 90.9
|
| 21 |
+
},
|
| 22 |
+
"glide_100_10": {
|
| 23 |
+
"ap": 99.52,
|
| 24 |
+
"auroc": 99.54,
|
| 25 |
+
"real_acc": 98.0,
|
| 26 |
+
"fake_acc": 97.6,
|
| 27 |
+
"acc": 97.8
|
| 28 |
+
},
|
| 29 |
+
"glide_100_27": {
|
| 30 |
+
"ap": 99.53,
|
| 31 |
+
"auroc": 99.61,
|
| 32 |
+
"real_acc": 98.0,
|
| 33 |
+
"fake_acc": 96.7,
|
| 34 |
+
"acc": 97.35
|
| 35 |
+
},
|
| 36 |
+
"glide_50_27": {
|
| 37 |
+
"ap": 99.55,
|
| 38 |
+
"auroc": 99.61,
|
| 39 |
+
"real_acc": 98.0,
|
| 40 |
+
"fake_acc": 96.9,
|
| 41 |
+
"acc": 97.45
|
| 42 |
+
},
|
| 43 |
+
"guided": {
|
| 44 |
+
"ap": 78.12,
|
| 45 |
+
"auroc": 82.46,
|
| 46 |
+
"real_acc": 62.9,
|
| 47 |
+
"fake_acc": 85.0,
|
| 48 |
+
"acc": 73.95
|
| 49 |
+
},
|
| 50 |
+
"ldm_100": {
|
| 51 |
+
"ap": 99.57,
|
| 52 |
+
"auroc": 99.61,
|
| 53 |
+
"real_acc": 98.0,
|
| 54 |
+
"fake_acc": 97.9,
|
| 55 |
+
"acc": 97.95
|
| 56 |
+
},
|
| 57 |
+
"ldm_200": {
|
| 58 |
+
"ap": 99.63,
|
| 59 |
+
"auroc": 99.69,
|
| 60 |
+
"real_acc": 98.0,
|
| 61 |
+
"fake_acc": 98.3,
|
| 62 |
+
"acc": 98.15
|
| 63 |
+
},
|
| 64 |
+
"ldm_200_cfg": {
|
| 65 |
+
"ap": 99.49,
|
| 66 |
+
"auroc": 99.48,
|
| 67 |
+
"real_acc": 98.0,
|
| 68 |
+
"fake_acc": 97.9,
|
| 69 |
+
"acc": 97.95
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"date": "2026-10-27T03:26:05",
|
| 73 |
+
"synthscope_version": "0.1.0"
|
| 74 |
+
}
|
repro/bench_results/npr_official_nprpth_forensynths8.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "npr_official_nprpth_forensynths8",
|
| 3 |
+
"model": "npr",
|
| 4 |
+
"ckpt": "registry-default",
|
| 5 |
+
"benchmark": "forensynths",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 8,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 92.3,
|
| 11 |
+
"map": 94.22,
|
| 12 |
+
"mauroc": 96.08
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"biggan": {
|
| 16 |
+
"ap": 87.8,
|
| 17 |
+
"auroc": 92.36,
|
| 18 |
+
"real_acc": 70.0,
|
| 19 |
+
"fake_acc": 98.7,
|
| 20 |
+
"acc": 84.35
|
| 21 |
+
},
|
| 22 |
+
"cyclegan": {
|
| 23 |
+
"ap": 98.45,
|
| 24 |
+
"auroc": 99.04,
|
| 25 |
+
"real_acc": 96.06,
|
| 26 |
+
"fake_acc": 96.14,
|
| 27 |
+
"acc": 96.1
|
| 28 |
+
},
|
| 29 |
+
"deepfake": {
|
| 30 |
+
"ap": 82.4,
|
| 31 |
+
"auroc": 86.85,
|
| 32 |
+
"real_acc": 70.52,
|
| 33 |
+
"fake_acc": 89.96,
|
| 34 |
+
"acc": 80.24
|
| 35 |
+
},
|
| 36 |
+
"gaugan": {
|
| 37 |
+
"ap": 85.49,
|
| 38 |
+
"auroc": 90.75,
|
| 39 |
+
"real_acc": 65.54,
|
| 40 |
+
"fake_acc": 99.46,
|
| 41 |
+
"acc": 82.5
|
| 42 |
+
},
|
| 43 |
+
"progan": {
|
| 44 |
+
"ap": 99.99,
|
| 45 |
+
"auroc": 99.99,
|
| 46 |
+
"real_acc": 99.83,
|
| 47 |
+
"fake_acc": 99.75,
|
| 48 |
+
"acc": 99.79
|
| 49 |
+
},
|
| 50 |
+
"stargan": {
|
| 51 |
+
"ap": 99.94,
|
| 52 |
+
"auroc": 99.96,
|
| 53 |
+
"real_acc": 98.75,
|
| 54 |
+
"fake_acc": 99.95,
|
| 55 |
+
"acc": 99.35
|
| 56 |
+
},
|
| 57 |
+
"stylegan": {
|
| 58 |
+
"ap": 99.78,
|
| 59 |
+
"auroc": 99.75,
|
| 60 |
+
"real_acc": 99.65,
|
| 61 |
+
"fake_acc": 95.76,
|
| 62 |
+
"acc": 97.7
|
| 63 |
+
},
|
| 64 |
+
"stylegan2": {
|
| 65 |
+
"ap": 99.94,
|
| 66 |
+
"auroc": 99.94,
|
| 67 |
+
"real_acc": 99.74,
|
| 68 |
+
"fake_acc": 97.02,
|
| 69 |
+
"acc": 98.38
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"date": "2026-10-27T02:49:02",
|
| 73 |
+
"synthscope_version": "0.1.0"
|
| 74 |
+
}
|
repro/bench_results/npr_official_nprpth_ufd.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "npr_official_nprpth_ufd",
|
| 3 |
+
"model": "npr",
|
| 4 |
+
"ckpt": "registry-default",
|
| 5 |
+
"benchmark": "ufd",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/universialfakedetection/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 8,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 94.57,
|
| 11 |
+
"map": 97.16,
|
| 12 |
+
"mauroc": 97.68
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"dalle": {
|
| 16 |
+
"ap": 98.96,
|
| 17 |
+
"auroc": 98.96,
|
| 18 |
+
"real_acc": 98.5,
|
| 19 |
+
"fake_acc": 89.2,
|
| 20 |
+
"acc": 93.85
|
| 21 |
+
},
|
| 22 |
+
"glide_100_10": {
|
| 23 |
+
"ap": 99.63,
|
| 24 |
+
"auroc": 99.64,
|
| 25 |
+
"real_acc": 98.5,
|
| 26 |
+
"fake_acc": 96.7,
|
| 27 |
+
"acc": 97.6
|
| 28 |
+
},
|
| 29 |
+
"glide_100_27": {
|
| 30 |
+
"ap": 99.55,
|
| 31 |
+
"auroc": 99.56,
|
| 32 |
+
"real_acc": 98.5,
|
| 33 |
+
"fake_acc": 95.7,
|
| 34 |
+
"acc": 97.1
|
| 35 |
+
},
|
| 36 |
+
"glide_50_27": {
|
| 37 |
+
"ap": 99.61,
|
| 38 |
+
"auroc": 99.62,
|
| 39 |
+
"real_acc": 98.5,
|
| 40 |
+
"fake_acc": 96.6,
|
| 41 |
+
"acc": 97.55
|
| 42 |
+
},
|
| 43 |
+
"guided": {
|
| 44 |
+
"ap": 80.28,
|
| 45 |
+
"auroc": 84.23,
|
| 46 |
+
"real_acc": 65.8,
|
| 47 |
+
"fake_acc": 84.4,
|
| 48 |
+
"acc": 75.1
|
| 49 |
+
},
|
| 50 |
+
"ldm_100": {
|
| 51 |
+
"ap": 99.8,
|
| 52 |
+
"auroc": 99.87,
|
| 53 |
+
"real_acc": 98.5,
|
| 54 |
+
"fake_acc": 98.8,
|
| 55 |
+
"acc": 98.65
|
| 56 |
+
},
|
| 57 |
+
"ldm_200": {
|
| 58 |
+
"ap": 99.76,
|
| 59 |
+
"auroc": 99.82,
|
| 60 |
+
"real_acc": 98.5,
|
| 61 |
+
"fake_acc": 98.4,
|
| 62 |
+
"acc": 98.45
|
| 63 |
+
},
|
| 64 |
+
"ldm_200_cfg": {
|
| 65 |
+
"ap": 99.73,
|
| 66 |
+
"auroc": 99.76,
|
| 67 |
+
"real_acc": 98.5,
|
| 68 |
+
"fake_acc": 98.1,
|
| 69 |
+
"acc": 98.3
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"date": "2026-10-27T02:49:53",
|
| 73 |
+
"synthscope_version": "0.1.0"
|
| 74 |
+
}
|
repro/bench_results/npr_officialcode_rerun_forensynths8.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "npr_officialcode_rerun_forensynths8",
|
| 3 |
+
"model": "npr",
|
| 4 |
+
"ckpt": "/ssdstore/ssalehi/SynthScope/models/npr_official_rerun/4class-resnet-car-cat-chair-horse2026_10_27_03_27_09/model_epoch_last.pth",
|
| 5 |
+
"benchmark": "forensynths",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 8,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 81.47,
|
| 11 |
+
"map": 92.72,
|
| 12 |
+
"mauroc": 93.97
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"biggan": {
|
| 16 |
+
"ap": 90.16,
|
| 17 |
+
"auroc": 91.79,
|
| 18 |
+
"real_acc": 91.95,
|
| 19 |
+
"fake_acc": 65.2,
|
| 20 |
+
"acc": 78.58
|
| 21 |
+
},
|
| 22 |
+
"cyclegan": {
|
| 23 |
+
"ap": 97.81,
|
| 24 |
+
"auroc": 98.64,
|
| 25 |
+
"real_acc": 99.24,
|
| 26 |
+
"fake_acc": 45.19,
|
| 27 |
+
"acc": 72.22
|
| 28 |
+
},
|
| 29 |
+
"deepfake": {
|
| 30 |
+
"ap": 73.75,
|
| 31 |
+
"auroc": 75.52,
|
| 32 |
+
"real_acc": 95.57,
|
| 33 |
+
"fake_acc": 20.53,
|
| 34 |
+
"acc": 58.05
|
| 35 |
+
},
|
| 36 |
+
"gaugan": {
|
| 37 |
+
"ap": 80.85,
|
| 38 |
+
"auroc": 86.74,
|
| 39 |
+
"real_acc": 90.64,
|
| 40 |
+
"fake_acc": 42.6,
|
| 41 |
+
"acc": 66.62
|
| 42 |
+
},
|
| 43 |
+
"progan": {
|
| 44 |
+
"ap": 100.0,
|
| 45 |
+
"auroc": 100.0,
|
| 46 |
+
"real_acc": 100.0,
|
| 47 |
+
"fake_acc": 99.87,
|
| 48 |
+
"acc": 99.94
|
| 49 |
+
},
|
| 50 |
+
"stargan": {
|
| 51 |
+
"ap": 99.99,
|
| 52 |
+
"auroc": 99.99,
|
| 53 |
+
"real_acc": 100.0,
|
| 54 |
+
"fake_acc": 81.54,
|
| 55 |
+
"acc": 90.77
|
| 56 |
+
},
|
| 57 |
+
"stylegan": {
|
| 58 |
+
"ap": 99.34,
|
| 59 |
+
"auroc": 99.23,
|
| 60 |
+
"real_acc": 99.98,
|
| 61 |
+
"fake_acc": 83.07,
|
| 62 |
+
"acc": 91.53
|
| 63 |
+
},
|
| 64 |
+
"stylegan2": {
|
| 65 |
+
"ap": 99.89,
|
| 66 |
+
"auroc": 99.87,
|
| 67 |
+
"real_acc": 99.99,
|
| 68 |
+
"fake_acc": 88.17,
|
| 69 |
+
"acc": 94.08
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"date": "2026-10-27T13:08:53",
|
| 73 |
+
"synthscope_version": "0.1.0"
|
| 74 |
+
}
|
repro/bench_results/npr_officialcode_rerun_ufd.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "npr_officialcode_rerun_ufd",
|
| 3 |
+
"model": "npr",
|
| 4 |
+
"ckpt": "/ssdstore/ssalehi/SynthScope/models/npr_official_rerun/4class-resnet-car-cat-chair-horse2026_10_27_03_27_09/model_epoch_last.pth",
|
| 5 |
+
"benchmark": "ufd",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/universialfakedetection/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 8,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 87.61,
|
| 11 |
+
"map": 96.56,
|
| 12 |
+
"mauroc": 96.43
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"dalle": {
|
| 16 |
+
"ap": 92.55,
|
| 17 |
+
"auroc": 92.27,
|
| 18 |
+
"real_acc": 99.1,
|
| 19 |
+
"fake_acc": 38.4,
|
| 20 |
+
"acc": 68.75
|
| 21 |
+
},
|
| 22 |
+
"glide_100_10": {
|
| 23 |
+
"ap": 99.17,
|
| 24 |
+
"auroc": 99.15,
|
| 25 |
+
"real_acc": 99.1,
|
| 26 |
+
"fake_acc": 89.6,
|
| 27 |
+
"acc": 94.35
|
| 28 |
+
},
|
| 29 |
+
"glide_100_27": {
|
| 30 |
+
"ap": 98.95,
|
| 31 |
+
"auroc": 98.95,
|
| 32 |
+
"real_acc": 99.1,
|
| 33 |
+
"fake_acc": 84.9,
|
| 34 |
+
"acc": 92.0
|
| 35 |
+
},
|
| 36 |
+
"glide_50_27": {
|
| 37 |
+
"ap": 99.0,
|
| 38 |
+
"auroc": 98.94,
|
| 39 |
+
"real_acc": 99.1,
|
| 40 |
+
"fake_acc": 87.0,
|
| 41 |
+
"acc": 93.05
|
| 42 |
+
},
|
| 43 |
+
"guided": {
|
| 44 |
+
"ap": 85.77,
|
| 45 |
+
"auroc": 85.06,
|
| 46 |
+
"real_acc": 89.5,
|
| 47 |
+
"fake_acc": 56.5,
|
| 48 |
+
"acc": 73.0
|
| 49 |
+
},
|
| 50 |
+
"ldm_100": {
|
| 51 |
+
"ap": 99.03,
|
| 52 |
+
"auroc": 99.1,
|
| 53 |
+
"real_acc": 99.1,
|
| 54 |
+
"fake_acc": 87.8,
|
| 55 |
+
"acc": 93.45
|
| 56 |
+
},
|
| 57 |
+
"ldm_200": {
|
| 58 |
+
"ap": 99.08,
|
| 59 |
+
"auroc": 99.12,
|
| 60 |
+
"real_acc": 99.1,
|
| 61 |
+
"fake_acc": 87.5,
|
| 62 |
+
"acc": 93.3
|
| 63 |
+
},
|
| 64 |
+
"ldm_200_cfg": {
|
| 65 |
+
"ap": 98.95,
|
| 66 |
+
"auroc": 98.85,
|
| 67 |
+
"real_acc": 99.1,
|
| 68 |
+
"fake_acc": 86.9,
|
| 69 |
+
"acc": 93.0
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"date": "2026-10-27T13:09:44",
|
| 73 |
+
"synthscope_version": "0.1.0"
|
| 74 |
+
}
|
repro/bench_results/npr_trained_best_forensynths8.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "npr_trained_best_forensynths8",
|
| 3 |
+
"model": "npr",
|
| 4 |
+
"ckpt": "/ssdstore/ssalehi/SynthScope/models/npr_v1/model_best.pth",
|
| 5 |
+
"benchmark": "forensynths",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 8,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 84.3,
|
| 11 |
+
"map": 91.81,
|
| 12 |
+
"mauroc": 93.3
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"biggan": {
|
| 16 |
+
"ap": 89.71,
|
| 17 |
+
"auroc": 90.53,
|
| 18 |
+
"real_acc": 90.3,
|
| 19 |
+
"fake_acc": 68.7,
|
| 20 |
+
"acc": 79.5
|
| 21 |
+
},
|
| 22 |
+
"cyclegan": {
|
| 23 |
+
"ap": 97.55,
|
| 24 |
+
"auroc": 98.21,
|
| 25 |
+
"real_acc": 98.49,
|
| 26 |
+
"fake_acc": 66.62,
|
| 27 |
+
"acc": 82.55
|
| 28 |
+
},
|
| 29 |
+
"deepfake": {
|
| 30 |
+
"ap": 69.88,
|
| 31 |
+
"auroc": 74.96,
|
| 32 |
+
"real_acc": 70.37,
|
| 33 |
+
"fake_acc": 66.53,
|
| 34 |
+
"acc": 68.45
|
| 35 |
+
},
|
| 36 |
+
"gaugan": {
|
| 37 |
+
"ap": 78.39,
|
| 38 |
+
"auroc": 83.97,
|
| 39 |
+
"real_acc": 89.12,
|
| 40 |
+
"fake_acc": 44.8,
|
| 41 |
+
"acc": 66.96
|
| 42 |
+
},
|
| 43 |
+
"progan": {
|
| 44 |
+
"ap": 100.0,
|
| 45 |
+
"auroc": 100.0,
|
| 46 |
+
"real_acc": 100.0,
|
| 47 |
+
"fake_acc": 99.65,
|
| 48 |
+
"acc": 99.83
|
| 49 |
+
},
|
| 50 |
+
"stargan": {
|
| 51 |
+
"ap": 99.99,
|
| 52 |
+
"auroc": 99.99,
|
| 53 |
+
"real_acc": 100.0,
|
| 54 |
+
"fake_acc": 75.49,
|
| 55 |
+
"acc": 87.74
|
| 56 |
+
},
|
| 57 |
+
"stylegan": {
|
| 58 |
+
"ap": 99.04,
|
| 59 |
+
"auroc": 98.85,
|
| 60 |
+
"real_acc": 99.93,
|
| 61 |
+
"fake_acc": 84.51,
|
| 62 |
+
"acc": 92.22
|
| 63 |
+
},
|
| 64 |
+
"stylegan2": {
|
| 65 |
+
"ap": 99.9,
|
| 66 |
+
"auroc": 99.88,
|
| 67 |
+
"real_acc": 99.94,
|
| 68 |
+
"fake_acc": 94.29,
|
| 69 |
+
"acc": 97.11
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"date": "2026-10-27T02:58:43",
|
| 73 |
+
"synthscope_version": "0.1.0"
|
| 74 |
+
}
|
repro/bench_results/npr_trained_best_ufd.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "npr_trained_best_ufd",
|
| 3 |
+
"model": "npr",
|
| 4 |
+
"ckpt": "/ssdstore/ssalehi/SynthScope/models/npr_v1/model_best.pth",
|
| 5 |
+
"benchmark": "ufd",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/universialfakedetection/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 8,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 85.34,
|
| 11 |
+
"map": 95.56,
|
| 12 |
+
"mauroc": 95.3
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"dalle": {
|
| 16 |
+
"ap": 92.85,
|
| 17 |
+
"auroc": 92.7,
|
| 18 |
+
"real_acc": 98.5,
|
| 19 |
+
"fake_acc": 44.5,
|
| 20 |
+
"acc": 71.5
|
| 21 |
+
},
|
| 22 |
+
"glide_100_10": {
|
| 23 |
+
"ap": 98.54,
|
| 24 |
+
"auroc": 98.22,
|
| 25 |
+
"real_acc": 98.5,
|
| 26 |
+
"fake_acc": 90.7,
|
| 27 |
+
"acc": 94.6
|
| 28 |
+
},
|
| 29 |
+
"glide_100_27": {
|
| 30 |
+
"ap": 97.85,
|
| 31 |
+
"auroc": 97.35,
|
| 32 |
+
"real_acc": 98.5,
|
| 33 |
+
"fake_acc": 86.0,
|
| 34 |
+
"acc": 92.25
|
| 35 |
+
},
|
| 36 |
+
"glide_50_27": {
|
| 37 |
+
"ap": 98.08,
|
| 38 |
+
"auroc": 97.6,
|
| 39 |
+
"real_acc": 98.5,
|
| 40 |
+
"fake_acc": 88.6,
|
| 41 |
+
"acc": 93.55
|
| 42 |
+
},
|
| 43 |
+
"guided": {
|
| 44 |
+
"ap": 84.94,
|
| 45 |
+
"auroc": 83.39,
|
| 46 |
+
"real_acc": 89.7,
|
| 47 |
+
"fake_acc": 58.6,
|
| 48 |
+
"acc": 74.15
|
| 49 |
+
},
|
| 50 |
+
"ldm_100": {
|
| 51 |
+
"ap": 97.13,
|
| 52 |
+
"auroc": 97.49,
|
| 53 |
+
"real_acc": 98.5,
|
| 54 |
+
"fake_acc": 69.3,
|
| 55 |
+
"acc": 83.9
|
| 56 |
+
},
|
| 57 |
+
"ldm_200": {
|
| 58 |
+
"ap": 97.01,
|
| 59 |
+
"auroc": 97.37,
|
| 60 |
+
"real_acc": 98.5,
|
| 61 |
+
"fake_acc": 67.6,
|
| 62 |
+
"acc": 83.05
|
| 63 |
+
},
|
| 64 |
+
"ldm_200_cfg": {
|
| 65 |
+
"ap": 98.09,
|
| 66 |
+
"auroc": 98.25,
|
| 67 |
+
"real_acc": 98.5,
|
| 68 |
+
"fake_acc": 80.9,
|
| 69 |
+
"acc": 89.7
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"date": "2026-10-27T02:59:33",
|
| 73 |
+
"synthscope_version": "0.1.0"
|
| 74 |
+
}
|
repro/bench_results/npr_trained_last_forensynths8.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "npr_trained_last_forensynths8",
|
| 3 |
+
"model": "npr",
|
| 4 |
+
"ckpt": "/ssdstore/ssalehi/SynthScope/models/npr_v1/model_last.pth",
|
| 5 |
+
"benchmark": "forensynths",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/CNNDetection/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 8,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 84.09,
|
| 11 |
+
"map": 93.83,
|
| 12 |
+
"mauroc": 94.94
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"biggan": {
|
| 16 |
+
"ap": 91.45,
|
| 17 |
+
"auroc": 92.23,
|
| 18 |
+
"real_acc": 91.6,
|
| 19 |
+
"fake_acc": 68.9,
|
| 20 |
+
"acc": 80.25
|
| 21 |
+
},
|
| 22 |
+
"cyclegan": {
|
| 23 |
+
"ap": 99.07,
|
| 24 |
+
"auroc": 99.42,
|
| 25 |
+
"real_acc": 99.55,
|
| 26 |
+
"fake_acc": 47.84,
|
| 27 |
+
"acc": 73.69
|
| 28 |
+
},
|
| 29 |
+
"deepfake": {
|
| 30 |
+
"ap": 74.32,
|
| 31 |
+
"auroc": 78.49,
|
| 32 |
+
"real_acc": 90.47,
|
| 33 |
+
"fake_acc": 35.36,
|
| 34 |
+
"acc": 62.91
|
| 35 |
+
},
|
| 36 |
+
"gaugan": {
|
| 37 |
+
"ap": 86.04,
|
| 38 |
+
"auroc": 89.64,
|
| 39 |
+
"real_acc": 89.12,
|
| 40 |
+
"fake_acc": 59.62,
|
| 41 |
+
"acc": 74.37
|
| 42 |
+
},
|
| 43 |
+
"progan": {
|
| 44 |
+
"ap": 100.0,
|
| 45 |
+
"auroc": 100.0,
|
| 46 |
+
"real_acc": 100.0,
|
| 47 |
+
"fake_acc": 99.62,
|
| 48 |
+
"acc": 99.81
|
| 49 |
+
},
|
| 50 |
+
"stargan": {
|
| 51 |
+
"ap": 99.99,
|
| 52 |
+
"auroc": 99.99,
|
| 53 |
+
"real_acc": 100.0,
|
| 54 |
+
"fake_acc": 80.49,
|
| 55 |
+
"acc": 90.25
|
| 56 |
+
},
|
| 57 |
+
"stylegan": {
|
| 58 |
+
"ap": 99.83,
|
| 59 |
+
"auroc": 99.8,
|
| 60 |
+
"real_acc": 100.0,
|
| 61 |
+
"fake_acc": 90.1,
|
| 62 |
+
"acc": 95.05
|
| 63 |
+
},
|
| 64 |
+
"stylegan2": {
|
| 65 |
+
"ap": 99.93,
|
| 66 |
+
"auroc": 99.92,
|
| 67 |
+
"real_acc": 100.0,
|
| 68 |
+
"fake_acc": 92.79,
|
| 69 |
+
"acc": 96.39
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"date": "2026-10-27T02:53:54",
|
| 73 |
+
"synthscope_version": "0.1.0"
|
| 74 |
+
}
|
repro/bench_results/npr_trained_last_ufd.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "npr_trained_last_ufd",
|
| 3 |
+
"model": "npr",
|
| 4 |
+
"ckpt": "/ssdstore/ssalehi/SynthScope/models/npr_v1/model_last.pth",
|
| 5 |
+
"benchmark": "ufd",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/universialfakedetection/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 8,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 88.91,
|
| 11 |
+
"map": 97.49,
|
| 12 |
+
"mauroc": 97.23
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"dalle": {
|
| 16 |
+
"ap": 95.29,
|
| 17 |
+
"auroc": 94.52,
|
| 18 |
+
"real_acc": 99.7,
|
| 19 |
+
"fake_acc": 38.1,
|
| 20 |
+
"acc": 68.9
|
| 21 |
+
},
|
| 22 |
+
"glide_100_10": {
|
| 23 |
+
"ap": 99.73,
|
| 24 |
+
"auroc": 99.73,
|
| 25 |
+
"real_acc": 99.7,
|
| 26 |
+
"fake_acc": 94.1,
|
| 27 |
+
"acc": 96.9
|
| 28 |
+
},
|
| 29 |
+
"glide_100_27": {
|
| 30 |
+
"ap": 99.55,
|
| 31 |
+
"auroc": 99.5,
|
| 32 |
+
"real_acc": 99.7,
|
| 33 |
+
"fake_acc": 89.7,
|
| 34 |
+
"acc": 94.7
|
| 35 |
+
},
|
| 36 |
+
"glide_50_27": {
|
| 37 |
+
"ap": 99.64,
|
| 38 |
+
"auroc": 99.6,
|
| 39 |
+
"real_acc": 99.7,
|
| 40 |
+
"fake_acc": 92.3,
|
| 41 |
+
"acc": 96.0
|
| 42 |
+
},
|
| 43 |
+
"guided": {
|
| 44 |
+
"ap": 86.62,
|
| 45 |
+
"auroc": 85.23,
|
| 46 |
+
"real_acc": 90.0,
|
| 47 |
+
"fake_acc": 59.9,
|
| 48 |
+
"acc": 74.95
|
| 49 |
+
},
|
| 50 |
+
"ldm_100": {
|
| 51 |
+
"ap": 99.71,
|
| 52 |
+
"auroc": 99.79,
|
| 53 |
+
"real_acc": 99.7,
|
| 54 |
+
"fake_acc": 87.0,
|
| 55 |
+
"acc": 93.35
|
| 56 |
+
},
|
| 57 |
+
"ldm_200": {
|
| 58 |
+
"ap": 99.7,
|
| 59 |
+
"auroc": 99.78,
|
| 60 |
+
"real_acc": 99.7,
|
| 61 |
+
"fake_acc": 85.3,
|
| 62 |
+
"acc": 92.5
|
| 63 |
+
},
|
| 64 |
+
"ldm_200_cfg": {
|
| 65 |
+
"ap": 99.7,
|
| 66 |
+
"auroc": 99.71,
|
| 67 |
+
"real_acc": 99.7,
|
| 68 |
+
"fake_acc": 88.3,
|
| 69 |
+
"acc": 94.0
|
| 70 |
+
}
|
| 71 |
+
},
|
| 72 |
+
"date": "2026-10-27T02:54:44",
|
| 73 |
+
"synthscope_version": "0.1.0"
|
| 74 |
+
}
|
repro/bench_results/omniaid_bfree_online.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"tag": "omniaid_bfree_online",
|
| 3 |
+
"model": "omniaid",
|
| 4 |
+
"ckpt": "registry-default",
|
| 5 |
+
"benchmark": "pergen",
|
| 6 |
+
"data_root": "/ssdstore/ssalehi/SynthScope/BFreeOnline/test",
|
| 7 |
+
"jpeg_fakes": null,
|
| 8 |
+
"n_generators": 1,
|
| 9 |
+
"means": {
|
| 10 |
+
"acc": 70.26,
|
| 11 |
+
"map": 91.7,
|
| 12 |
+
"mauroc": 87.18
|
| 13 |
+
},
|
| 14 |
+
"per_generator": {
|
| 15 |
+
"viral": {
|
| 16 |
+
"ap": 91.7,
|
| 17 |
+
"auroc": 87.18,
|
| 18 |
+
"real_acc": 94.8,
|
| 19 |
+
"fake_acc": 45.72,
|
| 20 |
+
"acc": 70.26
|
| 21 |
+
}
|
| 22 |
+
},
|
| 23 |
+
"date": "2026-10-27T08:44:32",
|
| 24 |
+
"synthscope_version": "0.1.0"
|
| 25 |
+
}
|