{"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"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models\nfrom PIL import Image\nfrom tqdm.notebook import tqdm\nimport matplotlib.pyplot as plt\nimport time\n\n# --- CONFIGURATION ---\nBATCH_SIZE = 32      # Good balance for T4 GPU\nIMG_SIZE = 224       # Standard for Xception\nEPOCHS = 20          # Enough to learn, short enough to finish\nLEARNING_RATE = 0.0001 # Low rate for fine-tuning\n\n# --- DEVICE SETUP ---\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"✅ Device Selected: {device}\")\nif device.type == 'cuda':\n    print(f\"   GPU Name: {torch.cuda.get_device_name(0)}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-11-30T07:27:03.460497Z","iopub.execute_input":"2025-11-30T07:27:03.460693Z","iopub.status.idle":"2025-11-30T07:27:12.396319Z","shell.execute_reply.started":"2025-11-30T07:27:03.460667Z","shell.execute_reply":"2025-11-30T07:27:12.395675Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# We use the sample folder for the project demo (approx 400 videos)\nDATA_ROOT = \"/kaggle/input/deepfake-detection-challenge/train_sample_videos/\"\nMETADATA_PATH = os.path.join(DATA_ROOT, \"metadata.json\")\n\nprint(f\"📂 Loading metadata from: {METADATA_PATH}\")\n\ntry:\n    # Read JSON and transpose to DataFrame\n    df = pd.read_json(METADATA_PATH).T\n    df.reset_index(inplace=True)\n    df.rename(columns={'index': 'filename', 'label': 'label_str'}, inplace=True)\n    \n    # Create numeric label (FAKE=1, REAL=0)\n    df['label'] = df['label_str'].apply(lambda x: 1 if x == 'FAKE' else 0)\n    \n    # Create full path column\n    df['full_path'] = df['filename'].apply(lambda x: os.path.join(DATA_ROOT, x))\n    \n    # Verification\n    print(f\"✅ Metadata Loaded. Total Videos: {len(df)}\")\n    print(f\"   Fake Videos: {len(df[df['label'] == 1])}\")\n    print(f\"   Real Videos: {len(df[df['label'] == 0])}\")\n    \nexcept Exception as e:\n    print(f\"❌ Error loading metadata: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T07:28:04.777966Z","iopub.execute_input":"2025-11-30T07:28:04.778312Z","iopub.status.idle":"2025-11-30T07:28:04.883666Z","shell.execute_reply.started":"2025-11-30T07:28:04.778283Z","shell.execute_reply":"2025-11-30T07:28:04.882846Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DeepfakeDataset(Dataset):\n    def __init__(self, df, frames_per_video, transform=None):\n        self.df = df\n        self.frames_per_video = frames_per_video\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        video_path = self.df.loc[idx, 'full_path']\n        label = torch.tensor(self.df.loc[idx, 'label'], dtype=torch.float)\n        \n        frames = []\n        try:\n            cap = cv2.VideoCapture(video_path)\n            total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))\n            \n            if total_frames <= 0: return None # Skip bad videos\n            \n            # Smart Sampling: Get frames evenly distributed\n            indices = np.linspace(0, total_frames-1, self.frames_per_video, dtype=int)\n            \n            for i in indices:\n                cap.set(cv2.CAP_PROP_POS_FRAMES, i)\n                ret, frame = cap.read()\n                if not ret: continue\n                \n                # --- CENTER CROP (Focus on the face area) ---\n                h, w, _ = frame.shape\n                short_edge = min(h, w)\n                crop_size = int(short_edge * 0.75) # Take 75% of center\n                \n                center_x, center_y = w // 2, h // 2\n                x1 = center_x - crop_size // 2\n                y1 = center_y - crop_size // 2\n                \n                frame = frame[y1:y1+crop_size, x1:x1+crop_size]\n                \n                # Convert BGR (OpenCV) to RGB (PIL)\n                frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n                frame = Image.fromarray(frame)\n                \n                # Apply Transforms\n                if self.transform:\n                    frame = self.transform(frame)\n                \n                frames.append(frame)\n                \n            cap.release()\n        except Exception as e:\n            return None\n\n        if len(frames) == 0: return None\n\n        # Stack frames into a tensor [Sequence_Length, Channels, H, W]\n        return torch.stack(frames), label\n\n# --- COLLATE FUNCTION (Prevents crashes on bad videos) ---\ndef collate_fn(batch):\n    # Filter out None values\n    batch = [item for item in batch if item is not None]\n    if not batch: return torch.tensor([]), torch.tensor([])\n    \n    # Flatten the batch: We treat every frame as an independent image for training\n    all_faces = []\n    all_labels = []\n    \n    for faces, label in batch:\n        all_faces.extend(faces)\n        all_labels.extend([label] * len(faces))\n        \n    return torch.stack(all_faces), torch.stack(all_labels)\n\nprint(\"✅ DeepfakeDataset and Collate Function Defined.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T07:30:14.737593Z","iopub.execute_input":"2025-11-30T07:30:14.737984Z","iopub.status.idle":"2025-11-30T07:30:14.748863Z","shell.execute_reply.started":"2025-11-30T07:30:14.737960Z","shell.execute_reply":"2025-11-30T07:30:14.748180Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- BLOCK 4: TRANSFORMS & LOADERS ---\n# 1. AUGMENTATION (Training)\ntrain_transform = transforms.Compose([\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.RandomHorizontalFlip(p=0.5),\n    transforms.RandomRotation(degrees=15),\n    transforms.ColorJitter(brightness=0.2, contrast=0.2),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n])\n\n# 2. CLEAN (Validation)\nval_transform = transforms.Compose([\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n])\n\n# 3. SPLIT DATA\ntrain_df = df.sample(frac=0.8, random_state=42)\nval_df = df.drop(train_df.index)\n\n# 4. CREATE LOADERS\n# FRAMES_PER_VIDEO = 10 (Good balance)\ntrain_dataset = DeepfakeDataset(train_df.reset_index(drop=True), 10, train_transform)\nval_dataset = DeepfakeDataset(val_df.reset_index(drop=True), 10, val_transform)\n\n# SAFE MODE: num_workers=0 prevents the \"300% CPU\" deadlock\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=0, collate_fn=collate_fn)\nval_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=0, collate_fn=collate_fn)\n\nprint(f\"✅ Loaders Ready. Train Videos: {len(train_df)}, Val Videos: {len(val_df)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-30T07:30:32.083084Z","iopub.execute_input":"2025-11-30T07:30:32.083375Z","iopub.status.idle":"2025-11-30T07:30:32.093643Z","shell.execute_reply.started":"2025-11-30T07:30:32.083352Z","shell.execute_reply":"2025-11-30T07:30:32.092898Z"}},"outputs":[],"execution_count":null}]}