{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.14"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":11848,"databundleVersionId":862157,"sourceType":"competition"}],"dockerImageVersionId":30787,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom glob import glob\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import roc_auc_score, roc_curve\nfrom PIL import Image\n\nimport torch\nfrom torch import nn, optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T23:13:24.649192Z","iopub.execute_input":"2024-12-08T23:13:24.649596Z","iopub.status.idle":"2024-12-08T23:13:31.676117Z","shell.execute_reply.started":"2024-12-08T23:13:24.649551Z","shell.execute_reply":"2024-12-08T23:13:31.674728Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Hyperparameters\nSAMPLE_COUNT = 85000\nTRAINING_RATIO = 0.9\nIMAGE_SIZE = 96\nEPOCHS = 6\nBATCH_SIZE = 32\nLEARNING_RATE = 0.0001\nINPUT_DIR = '/kaggle/input/histopathologic-cancer-detection/'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T23:13:31.677617Z","iopub.execute_input":"2024-12-08T23:13:31.678015Z","iopub.status.idle":"2024-12-08T23:13:31.682590Z","shell.execute_reply.started":"2024-12-08T23:13:31.677982Z","shell.execute_reply":"2024-12-08T23:13:31.681702Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Dataset class\nclass CancerDataset(Dataset):\n    def __init__(self, dataframe, transform=None):\n        self.dataframe = dataframe\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n        row = self.dataframe.iloc[idx]\n        image = Image.open(row['path']).convert(\"RGB\")\n        label = torch.tensor(row['label'], dtype=torch.float32)\n        if self.transform:\n            image = self.transform(image)\n        return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T23:13:31.683773Z","iopub.execute_input":"2024-12-08T23:13:31.684695Z","iopub.status.idle":"2024-12-08T23:13:31.696802Z","shell.execute_reply.started":"2024-12-08T23:13:31.684665Z","shell.execute_reply":"2024-12-08T23:13:31.696010Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load and prepare data\ntraining_dir = os.path.join(INPUT_DIR, 'train/')\ndata_frame = pd.DataFrame({'path': glob(os.path.join(training_dir, '*.tif'))})\ndata_frame['id'] = data_frame['path'].map(lambda x: x.split('/')[-1].split('.')[0])\nlabels = pd.read_csv(os.path.join(INPUT_DIR, 'train_labels.csv'))\ndata_frame = data_frame.merge(labels, on='id')\nnegatives = data_frame[data_frame['label'] == 0].sample(SAMPLE_COUNT)\npositives = data_frame[data_frame['label'] == 1].sample(SAMPLE_COUNT)\ndata_frame = pd.concat([negatives, positives]).reset_index(drop=True)\n\ntrain_df, val_df = train_test_split(data_frame, train_size=TRAINING_RATIO, stratify=data_frame['label'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T23:13:31.698359Z","iopub.execute_input":"2024-12-08T23:13:31.698605Z","iopub.status.idle":"2024-12-08T23:13:34.519251Z","shell.execute_reply.started":"2024-12-08T23:13:31.698580Z","shell.execute_reply":"2024-12-08T23:13:34.518516Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Transforms\ntrain_transforms = transforms.Compose([\n    transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.RandomRotation(90),\n    transforms.ToTensor(),\n])\n\nval_transforms = transforms.Compose([\n    transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n    transforms.ToTensor(),\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T23:13:34.520815Z","iopub.execute_input":"2024-12-08T23:13:34.521246Z","iopub.status.idle":"2024-12-08T23:13:34.526754Z","shell.execute_reply.started":"2024-12-08T23:13:34.521204Z","shell.execute_reply":"2024-12-08T23:13:34.525829Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# DataLoaders\ntrain_dataset = CancerDataset(train_df, transform=train_transforms)\nval_dataset = CancerDataset(val_df, transform=val_transforms)\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T23:13:34.527955Z","iopub.execute_input":"2024-12-08T23:13:34.528198Z","iopub.status.idle":"2024-12-08T23:13:34.539901Z","shell.execute_reply.started":"2024-12-08T23:13:34.528151Z","shell.execute_reply":"2024-12-08T23:13:34.539209Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class UNet(nn.Module):\n    def __init__(self, in_channels=3, out_channels=1):\n        super(UNet, self).__init__()\n        self.encoder = nn.Sequential(\n            self.double_conv(in_channels, 64),\n            self.downsample(64, 128),\n            self.downsample(128, 256),\n            self.downsample(256, 512)\n        )\n        self.decoder = nn.Sequential(\n            self.upsample(512, 256),\n            self.upsample(256, 128),\n            self.upsample(128, 64),\n            nn.Conv2d(64, out_channels, kernel_size=1)\n        )\n        self.sigmoid = nn.Sigmoid()\n\n        # Add Global Average Pooling layer for classification\n        self.global_avg_pool = nn.AdaptiveAvgPool2d(1)\n\n    def double_conv(self, in_channels, out_channels):\n        return nn.Sequential(\n            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True)\n        )\n\n    def downsample(self, in_channels, out_channels):\n        return nn.Sequential(\n            nn.MaxPool2d(2),\n            self.double_conv(in_channels, out_channels)\n        )\n\n    def upsample(self, in_channels, out_channels):\n        return nn.Sequential(\n            nn.ConvTranspose2d(in_channels, out_channels, kernel_size=2, stride=2),\n            self.double_conv(out_channels, out_channels)\n        )\n\n    def forward(self, x):\n        enc1 = self.encoder[0](x)\n        enc2 = self.encoder[1](enc1)\n        enc3 = self.encoder[2](enc2)\n        enc4 = self.encoder[3](enc3)\n\n        dec3 = self.decoder[0](enc4)\n        dec2 = self.decoder[1](dec3)\n        dec1 = self.decoder[2](dec2)\n        output = self.decoder[3](dec1)\n\n        # Apply global average pooling to reduce spatial dimensions to 1x1\n        output = self.global_avg_pool(output)\n\n        # Squeeze to remove extra dimensions (batch_size, 1, 1, 1 -> batch_size, 1)\n        return self.sigmoid(output).squeeze(dim=-1).squeeze(dim=-1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T23:13:34.540735Z","iopub.execute_input":"2024-12-08T23:13:34.540977Z","iopub.status.idle":"2024-12-08T23:13:34.551786Z","shell.execute_reply.started":"2024-12-08T23:13:34.540952Z","shell.execute_reply":"2024-12-08T23:13:34.550962Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Initialize model, loss, and optimizer\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nmodel = UNet(in_channels=3, out_channels=1).to(device)\ncriterion = nn.BCELoss()\noptimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T23:13:34.552648Z","iopub.execute_input":"2024-12-08T23:13:34.552957Z","iopub.status.idle":"2024-12-08T23:13:34.925624Z","shell.execute_reply.started":"2024-12-08T23:13:34.552931Z","shell.execute_reply":"2024-12-08T23:13:34.924921Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_model(model, criterion, optimizer, train_loader, val_loader, epochs):\n    train_loss, val_loss = [], []\n    for epoch in range(epochs):\n        model.train()\n        running_loss = 0.0\n        for images, labels in train_loader:\n            images, labels = images.to(device), labels.to(device)\n\n            # Reshape labels to match model output size\n            labels = labels.view(-1, 1)  # From [batch_size] to [batch_size, 1]\n\n            optimizer.zero_grad()\n            outputs = model(images)  # Output size: [batch_size, 1]\n            loss = criterion(outputs, labels)  # Both `outputs` and `labels` now match\n            loss.backward()\n            optimizer.step()\n            running_loss += loss.item()\n\n        train_loss.append(running_loss / len(train_loader))\n\n        # Validation\n        model.eval()\n        val_running_loss = 0.0\n        with torch.no_grad():\n            for images, labels in val_loader:\n                images, labels = images.to(device), labels.to(device)\n\n                # Reshape labels to match model output size\n                labels = labels.view(-1, 1)  # From [batch_size] to [batch_size, 1]\n\n                outputs = model(images)\n                loss = criterion(outputs, labels)\n                val_running_loss += loss.item()\n\n        val_loss.append(val_running_loss / len(val_loader))\n        print(f\"Epoch {epoch+1}/{epochs}, Train Loss: {train_loss[-1]:.4f}, Val Loss: {val_loss[-1]:.4f}\")\n\n    return train_loss, val_loss\n\ntrain_loss, val_loss = train_model(model, criterion, optimizer, train_loader, val_loader, EPOCHS)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-08T23:13:34.926501Z","iopub.execute_input":"2024-12-08T23:13:34.926737Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Save model\ntorch.save(model.state_dict(), \"unet_histopathologic.pth\")\nprint(\"Model saved as 'unet_histopathologic.pth'.\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot training and validation loss\nplt.plot(range(EPOCHS), train_loss, label=\"Train Loss\")\nplt.plot(range(EPOCHS), val_loss, label=\"Validation Loss\")\nplt.legend()\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Loss\")\nplt.title(\"Training and Validation Loss\")\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}