{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":16880,"databundleVersionId":858837,"sourceType":"competition"},{"sourceId":5380830,"sourceType":"datasetVersion","datasetId":3120670},{"sourceId":10125851,"sourceType":"datasetVersion","datasetId":6248577},{"sourceId":12998577,"sourceType":"datasetVersion","datasetId":8228126}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install --upgrade pillow\n!pip install timm==0.4.12\n!pip install facenet-pytorch==2.5.2\n!pip install torch==1.9.1+cpu torchvision==0.10.1+cpu torchaudio==0.9.1 -f https://download.pytorch.org/whl/torch_stable.html\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-09T13:07:32.375102Z","iopub.execute_input":"2025-09-09T13:07:32.375331Z","iopub.status.idle":"2025-09-09T13:09:07.516189Z","shell.execute_reply.started":"2025-09-09T13:07:32.375305Z","shell.execute_reply":"2025-09-09T13:09:07.515467Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os, zipfile, glob\nimport pandas as pd\nimport torch\n\n# ------------------------------\n# 0) Unzip the entire dataset\n# ------------------------------\nzip_path = \"/kaggle/input/ffc40-degraded/kaggle\"\nextract_dir = \"/kaggle/working/ffc40-degraded\"\n\nif not os.path.exists(extract_dir):\n    os.makedirs(extract_dir, exist_ok=True)\n    with zipfile.ZipFile(zip_path, 'r') as zf:\n        zf.extractall(extract_dir)\n    print(\"✅ Extracted zip\")\nelse:\n    print(\"📂 Already extracted\")\n\n# ------------------------------\n# 1) Check faces_cache path\n# ------------------------------\nfaces_cache_dir = \"/kaggle/input/ffc40-degraded/kaggle/working/ff-c40/faces_cache\"\nprint(\"Looking for .pt files in:\", faces_cache_dir)\n\n# ------------------------------\n# 2) Verify that files exist\n# ------------------------------\npt_files = glob.glob(os.path.join(faces_cache_dir, \"**/*.pt\"), recursive=True)\nprint(\"Number of .pt files found:\", len(pt_files))\n\n# If 0, the path is wrong → stop here\nif len(pt_files) == 0:\n    raise ValueError(\"No .pt files found! Check your path inside the zip.\")\n\n# ------------------------------\n# 3) Build DataFrame\n# ------------------------------\ndf = pd.DataFrame({\"face_path\": pt_files})\nprint(df.head())\n\n# ------------------------------\n# 4) Quick check: inspect one .pt\n# ------------------------------\n# Pick one .pt file\nsample_path = pt_files[0]\ndata = torch.load(sample_path)\n\n# Print full content safely\nprint(\"Type:\", type(data))\nif isinstance(data, dict):\n    print(\"Keys in dict:\", data.keys())\n    for k,v in data.items():\n        print(f\"Key: {k}, Type: {type(v)}, \", end=\"\")\n        if isinstance(v, torch.Tensor):\n            print(f\"Shape: {v.shape}\")\n        else:\n            print(f\"Value preview: {v}\")\nelse:\n    # maybe it's a tensor or something else\n    print(data)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-09T13:09:07.518064Z","iopub.execute_input":"2025-09-09T13:09:07.518297Z","iopub.status.idle":"2025-09-09T13:09:12.483698Z","shell.execute_reply.started":"2025-09-09T13:09:07.518273Z","shell.execute_reply":"2025-09-09T13:09:12.482785Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nimport glob, os, pandas as pd\n\n# -------------------------\n# Dataset\n# -------------------------\nclass CachedFaceDataset(Dataset):\n    def __init__(self, df, transform=None):\n        self.df = df\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        data = torch.load(self.df.iloc[idx][\"face_path\"])\n        img, label = data[\"face\"].float(), data[\"label\"]\n        if img.ndim == 3 and img.shape[0] != 3:\n            img = img.permute(2,0,1)\n        if self.transform:\n            img = self.transform(img)\n        return img, torch.tensor(label).long()\n\n# -------------------------\n# Build DataFrame & Split\n# -------------------------\nfaces_cache_dir = \"/kaggle/input/ffc40-degraded/kaggle/working/ff-c40/faces_cache\"\nfiles = glob.glob(os.path.join(faces_cache_dir, \"*.pt\"))\ndf = pd.DataFrame({\"face_path\": files})\ndf = df.sample(frac=1, random_state=42).reset_index(drop=True)\n\nnum_total = len(df)\nnum_train = int(0.8*num_total)\nnum_val   = int(0.1*num_total)\nnum_test  = num_total - num_train - num_val\n\ntrain_df = df.iloc[:num_train]\nval_df   = df.iloc[num_train:num_train+num_val]\ntest_df  = df.iloc[num_train+num_val:]\n\nprint(f\"Total: {num_total}, Train: {len(train_df)}, Val: {len(val_df)}, Test: {len(test_df)}\")\n\n# -------------------------\n# Dataloaders\n# -------------------------\nBATCH_SIZE = 8\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\ntf = transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])\ntrain_loader = DataLoader(CachedFaceDataset(train_df, transform=tf),\n                          batch_size=BATCH_SIZE, shuffle=True, num_workers=2)\nval_loader   = DataLoader(CachedFaceDataset(val_df, transform=tf),\n                          batch_size=BATCH_SIZE, shuffle=False, num_workers=2)\ntest_loader  = DataLoader(CachedFaceDataset(test_df, transform=tf),\n                          batch_size=BATCH_SIZE, shuffle=False, num_workers=2)\n\nimport torch.nn as nn\nimport torch.optim as optim\nimport timm\n\n# -------------------------\n# Model\n# -------------------------\nmodel = timm.create_model('xception', pretrained=True, num_classes=2).to(DEVICE)\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=1e-4)\n\nfrom tqdm import tqdm\nimport matplotlib.pyplot as plt\n\nNUM_EPOCHS = 10\n\ntrain_losses, val_losses = [], []\ntrain_accs, val_accs = [], []\n\nfor epoch in range(NUM_EPOCHS):\n    model.train()\n    running_loss, total, correct = 0.0, 0, 0\n    loop = tqdm(train_loader, desc=f\"Epoch [{epoch+1}/{NUM_EPOCHS}]\", leave=False)\n    for imgs, labels in loop:\n        imgs, labels = imgs.to(DEVICE), labels.to(DEVICE)\n        optimizer.zero_grad()\n        out = model(imgs)\n        loss = criterion(out, labels)\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item() * imgs.size(0)\n        preds = out.argmax(dim=1)\n        correct += (preds == labels).sum().item()\n        total += imgs.size(0)\n        \n        loop.set_postfix(loss=loss.item(), acc=correct/total)\n\n    epoch_loss = running_loss / total\n    epoch_acc  = correct / total\n    train_losses.append(epoch_loss)\n    train_accs.append(epoch_acc)\n\n    # -------------------------\n    # Validation\n    # -------------------------\n    model.eval()\n    val_loss, val_total, val_correct = 0.0, 0, 0\n    with torch.no_grad():\n        for imgs, labels in val_loader:\n            imgs, labels = imgs.to(DEVICE), labels.to(DEVICE)\n            out = model(imgs)\n            loss = criterion(out, labels)\n            val_loss += loss.item() * imgs.size(0)\n            preds = out.argmax(dim=1)\n            val_correct += (preds == labels).sum().item()\n            val_total += imgs.size(0)\n    \n    val_loss_epoch = val_loss / val_total\n    val_acc_epoch  = val_correct / val_total\n    val_losses.append(val_loss_epoch)\n    val_accs.append(val_acc_epoch)\n    \n    print(f\"Epoch {epoch+1}: Train Loss={epoch_loss:.4f}, Train Acc={epoch_acc:.4f} | Val Loss={val_loss_epoch:.4f}, Val Acc={val_acc_epoch:.4f}\")\n\nplt.figure(figsize=(12,5))\nplt.subplot(1,2,1)\nplt.plot(train_losses, label=\"Train Loss\")\nplt.plot(val_losses, label=\"Val Loss\")\nplt.title(\"Loss Curve\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend()\n\nplt.subplot(1,2,2)\nplt.plot(train_accs, label=\"Train Acc\")\nplt.plot(val_accs, label=\"Val Acc\")\nplt.title(\"Accuracy Curve\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")\nplt.legend()\nplt.show()\n\nfrom sklearn.metrics import confusion_matrix\nimport seaborn as sns\nimport numpy as np\n\nmodel.eval()\nall_preds, all_labels = [], []\nwith torch.no_grad():\n    for imgs, labels in test_loader:\n        imgs, labels = imgs.to(DEVICE), labels.to(DEVICE)\n        out = model(imgs)\n        preds = out.argmax(dim=1)\n        all_preds.extend(preds.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n\nacc = np.mean(np.array(all_preds) == np.array(all_labels))\ncm = confusion_matrix(all_labels, all_preds)\n\nprint(f\"Test Accuracy: {acc:.4f}\")\n\nplt.figure(figsize=(5,4))\nsns.heatmap(cm, annot=True, fmt='d', cmap=\"Blues\", xticklabels=[\"REAL\",\"FAKE\"], yticklabels=[\"REAL\",\"FAKE\"])\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"Actual\")\nplt.title(\"Test Confusion Matrix\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-09T13:10:09.744091Z","iopub.execute_input":"2025-09-09T13:10:09.744577Z","iopub.status.idle":"2025-09-09T13:13:52.008002Z","shell.execute_reply.started":"2025-09-09T13:10:09.744550Z","shell.execute_reply":"2025-09-09T13:13:52.007133Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}