Deepfake Detection Challenge: Baseline Implementation with EfficientNet

Data Preparation and Understanding

Dataset Structure

The competition provides training and validation datasets in the first phase. The training set (train_label.txt) is used for model training, while the validation set (val_label.txt) serves for hyperparameter tuning and model selection. Each line in these files contains two components: the image filename and its corresponding label (label=1 indicates a Deepfake image, label=0 indicates an authentic face image).

train_label.txt

img_name,target
3381ccbc4df9e7778b720d53a2987014.jpg,1
63fee8a89581307c0b4fd05a48e0ff79.jpg,0

val_label.txt

img_name,target
cd0e3907b3312f6046b98187fc25f9c7.jpg,1
aa92be19d0adf91a641301cfcce71e8a.jpg,0

In the second phase, the organizers release the test set. Participants submit prediction files containing probability scores for each test image.

prediction.txt

img_name,y_pred
cd0e3907b3312f6046b98187fc25f9c7.jpg,1
aa92be19d0adf91a641301cfcce71e8a.jpg,0.5

Environment Setup

!pip install timm

The timm library (PyTorch Image Models) provides access to over 600 pre-trained image recognition models including EfficientNet, ResNet, ViT, VGG, and MobileNet series. These pre-trained weights enable trensfer learning and feature extraction for downstream tasks.

Code Implementation

Import Dependencies

import torch
torch.manual_seed(0)
torch.backends.cudnn.deterministic = False
torch.backends.cudnn.benchmark = True

import torchvision.models as models
import torchvision.transforms as transforms
import torchvision.datasets as datasets
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
from torch.utils.data.dataset import Dataset
import timm
import time

import pandas as pd
import numpy as np
import cv2
from PIL import Image
from tqdm import tqdm_notebook

train_label = pd.read_csv('/kaggle/input/deepfake/phase1/trainset_label.txt')
val_label = pd.read_csv('/kaggle/input/deepfake/phase1/valset_label.txt')

train_label['path'] = '/kaggle/input/deepfake/phase1/trainset/' + train_label['img_name']
val_label['path'] = '/kaggle/input/deepfake/phase1/valset/' + val_label['img_name']

Utility Classes and Functions

The following class computes and stores running averages of metrics:

class MetricsTracker:
    """Computes and stores the average and current value"""
    def __init__(self, metric_name, fmt=':f'):
        self.metric_name = metric_name
        self.fmt = fmt
        self.reset()

    def reset(self):
        self.val = 0
        self.avg = 0
        self.sum = 0
        self.count = 0

    def update(self, value, batch_size=1):
        self.val = value
        self.sum += value * batch_size
        self.count += batch_size
        self.avg = self.sum / self.count

    def __str__(self):
        fmtstr = f'{self.metric_name} {self.val{self.fmt}} ({self.avg{self.fmt}})'
        return fmtstr.format(**self.__dict__)

The custom Dataset class handles image loading and preprocessing:

class FaceDataset(Dataset):
    def __init__(self, image_paths, labels, transform=None):
        self.image_paths = image_paths
        self.labels = labels
        self.transform = transform
    
    def __getitem__(self, index):
        img = Image.open(self.image_paths[index]).convert('RGB')
        
        if self.transform is not None:
            img = self.transform(img)
        
        return img, torch.from_numpy(np.array(self.labels[index]))
    
    def __len__(self):
        return len(self.image_paths)

Validation Function

This function evaluates model performance on the validation set, computing average loss and Top-1 accuracy:

def evaluate_model(val_loader, network, loss_fn):
    timer = MetricsTracker('Time', ':6.3f')
    loss_tracker = MetricsTracker('Loss', ':.4e')
    accuracy_tracker = MetricsTracker('Acc@1', ':6.2f')
    
    network.eval()
    
    with torch.no_grad():
        end = time.time()
        for batch_idx, (inputs, targets) in tqdm_notebook(enumerate(val_loader), total=len(val_loader)):
            inputs = inputs.cuda()
            targets = targets.cuda()

            outputs = network(inputs)
            loss = loss_fn(outputs, targets)

            acc = (outputs.argmax(1).view(-1) == targets.float().view(-1)).float().mean() * 100
            loss_tracker.update(loss.item(), inputs.size(0))
            accuracy_tracker.update(acc, inputs.size(0))
            timer.update(time.time() - end)
            end = time.time()

        print(f' * Acc@1 {accuracy_tracker.avg:.3f}')
        return accuracy_tracker

Prediction Function with Test-Time Augmentation

