File size: 4,545 Bytes
534b24f
 
 
 
 
 
 
44db0f5
534b24f
 
44db0f5
534b24f
 
d4cc7bd
 
 
 
 
 
 
 
 
 
 
 
 
534b24f
 
 
 
44db0f5
534b24f
 
 
 
 
44db0f5
 
 
 
534b24f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44db0f5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
534b24f
 
 
d4cc7bd
 
 
534b24f
 
 
 
d4cc7bd
 
534b24f
44db0f5
534b24f
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
---
license: mit
tags: [deepfake-detection, image-classification, forensics, tensorflow, keras]
library_name: keras
pipeline_tag: image-classification
---

# OpenForensics Deepfake Detector (v2)

A multi-backbone CNN ensemble that classifies face crops as **Real** or
**Fake**. Backbones: resnet50, vgg16, efficientnetv2b0. Their pooled embeddings are
concatenated and read by a shared classifier head.

> ### ⚠️ Scope: face swaps, not AI-generated images
>
> Trained on OpenForensics, where a fake is a GAN face **Poisson-blended into
> a real photograph**. The model therefore looks for the *seam* left by that
> compositing. An image generated whole by a diffusion model — ChatGPT,
> Gemini, Midjourney, Stable Diffusion — has no seam, and this model calls it
> genuine: measured on ChatGPT and Gemini output, **10 of 10 were classified
> as Real, most scoring above 0.999**.
>
> **A high `probability_real` is not evidence that an image is not
> AI-generated.** For that you need a synthetic-image detector, which is a
> different problem with different features.

## Output

A single sigmoid: **P(Real)**. Fake is `1 - p`.

Decision threshold **0.362** and temperature **0.876** were fitted on a held-out validation split (target_recall criterion) and are carried in `serving.json`.

## Test metrics

| Metric | Value |
|---|---|
| Accuracy | 0.9480 |
| ROC-AUC | 0.9899 |
| PR-AUC | 0.9900 |
| Real images called fake | 25 (2.5%) |

Measured on a held-out test split with horizontal-flip test-time
augmentation. The split is content-hash deduplicated against train and
validation, so no image appears in more than one split.

## Input

Resize to 224x224, scale to `[0, 1]`, shape `(N, 224, 224, 3)` float32.
Per-backbone normalisation happens **inside** the model — do not apply
`preprocess_input` yourself.

```python
from huggingface_hub import snapshot_download
import tensorflow as tf, numpy as np, json
from PIL import Image

path = snapshot_download("adarshcod30/openforensics-ensemble")
model = tf.keras.models.load_model(f"{path}/model.keras", compile=False)
card = json.load(open(f"{path}/serving.json"))

img = Image.open("face.jpg").convert("RGB").resize((224, 224))
x = np.asarray(img, dtype="float32")[None] / 255.0
p = float(model.predict(x)[0, 0])
print("Real" if p >= card["decision"]["threshold"] else "Fake", p)
```

Loading needs the `PreprocessLayer` custom layer from
[the repo](https://github.com/adarshcod30/OpenForensics), or pass it via
`custom_objects`.

## Training data

The face-cropped OpenForensics distribution (190,334 images at 256x256).
Training used corruption-matched augmentation — desaturation, colour cast, noise, speckle, blur, JPEG artefacts, pixelation, brightness shift and occlusion — because the test split is measurably more degraded than train.


## Robustness

Accuracy with a single degradation family applied to the whole test set, one at a time.

| Degradation | Accuracy | ROC-AUC | vs clean |
|---|---|---|---|
| clean | 0.9405 | 0.9899 | — |
| desaturate | 0.9255 | 0.9874 | -0.0150 |
| colour_cast | 0.9230 | 0.9873 | -0.0175 |
| gaussian_noise | 0.9105 | 0.9827 | -0.0300 |
| speckle | 0.8745 | 0.9786 | -0.0660 |
| blur | 0.9025 | 0.9798 | -0.0380 |
| jpeg_artifact | 0.9260 | 0.9854 | -0.0145 |
| pixelate | 0.8725 | 0.9623 | -0.0680 |
| brightness_shift | 0.9210 | 0.9865 | -0.0195 |
| occlusion | 0.9300 | 0.9856 | -0.0105 |

## Limitations

- **Face swaps only.** See the scope notice above: fully AI-generated images
  are outside what this model can detect, and it reports them as genuine with
  high confidence.
- Trained on **face crops**. Behaviour on full scenes or non-face images is
  undefined.
- A score near the threshold is not evidence. Treat the margin as part of
  the output.
- Performance degrades on manipulation methods absent from OpenForensics,
  which was released in 2021 and predates current diffusion generators.
- Research and educational use. Not a forensic authority.
- **Validation tracks test closely.** Recall on genuine images at threshold 0.5 is 0.986 on validation and 0.961 on test — a gap of 0.025. The 10th percentile of scores on genuine images is 0.977 and 0.830 respectively, so the operating point fitted on validation transfers. This is a property of the corruption-matched augmentation, not of the benchmark.

## Citation

> Trung-Nghia Le, Huy H. Nguyen, Junichi Yamagishi, Isao Echizen,
> "OpenForensics: Large-Scale Challenging Dataset For Multi-Face Forgery
> Detection And Segmentation In-The-Wild", ICCV 2021.