{"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":20270,"databundleVersionId":1222630,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# PyTorch EfficientNet Training Code for SIIM-ISIC Melanoma Classification\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision.transforms as transforms\nfrom efficientnet_pytorch import EfficientNet\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image\nimport os\nfrom sklearn.model_selection import train_test_split\nfrom tqdm import tqdm\n\n# Set device\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(f'Using device: {device}')\n\n# Hyperparameters\nBATCH_SIZE = 32\nNUM_EPOCHS = 10\nLEARNING_RATE = 0.001\nNUM_CLASSES = 2  # Binary classification: benign vs malignant\n\n# Data transforms\ntrain_transforms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.RandomRotation(20),\n    transforms.ColorJitter(brightness=0.2, contrast=0.2),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n])\n\nval_transforms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n])\n\n# Custom Dataset\nclass MelanomaDataset(Dataset):\n    def __init__(self, df, image_dir, transform=None):\n        self.df = df\n        self.image_dir = image_dir\n        self.transform = transform\n    \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):\n        img_name = self.df.iloc[idx]['image_name']\n        img_path = os.path.join(self.image_dir, f'{img_name}.jpg')\n        image = Image.open(img_path).convert('RGB')\n        label = self.df.iloc[idx]['target']\n        \n        if self.transform:\n            image = self.transform(image)\n        \n        return image, label\n\n# Load model\nmodel = EfficientNet.from_pretrained('efficientnet-b0', num_classes=NUM_CLASSES)\nmodel = model.to(device)\n\n# Loss and optimizer\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)\nscheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', patience=2, verbose=True)\n\n# Training function\ndef train_epoch(model, dataloader, criterion, optimizer, device):\n    model.train()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n    \n    for images, labels in tqdm(dataloader, desc='Training'):\n        images, labels = images.to(device), labels.to(device)\n        \n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item()\n        _, predicted = torch.max(outputs.data, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n    \n    epoch_loss = running_loss / len(dataloader)\n    epoch_acc = 100 * correct / total\n    return epoch_loss, epoch_acc\n\n# Validation function\ndef validate_epoch(model, dataloader, criterion, device):\n    model.eval()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n    \n    with torch.no_grad():\n        for images, labels in tqdm(dataloader, desc='Validating'):\n            images, labels = images.to(device), labels.to(device)\n            \n            outputs = model(images)\n            loss = criterion(outputs, labels)\n            \n            running_loss += loss.item()\n            _, predicted = torch.max(outputs.data, 1)\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n    \n    epoch_loss = running_loss / len(dataloader)\n    epoch_acc = 100 * correct / total\n    return epoch_loss, epoch_acc\n\n# Training loop\nprint('\\nStarting training...')\nbest_val_acc = 0.0\nbest_val_loss = float('inf')\n\nfor epoch in range(NUM_EPOCHS):\n    print(f'\\nEpoch {epoch+1}/{NUM_EPOCHS}')\n    \n    train_loss, train_acc = train_epoch(model, train_loader, criterion, optimizer, device)\n    val_loss, val_acc = validate_epoch(model, val_loader, criterion, device)\n    \n    print(f'Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}%')\n    print(f'Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.2f}%')\n    \n    # Learning rate scheduling\n    scheduler.step(val_loss)\n    \n    # Save best model based on validation loss\n    if val_loss < best_val_loss:\n        best_val_loss = val_loss\n        torch.save(model.state_dict(), '/kaggle/working/efficientnet_b0_best.pth')\n        print('Best model saved!')\n    \n    # Track best accuracy for logging\n    if val_acc > best_val_acc:\n        best_val_acc = val_acc\n\nprint('\\nTraining complete!')\nprint(f'Best validation accuracy: {best_val_acc:.2f}%')\nprint(f'Best validation loss: {best_val_loss:.4f}')\n\n# Save final model\ntorch.save(model.state_dict(), '/kaggle/working/efficientnet_b0_final.pth')\nprint('\\nFinal model saved to /kaggle/working/efficientnet_b0_final.pth')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null}]}