{"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":59093,"databundleVersionId":7469972,"sourceType":"competition"}],"dockerImageVersionId":30919,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-03-18T14:11:43.138427Z","iopub.execute_input":"2025-03-18T14:11:43.138810Z","iopub.status.idle":"2025-03-18T14:12:40.931180Z","shell.execute_reply.started":"2025-03-18T14:11:43.138781Z","shell.execute_reply":"2025-03-18T14:12:40.930128Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install efficientnet_pytorch","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-18T14:12:40.932495Z","iopub.execute_input":"2025-03-18T14:12:40.932919Z","iopub.status.idle":"2025-03-18T14:12:49.686858Z","shell.execute_reply.started":"2025-03-18T14:12:40.932894Z","shell.execute_reply":"2025-03-18T14:12:49.685653Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\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 DataLoader, Dataset, random_split\nfrom torchvision import datasets, models, transforms\nfrom sklearn.model_selection import train_test_split\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nimport warnings\nimport time\nimport cv2\nwarnings.filterwarnings('ignore')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-18T14:12:49.688972Z","iopub.execute_input":"2025-03-18T14:12:49.689310Z","iopub.status.idle":"2025-03-18T14:13:00.102905Z","shell.execute_reply.started":"2025-03-18T14:12:49.689282Z","shell.execute_reply":"2025-03-18T14:13:00.101846Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"start_time = time.time()\nBASE_DIR = \"/kaggle/input/hms-harmful-brain-activity-classification/\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-18T14:13:00.104442Z","iopub.execute_input":"2025-03-18T14:13:00.105027Z","iopub.status.idle":"2025-03-18T14:13:00.109681Z","shell.execute_reply.started":"2025-03-18T14:13:00.104985Z","shell.execute_reply":"2025-03-18T14:13:00.108759Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"brain_activities = ['Seizure', 'GPD', 'LRDA', 'Other', 'GRDA', 'LPD']\nactivity_mapping = {activity: idx for idx, activity in enumerate(brain_activities)}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-18T14:13:00.110855Z","iopub.execute_input":"2025-03-18T14:13:00.111180Z","iopub.status.idle":"2025-03-18T14:13:00.157672Z","shell.execute_reply.started":"2025-03-18T14:13:00.111146Z","shell.execute_reply":"2025-03-18T14:13:00.156812Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(f\"{BASE_DIR}train.csv\")\n\ndf_toy = df.sample(frac=0.2, random_state=42)\n# Split 80% Train, 20% Temp (Validation + Test)\ntrain_df, temp_df = train_test_split(df_toy, test_size=0.4, random_state=42)\n\n# Split 10% Validation, 10% Test from Temp\nval_df, test_df = train_test_split(temp_df, test_size=0.5, random_state=42)\n\n# Save to CSV\ntrain_df.to_csv(\"train.csv\", index=False)\nval_df.to_csv(\"validation.csv\", index=False)\ntest_df.to_csv(\"test.csv\", index=False)\n\nprint(\"Splitting done! Train:\", len(train_df), \"Val:\", len(val_df), \"Test:\", len(test_df))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-18T14:18:59.406885Z","iopub.execute_input":"2025-03-18T14:18:59.407266Z","iopub.status.idle":"2025-03-18T14:18:59.548132Z","shell.execute_reply.started":"2025-03-18T14:18:59.407240Z","shell.execute_reply":"2025-03-18T14:18:59.547305Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ChunkedBrainActivityDataset(Dataset):\n    def __init__(self, csv_file, base_dir, activity_mapping,md):\n        self.df = csv_file\n        self.base_dir = base_dir\n        self.activity_mapping = activity_mapping\n        self.resize_transform = transforms.Resize((224, 224))\n        self.md = md\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        spect_id, label, offset = self.df.iloc[idx][[\"spectrogram_id\", \"expert_consensus\", \"spectrogram_label_offset_seconds\"]]\n\n        temp_df = pd.read_parquet(f'{self.base_dir}/train_spectrograms/{spect_id}.parquet')\n        temp_df.drop(['time'], axis=1, inplace=True)\n\n        start = int(offset) // 2\n        temp_df = temp_df[start:start+300]\n        temp_df = np.log1p(temp_df)\n        temp_df /= temp_df.max()\n        temp_arr = np.nan_to_num(temp_df.to_numpy(), nan=1e-4)\n\n        # Use OpenCV to apply a colormap and convert to RGB\n        temp_arr_uint8 = np.uint8(255 * temp_arr)\n        rgb_image = cv2.applyColorMap(temp_arr_uint8, cv2.COLORMAP_JET)\n\n        # Normalize to [0, 1] and convert to tensor\n        rgb_image = rgb_image.astype(np.float32) / 255.0\n        rgb_image_tensor = torch.tensor(rgb_image).permute(2, 0, 1)  # (C, H, W)\n        rgb_image_tensor = self.resize_transform(rgb_image_tensor)\n            \n        y = self.activity_mapping[label]\n        y_tensor = torch.nn.functional.one_hot(torch.tensor(y, dtype=torch.long), num_classes=6).float()\n        \n        return rgb_image_tensor, y_tensor","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-18T14:19:01.739001Z","iopub.execute_input":"2025-03-18T14:19:01.739351Z","iopub.status.idle":"2025-03-18T14:19:01.747939Z","shell.execute_reply.started":"2025-03-18T14:19:01.739324Z","shell.execute_reply":"2025-03-18T14:19:01.747037Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Now create DataLoader with the chunked dataset\n# chunk_size = 1000  # Adjust chunk size according to memory constraints\n\ntrain_dataset = ChunkedBrainActivityDataset(csv_file=train_df, base_dir=BASE_DIR, activity_mapping=activity_mapping,md = \"lr\")\nval_dataset = ChunkedBrainActivityDataset(csv_file=val_df, base_dir=BASE_DIR, activity_mapping=activity_mapping,md = \"lr\")\ntest_dataset = ChunkedBrainActivityDataset(csv_file=test_df, base_dir=BASE_DIR, activity_mapping=activity_mapping,md = \"lr\")\n\ntrain_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers= 2, pin_memory=True, prefetch_factor=2)\nval_loader = DataLoader(val_dataset, batch_size=64, shuffle=False, num_workers= 2, pin_memory=True, prefetch_factor=2)\ntest_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers= 2, pin_memory=True, prefetch_factor=2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-18T14:19:02.683348Z","iopub.execute_input":"2025-03-18T14:19:02.683640Z","iopub.status.idle":"2025-03-18T14:19:02.689359Z","shell.execute_reply.started":"2025-03-18T14:19:02.683617Z","shell.execute_reply":"2025-03-18T14:19:02.688366Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torchvision.models as models\nimport torchvision.transforms as transforms\nfrom torchvision.transforms.functional import to_pil_image\nfrom torch.utils.data import DataLoader\nimport numpy as np\nimport pandas as pd\n\nclass SimCLR(nn.Module):\n    def __init__(self, base_model, projection_dim=128):\n        super(SimCLR, self).__init__()\n        self.encoder = base_model\n        self.encoder.fc = nn.Identity()  # Remove the final fully connected layer\n        \n        # Projection head\n        self.projection = nn.Sequential(\n            nn.Linear(2048, 2048),\n            nn.ReLU(),\n            nn.Linear(2048, projection_dim)\n        )\n    \n    def forward(self, x):\n        h = self.encoder(x)\n        z = self.projection(h)\n        return h, z\n\nclass NTXentLoss(nn.Module):\n    def __init__(self, temperature=0.1, neg_pair_fraction=0.2):\n        super(NTXentLoss, self).__init__()\n        self.temperature = temperature\n        self.neg_pair_fraction = neg_pair_fraction\n    \n    def forward(self, out1, out2):\n        # Normalize the outputs\n        out1 = torch.nn.functional.normalize(out1, dim=1)\n        out2 = torch.nn.functional.normalize(out2, dim=1)\n        \n        # Compute similarity matrix\n        sim_matrix = torch.exp(torch.mm(out1, out2.T) / self.temperature)\n        \n        # Positive pairs are on the diagonal\n        pos_pairs = torch.diag(sim_matrix)\n        \n        # Select only a fraction of negative pairs\n        num_neg = int(self.neg_pair_fraction * (sim_matrix.size(1) - 1))\n        neg_pairs = torch.topk(sim_matrix, num_neg, dim=1, largest=False)[0].sum(dim=1)\n        \n        # Compute loss\n        loss = -torch.log(pos_pairs / neg_pairs).mean()\n        \n        return loss\n\n# Set device\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Instantiate the model and move it to the appropriate device\nbase_model = models.resnet50(pretrained=False)\nmodel = SimCLR(base_model).to(device)\n\n# Define the loss function and optimizer\ncriterion = NTXentLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=0.001)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-18T14:19:03.406383Z","iopub.execute_input":"2025-03-18T14:19:03.406693Z","iopub.status.idle":"2025-03-18T14:19:04.333039Z","shell.execute_reply.started":"2025-03-18T14:19:03.406670Z","shell.execute_reply":"2025-03-18T14:19:04.332276Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class RandomAugmentation(nn.Module):\n    def __init__(self):\n        super(RandomAugmentation, self).__init__()\n        self.augmentations = transforms.Compose([\n            transforms.RandomResizedCrop(224),\n            transforms.RandomHorizontalFlip(),\n            transforms.ColorJitter(0.4, 0.4, 0.4, 0.2),\n            transforms.ToTensor(),\n            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n        ])\n    \n    def forward(self, x):\n        if len(x.shape) == 4:\n            return torch.stack([self.augmentations(to_pil_image(img)) for img in x])\n        elif len(x.shape) == 3:\n            return self.augmentations(to_pil_image(x))\n        else:\n            raise ValueError(f\"Invalid input shape {x.shape}\")\n\n\nnum_epochs = 20\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    for images, _ in train_loader:\n        images = images.cpu()\n        aug1, aug2 = RandomAugmentation()(images), RandomAugmentation()(images)\n        aug1, aug2 = aug1.to(device), aug2.to(device)\n        _, z1 = model(aug1)\n        _, z2 = model(aug2)\n        loss = criterion(z1, z2)\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n        running_loss += loss.item() * images.size(0)\n    print(f\"Epoch {epoch+1}/{num_epochs}, Loss: {running_loss / len(train_loader.dataset):.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-18T14:25:44.421729Z","iopub.execute_input":"2025-03-18T14:25:44.422133Z","iopub.status.idle":"2025-03-18T14:31:16.688935Z","shell.execute_reply.started":"2025-03-18T14:25:44.422101Z","shell.execute_reply":"2025-03-18T14:31:16.687867Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Fine-tuning with logistic regression\nclass FineTuneModel(nn.Module):\n    def __init__(self, encoder, num_classes=6):\n        super(FineTuneModel, self).__init__()\n        self.encoder = encoder\n        self.logistic_regression = nn.Linear(2048, num_classes)\n    \n    def forward(self, x):\n        h = self.encoder(x)\n        logits = self.logistic_regression(h)\n        return logits\n\nfine_tune_model = FineTuneModel(model.encoder).to(device)\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(fine_tune_model.logistic_regression.parameters(), lr=0.001)\n\nnum_epochs = 20\nfor epoch in range(num_epochs):\n    fine_tune_model.train()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n    for images, targets in train_loader:\n        images = images.to(device)\n        targets = targets.to(device)\n        labels = torch.argmax(targets, dim=1)\n        \n        optimizer.zero_grad()\n        logits = fine_tune_model(images)\n        loss = criterion(logits, labels)\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item() * images.size(0)\n        _, preds = torch.max(logits, dim=1)\n        total += labels.size(0)\n        correct += (preds == labels).sum().item()\n    \n    epoch_loss = running_loss / total\n    epoch_acc = 100 * correct / total\n    print(f\"Epoch [{epoch+1}/{num_epochs}], Loss: {epoch_loss:.4f}, Accuracy: {epoch_acc:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-18T14:31:20.621020Z","iopub.execute_input":"2025-03-18T14:31:20.621390Z","iopub.status.idle":"2025-03-18T14:32:49.874059Z","shell.execute_reply.started":"2025-03-18T14:31:20.621362Z","shell.execute_reply":"2025-03-18T14:32:49.872836Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fine_tune_model.eval()\ntest_correct = 0\ntest_total = 0\nwith torch.no_grad():\n    for images, targets in test_loader:\n        images = images.to(device)\n        targets = targets.to(device)\n        labels = torch.argmax(targets, dim=1)\n        logits = fine_tune_model(images)\n        _, preds = torch.max(logits, dim=1)\n        test_total += labels.size(0)\n        test_correct += (preds == labels).sum().item()\n\ntest_accuracy = 100 * test_correct / test_total\nprint(f\"Test Accuracy: {test_accuracy:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-18T14:32:57.808208Z","iopub.execute_input":"2025-03-18T14:32:57.808572Z","iopub.status.idle":"2025-03-18T14:33:05.629797Z","shell.execute_reply.started":"2025-03-18T14:32:57.808543Z","shell.execute_reply":"2025-03-18T14:33:05.628786Z"}},"outputs":[],"execution_count":null}]}