--- language: en tags: - image-classification - ai-generated-image-detection - pytorch - torchscript - deepfake-detection - computer-vision library_name: pytorch pipeline_tag: image-classification --- # TruthLens — AI Image Authenticity Detector Classifies images as **AI-generated (FAKE)** or **authentic (REAL)** using a multi-branch convolutional neural network that analyses spatial, frequency, and noise-level features simultaneously. The model is exported as a self-contained **TorchScript** file — no architecture definition is required to run it. --- ## Model Details | Property | Value | |---|---| | Architecture | `ImprovedDetector` (multi-branch CNN) | | Task | Binary image classification (FAKE / REAL) | | Input | RGB image, resized to **32 × 32**, normalized to `[-1, 1]` | | Output | `(logit, gates)` — scalar logit + 3-weight gate vector | | Decision threshold | `sigmoid(logit) > 0.5` → **FAKE** | | Format | TorchScript (`.pt`) | --- ## Architecture The model is built around three independent branches that each look for a different class of evidence, feeding into a shared gated fusion layer. ### Spatial Branch Processes raw pixel data through a stem convolution followed by three residual blocks. Looks for visual artifacts, unnatural textures, and lighting inconsistencies that generative models leave behind. ### Frequency Branch Operates in the frequency domain by computing the **FFT magnitude**, **FFT phase**, and a **DCT approximation** of the input — each processed by its own convolutional block. Detects the spectral fingerprints that generative models produce but real camera sensors don't. ### Noise Branch Applies a fixed high-pass filter to isolate the high-frequency noise layer of the image, then analyses it with a small CNN. Real photographs carry consistent sensor noise; AI images tend to have suspiciously smooth or periodically structured noise patterns. ### Gated Fusion All three branch embeddings are concatenated and passed through a two-layer gate network with a **Softmax output**, producing three scalar weights that sum to 1.0. These weights are used to compute a weighted sum of projected branch features before the final classifier. The gate weights are returned alongside the logit, giving per-image explainability. ``` Input image (3 × 32 × 32) │ ┌────┴────────────────────┐ │ │ │ Spatial Frequency Noise (512-d) (512-d) (256-d) │ │ │ └────────────┴────────────┘ │ Gated Fusion (learned weights → 512-d) │ Classifier (512 → 256 → 64 → 1) │ logit ``` --- ## Usage ### Direct inference (Python) ```python import torch from PIL import Image import torchvision.transforms as T from huggingface_hub import hf_hub_download # Load model path = hf_hub_download("Medsa/ai-image-authenticity-detector", "detector_scripted.pt") model = torch.jit.load(path, map_location="cpu") model.eval() # Preprocess transform = T.Compose([ T.Resize((32, 32)), T.ToTensor(), T.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]), ]) img = Image.open("photo.jpg").convert("RGB") tensor = transform(img).unsqueeze(0) # (1, 3, 32, 32) # Inference with torch.no_grad(): logit, gates = model(tensor) fake_prob = torch.sigmoid(logit).item() verdict = "FAKE" if fake_prob > 0.5 else "REAL" print(f"Verdict : {verdict}") print(f"Fake prob : {fake_prob:.4f}") print(f"Real prob : {1 - fake_prob:.4f}") print(f"Gates : spatial={gates[0,0]:.3f} freq={gates[0,1]:.3f} noise={gates[0,2]:.3f}") ``` ### Via the TruthLens API The model is also served by the [TruthLens](https://github.com/papi386/TruthLens-AI-Image-Authenticity-Detector) web application: ```bash curl -X POST http://localhost:5000/predict \ -F "files=@photo.jpg" ``` --- ## Output Format The model returns a tuple `(logit, gates)`: | Output | Shape | Description | |---|---|---| | `logit` | `(1,)` | Raw pre-sigmoid score. Pass through `sigmoid()` to get `fake_prob`. | | `gates` | `(1, 3)` | Softmax-normalized branch weights: `[spatial, frequency, noise]` | ```python fake_prob = torch.sigmoid(logit).item() # probability image is AI-generated real_prob = 1 - fake_prob # probability image is authentic spatial_weight = gates[0, 0].item() frequency_weight = gates[0, 1].item() noise_weight = gates[0, 2].item() ``` Gate weights indicate which branch was most influential for a given image. They always sum to 1.0. --- ## Preprocessing The model expects images preprocessed exactly as follows: ```python transforms.Compose([ transforms.Resize((32, 32)), transforms.ToTensor(), # scales to [0, 1] transforms.Normalize([0.5, 0.5, 0.5], # shifts to [-1, 1] [0.5, 0.5, 0.5]), ]) ``` Inputs that are not normalized to `[-1, 1]` will produce unreliable results. --- ## Files | File | Description | |---|---| | `detector_scripted.pt` | TorchScript model — load with `torch.jit.load()` | --- ## Training **Dataset:** [CIFAKE](https://huggingface.co/datasets/birdy654/cifake-real-and-ai-generated-synthetic-images) — a balanced dataset of real CIFAR-10 images and AI-generated counterparts at 32 × 32 resolution. **Optimizer:** AdamW (`lr=3e-4`, `weight_decay=1e-4`) with cosine annealing down to `1e-6` over 50 epochs, batch size 64, gradient clipping at norm 1.0. Early stopping with patience 5. **Data augmentation (train only):** random horizontal/vertical flips, color jitter (brightness, contrast, saturation), and small random rotation (±10°). **Loss function:** standard BCE plus a gate diversity penalty to prevent branch collapse — an issue where the gate learns to ignore one or more branches entirely and routes everything through a single one: ``` L = BCE(logit, label) − λ · H(gates) ``` where `H(gates)` is the mean gate entropy across the batch. Maximising entropy keeps all three branches actively contributing. The weight λ starts at 0.3 and decays by 0.9 per epoch (floored at 0.05), so the penalty is strong early on to force branch diversity, then relaxes as the gate is allowed to specialise. --- ## Limitations - Input resolution is fixed at **32 × 32**. Fine spatial detail is lost; the model relies on statistical patterns rather than high-frequency pixel-level artifacts. - Performance may degrade on image types significantly underrepresented in training data (e.g. medical images, satellite imagery). - Like all detectors, it can be fooled by adversarial post-processing (JPEG compression, noise injection, resizing pipelines).