{"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":"gpu","dataSources":[{"sourceId":112899,"databundleVersionId":13449579,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Grand X-Ray Slam: Division A - Complete Solution\n\n<figure>\n        <img src=\"https://www.kaggle.com/competitions/112899/images/header\" alt =\"Audio Art\" style='width:800px;height:500px;'>\n        <figcaption>","metadata":{}},{"cell_type":"markdown","source":"## Setup and Imports","metadata":{}},{"cell_type":"code","source":"# Import necessary libraries\nimport os\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom PIL import Image\nimport cv2\nfrom tqdm import tqdm\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Deep learning imports\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 sklearn.metrics import roc_auc_score\nfrom sklearn.model_selection import train_test_split\n\n# Set up device\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(f\"Using device: {device}\")\n\n# Set random seeds for reproducibility\ntorch.manual_seed(42)\nnp.random.seed(42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-06T15:27:39.993556Z","iopub.execute_input":"2025-09-06T15:27:39.993843Z","iopub.status.idle":"2025-09-06T15:27:49.616745Z","shell.execute_reply.started":"2025-09-06T15:27:39.993821Z","shell.execute_reply":"2025-09-06T15:27:49.616103Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  Data Loading and Column Detection","metadata":{}},{"cell_type":"code","source":"# Load the training data\ntrain_df = pd.read_csv('/kaggle/input/grand-xray-slam-division-a/train1.csv')\nprint(f\"Training data shape: {train_df.shape}\")\n\n# Display column names to identify the correct image column\nprint(\"\\nColumn names in train_df:\")\nprint(train_df.columns.tolist())\n\n# Display first few rows\nprint(\"\\nFirst 3 rows:\")\nprint(train_df.head(3))\n\n# Define the labels for the competition\nlabels = ['Atelectasis', 'Cardiomegaly', 'Consolidation', 'Edema', \n          'Enlarged Cardiomediastinum', 'Fracture', 'Lung Lesion', \n          'Lung Opacity', 'No Finding', 'Pleural Effusion', \n          'Pleural Other', 'Pneumonia', 'Pneumothorax', 'Support Devices']\n\n# Find the correct column name for the image filename\nimage_col = None\npossible_names = ['Image_Name', 'Image_Name', 'ImageName', 'image_name', 'filename', 'Image_name', 'Image']\nfor col in possible_names:\n    if col in train_df.columns:\n        image_col = col\n        break\n\nif image_col is None:\n    # If none of the expected names are found, use the first column that seems like it could be image names\n    for col in train_df.columns:\n        if any(term in col.lower() for term in ['image', 'file', 'name']):\n            image_col = col\n            break\n    if image_col is None:\n        image_col = train_df.columns[0]  # Use first column as fallback\n\nprint(f\"\\nUsing column '{image_col}' for image names\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-06T15:27:52.501692Z","iopub.execute_input":"2025-09-06T15:27:52.502572Z","iopub.status.idle":"2025-09-06T15:27:52.833328Z","shell.execute_reply.started":"2025-09-06T15:27:52.50255Z","shell.execute_reply":"2025-09-06T15:27:52.832555Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data Exploration and Visualization","metadata":{}},{"cell_type":"code","source":"# Check for missing values\nprint(\"\\nMissing values:\")\nprint(train_df.isnull().sum())\n\n# Basic statistics\nprint(\"\\nBasic statistics:\")\nprint(train_df.describe())\n\n# Visualize the distribution of labels\nplt.figure(figsize=(20, 10))\nlabel_counts = train_df[labels].sum().sort_values(ascending=False)\nsns.barplot(x=label_counts.values, y=label_counts.index)\nplt.title('Distribution of Thoracic Conditions')\nplt.xlabel('Count')\nplt.ylabel('Condition')\nplt.tight_layout()\nplt.savefig('/kaggle/working/label_distribution.png')\nplt.show()\n\n# Check co-occurrence of conditions\nplt.figure(figsize=(15, 12))\ncorrelation_matrix = train_df[labels].corr()\nsns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0, fmt='.2f')\nplt.title('Correlation Matrix of Thoracic Conditions')\nplt.tight_layout()\nplt.savefig('/kaggle/working/label_correlation.png')\nplt.show()\n\n# Distribution of views (if available)\nif 'ViewPosition' in train_df.columns:\n    plt.figure(figsize=(12, 6))\n    view_counts = train_df['ViewPosition'].value_counts()\n    plt.pie(view_counts.values, labels=view_counts.index, autopct='%1.1f%%')\n    plt.title('Distribution of View Positions')\n    plt.savefig('/kaggle/working/view_distribution.png')\n    plt.show()\n\n# Age distribution (if available)\nif 'Age' in train_df.columns:\n    plt.figure(figsize=(12, 6))\n    sns.histplot(train_df['Age'].dropna(), bins=30, kde=True)\n    plt.title('Age Distribution')\n    plt.xlabel('Age')\n    plt.ylabel('Count')\n    plt.savefig('/kaggle/working/age_distribution.png')\n    plt.show()\n\n# Sex distribution (if available)\nif 'Sex' in train_df.columns:\n    plt.figure(figsize=(8, 6))\n    sex_counts = train_df['Sex'].value_counts()\n    sns.barplot(x=sex_counts.index, y=sex_counts.values)\n    plt.title('Sex Distribution')\n    plt.xlabel('Sex')\n    plt.ylabel('Count')\n    plt.savefig('/kaggle/working/sex_distribution.png')\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-06T15:27:56.318376Z","iopub.execute_input":"2025-09-06T15:27:56.319126Z","iopub.status.idle":"2025-09-06T15:27:59.089694Z","shell.execute_reply.started":"2025-09-06T15:27:56.319096Z","shell.execute_reply":"2025-09-06T15:27:59.088918Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Sample Image Visualization","metadata":{}},{"cell_type":"code","source":"# Sample and display some images (limited to save memory)\ndef display_sample_images(df, num_images=6):\n    fig, axes = plt.subplots(2, 3, figsize=(15, 10))\n    axes = axes.ravel()\n    \n    sample_indices = np.random.choice(len(df), num_images, replace=False)\n    \n    for i, idx in enumerate(sample_indices):\n        # Get the correct image path\n        img_filename = df.iloc[idx][image_col]\n        img_path = os.path.join('/kaggle/input/grand-xray-slam-division-a/train1', img_filename)\n        \n        try:\n            img = Image.open(img_path)\n            \n            axes[i].imshow(img, cmap='gray')\n            axes[i].set_title(f\"Image: {img_filename}\")\n            \n            # Show positive labels\n            positive_labels = [label for label in labels if df.iloc[idx][label] == 1]\n            if positive_labels:\n                axes[i].set_xlabel(f\"Labels: {', '.join(positive_labels)}\")\n            \n            axes[i].axis('off')\n            \n        except Exception as e:\n            print(f\"Error loading image {img_path}: {e}\")\n            axes[i].text(0.5, 0.5, f\"Error loading\\n{img_filename}\", \n                        ha='center', va='center', transform=axes[i].transAxes)\n            axes[i].axis('off')\n    \n    plt.tight_layout()\n    plt.savefig('/kaggle/working/sample_images.png')\n    plt.show()\n\n# Display sample images\ndisplay_sample_images(train_df, num_images=6)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-06T15:28:03.168515Z","iopub.execute_input":"2025-09-06T15:28:03.168789Z","iopub.status.idle":"2025-09-06T15:28:09.803715Z","shell.execute_reply.started":"2025-09-06T15:28:03.168767Z","shell.execute_reply":"2025-09-06T15:28:09.802745Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Dataset Class and Data Transformations","metadata":{}},{"cell_type":"code","source":"class ChestXRayDataset(Dataset):\n    def __init__(self, df, image_dir, transform=None, is_test=False, image_col=None):\n        self.df = df\n        self.image_dir = image_dir\n        self.transform = transform\n        self.is_test = is_test\n        \n        # Determine the image column name\n        if image_col is None:\n            # Auto-detect image column\n            possible_names = ['Image_Name', 'Image_Name', 'ImageName', 'image_name', 'filename', 'Image_name']\n            for col in possible_names:\n                if col in df.columns:\n                    self.image_col = col\n                    break\n            else:\n                # If none found, use first column\n                self.image_col = df.columns[0]\n        else:\n            self.image_col = image_col\n        \n        # For training data, we have labels\n        if not is_test:\n            self.labels = df[labels].values\n    \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):\n        img_name = str(self.df.iloc[idx][self.image_col])\n        img_path = os.path.join(self.image_dir, img_name)\n        \n        # Load image\n        try:\n            image = Image.open(img_path).convert('RGB')\n        except Exception as e:\n            print(f\"Error loading image {img_path}: {e}\")\n            # Return a blank image as fallback\n            image = Image.new('RGB', (256, 256), color='black')\n        \n        if self.transform:\n            image = self.transform(image)\n        \n        if self.is_test:\n            return image, img_name\n        else:\n            labels = torch.FloatTensor(self.labels[idx])\n            return image, labels\n\n# Data augmentation and normalization\ntrain_transform = transforms.Compose([\n    transforms.Resize((256, 256)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(10),\n    transforms.ColorJitter(brightness=0.2, contrast=0.2),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])\n\nval_transform = transforms.Compose([\n    transforms.Resize((256, 256)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-06T15:28:19.380169Z","iopub.execute_input":"2025-09-06T15:28:19.380428Z","iopub.status.idle":"2025-09-06T15:28:19.389419Z","shell.execute_reply.started":"2025-09-06T15:28:19.380411Z","shell.execute_reply":"2025-09-06T15:28:19.388718Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model Architecture","metadata":{}},{"cell_type":"code","source":"class ChestXRayModel(nn.Module):\n    def __init__(self, num_classes=14, pretrained=True):\n        super(ChestXRayModel, self).__init__()\n        \n        # Use EfficientNet as base model\n        self.base_model = models.efficientnet_b0(pretrained=pretrained)\n        \n        # Replace the classifier\n        num_features = self.base_model.classifier[1].in_features\n        self.base_model.classifier = nn.Identity()  # Remove original classifier\n        \n        # Add custom classifier\n        self.classifier = nn.Sequential(\n            nn.Linear(num_features, 512),\n            nn.ReLU(),\n            nn.Dropout(0.3),\n            nn.Linear(512, num_classes),\n            nn.Sigmoid()\n        )\n    \n    def forward(self, x):\n        features = self.base_model(x)\n        return self.classifier(features)\n\n# Create model\nmodel = ChestXRayModel(num_classes=14)\nmodel = model.to(device)\nprint(f\"Model created with {sum(p.numel() for p in model.parameters()):,} parameters\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-06T15:28:22.191149Z","iopub.execute_input":"2025-09-06T15:28:22.191447Z","iopub.status.idle":"2025-09-06T15:28:22.655801Z","shell.execute_reply.started":"2025-09-06T15:28:22.191424Z","shell.execute_reply":"2025-09-06T15:28:22.655164Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Training Setup","metadata":{}},{"cell_type":"code","source":"# For demonstration, we'll use a small subset of data\nsample_size = min(2000, len(train_df))  # Use smaller sample for demonstration\nsample_df = train_df.sample(sample_size, random_state=42)\n\n# Split data into train and validation\ntrain_data, val_data = train_test_split(\n    sample_df, test_size=0.2, random_state=42, stratify=sample_df[labels].sum(axis=1)\n)\n\nprint(f\"Train size: {len(train_data)}\")\nprint(f\"Validation size: {len(val_data)}\")\n\n# Create datasets and dataloaders\ntrain_dataset = ChestXRayDataset(train_data, '/kaggle/input/grand-xray-slam-division-a/train1', \n                                transform=train_transform, image_col=image_col)\nval_dataset = ChestXRayDataset(val_data, '/kaggle/input/grand-xray-slam-division-a/train1', \n                              transform=val_transform, image_col=image_col)\n\ntrain_loader = DataLoader(train_dataset, batch_size=16, shuffle=True, num_workers=2)\nval_loader = DataLoader(val_dataset, batch_size=16, shuffle=False, num_workers=2)\n\n# Calculate class weights for imbalanced data\ndef calculate_class_weights(df, labels):\n    class_weights = []\n    for label in labels:\n        positive = df[label].sum()\n        negative = len(df) - positive\n        weight = negative / (positive + 1e-6)  # Add small epsilon to avoid division by zero\n        class_weights.append(weight)\n    \n    return torch.FloatTensor(class_weights).to(device)\n\n# Loss function and optimizer\nclass_weights = calculate_class_weights(train_df, labels)\ncriterion = nn.BCELoss(weight=class_weights)\noptimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-5)\nscheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='max', patience=2, factor=0.5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-06T15:28:25.438558Z","iopub.execute_input":"2025-09-06T15:28:25.439199Z","iopub.status.idle":"2025-09-06T15:28:25.460386Z","shell.execute_reply.started":"2025-09-06T15:28:25.439176Z","shell.execute_reply":"2025-09-06T15:28:25.459707Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Training Functions","metadata":{}},{"cell_type":"code","source":"def train_epoch(model, loader, optimizer, criterion, device):\n    model.train()\n    running_loss = 0.0\n    \n    for images, targets in tqdm(loader, desc=\"Training\"):\n        images = images.to(device)\n        targets = targets.to(device)\n        \n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, targets)\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item() * images.size(0)\n    \n    return running_loss / len(loader.dataset)\n\ndef validate(model, loader, criterion, device):\n    model.eval()\n    running_loss = 0.0\n    all_outputs = []\n    all_targets = []\n    \n    with torch.no_grad():\n        for images, targets in tqdm(loader, desc=\"Validation\"):\n            images = images.to(device)\n            targets = targets.to(device)\n            \n            outputs = model(images)\n            loss = criterion(outputs, targets)\n            \n            running_loss += loss.item() * images.size(0)\n            all_outputs.append(outputs.cpu().numpy())\n            all_targets.append(targets.cpu().numpy())\n    \n    all_outputs = np.concatenate(all_outputs)\n    all_targets = np.concatenate(all_targets)\n    \n    # Calculate AUC for each class\n    auc_scores = []\n    for i in range(all_targets.shape[1]):\n        try:\n            # Check if we have both positive and negative samples\n            if len(np.unique(all_targets[:, i])) > 1:\n                auc = roc_auc_score(all_targets[:, i], all_outputs[:, i])\n                auc_scores.append(auc)\n            else:\n                auc_scores.append(0.5)  # Neutral score for constant targets\n        except:\n            auc_scores.append(0.5)\n    \n    return running_loss / len(loader.dataset), np.mean(auc_scores), auc_scores\n\ndef train_model(model, train_loader, val_loader, optimizer, criterion, scheduler, epochs, device):\n    best_auc = 0.0\n    train_losses = []\n    val_losses = []\n    val_aucs = []\n    \n    for epoch in range(epochs):\n        print(f\"\\nEpoch {epoch+1}/{epochs}\")\n        print(\"-\" * 50)\n        \n        # Train\n        train_loss = train_epoch(model, train_loader, optimizer, criterion, device)\n        train_losses.append(train_loss)\n        \n        # Validate\n        val_loss, mean_auc, class_aucs = validate(model, val_loader, criterion, device)\n        val_losses.append(val_loss)\n        val_aucs.append(mean_auc)\n        \n        # Update scheduler\n        scheduler.step(mean_auc)\n        \n        print(f'Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}, Val AUC: {mean_auc:.4f}')\n        \n        # Print class-wise AUC for top 5 classes\n        class_auc_df = pd.DataFrame({'Class': labels, 'AUC': class_aucs})\n        class_auc_df = class_auc_df.sort_values('AUC', ascending=False)\n        print(\"Top 5 classes by AUC:\")\n        for i, row in class_auc_df.head().iterrows():\n            print(f'  {row[\"Class\"]}: {row[\"AUC\"]:.4f}')\n        \n        # Save best model\n        if mean_auc > best_auc:\n            best_auc = mean_auc\n            torch.save(model.state_dict(), '/kaggle/working/best_model.pth')\n            print(f'New best model saved with AUC: {best_auc:.4f}')\n    \n    return train_losses, val_losses, val_aucs\n\n# Train the model for a few epochs (using sample data)\nprint(\"Starting training with sample data...\")\ntrain_losses, val_losses, val_aucs = train_model(\n    model, train_loader, val_loader, optimizer, criterion, scheduler, epochs=3, device=device\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-06T15:28:28.269649Z","iopub.execute_input":"2025-09-06T15:28:28.269953Z","iopub.status.idle":"2025-09-06T15:31:30.755936Z","shell.execute_reply.started":"2025-09-06T15:28:28.269926Z","shell.execute_reply":"2025-09-06T15:31:30.755039Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Enhanced Model Training","metadata":{}},{"cell_type":"code","source":"# Enhanced training with more epochs and better regularization\ndef enhanced_training():\n    # Reload the best model\n    model.load_state_dict(torch.load('/kaggle/working/best_model.pth'))\n    \n    # Use a more aggressive learning rate schedule\n    enhanced_optimizer = optim.Adam(model.parameters(), lr=0.0005, weight_decay=1e-5)\n    enhanced_scheduler = optim.lr_scheduler.CosineAnnealingLR(enhanced_optimizer, T_max=5)\n    \n    # Use focal loss for better handling of class imbalance\n    class FocalLoss(nn.Module):\n        def __init__(self, alpha=1, gamma=2, reduction='mean'):\n            super(FocalLoss, self).__init__()\n            self.alpha = alpha\n            self.gamma = gamma\n            self.reduction = reduction\n        \n        def forward(self, inputs, targets):\n            BCE_loss = nn.BCELoss(reduction='none')(inputs, targets)\n            pt = torch.exp(-BCE_loss)\n            F_loss = self.alpha * (1-pt)**self.gamma * BCE_loss\n            \n            if self.reduction == 'mean':\n                return torch.mean(F_loss)\n            elif self.reduction == 'sum':\n                return torch.sum(F_loss)\n            else:\n                return F_loss\n    \n    focal_criterion = FocalLoss()\n    \n    print(\"Starting enhanced training with focal loss...\")\n    enhanced_losses, enhanced_val_losses, enhanced_val_aucs = train_model(\n        model, train_loader, val_loader, enhanced_optimizer, focal_criterion, enhanced_scheduler, epochs=5, device=device\n    )\n    \n    return enhanced_losses, enhanced_val_losses, enhanced_val_aucs\n\n# Uncomment to run enhanced training\n# enhanced_losses, enhanced_val_losses, enhanced_val_aucs = enhanced_training()\n# plot_training_progress(enhanced_losses, enhanced_val_losses, enhanced_val_aucs)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-06T15:31:59.45783Z","iopub.execute_input":"2025-09-06T15:31:59.458245Z","iopub.status.idle":"2025-09-06T15:31:59.465323Z","shell.execute_reply.started":"2025-09-06T15:31:59.458218Z","shell.execute_reply":"2025-09-06T15:31:59.464639Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Training Visualization","metadata":{}},{"cell_type":"code","source":"def plot_training_progress(train_losses, val_losses, val_aucs):\n    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))\n    \n    # Plot losses\n    ax1.plot(train_losses, label='Train Loss', marker='o')\n    ax1.plot(val_losses, label='Validation Loss', marker='o')\n    ax1.set_xlabel('Epoch')\n    ax1.set_ylabel('Loss')\n    ax1.set_title('Training and Validation Loss')\n    ax1.legend()\n    ax1.grid(True)\n    \n    # Plot AUC\n    ax2.plot(val_aucs, label='Validation AUC', color='green', marker='o')\n    ax2.set_xlabel('Epoch')\n    ax2.set_ylabel('AUC')\n    ax2.set_title('Validation AUC Score')\n    ax2.legend()\n    ax2.grid(True)\n    \n    plt.tight_layout()\n    plt.savefig('/kaggle/working/training_progress.png')\n    plt.show()\n\n# Plot training progress\nplot_training_progress(train_losses, val_losses, val_aucs)\n\n# Save training metrics\nmetrics_df = pd.DataFrame({\n    'epoch': range(1, len(train_losses) + 1),\n    'train_loss': train_losses,\n    'val_loss': val_losses,\n    'val_auc': val_aucs\n})\nmetrics_df.to_csv('/kaggle/working/training_metrics.csv', index=False)\nprint(\"Training metrics saved to /kaggle/working/training_metrics.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-06T15:32:02.323601Z","iopub.execute_input":"2025-09-06T15:32:02.324307Z","iopub.status.idle":"2025-09-06T15:32:02.915566Z","shell.execute_reply.started":"2025-09-06T15:32:02.324286Z","shell.execute_reply":"2025-09-06T15:32:02.914811Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model Ensemble","metadata":{}},{"cell_type":"code","source":"# Create ensemble of models for better performance\ndef create_ensemble(models_list, test_loader, device):\n    all_predictions = []\n    \n    for model in models_list:\n        model.eval()\n        model_predictions = []\n        \n        with torch.no_grad():\n            for images, names in tqdm(test_loader, desc=\"Model predictions\"):\n                images = images.to(device)\n                outputs = model(images)\n                model_predictions.append(outputs.cpu().numpy())\n        \n        all_predictions.append(np.concatenate(model_predictions))\n    \n    # Average predictions from all models\n    ensemble_pred = np.mean(all_predictions, axis=0)\n    return ensemble_pred\n\n# Create multiple models with different architectures\ndef create_different_models():\n    models_list = []\n    \n    # EfficientNet-B0\n    model1 = ChestXRayModel(num_classes=14)\n    models_list.append(model1)\n    \n    # DenseNet-121\n    class DenseNetModel(nn.Module):\n        def __init__(self, num_classes=14):\n            super(DenseNetModel, self).__init__()\n            self.base_model = models.densenet121(pretrained=True)\n            num_features = self.base_model.classifier.in_features\n            self.base_model.classifier = nn.Sequential(\n                nn.Linear(num_features, 512),\n                nn.ReLU(),\n                nn.Dropout(0.3),\n                nn.Linear(512, num_classes),\n                nn.Sigmoid()\n            )\n        \n        def forward(self, x):\n            return self.base_model(x)\n    \n    model2 = DenseNetModel(num_classes=14)\n    models_list.append(model2)\n    \n    # ResNet-50\n    class ResNetModel(nn.Module):\n        def __init__(self, num_classes=14):\n            super(ResNetModel, self).__init__()\n            self.base_model = models.resnet50(pretrained=True)\n            num_features = self.base_model.fc.in_features\n            self.base_model.fc = nn.Sequential(\n                nn.Linear(num_features, 512),\n                nn.ReLU(),\n                nn.Dropout(0.3),\n                nn.Linear(512, num_classes),\n                nn.Sigmoid()\n            )\n        \n        def forward(self, x):\n            return self.base_model(x)\n    \n    model3 = ResNetModel(num_classes=14)\n    models_list.append(model3)\n    \n    return models_list\n\n# Train ensemble (commented out for time)\n# print(\"Creating model ensemble...\")\n# ensemble_models = create_different_models()\n# for i, model in enumerate(ensemble_models):\n#     model = model.to(device)\n#     print(f\"Training model {i+1}/{len(ensemble_models)}\")\n#     train_model(model, train_loader, val_loader, optimizer, criterion, scheduler, epochs=2, device=device)\n#     torch.save(model.state_dict(), f'/kaggle/working/model_{i+1}.pth')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-06T15:32:08.758451Z","iopub.execute_input":"2025-09-06T15:32:08.758738Z","iopub.status.idle":"2025-09-06T15:32:08.767385Z","shell.execute_reply.started":"2025-09-06T15:32:08.758718Z","shell.execute_reply":"2025-09-06T15:32:08.766762Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  Enhanced Submission with Post-processing","metadata":{}},{"cell_type":"code","source":"# Enhanced submission with calibration and post-processing\ndef create_enhanced_submission(model, test_dir, transform, device, sample_submission_path, image_col):\n    # Load sample submission\n    sample_submission = pd.read_csv(sample_submission_path)\n    sample_image_col = 'Image_name' if 'Image_name' in sample_submission.columns else sample_submission.columns[0]\n    \n    # Create test dataset\n    test_df = pd.DataFrame({image_col: sample_submission[sample_image_col]})\n    test_dataset = ChestXRayDataset(test_df, test_dir, transform=transform, is_test=True, image_col=image_col)\n    test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False, num_workers=2)\n    \n    model.eval()\n    predictions = []\n    image_names = []\n    \n    with torch.no_grad():\n        for images, names in tqdm(test_loader, desc=\"Creating enhanced predictions\"):\n            images = images.to(device)\n            outputs = model(images)\n            predictions.append(outputs.cpu().numpy())\n            image_names.extend(names)\n    \n    predictions = np.concatenate(predictions)\n    \n    # Apply temperature scaling calibration\n    def temperature_scale(logits, temperature=0.8):\n        return logits ** (1/temperature)\n    \n    calibrated_predictions = temperature_scale(predictions, temperature=0.8)\n    \n    # Apply label correlation adjustment (if conditions are correlated)\n    correlation_matrix = train_df[labels].corr().values\n    adjusted_predictions = np.dot(calibrated_predictions, correlation_matrix)\n    adjusted_predictions = np.clip(adjusted_predictions, 0, 1)  # Ensure valid probabilities\n    \n    # Create submission dataframe\n    submission_df = pd.DataFrame(adjusted_predictions, columns=labels)\n    submission_df.insert(0, sample_image_col, image_names)\n    submission_df.columns = sample_submission.columns\n    \n    return submission_df\n\n# Create enhanced submission\nprint(\"Creating enhanced submission with calibration...\")\nenhanced_submission = create_enhanced_submission(\n    model, \n    '/kaggle/input/grand-xray-slam-division-a/test1', \n    val_transform, \n    device,\n    '/kaggle/input/grand-xray-slam-division-a/sample_submission_1.csv',\n    image_col=image_col\n)\n\n# Save enhanced submission\nenhanced_submission.to_csv('/kaggle/working/enhanced_submission.csv', index=False)\nprint(\"Enhanced submission file created at /kaggle/working/enhanced_submission.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-06T15:32:12.617349Z","iopub.execute_input":"2025-09-06T15:32:12.618035Z","iopub.status.idle":"2025-09-06T15:59:01.86004Z","shell.execute_reply.started":"2025-09-06T15:32:12.61801Z","shell.execute_reply":"2025-09-06T15:59:01.859238Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Final Optimization and Submission","metadata":{}},{"cell_type":"code","source":"# Create the final optimized submission\ndef create_final_submission():\n    # Load the best model\n    model.load_state_dict(torch.load('/kaggle/working/best_model.pth'))\n    \n    # Create submission with test-time augmentation\n    def predict_with_tta(model, test_loader, device, n_augmentations=3):\n        model.eval()\n        all_predictions = []\n        \n        # Define TTA transformations\n        tta_transforms = [\n            transforms.Compose([\n                transforms.Resize((256, 256)),\n                transforms.ToTensor(),\n                transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n            ]),\n            transforms.Compose([\n                transforms.Resize((256, 256)),\n                transforms.RandomHorizontalFlip(p=1.0),\n                transforms.ToTensor(),\n                transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n            ]),\n            transforms.Compose([\n                transforms.Resize((256, 256)),\n                transforms.RandomRotation(10),\n                transforms.ToTensor(),\n                transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n            ])\n        ]\n        \n        image_names = []\n        for images, names in tqdm(test_loader, desc=\"TTA predictions\"):\n            batch_predictions = []\n            \n            for tta_transform in tta_transforms[:n_augmentations]:\n                # Apply different augmentation\n                augmented_images = torch.stack([tta_transform(Image.fromarray((img.permute(1, 2, 0).numpy() * 255).astype(np.uint8))) \n                                              for img in images])\n                \n                augmented_images = augmented_images.to(device)\n                with torch.no_grad():\n                    outputs = model(augmented_images)\n                    batch_predictions.append(outputs.cpu().numpy())\n            \n            # Average predictions from different augmentations\n            avg_predictions = np.mean(batch_predictions, axis=0)\n            all_predictions.append(avg_predictions)\n            image_names.extend(names)\n        \n        return np.concatenate(all_predictions), image_names\n    \n    # Load test data\n    sample_submission = pd.read_csv('/kaggle/input/grand-xray-slam-division-a/sample_submission_1.csv')\n    sample_image_col = 'Image_name' if 'Image_name' in sample_submission.columns else sample_submission.columns[0]\n    \n    test_df = pd.DataFrame({image_col: sample_submission[sample_image_col]})\n    test_dataset = ChestXRayDataset(test_df, '/kaggle/input/grand-xray-slam-division-a/test1', \n                                   transform=val_transform, is_test=True, image_col=image_col)\n    test_loader = DataLoader(test_dataset, batch_size=16, shuffle=False, num_workers=2)\n    \n    # Get TTA predictions\n    tta_predictions, image_names = predict_with_tta(model, test_loader, device, n_augmentations=2)\n    \n    # Create final submission\n    final_submission = pd.DataFrame(tta_predictions, columns=labels)\n    final_submission.insert(0, sample_image_col, image_names)\n    final_submission.columns = sample_submission.columns\n    \n    return final_submission\n\n# Create final submission with TTA\nprint(\"Creating final submission with Test-Time Augmentation...\")\nfinal_submission = create_final_submission()\nfinal_submission.to_csv('/kaggle/working/submission.csv', index=False)\nprint(\"Final submission file created at /kaggle/working/submission.csv\")\n\n# Final summary\nprint(\"=\" * 70)\nprint(\"FINAL SUBMISSION READY!\")\nprint(\"=\" * 70)\nprint(\"Available submission files:\")\nprint(\"1. /kaggle/working/submission.csv - Basic predictions\")\nprint(\"2. /kaggle/working/enhanced_submission.csv - With calibration\")\nprint(\"3. /kaggle/working/submission.csv - With TTA (Recommended)\")\nprint(\"=\" * 70)\nprint(\"Recommendation: Use final_submission.csv for your competition entry!\")\nprint(\"=\" * 70)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-06T16:00:07.067181Z","iopub.execute_input":"2025-09-06T16:00:07.067544Z","iopub.status.idle":"2025-09-06T16:26:59.582746Z","shell.execute_reply.started":"2025-09-06T16:00:07.06752Z","shell.execute_reply":"2025-09-06T16:26:59.581962Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}