Spaces:
Sleeping
Sleeping
File size: 4,754 Bytes
b0c6daf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | """
Trust Meta-Classifier β validates primary deepfake detector's predictions.
Architecture: 516-dim input β 128 β 64 β 3 classes
Input features:
- 512-dim face embedding from InceptionResnetV1 backbone
- 2-dim one-hot encoded primary label (FAKE/REAL)
- 1-dim primary confidence (0.0β1.0)
- 1-dim normalized latency
Output classes:
0 = UNTRUSTED β primary prediction is likely wrong
1 = TRUSTED β primary prediction is reliable
2 = NEEDS_REVIEW β ambiguous, queue for human review
"""
import os
import torch
import torch.nn as nn
import torch.nn.functional as F
TRUST_VERDICTS = {0: "UNTRUSTED", 1: "TRUSTED", 2: "NEEDS_REVIEW"}
if os.path.exists("models/trust_meta.pt"):
TRUST_WEIGHTS_PATH = "models/trust_meta.pt"
else:
TRUST_WEIGHTS_PATH = os.path.join(os.path.dirname(__file__), "..", "models", "trust_meta.pt")
class TrustMetaClassifier(nn.Module):
"""
Lightweight MLP that evaluates whether the primary model's
prediction should be trusted, flagged, or sent for human review.
"""
def __init__(self, input_dim=516, hidden1=128, hidden2=64, num_classes=3):
super().__init__()
self.net = nn.Sequential(
nn.Linear(input_dim, hidden1),
nn.BatchNorm1d(hidden1),
nn.GELU(),
nn.Dropout(0.3),
nn.Linear(hidden1, hidden2),
nn.BatchNorm1d(hidden2),
nn.GELU(),
nn.Dropout(0.2),
nn.Linear(hidden2, num_classes),
)
def forward(self, x):
return self.net(x)
def build_trust_input(embedding, primary_pred_idx, confidence, latency_ms, device="cpu"):
"""
Constructs the 516-dim input tensor for the TrustMetaClassifier.
Args:
embedding: torch.Tensor of shape (512,) β face embedding from backbone
primary_pred_idx: int β 0 for FAKE, 1 for REAL
confidence: float β primary model's confidence (0.0β1.0)
latency_ms: float β inference latency in milliseconds
device: str β torch device
Returns:
torch.Tensor of shape (1, 516)
"""
# One-hot encode primary label
label_onehot = F.one_hot(
torch.tensor([primary_pred_idx], dtype=torch.long), num_classes=2
).float().to(device)
# Normalize latency: typical range 20-200ms β 0-1
latency_norm = min(latency_ms / 200.0, 1.0)
# Concatenate all features
trust_input = torch.cat([
embedding.unsqueeze(0) if embedding.dim() == 1 else embedding,
label_onehot,
torch.tensor([[confidence]], dtype=torch.float32).to(device),
torch.tensor([[latency_norm]], dtype=torch.float32).to(device),
], dim=1)
return trust_input
def load_trust_model(device="cpu"):
"""
Loads the trust meta-classifier from disk.
If no weights file exists (cold start), returns an untrained model
that will default to TRUSTED for all predictions.
Returns:
tuple: (model, cold_start: bool)
"""
trust_model = TrustMetaClassifier().to(device)
if os.path.exists(TRUST_WEIGHTS_PATH):
trust_model.load_state_dict(
torch.load(TRUST_WEIGHTS_PATH, map_location=device)
)
trust_model.eval()
print(f"[TRUST] Meta-classifier loaded from {TRUST_WEIGHTS_PATH}")
return trust_model, False
else:
trust_model.eval()
print("[TRUST] No weights found β running in COLD START mode (all predictions trusted)")
return trust_model, True
def predict_trust(trust_model, trust_input, cold_start=False):
"""
Runs the trust meta-classifier on the prepared input.
In cold-start mode (no training data yet), returns TRUSTED by default
so the system doesn't block predictions before any human review happens.
Args:
trust_model: TrustMetaClassifier instance
trust_input: torch.Tensor of shape (1, 516)
cold_start: bool β if True, bypass model and return TRUSTED
Returns:
dict with keys: trust_verdict (str), trust_score (float), cold_start (bool)
"""
if cold_start:
return {
"trust_verdict": "TRUSTED",
"trust_score": 1.0,
"cold_start": True,
}
with torch.no_grad():
logits = trust_model(trust_input)
probs = torch.softmax(logits, dim=1)[0]
verdict_idx = int(torch.argmax(probs))
score = float(probs[verdict_idx])
return {
"trust_verdict": TRUST_VERDICTS[verdict_idx],
"trust_score": round(score, 4),
"cold_start": False,
}
|