{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":10418,"databundleVersionId":862236,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.nn.functional as F\nfrom torchvision import models, transforms\nfrom torch.utils.data import Dataset, DataLoader\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nfrom sklearn.metrics import average_precision_score, f1_score\nimport random\n\n# Paths\nDATA_DIR = \"/kaggle/input/humanatlas/d\"\nDATA_DIR = \"/kaggle/input/human-protein-atlas-image-classification\"\nTRAIN_DIR = os.path.join(DATA_DIR, \"train\")\nCSV_PATH = os.path.join(DATA_DIR, \"train.csv\")\n\n# Load CSV\ndf = pd.read_csv(CSV_PATH)\ndf['Target'] = df['Target'].apply(lambda x: list(map(int, str(x).split())) if pd.notna(x) else [])\n\n# Define Custom Dataset\nclass ProteinDataset(Dataset):\n    def __init__(self, df, img_dir, transform=None):\n        self.df = df\n        self.img_dir = img_dir\n        self.transform = transform\n        self.num_classes = 28\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        img_id = self.df.iloc[idx]['Id']\n        img_paths = [os.path.join(self.img_dir, f\"{img_id}_{color}.png\") for color in [\"red\", \"green\", \"blue\", \"yellow\"]]\n\n        # Load 4-channel image\n        channels = [np.array(Image.open(img_path), dtype=np.float32) if os.path.exists(img_path) else np.zeros((512, 512), dtype=np.float32) for img_path in img_paths]\n        image = np.stack(channels, axis=-1)  # Shape: (H, W, 4)\n\n        # Normalize to 0-1\n        image = image / 255.0\n\n        # Apply transforms\n        if self.transform:\n            augmented = self.transform(image=image)\n            image = augmented[\"image\"]\n\n        # Multi-label target\n        target = np.zeros(self.num_classes, dtype=np.float32)\n        for label in self.df.iloc[idx]['Target']:\n            target[label] = 1.0\n        return image, torch.tensor(target, dtype=torch.float32)\n\n# Define Data Augmentation\ntrain_transform = A.Compose([\n    A.RandomResizedCrop(224, 224),\n    A.HorizontalFlip(p=0.5),\n    A.VerticalFlip(p=0.5),\n    A.Rotate(limit=30),\n    A.Normalize(mean=[0.5]*4, std=[0.5]*4, max_pixel_value=1.0),\n    ToTensorV2()\n])\n\nval_transform = A.Compose([\n    A.Resize(224, 224),\n    A.Normalize(mean=[0.5]*4, std=[0.5]*4, max_pixel_value=1.0),\n    ToTensorV2()\n])\n\n# Dataloaders\ntrain_dataset = ProteinDataset(df, TRAIN_DIR, transform=train_transform)\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=2, pin_memory=True, persistent_workers=True)\n\n\n# Modify ResNet to accept 4 channels\nclass CustomResNet(nn.Module):\n    def __init__(self, num_classes=28):\n        super().__init__()\n        from torchvision.models import ResNet50_Weights\n        self.model = models.resnet50(weights=ResNet50_Weights.DEFAULT)\n\n        # Modify first conv layer for 4-channel input\n        self.model.conv1 = nn.Conv2d(4, 64, kernel_size=7, stride=2, padding=3, bias=False)\n\n        # Modify output layer\n        self.model.fc = nn.Linear(self.model.fc.in_features, num_classes)\n\n    def forward(self, x):\n        return self.model(x)\n\n# Weighted BCE Loss for Imbalanced Labels\nclass WeightedBCELoss(nn.Module):\n    def __init__(self, pos_weight):\n        super().__init__()\n        self.criterion = nn.BCEWithLogitsLoss(pos_weight=pos_weight)\n\n    def forward(self, inputs, targets):\n        return self.criterion(inputs, targets)\n\n# Calculate class weights based on frequency\nlabel_counts = np.sum([target.numpy() for _, target in train_dataset], axis=0)\npos_weight = torch.tensor((len(train_dataset) - label_counts) / (label_counts + 1e-6), dtype=torch.float32)\n\n# Training Loop\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nmodel = CustomResNet().to(device)\ncriterion = WeightedBCELoss(pos_weight.to(device))\noptimizer = optim.Adam(model.parameters(), lr=1e-4)\n\nnum_epochs = 50\n\n# Evaluation Metrics\ndef mean_average_precision(y_true, y_pred):\n    ap_per_class = []\n    for i in range(y_true.shape[1]):\n        ap = average_precision_score(y_true[:, i], y_pred[:, i])\n        ap_per_class.append(ap)\n    return np.mean(ap_per_class)\n\ndef f1_metric(y_true, y_pred, threshold=0.5):\n    y_pred = (y_pred > threshold).astype(int)\n    return f1_score(y_true, y_pred, average=\"macro\")\n\n# Training with mAP and F1 Score Tracking\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n\n    for images, targets in train_loader:\n        images, targets = images.to(device), targets.to(device)\n\n        optimizer.zero_grad()\n        outputs = model(images)\n\n        loss = criterion(outputs, targets)\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item()\n\n    # Evaluation after each epoch\n    model.eval()\n    y_true_list, y_pred_list = [], []\n\n    with torch.no_grad():\n        for images, targets in train_loader:\n            images, targets = images.to(device), targets.to(device)\n            outputs = model(images)\n            y_true_list.append(targets.cpu().numpy())\n            y_pred_list.append(torch.sigmoid(outputs).cpu().numpy())  # Apply sigmoid to get probabilities\n\n    y_true = np.vstack(y_true_list)\n    y_pred = np.vstack(y_pred_list)\n\n    map_score = mean_average_precision(y_true, y_pred)\n    f1_score_macro = f1_metric(y_true, y_pred)\n\n    print(f\"Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss / len(train_loader):.4f}, mAP: {map_score:.4f}, F1 Score: {f1_score_macro:.4f}\")\n\nprint(\"Training Completed.\")\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null}]}