{"metadata":{"colab":{"provenance":[],"gpuType":"T4"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"},"accelerator":"GPU"},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Data Installation ⏬","metadata":{"id":"n0sXHiHMS5AC"}},{"cell_type":"code","source":"!pip install kaggle","metadata":{"id":"2BcAppiKxGzo","outputId":"fbda4a41-4f4d-48f3-b9b6-879497e82735"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from google.colab import files\nimport os\nif not(os.path.exists(\"kaggle.json\")):\n  files.upload()\n!pip install --upgrade --force-reinstall --no-deps kaggle\n!mkdir -p ~/.kaggle\n!cp kaggle.json ~/.kaggle/\n!ls ~/.kaggle\n!chmod 600 /root/.kaggle/kaggle.json","metadata":{"id":"CBK4ea3A1EMG","outputId":"8a3bfe02-b7bc-4d8b-e618-a97e00f6e778"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!kaggle competitions download -c bioverse-hackathon","metadata":{"id":"Q65Gh44H0xsW","outputId":"e4573413-4f03-43f4-cf5b-815da31efcf9"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!unzip bioverse-hackathon.zip","metadata":{"id":"3A_beUk7198l","outputId":"677171da-7f4b-41d8-83f0-cdca3ee0e4a7"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.optim import lr_scheduler\nimport torch.backends.cudnn as cudnn\nimport numpy as np\nimport torchvision\nfrom torchvision import datasets, models, transforms\nimport matplotlib.pyplot as plt\nimport time\nimport os\nimport cv2\nimport random\nfrom PIL import Image\nfrom tempfile import TemporaryDirectory\nfrom torch.utils.data import random_split\ncudnn.benchmark = True\nplt.ion()","metadata":{"id":"wnKHRo9bxDuE","outputId":"13054971-4a73-4ef6-e8fd-e15d5f0be730"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data loading and augmentation 💪","metadata":{"id":"zhHFYcuJ2Vea"}},{"cell_type":"code","source":"import torchvision.transforms.functional as F\nfrom PIL import Image\n\nclass AdjustSharpness:\n    def __init__(self, sharpness_factor):\n        self.sharpness_factor = sharpness_factor\n\n    def __call__(self, img):\n        img = F.adjust_sharpness(img, self.sharpness_factor)\n        return img\n\ndef apply_sharpness_multiple_times(image, num_times):\n    sharpness_factor = random.uniform(1.5, 2.5)\n    for _ in range(num_times):\n        sharpness_transform = AdjustSharpness(sharpness_factor)\n        image = sharpness_transform(image)\n    return image\n\n","metadata":{"id":"CaG3GotY-_26"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torchvision.transforms as transforms\nfrom torchvision import datasets\nfrom torch.utils.data import random_split, DataLoader, ConcatDataset\nfrom torchvision.transforms import v2\nimport torchvision.transforms.functional as F\n\ndef MakeDataset(data_dir, train_transform=None, test_transform=None, train_transform2=None, train_transform3=None, transform4=None, train_transform5=None):\n    global train_dataset\n    global test_dataset\n\n    default_transform = transforms.Compose([\n        transforms.Resize((224, 224)),\n        transforms.ToTensor(),\n    ])\n\n    if train_transform is None:\n        train_transform = default_transform\n\n    if test_transform is None:\n        test_transform = default_transform\n\n    dataset = datasets.ImageFolder(root=data_dir, transform=None)\n    print(dataset.classes)\n    encoded_label = 0\n\n    class_name = list(dataset.class_to_idx.keys())[list(dataset.class_to_idx.values()).index(encoded_label)]\n    print(class_name)\n\n    total_samples = len(dataset)\n    train_size = int(0.3 * total_samples)\n    test_size = int(train_size * 0.2)\n    validate_size = total_samples - (train_size + test_size)\n    train_dataset, test_dataset, validate_dataset = random_split(dataset, [train_size, test_size, validate_size])\n    train_dataset2, test_dataset2, validate_dataset2 = random_split(dataset, [train_size, test_size, validate_size])\n    train_dataset3, test_dataset3, validate_dataset3 = random_split(dataset, [train_size, test_size, validate_size])\n    train_dataset4, test_dataset4, validate_dataset4 = random_split(dataset, [train_size, test_size, validate_size])\n\n    train_dataset.dataset.transform = train_transform\n    train_dataset2.dataset.transform = train_transform2\n    train_dataset3.dataset.transform = train_transform3\n    train_dataset4.dataset.transform = train_transform4\n    test_dataset.dataset.transform = test_transform\n\n    batch_size = 32\n    concat_dataset = ConcatDataset([train_dataset, train_dataset2, train_dataset3, train_dataset4])\n\n\n    train_loader = DataLoader(concat_dataset, batch_size=batch_size, shuffle=True)\n    test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)\n    validate_loader = DataLoader(validate_dataset, batch_size=batch_size, shuffle=False)\n    train_samples = len(train_loader.dataset)\n    test_samples = len(test_loader.dataset)\n    validate_samples = len(validate_loader.dataset)\n\n    print(f\"Number of samples in train_loader: {train_samples}\")\n    print(f\"Number of samples in test_loader: {test_samples}\")\n    print(f\"Number of samples in validate_loader: {validate_samples}\")\n\n    return train_loader, test_loader, validate_loader\n\n\nsharpness_factor = 10.0\nsharpness_transform = AdjustSharpness(sharpness_factor)\n\ntrain_transform2 = v2.Compose([\ntransforms.Resize((224,224)),\ntransforms.RandomHorizontalFlip(),\nsharpness_transform,\ntransforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\ntransforms.ToTensor(),\n])\n\n\ntrain_transform = transforms.Compose([\n    transforms.RandomResizedCrop(224),\n    transforms.RandomHorizontalFlip(),\n    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),\n    transforms.RandomRotation(15),\n    transforms.RandomAffine(degrees=0, translate=(0.1, 0.1)),\n    transforms.GaussianBlur(kernel_size=5),\n    transforms.ToTensor(),\n])\n\n\ntest_transform = v2.Compose([\n    transforms.Resize((224,224)),\n    transforms.ToTensor(),\n])\n\nH, W = 224, 224\nimg = torch.randint(0, 256, size=(3, H, W), dtype=torch.uint8)\n\ntrain_transform3 = v2.Compose([\n    v2.RandomResizedCrop(size=(224, 224), antialias=True),\n    v2.RandomHorizontalFlip(p=0.5),\n])\n\ntrain_transform4 = transforms.Compose([\n    v2.RandomResizedCrop(size=(224, 224), antialias=True),\n    v2.ScaleJitter(224),\n])\ntrain_transform5 = transforms.Compose([\n    v2.RandomResizedCrop(size=(224, 224), antialias=True),\n    v2.ScaleJitter(224),\n    sharpness_transform,\n])\n\ndata_dir = '/content/train'\n\ntrain_loader, test_loader, validate_loader = MakeDataset(data_dir, train_transform, test_transform, train_transform2, train_transform3, train_transform4)\n\n","metadata":{"id":"slf22C4J0fJP","outputId":"f5fa46d4-88ce-4817-aabd-52a30ee0dbda"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data visualization 📈","metadata":{"id":"viprEfZc2xS6"}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\n\n# Define a function to display an image\ndef show_image(image, title=None):\n    plt.imshow(np.transpose(image.numpy(), (1, 2, 0)))\n    plt.axis('off')\n    if title:\n        plt.title(title)\n\ndata_iterator = iter(train_loader)\nimages, labels = next(data_iterator)\n\nshow_image(images[20])\nplt.show()\nprint(images[10].shape)\n","metadata":{"id":"uR0dXVAZ2l0I","outputId":"c1f52244-ea65-43ee-efe3-e928291073db"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train ResNet50 Model 🏃‍♂️","metadata":{"id":"yJGBNGH323Xc"}},{"cell_type":"code","source":"import os\nimport time\nimport copy\nimport torch\nfrom torch.optim import lr_scheduler\nfrom tempfile import TemporaryDirectory\n\ndef train_model(model, criterion, optimizer, scheduler, train_loader, test_loader, num_epochs=25):\n    since = time.time()\n    global best_model_params_path\n\n    best_acc = 0.0\n    best_epoch = 0\n    consecutive_no_improvement = 0\n    early_stop = False\n    losses = []\n    for epoch in range(num_epochs):\n        if early_stop:\n            break\n\n        print(f'Epoch {epoch}/{num_epochs - 1}')\n        print('-' * 10)\n\n        for phase in ['train', 'val']:\n            if phase == 'train':\n                model.train()\n                data_loader = train_loader\n            else:\n                model.eval()\n                data_loader = test_loader\n\n            running_loss = 0.0\n            running_corrects = 0\n\n            for inputs, labels in data_loader:\n                inputs = inputs.to(device)\n                labels = labels.to(device)\n\n                optimizer.zero_grad()\n\n                with torch.set_grad_enabled(phase == 'train'):\n                    outputs = model(inputs)\n                    _, preds = torch.max(outputs, 1)\n                    loss = criterion(outputs, labels)\n\n                    if phase == 'train':\n                        loss.backward()\n                        optimizer.step()\n\n                running_loss += loss.item() * inputs.size(0)\n                running_corrects += torch.sum(preds == labels.data)\n\n            if phase == 'train':\n                scheduler.step()\n\n            epoch_loss = running_loss / len(data_loader.dataset)\n            losses.append(epoch_loss)\n            epoch_acc = running_corrects.double() / len(data_loader.dataset)\n\n            print(f'{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}')\n\n            if phase == 'val' and epoch_acc > best_acc:\n                best_acc = epoch_acc\n                if not os.path.exists('checkpoints'):\n                    os.mkdir('checkpoints')\n\n                checkpoint_filename = f\"best_model_acc_{best_acc:.4f}.pt\"\n                torch.save(model.state_dict(), checkpoint_filename)\n\n                consecutive_no_improvement = 0\n            elif phase == 'val':\n                consecutive_no_improvement += 1\n\n            print()\n            patience = 8\n            if consecutive_no_improvement >= patience:\n                print(f'Early stopping after {patience} consecutive epochs with no improvement.')\n                early_stop = True\n                break\n\n    time_elapsed = time.time() - since\n    print(f'Training complete in {time_elapsed // 60:.0f}m {time_elapsed % 60:.0f}s')\n    print(f'Best val Acc: {best_acc:.4f}')\n    return model, losses\n","metadata":{"id":"djltIYaa2zU5"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_ft = models.resnet50(pretrained=True, progress=True)\n\nnum_ftrs = model_ft.fc.in_features\n# Replace FC layer with custom layer\n\nmodel_ft.fc = nn.Sequential(nn.Dropout(),\n                         nn.Linear(model_ft.fc.in_features, 1),\n                         nn.Softmax())\n\nmodel_ft.fc = nn.Linear(num_ftrs, 3)\n\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nmodel = model_ft.to(device)\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)\nscheduler = lr_scheduler.StepLR(optimizer, step_size=4, gamma=0.1)\n\ncriterion = nn.CrossEntropyLoss()","metadata":{"id":"YScpvBOg27sV","outputId":"37a5e6be-0705-4fbc-82d3-9137fe625ed8"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Fine Tuning 🪛","metadata":{"id":"v2mXJCzVnweN"}},{"cell_type":"code","source":"trained_model, losses = train_model(model_ft, criterion, optimizer, scheduler, train_loader, test_loader, num_epochs=100)","metadata":{"id":"vtBd6W982-w8","outputId":"b6bac70c-7cf8-4d44-f47f-1ae700b236a2"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Loss Scatter Plot 🚀","metadata":{"id":"0SHsAs9fn0o9"}},{"cell_type":"code","source":"plt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.title('Training and Validation Accuracy')\nplt.legend()\nplt.scatter([i+1 for i in range(len(losses))], losses)","metadata":{"id":"hP-wfJG9QrBa","outputId":"07f44e5c-a58d-4ca8-d09c-52bc526887c4"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#Model Evaluation 🤓","metadata":{"id":"XOB8XE0744es"}},{"cell_type":"code","source":"trained_model.eval()","metadata":{"id":"P16Eo4gc3B4t","outputId":"5cd7e0c6-9adf-4231-819e-ad00c6668a60"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Prediction 🔮","metadata":{"id":"3xUM-QYeoA3Y"}},{"cell_type":"code","source":"def Predict(dataset, model, num_classes):\n    validate_true_labels = []\n    validate_predicted_labels = []\n    validate_predicted_probs = [[] for _ in range(num_classes)]\n\n    with torch.no_grad():\n        for inputs, labels in dataset:\n            inputs = inputs.to(device)\n            labels = labels.to(device)\n\n            validate_outputs = model(inputs)\n            validate_softmax_probs = torch.softmax(validate_outputs, dim=1)\n            _, preds = torch.max(validate_outputs, 1)\n\n            validate_true_labels.extend(labels.cpu().numpy())\n            validate_predicted_labels.extend(preds.cpu().numpy())\n\n            for i in range(num_classes):\n                validate_predicted_probs[i].extend(validate_softmax_probs[:, i].cpu().numpy())\n\n    return validate_true_labels, validate_predicted_labels, validate_predicted_probs\n","metadata":{"id":"lSW0MAWU4_G5"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.metrics import accuracy_score, precision_score, recall_score, roc_auc_score, f1_score, confusion_matrix\n\ndef Evaluate(predicted_probs_class1, true_labels, predicted_labels):\n    predicted_probs = predicted_probs_class1\n\n    accuracy = accuracy_score(true_labels, predicted_labels)\n    precision = precision_score(true_labels, predicted_labels, average='macro')\n    recall = recall_score(true_labels, predicted_labels, average='macro')\n    f1 = f1_score(true_labels, predicted_labels, average='macro')\n    cm = confusion_matrix(true_labels, predicted_labels)\n    macro_cm = cm.sum(axis=0) / len(true_labels)\n\n    print(f'Accuracy: {accuracy:.4f}')\n    print(f'Precision: {precision:.4f}')\n    print(f'Recall: {recall:.4f}')\n    print(f'F1-score: {f1:.4f}')\n    print(f'Macro-Averaged Confusion Matrix:\\n{macro_cm}')\n\n","metadata":{"id":"dat5Q9kQ5QfI"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Accuracies 👻","metadata":{"id":"QMTm6vWuoEqq"}},{"cell_type":"code","source":"validate_true_labels = []\nvalidate_predicted_labels = []\nvalidate_predicted_probs_class0 = []\nvalidate_predicted_probs_class1 = []\n\nvalidate_true_labels, validate_predicted_labels, validate_predicted_probs_class = Predict(validate_loader, trained_model, 3)\nEvaluate(validate_predicted_probs_class1, validate_true_labels, validate_predicted_labels)","metadata":{"id":"Ovp3HLXu5gNe","outputId":"cab58d2b-3662-49d0-d599-6f1d9c4ada50"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.load_state_dict(torch.load('/content/best_model_acc_0.9545.pt'))","metadata":{"id":"BoMQlvTH7__T","outputId":"170ecfab-ea6e-4aa0-b5d3-e3401541805a"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Testing On Test Data 🏁","metadata":{"id":"qQd-N9InkS5m"}},{"cell_type":"code","source":"!pip install tqdm","metadata":{"id":"snKjv3PJj99s","outputId":"6c51c740-ac11-40c2-a6f6-b76d60646084"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport torch\nfrom torchvision import transforms\nfrom PIL import Image\nfrom tqdm import tqdm\nimport pandas as pd\n\ncheckpoint = torch.load('/content/best_model_acc_0.9545.pt', map_location=torch.device('cpu'))\nmodel.load_state_dict(checkpoint)\nmodel.eval()\n\nclass_names = {\n    0: 'beneign',\n    1:'malignant',\n    2:'normal',\n}\n\n\ntest_transform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n])\n\ntest_folder = './test'\n\nimage_files = [f for f in os.listdir(test_folder) if f.endswith('.png')]\n\nresults = {}\n\n# Process each image\nfor image_file in tqdm(image_files):\n    image_path = os.path.join(test_folder, image_file)\n    rgb_image = Image.open(image_path).convert('RGB')\n    transformed_image = test_transform(rgb_image)\n    model.to('cpu')\n    transformed_image = transformed_image.to('cpu')\n    with torch.no_grad():\n        outputs = model(transformed_image.unsqueeze(0))\n        _, predicted = torch.max(outputs, 1)\n        predicted_class_name = class_names[predicted.item()]\n        results[image_file] = predicted_class_name\n\n\nresults_df = pd.DataFrame(list(results.items()), columns=['filename', 'class'])\noutput_csv_path = 'submission.csv'\n\nresults_df.to_csv(output_csv_path, index=False)\n\nprint(f\"Results written to {output_csv_path}\")\n","metadata":{"id":"MdyfwdetaCxE","outputId":"2dab4941-e5be-49c8-ce80-e1399eaea333"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Inference Time ⏰","metadata":{"id":"5mWXekdvk-MR"}},{"cell_type":"code","source":"%%time\nimage_path = os.path.join(test_folder, image_file)\n\nrgb_image = Image.open('./test/BAHEYA_114876720430193_10000027.png').convert('RGB')\n\n\ntransformed_image = test_transform(rgb_image)\nmodel.to('cpu')\ntransformed_image = transformed_image.to('cpu')\n\nwith torch.no_grad():\n    outputs = model(transformed_image.unsqueeze(0))\n\n    _, predicted = torch.max(outputs, 1)\n\n    predicted_class_name = class_names[predicted.item()]\n\n    results[image_file] = predicted_class_name\n","metadata":{"id":"gilUQUlbQ7dF","outputId":"ee74ab37-fcd5-40db-fee8-d4d809cedd8b"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(results)","metadata":{"id":"XdG_c7-7lWTA","outputId":"6c8a45b6-511a-49f7-bcd5-dfacb09fbcb8"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pd.read_csv('submission.csv')","metadata":{"id":"SvTNJa3idezQ","outputId":"12a90101-fc8f-4792-a23a-5ff1da3d62ca"},"execution_count":null,"outputs":[]}],"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"}}