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.