{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":92399,"databundleVersionId":11038207,"sourceType":"competition"},{"sourceId":295026,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":252624,"modelId":274092}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport tensorflow as tf\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Input, Conv3D, MaxPooling3D, Flatten, Dense, Dropout, concatenate\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint\nimport numpy as np\nimport pandas as pd\nimport cv2\nfrom tqdm import tqdm\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.metrics import confusion_matrix, classification_report","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-08T16:48:28.500158Z","iopub.execute_input":"2025-03-08T16:48:28.500640Z","iopub.status.idle":"2025-03-08T16:48:45.366917Z","shell.execute_reply.started":"2025-03-08T16:48:28.500603Z","shell.execute_reply":"2025-03-08T16:48:45.365617Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Enable mixed precision for better performance\ntf.keras.mixed_precision.set_global_policy('mixed_float16')\n\n# Multi-GPU Strategy\ntry:\n    strategy = tf.distribute.MirroredStrategy()\n    print(f\"Using {strategy.num_replicas_in_sync} GPUs for training!\")\nexcept:\n    strategy = tf.distribute.get_strategy()\n    print(\"Multi-GPU not available, using single GPU/CPU.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-08T16:48:45.368239Z","iopub.execute_input":"2025-03-08T16:48:45.368955Z","iopub.status.idle":"2025-03-08T16:48:45.414394Z","shell.execute_reply.started":"2025-03-08T16:48:45.368920Z","shell.execute_reply":"2025-03-08T16:48:45.413271Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_i3d_model(input_shape, num_classes):\n    inputs = Input(shape=input_shape)\n    \n    # First Conv3D block\n    x = Conv3D(64, kernel_size=(3, 3, 3), padding='same', activation='relu')(inputs)\n    x = MaxPooling3D(pool_size=(1, 2, 2))(x)\n    \n    # Second Conv3D block\n    x = Conv3D(128, kernel_size=(3, 3, 3), padding='same', activation='relu')(x)\n    x = MaxPooling3D(pool_size=(2, 2, 2))(x)\n    \n    # Third Conv3D block\n    x = Conv3D(256, kernel_size=(3, 3, 3), padding='same', activation='relu')(x)\n    x = MaxPooling3D(pool_size=(2, 2, 2))(x)\n    \n    # Flatten and Dense layers\n    x = Flatten()(x)\n    x = Dense(512, activation='relu')(x)\n    x = Dropout(0.5)(x)\n    outputs = Dense(num_classes, activation='softmax')(x)\n    \n    model = Model(inputs, outputs)\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-08T16:49:10.035926Z","iopub.execute_input":"2025-03-08T16:49:10.036290Z","iopub.status.idle":"2025-03-08T16:49:10.043893Z","shell.execute_reply.started":"2025-03-08T16:49:10.036259Z","shell.execute_reply":"2025-03-08T16:49:10.042473Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_two_stream_i3d_model(rgb_input_shape, flow_input_shape, num_classes):\n    # RGB stream\n    rgb_input = Input(shape=rgb_input_shape)\n    rgb_model = build_i3d_model(rgb_input_shape, num_classes)\n    rgb_output = rgb_model(rgb_input)\n    \n    # Optical flow stream\n    flow_input = Input(shape=flow_input_shape)\n    flow_model = build_i3d_model(flow_input_shape, num_classes)\n    flow_output = flow_model(flow_input)\n    \n    # Concatenate both streams\n    merged = concatenate([rgb_output, flow_output])\n    merged = Dense(512, activation='relu')(merged)\n    merged = Dropout(0.5)(merged)\n    outputs = Dense(num_classes, activation='softmax')(merged)\n    \n    model = Model([rgb_input, flow_input], outputs)\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-08T16:49:31.109549Z","iopub.execute_input":"2025-03-08T16:49:31.109973Z","iopub.status.idle":"2025-03-08T16:49:31.115985Z","shell.execute_reply.started":"2025-03-08T16:49:31.109940Z","shell.execute_reply":"2025-03-08T16:49:31.114905Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_video(video_path, frame_count=20, frame_size=(112, 112)):\n    cap = cv2.VideoCapture(video_path)\n    frames = []\n    \n    while len(frames) < frame_count:\n        ret, frame = cap.read()\n        if not ret:\n            break\n        frame = cv2.resize(frame, frame_size)\n        frame = frame.astype(np.float32) / 255.0  # Normalize to [0, 1]\n        frames.append(frame)\n    \n    cap.release()\n    \n    # Pad with black frames if video is shorter than frame_count\n    while len(frames) < frame_count:\n        frames.append(np.zeros((frame_size[0], frame_size[1], 3), dtype=np.float32))\n    \n    return np.array(frames)\n\n# Load optical flow data (you need to precompute optical flow)\ndef load_optical_flow(flow_path, frame_count=20, frame_size=(112, 112)):\n    flow = np.load(flow_path)\n    flow = flow[:frame_count]  # Truncate if longer than frame_count\n    flow = np.resize(flow, (frame_count, frame_size[0], frame_size[1], 2))  # Resize if necessary\n    return flow.astype(np.float32)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-08T16:49:45.694370Z","iopub.execute_input":"2025-03-08T16:49:45.694713Z","iopub.status.idle":"2025-03-08T16:49:45.702717Z","shell.execute_reply.started":"2025-03-08T16:49:45.694688Z","shell.execute_reply":"2025-03-08T16:49:45.701381Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_dataset(csv_path, video_folder, flow_folder, frame_count=20, frame_size=(112, 112)):\n    df = pd.read_csv(csv_path)\n    X_rgb, X_flow, Y = [], [], []\n    \n    for _, row in tqdm(df.iterrows(), total=len(df), desc=\"Loading dataset\"):\n        video_path = os.path.join(video_folder, f\"{int(row['id']):05d}.mp4\")\n        flow_path = os.path.join(flow_folder, f\"{int(row['id']):05d}.npy\")  # Assuming precomputed flow\n        \n        if os.path.exists(video_path) and os.path.exists(flow_path):\n            X_rgb.append(load_video(video_path, frame_count, frame_size))\n            X_flow.append(load_optical_flow(flow_path, frame_count, frame_size))\n            Y.append(row['target'])\n    \n    return np.array(X_rgb), np.array(X_flow), np.array(Y)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-08T16:49:54.168179Z","iopub.execute_input":"2025-03-08T16:49:54.168572Z","iopub.status.idle":"2025-03-08T16:49:54.175972Z","shell.execute_reply.started":"2025-03-08T16:49:54.168537Z","shell.execute_reply":"2025-03-08T16:49:54.174427Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os\nimport cv2\nfrom tqdm import tqdm\n\n# Define paths\ndataset_path = \"/kaggle/input/nexar-collision-prediction/\"\nvideo_folder = os.path.join(dataset_path, \"train\")\ncsv_path = os.path.join(dataset_path, \"train.csv\")\n\n# Load CSV file\ntrain_df = pd.read_csv(csv_path)\n\n# Convert 'id' to zero-padded filenames (to match video files)\ntrain_df[\"video_filename\"] = train_df[\"id\"].apply(lambda x: f\"{int(x):05d}.mp4\")\n\n# Drop missing videos\nexisting_videos = set(os.listdir(video_folder))\ntrain_df = train_df[train_df[\"video_filename\"].isin(existing_videos)].reset_index(drop=True)\n\nprint(f\"Dataset Size: {len(train_df)} videos\")\n\n# Load Video Function with Preprocessing\ndef load_video(video_path, frame_count=20, frame_size=(112, 112)):\n    cap = cv2.VideoCapture(video_path)\n    frames = []\n    \n    while len(frames) < frame_count:\n        ret, frame = cap.read()\n        if not ret:\n            break\n        frame = cv2.resize(frame, frame_size)\n        frame = frame.astype(np.float32) / 255.0  # Normalize\n        frames.append(frame)\n    \n    cap.release()\n    \n    # Pad with black frames if video is shorter than frame_count\n    while len(frames) < frame_count:\n        frames.append(np.zeros((frame_size[0], frame_size[1], 3), dtype=np.float32))\n    \n    return np.array(frames)\n\n# Load Full Dataset\ndef load_dataset():\n    X, Y = [], []\n    missing_videos = []\n    \n    for _, row in tqdm(train_df.iterrows(), total=len(train_df), desc=\"Loading videos\"):\n        video_path = os.path.join(video_folder, row[\"video_filename\"])\n        if os.path.exists(video_path):\n            X.append(load_video(video_path))\n            Y.append(row['target'])\n        else:\n            missing_videos.append(row['id'])\n    \n    if missing_videos:\n        print(f\"Warning: {len(missing_videos)} videos are missing.\")\n    \n    return np.array(X), np.array(Y)\n\n# Load dataset\nX, Y = load_dataset()\n\n# One-hot encode labels\nY = tf.keras.utils.to_categorical(Y, num_classes=2)\n\n# Verify dataset shapes\nprint(f\"X shape: {X.shape}\")\nprint(f\"Y shape: {Y.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-08T16:58:41.035611Z","iopub.execute_input":"2025-03-08T16:58:41.036097Z","iopub.status.idle":"2025-03-08T17:01:36.941783Z","shell.execute_reply.started":"2025-03-08T16:58:41.036061Z","shell.execute_reply":"2025-03-08T17:01:36.940220Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define input shapes\ninput_shape_rgb = (20, 112, 112, 3)\ninput_shape_flow = (20, 112, 112, 2)\nnum_classes = 2\n\n# Build the model\nwith strategy.scope():\n    model = build_two_stream_i3d_model(input_shape_rgb, input_shape_flow, num_classes)\n    model.compile(optimizer=Adam(learning_rate=0.0001), loss='binary_crossentropy', metrics=['accuracy'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-08T17:02:11.108195Z","iopub.execute_input":"2025-03-08T17:02:11.108635Z","iopub.status.idle":"2025-03-08T17:02:13.348942Z","shell.execute_reply.started":"2025-03-08T17:02:11.108603Z","shell.execute_reply":"2025-03-08T17:02:13.347732Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\n\n# Convert dataset to tf.data\ndef create_tf_dataset(X, Y, batch_size):\n    dataset = tf.data.Dataset.from_tensor_slices((X, Y))\n    dataset = dataset.shuffle(len(X)).batch(batch_size).prefetch(tf.data.experimental.AUTOTUNE)\n    return dataset\n\nbatch_size = 8  # Adjust based on your GPU memory\ntrain_dataset = create_tf_dataset(X, Y, batch_size)\n\n# Inspect the first batch\nfor batch in train_dataset.take(1):\n    X_batch, Y_batch = batch\n    print(f\"X_batch shape: {X_batch.shape}\")\n    print(f\"Y_batch shape: {Y_batch.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-08T17:06:20.760736Z","iopub.execute_input":"2025-03-08T17:06:20.761212Z","iopub.status.idle":"2025-03-08T17:06:39.295370Z","shell.execute_reply.started":"2025-03-08T17:06:20.761181Z","shell.execute_reply":"2025-03-08T17:06:39.294185Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install pytorchvideo\n!pip install --upgrade pip\n!pip install pytorchvideo --upgrade\n!pip install --upgrade torchvision","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-21T11:04:35.931016Z","iopub.execute_input":"2025-03-21T11:04:35.931291Z","iopub.status.idle":"2025-03-21T11:07:36.851290Z","shell.execute_reply.started":"2025-03-21T11:04:35.931270Z","shell.execute_reply":"2025-03-21T11:07:36.850238Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\nfrom PIL import Image\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, Dataset\n\nfrom torchvision.models import mobilenet_v2\nfrom torchvision.transforms import Compose, Resize, ToTensor, Normalize\n\n# === CONFIG ===\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"✅ Using device: {DEVICE}\")\n\n# Paths\nDATASET_PATH = \"/kaggle/input/nexar-collision-prediction\"\nTRAIN_CSV = os.path.join(DATASET_PATH, 'train.csv')\nVIDEO_FOLDER = os.path.join(DATASET_PATH, 'train')\n\n# Parameters\nINPUT_FRAMES = 16  # Video sequence length\nFRAME_SIZE = (224, 224)  # Resize frames to match MobileNetV2 input\nBATCH_SIZE = 16  # Increased batch size for multi-GPU\nEPOCHS = 10\nLEARNING_RATE = 1e-4\n\n# === DATA LOADING ===\nclass VideoDataset(Dataset):\n    def __init__(self, csv_path, video_folder, frame_count, frame_size):\n        self.data = pd.read_csv(csv_path)\n        self.video_folder = video_folder\n        self.frame_count = frame_count\n        self.frame_size = frame_size\n        self.transform = Compose([\n            Resize(frame_size),\n            ToTensor(),\n            Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # ImageNet normalization\n        ])\n\n    def __len__(self):\n        return len(self.data)\n\n    def load_video(self, video_path):\n        cap = cv2.VideoCapture(video_path)\n        frames = []\n        \n        while len(frames) < self.frame_count:\n            ret, frame = cap.read()\n            if not ret:\n                break\n\n            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)  # Convert BGR to RGB\n            frame = Image.fromarray(frame)  # Convert NumPy array to PIL Image\n            frame = self.transform(frame)  # Apply transforms\n            frames.append(frame)\n\n        cap.release()\n\n        # Pad with zero frames if needed\n        while len(frames) < self.frame_count:\n            frames.append(torch.zeros((3, *self.frame_size)))\n\n        return torch.stack(frames)  # (T, C, H, W)\n\n    def __getitem__(self, idx):\n        row = self.data.iloc[idx]\n        video_id = int(row['id'])\n        video_path = os.path.join(self.video_folder, f\"{video_id:05d}.mp4\")\n\n        if os.path.exists(video_path):\n            video = self.load_video(video_path)\n            label = torch.tensor(row['target'], dtype=torch.long)\n            return video, label\n        else:\n            empty_tensor = torch.zeros((self.frame_count, 3, *self.frame_size))\n            return empty_tensor, torch.tensor(row['target'], dtype=torch.long)\n\n# Create Dataset and DataLoader\ntrain_dataset = VideoDataset(TRAIN_CSV, VIDEO_FOLDER, INPUT_FRAMES, FRAME_SIZE)\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=4, pin_memory=True)\n\n# === MODEL ===\nclass MobileNetVideoClassifier(nn.Module):\n    def __init__(self):\n        super(MobileNetVideoClassifier, self).__init__()\n        self.backbone = mobilenet_v2(pretrained=True).features  # Extract features\n        self.pool = nn.AdaptiveAvgPool2d((1, 1))  # Global pooling\n        self.fc = nn.Linear(1280, 2)  # Final classification\n\n    def forward(self, x):\n        batch_size, seq_len, c, h, w = x.shape  # (B, T, C, H, W)\n        x = x.view(batch_size * seq_len, c, h, w)  # Merge batch & time\n        x = self.backbone(x)  # Feature extraction\n        x = self.pool(x)\n        x = x.view(batch_size, seq_len, -1)  # Reshape to (B, T, Features)\n        x = torch.mean(x, dim=1)  # Temporal pooling (average over time)\n        x = self.fc(x)\n        return x\n\n# Initialize model\nmodel = MobileNetVideoClassifier()\nif torch.cuda.device_count() > 1:\n    print(f\"✅ Using {torch.cuda.device_count()} GPUs\")\n    model = nn.DataParallel(model)  # Use multiple GPUs\nmodel.to(DEVICE)\n\n# === LOSS & OPTIMIZER ===\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)\n\n# === TRAINING FUNCTION ===\ndef train_model(model, dataloader, criterion, optimizer, epochs):\n    model.train()\n    \n    for epoch in range(epochs):\n        total_loss = 0\n        correct = 0\n        total = 0\n        \n        for videos, labels in tqdm(dataloader, desc=f\"Epoch {epoch + 1}/{epochs}\"):\n            videos, labels = videos.to(DEVICE), labels.to(DEVICE)\n\n            # Forward pass\n            outputs = model(videos)\n            loss = criterion(outputs, labels)\n\n            # Backward pass\n            optimizer.zero_grad()\n            loss.backward()\n            optimizer.step()\n\n            # Metrics\n            total_loss += loss.item()\n            _, preds = torch.max(outputs, 1)\n            correct += (preds == labels).sum().item()\n            total += labels.size(0)\n        \n        epoch_loss = total_loss / len(dataloader)\n        epoch_acc = correct / total\n\n        print(f\"Epoch [{epoch + 1}/{epochs}] - Loss: {epoch_loss:.4f} - Accuracy: {epoch_acc:.4f}\")\n\n# === TRAIN THE MODEL ===\ntrain_model(model, train_loader, criterion, optimizer, EPOCHS)\n\n# === SAVE MODEL ===\ntorch.save(model.state_dict(), \"mobilenet_collision_classifier.pth\")\nprint(\"✅ Model training complete and saved as mobilenet_collision_classifier.pth\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-21T11:07:49.096229Z","iopub.execute_input":"2025-03-21T11:07:49.096548Z","iopub.status.idle":"2025-03-21T11:39:53.828420Z","shell.execute_reply.started":"2025-03-21T11:07:49.096519Z","shell.execute_reply":"2025-03-21T11:39:53.827334Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport torch\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.metrics import confusion_matrix, classification_report\n\nfrom torchvision.models import mobilenet_v2\nfrom torchvision.transforms import Compose, Resize, ToTensor, Normalize\nfrom torch.utils.data import DataLoader, Dataset\nfrom PIL import Image\n\n# === CONFIG ===\nDEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'\nprint(f\"✅ Using device: {DEVICE}\")\n\n# Paths\nMODEL_PATH = \"/kaggle/working/mobilenet_collision_classifier_retrained.pth\"\nDATASET_PATH = \"/kaggle/input/nexar-collision-prediction\"\nVIDEO_FOLDER = os.path.join(DATASET_PATH, \"train\")\nTRAIN_CSV = os.path.join(DATASET_PATH, 'train.csv')\n\n# Parameters\nINPUT_FRAMES = 16\nFRAME_SIZE = (224, 224)\nBATCH_SIZE = 16\n\n# ✅ Check paths\nif not os.path.exists(MODEL_PATH):\n    raise FileNotFoundError(f\"🚨 Model file NOT found at {MODEL_PATH}\")\n\nif not os.path.exists(TRAIN_CSV):\n    raise FileNotFoundError(f\"🚨 train.csv NOT found at {TRAIN_CSV}\")\n\n# === DATA LOADING ===\nclass VideoDataset(Dataset):\n    def __init__(self, csv_path, video_folder, frame_count, frame_size):\n        self.data = pd.read_csv(csv_path).sample(frac=0.5, random_state=24).reset_index(drop=True)\n        self.video_folder = video_folder\n        self.frame_count = frame_count\n        self.frame_size = frame_size\n        self.transform = Compose([\n            Resize(frame_size),\n            ToTensor(),\n            Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # ImageNet normalization\n        ])\n\n    def __len__(self):\n        return len(self.data)\n\n    def load_video(self, video_path):\n        cap = cv2.VideoCapture(video_path)\n        frames = []\n\n        while len(frames) < self.frame_count:\n            ret, frame = cap.read()\n            if not ret:\n                break\n            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n            frame = Image.fromarray(frame)\n            frame = self.transform(frame)\n            frames.append(frame)\n\n        cap.release()\n\n        while len(frames) < self.frame_count:\n            frames.append(torch.zeros((3, *self.frame_size)))  # Pad with empty frames if needed\n\n        return torch.stack(frames)  # Shape: (T, C, H, W)\n\n    def __getitem__(self, idx):\n        row = self.data.iloc[idx]\n        video_id = int(row['id'])\n        video_path = os.path.join(self.video_folder, f\"{video_id:05d}.mp4\")\n\n        if os.path.exists(video_path):\n            video = self.load_video(video_path)\n            label = torch.tensor(row['target'], dtype=torch.long)\n            return video, label\n        else:\n            return torch.zeros((self.frame_count, 3, *self.frame_size)), torch.tensor(row['target'], dtype=torch.long)\n\n# ✅ Load evaluation dataset\neval_dataset = VideoDataset(TRAIN_CSV, VIDEO_FOLDER, INPUT_FRAMES, FRAME_SIZE)\neval_loader = DataLoader(eval_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=2)\n\n# === MODEL ===\nclass MobileNetVideoClassifier(torch.nn.Module):\n    def __init__(self):\n        super(MobileNetVideoClassifier, self).__init__()\n        self.backbone = mobilenet_v2(pretrained=False).features  # Same structure as training\n        self.pool = torch.nn.AdaptiveAvgPool2d((1, 1))\n        self.fc = torch.nn.Linear(1280, 2)  # Binary classification\n\n    def forward(self, x):\n        batch_size, seq_len, c, h, w = x.shape\n        x = x.view(batch_size * seq_len, c, h, w)  # Merge batch & time\n        x = self.backbone(x)  # Feature extraction\n        x = self.pool(x)\n        x = x.view(batch_size, seq_len, -1)  # Reshape to (B, T, Features)\n        x = torch.mean(x, dim=1)  # Temporal pooling (average over time)\n        x = self.fc(x)\n        return x\n\n# ✅ Load model\nmodel = MobileNetVideoClassifier().to(DEVICE)\n\n# ✅ Load trained weights\nstate_dict = torch.load(MODEL_PATH, map_location=DEVICE)\n\n# ✅ Remove 'module.' if trained with DataParallel\nnew_state_dict = {}\nfor k, v in state_dict.items():\n    new_k = k.replace('module.', '')  # Remove 'module.' prefix\n    new_state_dict[new_k] = v\n\nmodel.load_state_dict(new_state_dict)\nmodel.eval()\nprint(\"✅ Model loaded successfully.\")\n\n# === EVALUATION ===\nall_preds = []\nall_labels = []\n\nwith torch.no_grad():\n    correct = 0\n    total = 0\n    for videos, labels in eval_loader:\n        videos, labels = videos.to(DEVICE), labels.to(DEVICE)\n\n        # Forward pass\n        outputs = model(videos)\n        _, preds = torch.max(outputs, 1)\n\n        # Store results\n        all_preds.extend(preds.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n\n        # Accuracy calculation\n        correct += (preds == labels).sum().item()\n        total += labels.size(0)\n\n# ✅ Final accuracy\naccuracy = correct / total\nprint(f\"✅ Evaluation Accuracy: {accuracy:.4f}\")\n\n# ✅ Confusion matrix\nconf_matrix = confusion_matrix(all_labels, all_preds)\n\n# ✅ Plot confusion matrix\nplt.figure(figsize=(8, 6))\nsns.set(font_scale=1.2)\nsns.heatmap(conf_matrix, annot=True, fmt='d', cmap='Blues',\n            xticklabels=['No Crash', 'Crash'], yticklabels=['No Crash', 'Crash'])\nplt.xlabel(\"Predicted Label\", fontsize=12)\nplt.ylabel(\"True Label\", fontsize=12)\nplt.title(\"Confusion Matrix\", fontsize=14)\nplt.show()\n\n# ✅ Classification report\nprint(\"\\nClassification Report:\")\nprint(classification_report(all_labels, all_preds, target_names=['No Crash', 'Crash']))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-21T13:33:46.598745Z","iopub.execute_input":"2025-03-21T13:33:46.599095Z","iopub.status.idle":"2025-03-21T13:35:37.800468Z","shell.execute_reply.started":"2025-03-21T13:33:46.599055Z","shell.execute_reply":"2025-03-21T13:35:37.799284Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\nfrom PIL import Image\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import DataLoader, Dataset\nfrom torchvision.models import mobilenet_v2\nfrom torchvision.transforms import Compose, Resize, ToTensor, Normalize\n\n# === CONFIG ===\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"✅ Using device: {DEVICE}\")\n\n# Paths\nDATASET_PATH = \"/kaggle/input/nexar-collision-prediction\"\nTEST_CSV = os.path.join(DATASET_PATH, \"test.csv\")\nTEST_VIDEO_FOLDER = os.path.join(DATASET_PATH, \"test\")\nMODEL_PATH = \"/kaggle/input/mbnet-1/keras/default/1/mobilenet_collision_classifier.pth\"\n\n# Parameters\nINPUT_FRAMES = 16\nFRAME_SIZE = (224, 224)\nBATCH_SIZE = 16  \n\n# === DATA LOADING ===\nclass VideoDataset(Dataset):\n    def __init__(self, csv_path, video_folder, frame_count, frame_size):\n        self.data = pd.read_csv(csv_path)\n        self.video_folder = video_folder\n        self.frame_count = frame_count\n        self.frame_size = frame_size\n        self.transform = Compose([\n            Resize(frame_size),\n            ToTensor(),\n            Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n\n    def __len__(self):\n        return len(self.data)\n\n    def load_video(self, video_path):\n        cap = cv2.VideoCapture(video_path)\n        frames = []\n\n        while len(frames) < self.frame_count:\n            ret, frame = cap.read()\n            if not ret:\n                break\n\n            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n            frame = Image.fromarray(frame)\n            frame = self.transform(frame)\n            frames.append(frame)\n\n        cap.release()\n\n        # Pad with zero frames if needed\n        while len(frames) < self.frame_count:\n            frames.append(torch.zeros((3, *self.frame_size)))\n\n        return torch.stack(frames)  # (T, C, H, W)\n\n    def __getitem__(self, idx):\n        row = self.data.iloc[idx]\n        video_id = int(row[\"id\"])\n        video_path = os.path.join(self.video_folder, f\"{video_id:05d}.mp4\")\n\n        if os.path.exists(video_path):\n            video = self.load_video(video_path)\n        else:\n            video = torch.zeros((self.frame_count, 3, *self.frame_size))\n\n        return video, video_id\n\n# === MODEL ===\nclass MobileNetVideoClassifier(nn.Module):\n    def __init__(self):\n        super(MobileNetVideoClassifier, self).__init__()\n        self.backbone = mobilenet_v2(pretrained=True).features\n        self.pool = nn.AdaptiveAvgPool2d((1, 1))\n        self.fc = nn.Linear(1280, 2)  # Binary classification\n\n    def forward(self, x):\n        batch_size, seq_len, c, h, w = x.shape\n        x = x.view(batch_size * seq_len, c, h, w)\n        x = self.backbone(x)\n        x = self.pool(x)\n        x = x.view(batch_size, seq_len, -1)\n        x = torch.mean(x, dim=1)  # Temporal pooling\n        x = self.fc(x)\n        return x\n\n# === LOAD TRAINED MODEL ===\nmodel = MobileNetVideoClassifier()\n\n# ✅ Load state_dict with potential \"module.\" prefix removal\nstate_dict = torch.load(MODEL_PATH, map_location=DEVICE)\nnew_state_dict = {k.replace(\"module.\", \"\"): v for k, v in state_dict.items()}\nmodel.load_state_dict(new_state_dict, strict=False)\n\nmodel.to(DEVICE)\nmodel.eval()  # Set to evaluation mode\nprint(\"✅ Trained model loaded successfully!\")\n\n# === TEST DATA LOADING ===\ntest_dataset = VideoDataset(TEST_CSV, TEST_VIDEO_FOLDER, INPUT_FRAMES, FRAME_SIZE)\ntest_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=4, pin_memory=True)\n\n# === PREDICTION & SUBMISSION FILE CREATION ===\nsubmission_data = []\n\nwith torch.no_grad():\n    for videos, video_ids in tqdm(test_loader, desc=\"Generating Predictions\"):\n        videos = videos.to(DEVICE)\n\n        outputs = model(videos)\n        predictions = torch.argmax(outputs, dim=1).cpu().numpy()\n\n        for video_id, pred in zip(video_ids, predictions):\n            submission_data.append({\"id\": f\"{video_id:05d}\", \"target\": pred})\n\n# Create DataFrame and save CSV\nsubmission_df = pd.DataFrame(submission_data)\nsubmission_df.to_csv(\"submission.csv\", index=False)\nprint(\"✅ Submission file created: submission.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-21T13:59:59.329198Z","iopub.execute_input":"2025-03-21T13:59:59.329510Z","iopub.status.idle":"2025-03-21T14:02:27.530535Z","shell.execute_reply.started":"2025-03-21T13:59:59.329488Z","shell.execute_reply":"2025-03-21T14:02:27.529592Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\nfrom PIL import Image\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, Dataset\n\nfrom torchvision.models import mobilenet_v2\nfrom torchvision.transforms import Compose, Resize, ToTensor, Normalize\n\n# === CONFIG ===\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"✅ Using device: {DEVICE}\")\n\n# Paths\nDATASET_PATH = \"/kaggle/input/nexar-collision-prediction\"\nTRAIN_CSV = os.path.join(DATASET_PATH, 'train.csv')\nVIDEO_FOLDER = os.path.join(DATASET_PATH, 'train')\nPRETRAINED_MODEL_PATH = \"/kaggle/input/mbnet-1/keras/default/1/mobilenet_collision_classifier.pth\"  # ✅ Load pretrained model\n\n# Parameters\nINPUT_FRAMES = 16  \nFRAME_SIZE = (224, 224)  \nBATCH_SIZE = 16  \nEPOCHS = 20  \nLEARNING_RATE = 1e-4  \n\n# === DATA LOADING ===\nclass VideoDataset(Dataset):\n    def __init__(self, csv_path, video_folder, frame_count, frame_size):\n        self.data = pd.read_csv(csv_path)  \n        self.video_folder = video_folder\n        self.frame_count = frame_count\n        self.frame_size = frame_size\n        self.transform = Compose([\n            Resize(frame_size),\n            ToTensor(),\n            Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n\n    def __len__(self):\n        return len(self.data)\n\n    def load_video(self, video_path):\n        cap = cv2.VideoCapture(video_path)\n        frames = []\n        \n        while len(frames) < self.frame_count:\n            ret, frame = cap.read()\n            if not ret:\n                break\n\n            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n            frame = Image.fromarray(frame)\n            frame = self.transform(frame)\n            frames.append(frame)\n\n        cap.release()\n\n        # Pad with zero frames if needed\n        while len(frames) < self.frame_count:\n            frames.append(torch.zeros((3, *self.frame_size)))\n\n        return torch.stack(frames)  # (T, C, H, W)\n\n    def __getitem__(self, idx):\n        row = self.data.iloc[idx]\n        video_id = int(row['id'])\n        video_path = os.path.join(self.video_folder, f\"{video_id:05d}.mp4\")\n\n        if os.path.exists(video_path):\n            video = self.load_video(video_path)\n            label = torch.tensor(row['target'], dtype=torch.long)\n            return video, label\n        else:\n            empty_tensor = torch.zeros((self.frame_count, 3, *self.frame_size))\n            return empty_tensor, torch.tensor(row['target'], dtype=torch.long)\n\n# ✅ Use full dataset for training (no validation split)\ntrain_dataset = VideoDataset(TRAIN_CSV, VIDEO_FOLDER, INPUT_FRAMES, FRAME_SIZE)\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=4, pin_memory=True, drop_last=False)\n\n# === MODEL ===\nclass MobileNetVideoClassifier(nn.Module):\n    def __init__(self):\n        super(MobileNetVideoClassifier, self).__init__()\n        self.backbone = mobilenet_v2(pretrained=True).features\n        self.pool = nn.AdaptiveAvgPool2d((1, 1))\n        self.fc = nn.Linear(1280, 2)  # Binary classification\n\n    def forward(self, x):\n        batch_size, seq_len, c, h, w = x.shape\n        x = x.view(batch_size * seq_len, c, h, w)\n        x = self.backbone(x)\n        x = self.pool(x)\n        x = x.view(batch_size, seq_len, -1)\n        x = torch.mean(x, dim=1)  # Temporal pooling\n        x = self.fc(x)\n        return x\n\n# Initialize model\nmodel = MobileNetVideoClassifier()\n\n# ✅ Load pretrained model weights\nif os.path.exists(PRETRAINED_MODEL_PATH):\n    state_dict = torch.load(PRETRAINED_MODEL_PATH, map_location=DEVICE)\n    model.load_state_dict(state_dict, strict=False)  # Allow partial weight loading\n    print(\"✅ Pretrained model loaded successfully!\")\n\nif torch.cuda.device_count() > 1:\n    print(f\"✅ Using {torch.cuda.device_count()} GPUs\")\n    model = nn.DataParallel(model)\nmodel.to(DEVICE)\n\n# === LOSS & OPTIMIZER ===\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)\n\n# === TRAINING FUNCTION ===\ndef train_model(model, dataloader, criterion, optimizer, epochs):\n    model.train()\n    \n    for epoch in range(epochs):\n        total_loss = 0\n        correct = 0\n        total = 0\n        \n        for videos, labels in tqdm(dataloader, desc=f\"Epoch {epoch + 1}/{epochs}\"):\n            videos, labels = videos.to(DEVICE), labels.to(DEVICE)\n\n            # Forward pass\n            outputs = model(videos)\n            loss = criterion(outputs, labels)\n\n            # Backward pass\n            optimizer.zero_grad()\n            loss.backward()\n            optimizer.step()\n\n            # Metrics\n            total_loss += loss.item()\n            _, preds = torch.max(outputs, 1)\n            correct += (preds == labels).sum().item()\n            total += labels.size(0)\n        \n        epoch_loss = total_loss / len(dataloader)\n        epoch_acc = correct / total\n\n        print(f\"Epoch [{epoch + 1}/{epochs}] - Loss: {epoch_loss:.4f} - Accuracy: {epoch_acc:.4f}\")\n\n# === TRAIN THE MODEL ===\ntrain_model(model, train_loader, criterion, optimizer, EPOCHS)\n\n# === SAVE MODEL ===\ntorch.save(model.state_dict(), \"mobilenet_collision_classifier_retrained.pth\")\nprint(\"✅ Model retraining complete and saved as mobilenet_collision_classifier_retrained.pth\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-21T13:54:16.537167Z","iopub.execute_input":"2025-03-21T13:54:16.537612Z","iopub.status.idle":"2025-03-21T13:54:16.712478Z","shell.execute_reply.started":"2025-03-21T13:54:16.537583Z","shell.execute_reply":"2025-03-21T13:54:16.710698Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}