File size: 3,426 Bytes
cd24b4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
"""Verify a (trained or official) image detector by scoring a balanced real/fake sample
from the local ForenSynths/CNNDetection test set, through the SAME engine the web app uses.

Reports accuracy@0.5, real/fake accuracy, AP and AUC so the checkpoint's quality is a real,
reproducible number (not a training-log artifact).

  conda run -n SynthScope python interface/scripts/verify_model.py <key> [--n 200] [--gens progan,biggan]
"""
import argparse
import glob
import os
import random
import sys
from pathlib import Path

import numpy as np
import torch
from PIL import Image

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from backend.engine import get_engine     # noqa: E402
from backend.registry import get_spec     # noqa: E402

TEST_ROOT = "/hddstore/ssalehi/SynthScope/CNNDetection/test"


def list_split(gen, sub):
    d = os.path.join(TEST_ROOT, gen, sub)
    fs = []
    for e in ("*.png", "*.jpg", "*.jpeg", "*.JPEG", "*.webp", "*.bmp"):
        fs += glob.glob(os.path.join(d, "**", e), recursive=True)
    return sorted(set(fs))


def ap_auc(y, p):
    y = np.asarray(y); p = np.asarray(p)
    order = np.argsort(-p, kind="mergesort"); y = y[order]
    tp = np.cumsum(y == 1); fp = np.cumsum(y == 0)
    npos, nneg = (y == 1).sum(), (y == 0).sum()
    if npos == 0 or nneg == 0:
        return float("nan"), float("nan")
    prec = tp / np.maximum(tp + fp, 1); rec = tp / npos
    rec = np.concatenate([[0.0], rec]); prec = np.concatenate([[1.0], prec])
    ap = float(np.sum((rec[1:] - rec[:-1]) * prec[1:]))
    tpr = np.concatenate([[0.0], tp / npos]); fpr = np.concatenate([[0.0], fp / nneg])
    auc = float(np.sum((fpr[1:] - fpr[:-1]) * (tpr[1:] + tpr[:-1]) / 2.0))
    return ap, auc


@torch.no_grad()
def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("key")
    ap.add_argument("--n", type=int, default=200, help="images per class")
    ap.add_argument("--gens", default="progan,biggan,stylegan,cyclegan")
    ap.add_argument("--bs", type=int, default=64)
    args = ap.parse_args()

    engine = get_engine()
    spec = get_spec(args.key)
    model = engine._get_model(spec)

    items = []
    for gen in args.gens.split(","):
        reals = list_split(gen, "0_real"); fakes = list_split(gen, "1_fake")
        random.Random(0).shuffle(reals); random.Random(1).shuffle(fakes)
        per = max(1, args.n // len(args.gens.split(",")))
        items += [(p, 0) for p in reals[:per]] + [(p, 1) for p in fakes[:per]]
    random.Random(2).shuffle(items)
    print(f"{args.key}: scoring {len(items)} images from [{args.gens}] on {engine.device}")

    probs, labels = [], []
    for i in range(0, len(items), args.bs):
        chunk = items[i:i + args.bs]
        batch = torch.stack([spec.transform(Image.open(p).convert("RGB")) for p, _ in chunk]).to(engine.device)
        probs += engine._fake_prob(model, spec, batch).float().cpu().tolist()
        labels += [l for _, l in chunk]

    probs = np.array(probs); labels = np.array(labels)
    pred = (probs > 0.5).astype(int)
    acc = float((pred == labels).mean())
    racc = float((pred[labels == 0] == 0).mean())
    facc = float((pred[labels == 1] == 1).mean())
    apv, aucv = ap_auc(labels, probs)
    print(f"  acc@0.5={acc*100:.2f}  real_acc={racc*100:.2f}  fake_acc={facc*100:.2f}  "
          f"AP={apv*100:.2f}  AUC={aucv*100:.2f}  (mean P(fake)={probs.mean():.3f})")


if __name__ == "__main__":
    main()