{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":45867,"databundleVersionId":6924515,"sourceType":"competition"},{"sourceId":146982497,"sourceType":"kernelVersion"}],"dockerImageVersionId":30646,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch.nn.functional as F\n\nimport pandas as pd\nimport os\nfrom PIL import Image\nfrom torch.utils.data import Dataset, DataLoader, random_split\nfrom torchvision import transforms\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision.models as models\nfrom torchvision.datasets import ImageFolder\nfrom torchvision.utils import make_grid\nimport matplotlib.pyplot as plt","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv('/kaggle/input/UBC-OCEAN/train.csv')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"target_labels = train_df.is_tma.astype('int') # True/False into 0/1\ntrain_ids = train_df.image_id\ntbn_images_list = os.listdir('/kaggle/input/UBC-OCEAN/train_thumbnails')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"file_dict = {}\n\nfor file_name in tbn_images_list:\n    parts = file_name.split('_')\n\n    file_id = parts[0]\n    file_dict[file_id] = file_name","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# I see some images are missing from thumbnails, no discard them from our scope for the timing\n\nunique_ids = set(train_df['image_id'])\n\n\nfiltered_dict = {key: value for key, value in file_dict.items() if int(key) in unique_ids}","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Just making sure\nlen(filtered_dict) == len(tbn_images_list)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Make your custom dataset loader\n\nclass UCBDataset(Dataset):\n    def __init__(self, data_dict, root_dir, labels, transform=None):\n        self.data_dict = data_dict\n        self.root_dir = root_dir\n        self.labels = labels\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.data_dict)\n\n    def __getitem__(self, idx):\n        image_id, image_path = list(self.data_dict.items())[idx]\n        image = Image.open(os.path.join(self.root_dir, image_path))\n\n        if self.transform:\n            image = self.transform(image)\n\n        label = self.labels[idx]\n\n        return image, label #Throws images with its target label","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_root_dir = \"/kaggle/input/UBC-OCEAN/train_thumbnails\"\n\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),  # Baseline\n    transforms.ToTensor()\n])\n\ncustom_dataset = UCBDataset(data_dict=filtered_dict, root_dir=image_root_dir, labels=target_labels, transform=transform)\n\n# Making a validation split\nval_ratio = 0.2\ndataset_size = len(custom_dataset)\nval_size = int(val_ratio * dataset_size)\ntrain_size = dataset_size - val_size\ntrain_dataset, val_dataset = random_split(custom_dataset, [train_size, val_size])\n\n# Loading it into a dataloader\nbatch_size = 32\ntrain_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=batch_size)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nprint(device)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class SimpleCNN(nn.Module):\n    def __init__(self):\n        super(SimpleCNN, self).__init__()\n        self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)\n        self.relu1 = nn.ReLU()\n        self.maxpool1 = nn.MaxPool2d(kernel_size=2, stride=2)\n        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)\n        \n        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)\n        self.relu2 = nn.ReLU()\n        self.maxpool2 = nn.MaxPool2d(kernel_size=2, stride=2)\n        self.fc1 = nn.Linear(32 * 56 * 56, 128)\n        self.relu3 = nn.ReLU()\n        self.fc2 = nn.Linear(128, 2)\n\n    def forward(self, x):\n        x = self.pool(F.relu(self.conv1(x)))\n        x = self.pool(F.relu(self.conv2(x)))\n        x = self.conv1(x)\n        x = self.relu1(x)\n        \n        x = self.maxpool1(x)\n        x = self.conv2(x)\n        x = self.relu2(x)\n        x = self.maxpool2(x)\n        x = x.view(x.size(0), -1)\n        x = self.fc1(x)\n        x = self.relu3(x)\n        x = self.fc2(x)\n        return x\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = SimpleCNN().to(device) # Load model to GPU\n\ncriterion = nn.CrossEntropyLoss() # remember using a focal loss due to imbalanced and also augment it later\noptimizer = optim.Adam(model.parameters(), lr=0.001) # Will use a learning rate scheduler later","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"num_epochs = 10 # Good to start with 10\n\n# Save losses for plot\ntrain_losses = []\nval_losses = []\nval_accuracies = []\n\n# Standard forward-backward propagation\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    for i, (images, labels) in enumerate(train_loader, 0):\n        images, labels = images.to(device), labels.to(device)\n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        running_loss += loss.item()\n\n    avg_train_loss = running_loss / len(train_loader)\n    train_losses.append(avg_train_loss)\n\n    # Validation\n    model.eval()\n    val_loss = 0.0\n    correct = 0\n    total = 0\n    with torch.no_grad():\n        for images, labels in val_loader:\n            images, labels = images.to(device), labels.to(device)\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n            val_loss += loss.item()\n\n            _, predicted = torch.max(outputs.data, 1)\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n\n    avg_val_loss = val_loss / len(val_loader)\n    val_losses.append(avg_val_loss)\n\n    accuracy = 100 * correct / total # Messed up accuracy metric, ignore accuracy in logs\n    val_accuracies.append(accuracy)\n\n    print(f'Epoch [{epoch + 1}/{num_epochs}], '\n          f'Train Loss: {avg_train_loss:.4f}, '\n          f'Validation Loss: {avg_val_loss:.4f}, '\n          f'Validation Accuracy: {accuracy:.2f}%')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(12, 6))\nplt.plot(train_losses, label='Training Loss', color='blue', linestyle='-', linewidth=1)\nplt.plot(val_losses, label='Validation Loss', color='red', linestyle='-', linewidth=1)\n\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.legend(loc='upper right')\n\nplt.title('Training and Validation Losses')\nplt.grid(True)\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}