HaseebArif11's picture
feat: modular v2.0 - EfficientNet-B3 + MTCNN face detection
98b9d75
Raw History Blame Contribute Delete
3.7 kB
import os
import shutil
import tempfile
import torch
import torch.nn as nn
import torchvision.models as models
def build_efficientnet_b3(num_classes=2, dropout=0.4, pretrained=True):
"""
Constructs EfficientNet-B3 model architecture for binary DeepFake detection.
Class order: 0 = fake / AI-generated, 1 = real / authentic.
Default classifier matches saved checkpoint architecture:
Dropout(0.4) -> Linear(1536, 512) -> SiLU() -> Dropout(0.4) -> Linear(512, 2)
"""
if pretrained:
try:
weights = models.EfficientNet_B3_Weights.DEFAULT
model = models.efficientnet_b3(weights=weights)
except Exception:
model = models.efficientnet_b3(weights="IMAGENET1K_V1")
else:
model = models.efficientnet_b3(weights=None)
in_features = model.classifier[1].in_features
model.classifier = nn.Sequential(
nn.Dropout(p=dropout, inplace=True),
nn.Linear(in_features, 512),
nn.SiLU(inplace=True),
nn.Dropout(p=dropout, inplace=True),
nn.Linear(512, num_classes)
)
return model
def load_model_weights(model: nn.Module, weights_path: str, device: torch.device) -> nn.Module:
"""
Loads saved model weights from a .pth file, PyTorch model checkpoint, or unzipped directory.
"""
if not os.path.exists(weights_path):
raise FileNotFoundError(f"Model weights path does not exist: {weights_path}")
loaded_obj = None
# Handle directory format (e.g. unzipped PyTorch archive folder)
if os.path.isdir(weights_path):
import zipfile
temp_pth = os.path.join(tempfile.gettempdir(), "temp_model_archive.pth")
try:
with zipfile.ZipFile(temp_pth, 'w', compression=zipfile.ZIP_STORED) as z:
for root, _, files in os.walk(weights_path):
for file in files:
full_path = os.path.join(root, file)
rel_path = os.path.relpath(full_path, weights_path)
arc_name = os.path.join("archive", rel_path).replace("\\", "/")
z.write(full_path, arcname=arc_name)
try:
loaded_obj = torch.load(temp_pth, map_location=device, weights_only=True)
except Exception:
loaded_obj = torch.load(temp_pth, map_location=device, weights_only=False)
finally:
if os.path.exists(temp_pth):
os.remove(temp_pth)
else:
# File path
try:
loaded_obj = torch.load(weights_path, map_location=device, weights_only=True)
except Exception:
loaded_obj = torch.load(weights_path, map_location=device, weights_only=False)
state_dict = loaded_obj
if isinstance(loaded_obj, nn.Module):
model = loaded_obj
model = model.to(device)
model.eval()
return model
elif isinstance(loaded_obj, dict):
if "state_dict" in loaded_obj:
state_dict = loaded_obj["state_dict"]
elif "model_state_dict" in loaded_obj:
state_dict = loaded_obj["model_state_dict"]
# Adapt classifier if state_dict has single-layer classifier (classifier.1.weight shape [2, 1536])
if "classifier.1.weight" in state_dict and state_dict["classifier.1.weight"].shape == torch.Size([2, 1536]):
in_features = model.classifier[1].in_features if hasattr(model.classifier[1], 'in_features') else 1536
model.classifier = nn.Sequential(
nn.Dropout(p=0.4, inplace=True),
nn.Linear(in_features, 2)
)
model.load_state_dict(state_dict)
model = model.to(device)
model.eval()
return model