def generate_predictions(test_loader, network, num_augmentations=10):
    network.eval()
    
    predictions_accumulated = None
    for _ in range(num_augmentations):
        batch_predictions = []
        with torch.no_grad():
            for inputs, _ in tqdm_notebook(enumerate(test_loader), total=len(test_loader)):
                inputs = inputs.cuda()
                outputs = network(inputs)
                probabilities = F.softmax(outputs, dim=1)
                probabilities = probabilities.data.cpu().numpy()
                batch_predictions.append(probabilities)
        
        batch_predictions = np.vstack(batch_predictions)
        
        if predictions_accumulated is None:
            predictions_accumulated = batch_predictions
        else:
            predictions_accumulated += batch_predictions
    
    return predictions_accumulated

Training Loop

def train_network(train_loader, network, loss_fn, optimizer, epoch_num):
    timer = MetricsTracker('Time', ':6.3f')
    loss_tracker = MetricsTracker('Loss', ':.4e')
    accuracy_tracker = MetricsTracker('Acc@1', ':6.2f')

    network.train()

    end = time.time()
    for batch_idx, (inputs, targets) in enumerate(train_loader):
        inputs = inputs.cuda(non_blocking=True)
        targets = targets.cuda(non_blocking=True)

        outputs = network(inputs)
        loss = loss_fn(outputs, targets)

        loss_tracker.update(loss.item(), inputs.size(0))

        acc = (outputs.argmax(1).view(-1) == targets.float().view(-1)).float().mean() * 100
        accuracy_tracker.update(acc, inputs.size(0))

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        timer.update(time.time() - end)
        end = time.time()

        if batch_idx % 100 == 0:
            print(f'Batch {batch_idx}/{len(train_loader)}')

Model Initialization

import timm
network = timm.create_model('efficientnet_b1', pretrained=True, num_classes=2)
network = network.cuda()

total_epochs = 3
batch_size = 32

The num_classes=2 parameter configures the final fully connected layer for binary classification (authentic vs. Deepfake).

Data Loading and Training Pipeline

train_loader = torch.utils.data.DataLoader(
    FaceDataset(
        train_label['path'], 
        train_label['target'],
        transforms.Compose([
            transforms.Resize((256, 256)),
            transforms.RandomHorizontalFlip(),
            transforms.RandomVerticalFlip(),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
        ])
    ), 
    batch_size=batch_size, 
    shuffle=True, 
    num_workers=4, 
    pin_memory=True
)

val_loader = torch.utils.data.DataLoader(
    FaceDataset(
        val_label['path'], 
        val_label['target'],
        transforms.Compose([
            transforms.Resize((256, 256)),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
        ])
    ), 
    batch_size=batch_size, 
    shuffle=False, 
    num_workers=4, 
    pin_memory=True
)

criterion = nn.CrossEntropyLoss().cuda()
optimizer = torch.optim.Adam(network.parameters(), 0.005)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=4, gamma=0.85)
best_accuracy = 0.0

for epoch in range(total_epochs):
    scheduler.step()
    print(f'Epoch: {epoch}')
    
    train_network(train_loader, network, criterion, optimizer, epoch)
    val_accuracy = evaluate_model(val_loader, network, criterion)
    
    if val_accuracy.avg.item() > best_accuracy:
        best_accuracy = round(val_accuracy.avg.item(), 2)
        torch.save(network.state_dict(), f'./model_{best_accuracy}.pt')

The data augmentation techniques (random horizontal and vertical flips) help increase effective dataset size and reduce overfitting during training.

Generating Predictions

test_loader = torch.utils.data.DataLoader(
    FaceDataset(
        val_label['path'], 
        val_label['target'],
        transforms.Compose([
            transforms.Resize((256, 256)),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
        ])
    ), 
    batch_size=batch_size, 
    shuffle=False, 
    num_workers=4, 
    pin_memory=True
)

val_label['y_pred'] = generate_predictions(test_loader, network, 1)[:, 1]
val_label[['img_name', 'y_pred']].to_csv('submit.csv', index=None)

Model Performance

After running the baseline with EfficientNet-B1 and the default configuration, the initial accuracy reaches approximately 0.5774. After switching to efficientnet_b1-32-3-full-unbalanced with optimized data preprocessing, accuracy improved significant to 0.9812. This demonstrates the importance of model selection and hyperparameter tuning in deep learning tasks.

Tags: Deepfake Detection EfficientNet pytorch Computer Vision transfer learning

Posted on Tue, 14 Jul 2026 16:42:08 +0000 by Mr. R