{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":16880,"databundleVersionId":858837,"sourceType":"competition"},{"sourceId":13305390,"sourceType":"datasetVersion","datasetId":6465615}],"dockerImageVersionId":29845,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# **Installing Nessecary Libraries, Pakages and Models**","metadata":{}},{"cell_type":"code","source":"import os\nfrom PIL import Image\nimport cv2\nimport random\nimport torch\nfrom torch.utils.data import DataLoader, TensorDataset\nimport torch.optim as optim\nimport torch.nn as nn\nfrom torchvision import models","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T20:19:15.273486Z","iopub.execute_input":"2025-10-12T20:19:15.273776Z","iopub.status.idle":"2025-10-12T20:19:16.821896Z","shell.execute_reply.started":"2025-10-12T20:19:15.273708Z","shell.execute_reply":"2025-10-12T20:19:16.821239Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **Path Loding**","metadata":{}},{"cell_type":"code","source":"# First dataset\ntrain_videos_path = '/kaggle/input/deepfake-detection-challenge/train_sample_videos'\nmetadata_path = '/kaggle/input/deepfake-detection-challenge/train_sample_videos/metadata.json'\ndestination_path = '/kaggle/working/metadata.json'\ntest_videos_path='/kaggle/input/deepfake-detection-challenge/test_videos'\ntrue_label_test='/kaggle/input/true-label-for-testvideo/true label.csv'\n# Second dataset\nsecond_dataset_path = '/kaggle/input/real-vs-fake-img/real-vs-fake'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T20:19:16.824138Z","iopub.execute_input":"2025-10-12T20:19:16.824397Z","iopub.status.idle":"2025-10-12T20:19:16.827934Z","shell.execute_reply.started":"2025-10-12T20:19:16.824349Z","shell.execute_reply":"2025-10-12T20:19:16.827280Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 1. Extract Frames from All 400 Videos:","metadata":{}},{"cell_type":"code","source":"import pandas as pd\n\nlabels_df = pd.read_csv('/kaggle/input/true-label-for-testvideo/metadata_converted.csv')  # Update with your actual CSV path\nvideo_to_label = dict(zip(labels_df['filenames'], labels_df['label(T=0/F=1)']))\n# Now video_to_label[\"aapnvogymq.mp4\"] gives 1 (fake) or 0 (real)\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T20:19:16.832621Z","iopub.execute_input":"2025-10-12T20:19:16.832838Z","iopub.status.idle":"2025-10-12T20:19:17.366375Z","shell.execute_reply.started":"2025-10-12T20:19:16.832793Z","shell.execute_reply":"2025-10-12T20:19:17.365254Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport os\nfrom tqdm import tqdm  # Add tqdm for progress\n\nvideo_dir = '/kaggle/input/deepfake-detection-challenge/train_sample_videos'\nframe_interval = 5  # Example: extract every 30th frame\n\n# Wrap the outer video loop with tqdm for overall progress\nfor video_filename, label in tqdm(video_to_label.items(), desc=\"Processing videos\", total=len(video_to_label)):\n    video_path = os.path.join(video_dir, video_filename)\n    class_name = 'fake' if label == 1 else 'real'\n    save_dir = f'/kaggle/working/training_frames/{class_name}'\n    os.makedirs(save_dir, exist_ok=True)\n    cap = cv2.VideoCapture(video_path)\n    frame_num = 0\n    frame_save_count = 0  # To count how many frames get saved\n    # Optional: tqdm in the frame loop only if you want (not strictly necessary if only a few frames will be saved)\n    while cap.isOpened():\n        ret, frame = cap.read()\n        if not ret:\n            break\n        if frame_num % frame_interval == 0:\n            frame_filename = f\"{os.path.splitext(video_filename)[0]}_frame{frame_num}.jpg\"\n            cv2.imwrite(os.path.join(save_dir, frame_filename), frame)\n            frame_save_count += 1\n        frame_num += 1\n    cap.release()\n    tqdm.write(f\"{video_filename}: saved {frame_save_count} frames to {class_name}\")\n\nprint(\"Frame extraction completed.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T20:19:17.367688Z","iopub.execute_input":"2025-10-12T20:19:17.368019Z","iopub.status.idle":"2025-10-12T20:42:13.564089Z","shell.execute_reply.started":"2025-10-12T20:19:17.367941Z","shell.execute_reply":"2025-10-12T20:42:13.563413Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport os\nfrom tqdm import tqdm\n\nvideo_dir = '/kaggle/input/deepfake-detection-challenge/test_videos'\nsave_dir = '/kaggle/working/test_frames'\nframe_interval = 5  # Extract every 5th frame\n\nos.makedirs(save_dir, exist_ok=True)\n\nfor video_filename in tqdm(os.listdir(video_dir), desc=\"Processing videos\"):\n    video_path = os.path.join(video_dir, video_filename)\n    cap = cv2.VideoCapture(video_path)\n    frame_num = 0\n    frame_save_count = 0\n    \n    while cap.isOpened():\n        ret, frame = cap.read()\n        if not ret:\n            break\n        if frame_num % frame_interval == 0:\n            frame_filename = f\"{os.path.splitext(video_filename)[0]}_frame{frame_num}.jpg\"\n            cv2.imwrite(os.path.join(save_dir, frame_filename), frame)\n            frame_save_count += 1\n        frame_num += 1\n    \n    cap.release()\n    tqdm.write(f\"{video_filename}: saved {frame_save_count} frames\")\n\nprint(\"Frame extraction completed.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T20:42:13.565337Z","iopub.execute_input":"2025-10-12T20:42:13.565561Z","iopub.status.idle":"2025-10-12T21:05:47.959225Z","shell.execute_reply.started":"2025-10-12T20:42:13.565523Z","shell.execute_reply":"2025-10-12T21:05:47.958473Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\n\nfolder_path1 = '/kaggle/working/training_frames/real'\nfolder_path2= '/kaggle/working/training_frames/fake'\nfolder_path3 = '/kaggle/working/test_frames'\n\njpg_count1 = len([file for file in os.listdir(folder_path1) if file.lower().endswith('.jpg')])\nprint(f'Total number of jpg files in training_frames/real/ Folder: {jpg_count1}')\n\njpg_count2 = len([file for file in os.listdir(folder_path2) if file.lower().endswith('.jpg')])\nprint(f'Total number of jpg files in training_frames/fake/ Folder: {jpg_count2}')\n\njpg_count3 = len([file for file in os.listdir(folder_path3) if file.lower().endswith('.jpg')])\nprint(f'Total number of jpg files in test_frames/ Folder: {jpg_count3}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T21:21:27.547823Z","iopub.execute_input":"2025-10-12T21:21:27.548092Z","iopub.status.idle":"2025-10-12T21:21:27.595802Z","shell.execute_reply.started":"2025-10-12T21:21:27.548054Z","shell.execute_reply":"2025-10-12T21:21:27.594980Z"},"_kg_hide-input":false},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **- Imports and paths**","metadata":{}},{"cell_type":"code","source":"from torchvision import datasets, transforms\nfrom torch.utils.data import DataLoader, Subset\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport os","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T21:22:37.439652Z","iopub.execute_input":"2025-10-12T21:22:37.439905Z","iopub.status.idle":"2025-10-12T21:22:37.444095Z","shell.execute_reply.started":"2025-10-12T21:22:37.439868Z","shell.execute_reply":"2025-10-12T21:22:37.443009Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"markdown","source":"# **- Paths and image size**","metadata":{}},{"cell_type":"code","source":"data_dir_train = '/kaggle/working/training_frames'\ndata_dir_test = '/kaggle/working/test_frames'\nimg_size = (224, 224)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T21:22:37.908569Z","iopub.execute_input":"2025-10-12T21:22:37.908947Z","iopub.status.idle":"2025-10-12T21:22:37.912860Z","shell.execute_reply.started":"2025-10-12T21:22:37.908872Z","shell.execute_reply":"2025-10-12T21:22:37.912140Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **- Transforms**","metadata":{}},{"cell_type":"code","source":"train_transform = transforms.Compose([\n    transforms.Resize(img_size),           # Resize to 224x224 pixels\n    transforms.RandomHorizontalFlip(),     # Randomly flip images left/right\n    transforms.ToTensor(),                 # Convert image to PyTorch tensor\n    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n])\nval_transform = transforms.Compose([\n    transforms.Resize(img_size),           # Resize for validation (no augmentation)\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T21:22:38.502968Z","iopub.execute_input":"2025-10-12T21:22:38.503228Z","iopub.status.idle":"2025-10-12T21:22:38.508068Z","shell.execute_reply.started":"2025-10-12T21:22:38.503183Z","shell.execute_reply":"2025-10-12T21:22:38.507389Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **- Mirror datasets (same folder, different transforms)**","metadata":{}},{"cell_type":"code","source":"train_dataset_aug   = datasets.ImageFolder(data_dir_train, transform=train_transform)\ntrain_dataset_plain = datasets.ImageFolder(data_dir_train, transform=val_transform)\nprint(\"Classes:\", train_dataset_aug.classes, \"Map:\", train_dataset_aug.class_to_idx)\nprint(\"Done\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T21:22:39.608094Z","iopub.execute_input":"2025-10-12T21:22:39.608376Z","iopub.status.idle":"2025-10-12T21:22:39.782996Z","shell.execute_reply.started":"2025-10-12T21:22:39.608336Z","shell.execute_reply":"2025-10-12T21:22:39.782300Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(torch.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T21:22:40.127839Z","iopub.execute_input":"2025-10-12T21:22:40.128104Z","iopub.status.idle":"2025-10-12T21:22:40.132136Z","shell.execute_reply.started":"2025-10-12T21:22:40.128067Z","shell.execute_reply":"2025-10-12T21:22:40.131368Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **- Deterministic 70/30 split into Subsets**","metadata":{}},{"cell_type":"code","source":"train_full_size = len(train_dataset_aug)\ntrain_size = int(0.70 * train_full_size)\nval_size   = train_full_size - train_size\n\n# fixed permutation of indices\ng = torch.Generator().manual_seed(42)\nindices = torch.randperm(train_full_size, generator=g).tolist()\ntrain_indices = indices[:train_size]\nval_indices   = indices[train_size:]\n\ntrain_subset = Subset(train_dataset_aug,   train_indices)   # augmentation\nval_subset   = Subset(train_dataset_plain, val_indices)     # no augmentation\nprint(f\"Train/Val sizes -> {len(train_subset)}/{len(val_subset)}\")\nprint(\"Done\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T21:22:41.108090Z","iopub.execute_input":"2025-10-12T21:22:41.108373Z","iopub.status.idle":"2025-10-12T21:22:41.116102Z","shell.execute_reply.started":"2025-10-12T21:22:41.108303Z","shell.execute_reply":"2025-10-12T21:22:41.115436Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **- Deterministic 70/30 split into Subsets**","metadata":{}},{"cell_type":"code","source":"batch_size = 32\ntrain_loader = DataLoader(train_subset, batch_size=batch_size, shuffle=True,  num_workers=2)\nval_loader   = DataLoader(val_subset,   batch_size=batch_size, shuffle=False, num_workers=2)\nprint(\"Done\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T21:27:04.219176Z","iopub.execute_input":"2025-10-12T21:27:04.219457Z","iopub.status.idle":"2025-10-12T21:27:04.224783Z","shell.execute_reply.started":"2025-10-12T21:27:04.219416Z","shell.execute_reply":"2025-10-12T21:27:04.223712Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **- Model setup**","metadata":{}},{"cell_type":"code","source":"import torchvision.models as models\nimport torch.nn as nn\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\nmodel = models.resnet18(pretrained=True)                 # Use weights trained on millions of images\nmodel.fc = nn.Linear(model.fc.in_features, 2)             # Output: 2 classes\nmodel = model.to(device)                                  # Move model to the GPU\n\n\nprint (\"Done\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T21:27:17.458696Z","iopub.execute_input":"2025-10-12T21:27:17.458959Z","iopub.status.idle":"2025-10-12T21:27:22.505239Z","shell.execute_reply.started":"2025-10-12T21:27:17.458922Z","shell.execute_reply":"2025-10-12T21:27:22.504475Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **- Loss, optimizer (AdamW), and optional scheduler**","metadata":{}},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4)\nscheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=10)\nprint(\"Done\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T21:29:45.439553Z","iopub.execute_input":"2025-10-12T21:29:45.439826Z","iopub.status.idle":"2025-10-12T21:29:45.445134Z","shell.execute_reply.started":"2025-10-12T21:29:45.439788Z","shell.execute_reply":"2025-10-12T21:29:45.444339Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **- Training loop with tqdm and best‑model saving**","metadata":{}},{"cell_type":"code","source":"from tqdm import tqdm\n\nnum_epochs = 10\nbest_acc = 0.0\npatience = 3\nno_improve = 0\nbest_path = \"/kaggle/working/best_model.pt\"\n\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    loop = tqdm(train_loader, total=len(train_loader), desc=f\"Epoch {epoch+1}/{num_epochs}\", unit=\"batch\")\n    for inputs, labels in loop:\n        inputs, labels = inputs.to(device), labels.to(device)\n        optimizer.zero_grad()\n        outputs = model(inputs)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        running_loss += loss.item()\n        loop.set_postfix(loss=loss.item())\n    avg_train_loss = running_loss / max(1, len(train_loader))\n\n    # validation\n    model.eval()\n    correct, total, val_loss = 0, 0, 0.0\n    with torch.no_grad():\n        vloop = tqdm(val_loader, total=len(val_loader), desc=\"Validation\", unit=\"batch\")\n        for inputs, labels in vloop:\n            inputs, labels = inputs.to(device), labels.to(device)\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n            val_loss += loss.item()\n            preds = outputs.argmax(1)\n            correct += (preds == labels).sum().item()\n            total += labels.size(0)\n    val_acc = 100.0 * correct / total\n    avg_val_loss = val_loss / max(1, len(val_loader))\n    print(f\"Epoch {epoch+1}: train_loss={avg_train_loss:.4f} val_loss={avg_val_loss:.4f} val_acc={val_acc:.2f}%\")\n\n    scheduler.step()\n\n    if val_acc > best_acc:\n        best_acc = val_acc\n        no_improve = 0\n        torch.save(model.state_dict(), best_path)\n    else:\n        no_improve += 1\n        if no_improve >= patience:\n            print(\"Early stopping.\")\n            break\n\nprint(f\"Best val_acc: {best_acc:.2f}% (saved to {best_path})\")\nprint(\"Done\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T23:05:20.617521Z","iopub.execute_input":"2025-10-12T23:05:20.617824Z","iopub.status.idle":"2025-10-12T23:05:28.534255Z","shell.execute_reply.started":"2025-10-12T23:05:20.617782Z","shell.execute_reply":"2025-10-12T23:05:28.532676Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **- Load best model and optional ONNX export (for faster CPU inference)**","metadata":{}},{"cell_type":"code","source":"# Load best weights for downstream test/inference\nmodel.load_state_dict(torch.load(best_path, map_location=device))\nmodel.eval()\nprint(\"Loaded best model.\")\n\n# Optional: export to ONNX\ndummy = torch.randn(1, 3, 224, 224, device=device)\nonnx_path = \"/kaggle/working/model.onnx\"\ntorch.onnx.export(model, dummy, onnx_path,\n                  input_names=[\"input\"], output_names=[\"logits\"],\n                  dynamic_axes={\"input\":{0:\"batch\"}, \"logits\":{0:\"batch\"}},\n                  )\nprint(f\"Exported ONNX to {onnx_path}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T23:08:10.397164Z","iopub.execute_input":"2025-10-12T23:08:10.397507Z","iopub.status.idle":"2025-10-12T23:08:11.031974Z","shell.execute_reply.started":"2025-10-12T23:08:10.397457Z","shell.execute_reply":"2025-10-12T23:08:11.031093Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **- Unlabeled test inference template**","metadata":{}},{"cell_type":"code","source":"from PIL import Image\n\nclass UnlabeledImageFolder(torch.utils.data.Dataset):\n    def __init__(self, root, transform=None):\n        self.root = root\n        self.transform = transform\n        self.files = [f for f in os.listdir(root) if f.lower().endswith(('.jpg','.jpeg','.png','.bmp','.tiff'))]\n        self.files.sort()\n    def __len__(self):\n        return len(self.files)\n    def __getitem__(self, idx):\n        fname = self.files[idx]\n        path = os.path.join(self.root, fname)\n        img = Image.open(path).convert(\"RGB\")\n        if self.transform:\n            img = self.transform(img)\n        return img, fname\n\ntest_dataset_unlabeled = UnlabeledImageFolder(data_dir_test, transform=val_transform)\ntest_loader = DataLoader(test_dataset_unlabeled, batch_size=32, shuffle=False, num_workers=2)\n\npred_rows = []\nwith torch.no_grad():\n    for imgs, names in tqdm(test_loader, total=len(test_loader), desc=\"Test Inference\"):\n        imgs = imgs.to(device)\n        logits = model(imgs)\n        probs = torch.softmax(logits, dim=1)\n        preds = probs.argmax(1).cpu().tolist()\n        confs = probs.max(1).values.cpu().tolist()\n        for n, p, c in zip(names, preds, confs):\n            pred_rows.append((n, p, c))\n\nimport pandas as pd\npd.DataFrame(pred_rows, columns=[\"filename\",\"pred_label(0=real,1=fake)\",\"confidence\"]).to_csv(\"/kaggle/working/test_preds.csv\", index=False)\nprint(\"Saved /kaggle/working/test_preds.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T23:10:10.734601Z","iopub.execute_input":"2025-10-12T23:10:10.734902Z","iopub.status.idle":"2025-10-12T23:17:46.147889Z","shell.execute_reply.started":"2025-10-12T23:10:10.734862Z","shell.execute_reply":"2025-10-12T23:17:46.146922Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # Correct way to use f-string with torch.save filename\n# torch.save(model.state_dict(), f'deepfake_resnet18ValAcc{val_acc:.2f}.pth')\n# print(f'Model saved as deepfake_resnet18ValAcc{val_acc:.2f}.pth')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T21:29:48.116055Z","iopub.status.idle":"2025-10-12T21:29:48.116537Z","shell.execute_reply":"2025-10-12T21:29:48.116304Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}