{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":45867,"databundleVersionId":6924515,"sourceType":"competition"}],"dockerImageVersionId":30626,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Importing  Libraries","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\n\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.metrics import confusion_matrix\nfrom sklearn.metrics import classification_report\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.optim import lr_scheduler\nimport torchvision\nfrom torch.utils.data import Dataset, random_split\nfrom torchvision import transforms\nfrom torchvision.io import read_image\nfrom torchvision import datasets, models\nfrom torchvision.transforms import ToTensor\nfrom torch.utils.data import DataLoader\nfrom torch.utils.data.sampler import SubsetRandomSampler\nimport torch.nn.functional as F\n\nfrom PIL import Image\n\nimport time\nimport os\nimport datetime\nimport copy\nimport shutil","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-12-25T03:04:17.464549Z","iopub.execute_input":"2023-12-25T03:04:17.46543Z","iopub.status.idle":"2023-12-25T03:04:20.315951Z","shell.execute_reply.started":"2023-12-25T03:04:17.465394Z","shell.execute_reply":"2023-12-25T03:04:20.314955Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check if GPU is available\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")  \nprint('device =',device)","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:20.317931Z","iopub.execute_input":"2023-12-25T03:04:20.318699Z","iopub.status.idle":"2023-12-25T03:04:20.348963Z","shell.execute_reply.started":"2023-12-25T03:04:20.318668Z","shell.execute_reply":"2023-12-25T03:04:20.348089Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pd.read_csv(r\"/kaggle/input/UBC-OCEAN/train.csv\")\ntest = pd.read_csv(r\"/kaggle/input/UBC-OCEAN/test.csv\")","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:20.350372Z","iopub.execute_input":"2023-12-25T03:04:20.350732Z","iopub.status.idle":"2023-12-25T03:04:20.385926Z","shell.execute_reply.started":"2023-12-25T03:04:20.350699Z","shell.execute_reply":"2023-12-25T03:04:20.385197Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train.head()","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:20.387827Z","iopub.execute_input":"2023-12-25T03:04:20.38813Z","iopub.status.idle":"2023-12-25T03:04:20.407535Z","shell.execute_reply.started":"2023-12-25T03:04:20.388104Z","shell.execute_reply":"2023-12-25T03:04:20.406438Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Make it easier to access tma and non_tma images\ntma = train[train[\"is_tma\"] == True]\nno_tma = train[train[\"is_tma\"] == False]\n\nno_tma['image_id_path'] = [f\"{i}_thumbnail.png\" for i in no_tma['image_id']]\ntma['image_id_path'] = [f\"{i}.png\" for i in tma['image_id']]\ntest['image_id_path'] = [f\"{i}_thumbnail.png\" for i in test['image_id']]","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:20.40863Z","iopub.execute_input":"2023-12-25T03:04:20.408932Z","iopub.status.idle":"2023-12-25T03:04:20.424183Z","shell.execute_reply.started":"2023-12-25T03:04:20.408904Z","shell.execute_reply":"2023-12-25T03:04:20.423098Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"no_tma.head()","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:20.425487Z","iopub.execute_input":"2023-12-25T03:04:20.425856Z","iopub.status.idle":"2023-12-25T03:04:20.442166Z","shell.execute_reply.started":"2023-12-25T03:04:20.425819Z","shell.execute_reply":"2023-12-25T03:04:20.441154Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pd.concat([tma,no_tma],ignore_index=True)","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:20.443485Z","iopub.execute_input":"2023-12-25T03:04:20.443835Z","iopub.status.idle":"2023-12-25T03:04:20.451734Z","shell.execute_reply.started":"2023-12-25T03:04:20.443801Z","shell.execute_reply":"2023-12-25T03:04:20.450738Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Encode the labels\nlaben = LabelEncoder()\n\nlabels = train['label']\nlaben.fit(labels)\ntrain['label'] = laben.transform(labels)","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:20.453052Z","iopub.execute_input":"2023-12-25T03:04:20.453382Z","iopub.status.idle":"2023-12-25T03:04:20.462773Z","shell.execute_reply.started":"2023-12-25T03:04:20.453353Z","shell.execute_reply":"2023-12-25T03:04:20.461695Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train.head()","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:20.464261Z","iopub.execute_input":"2023-12-25T03:04:20.464678Z","iopub.status.idle":"2023-12-25T03:04:20.479087Z","shell.execute_reply.started":"2023-12-25T03:04:20.46464Z","shell.execute_reply":"2023-12-25T03:04:20.477929Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Creating Data Loader\nTaken reference from: https://www.kaggle.com/code/shiyunlong07/pytorch-baseline-by-resnet-pretrained/notebook","metadata":{}},{"cell_type":"code","source":"class ImageLoader(Dataset):\n    def __init__(self, data, root_path, train_thumbnails, train_images, transform=None):\n        self.data = data\n        self.root_path = root_path\n        self.train_thumbnails = train_thumbnails\n        self.train_images = train_images\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n        is_tma = self.data.iloc[idx]['is_tma']\n        img_name = self.data.iloc[idx, -1]\n    \n        if is_tma:\n            path = os.path.join(root_path, self.train_images, img_name)\n        else:\n            path = os.path.join(root_path, self.train_thumbnails, img_name)\n\n        image = Image.open(path).convert('RGB')\n        if self.transform:\n            image = self.transform(image)\n\n        label = torch.tensor(self.data.iloc[idx, 1])\n        \n        return image, label","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:20.483251Z","iopub.execute_input":"2023-12-25T03:04:20.483838Z","iopub.status.idle":"2023-12-25T03:04:20.493111Z","shell.execute_reply.started":"2023-12-25T03:04:20.483807Z","shell.execute_reply":"2023-12-25T03:04:20.492062Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_transform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.RandomCrop(224, padding=4),\n    transforms.CenterCrop(224),\n    transforms.RandomHorizontalFlip(p=0.5),\n    transforms.RandomVerticalFlip(p=0.5),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406], \n                         [0.229, 0.224, 0.225]),\n])\n\nvalid_transform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406], \n                         [0.229, 0.224, 0.225])\n])\n\n# https://discuss.pytorch.org/t/simple-way-to-inverse-transform-normalization/4821\ninvTrans = transforms.Compose([ transforms.Normalize(mean = [ 0., 0., 0. ],\n                                                     std = [ 1/0.229, 1/0.224, 1/0.225 ]),\n                                transforms.Normalize(mean = [ -0.485, -0.456, -0.406 ],\n                                                     std = [ 1., 1., 1. ]),\n                               ])\n\nroot_path = \"/kaggle/input/UBC-OCEAN/\"\ntrain_thumbs = \"train_thumbnails/\"\ntrain_images = \"train_images/\"\n\ntest_thumbs = \"test_thumbnails/\"\ntest_images = \"test_images/\"\n\ndataset = ImageLoader(train, root_path, train_thumbs, train_images, train_transform)\n\ntrain_ratio = 0.8\nvalid_ratio = 0.2\n\ntrain_size = int(train_ratio * len(train))\nvalid_size = len(train) - train_size\n\ntrain_data, valid_data = random_split(dataset, [train_size, valid_size])\n\ntrain_data_loader = DataLoader(train_data, batch_size = 16, shuffle = False, num_workers = 4)\nvalid_data_loader = DataLoader(valid_data, batch_size = 16, shuffle = False, num_workers = 4)\nclasses = laben.classes_","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:20.494549Z","iopub.execute_input":"2023-12-25T03:04:20.494876Z","iopub.status.idle":"2023-12-25T03:04:20.528325Z","shell.execute_reply.started":"2023-12-25T03:04:20.494846Z","shell.execute_reply":"2023-12-25T03:04:20.527369Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"classes","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:20.529487Z","iopub.execute_input":"2023-12-25T03:04:20.530289Z","iopub.status.idle":"2023-12-25T03:04:20.536552Z","shell.execute_reply.started":"2023-12-25T03:04:20.530259Z","shell.execute_reply":"2023-12-25T03:04:20.535552Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Display random images\ndef imshow(img):\n    plt.imshow(np.transpose(img, (1, 2, 0)))  \n    \nfor i, (images, lbls) in enumerate(train_data_loader, 0):\n    images = invTrans(images)\n    images, labels = images.numpy(), laben.inverse_transform(lbls)\n    \n    fig = plt.figure(figsize=(15,8))\n    \n    for idx in np.arange(10):\n        ax = fig.add_subplot(2, 5, idx+1, xticks=[], yticks=[])\n        imshow(images[idx])\n        label = lbls[idx]\n        ax.set_title(labels[idx])\n    break","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:20.537865Z","iopub.execute_input":"2023-12-25T03:04:20.538252Z","iopub.status.idle":"2023-12-25T03:04:33.077551Z","shell.execute_reply.started":"2023-12-25T03:04:20.538217Z","shell.execute_reply":"2023-12-25T03:04:33.076555Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model Training","metadata":{}},{"cell_type":"code","source":"model = models.resnet34(weights='ResNet34_Weights.DEFAULT')\n\n# freeze all params\n# for params in model.parameters():\n#     params.requires_grad_ = False\n\n# add a new final layer\nnr_filters = model.fc.in_features  # number of input features of last layer\nmodel.fc = nn.Linear(nr_filters, len(classes))\n\nmodel = model.to(device)","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:33.078853Z","iopub.execute_input":"2023-12-25T03:04:33.079192Z","iopub.status.idle":"2023-12-25T03:04:37.103256Z","shell.execute_reply.started":"2023-12-25T03:04:33.079159Z","shell.execute_reply":"2023-12-25T03:04:37.102071Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define optimizer and loss criteria\ncriterion = nn.CrossEntropyLoss()\n# optimizer = optim.Adam(model.parameters(), lr = 0.0005)\noptimizer = torch.optim.SGD(model.parameters(), lr = 0.0005, momentum= 0.8)","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:37.104772Z","iopub.execute_input":"2023-12-25T03:04:37.105209Z","iopub.status.idle":"2023-12-25T03:04:37.11289Z","shell.execute_reply.started":"2023-12-25T03:04:37.105169Z","shell.execute_reply":"2023-12-25T03:04:37.111658Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Name of the weights file\nweights_filename = 'baseline_resnet34.pt'","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:37.114251Z","iopub.execute_input":"2023-12-25T03:04:37.114571Z","iopub.status.idle":"2023-12-25T03:04:37.12731Z","shell.execute_reply.started":"2023-12-25T03:04:37.114543Z","shell.execute_reply":"2023-12-25T03:04:37.126195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\n\nn_epochs = 30\nval_loss = []\nval_acc = []\ntrain_loss = []\ntrain_acc = []\ntotal_step = len(train_data_loader)\n\nfor epoch in range(n_epochs):\n    running_loss = 0.0\n    correct = 0\n    total=0\n    print(f'\\nEpoch {epoch+1}:')\n    for batch_idx, (data_, target_) in enumerate(train_data_loader):        \n        data_, target_ = data_.to(device), target_.to(device)\n        # zero the parameter gradients\n        optimizer.zero_grad()\n        # forward + backward + optimize\n        outputs = model(data_)\n        loss = criterion(outputs, target_)\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item()\n        _,pred = torch.max(outputs, dim=1)\n        correct += torch.sum(pred==target_).item()\n        total += target_.size(0)\n        \n        if (batch_idx) %8 == 0:  \n            print ('Epoch [{} of {}], Step [{} of {}], Loss: {:.4f}' \n                   .format(epoch+1, n_epochs, batch_idx, total_step, loss.item()))\n    \n    train_acc.append(100 * correct / total)\n    train_loss.append(running_loss/total_step)\n    print(f'train loss:      {np.mean(train_loss):.3f}, train acc:      {(100 * correct / total):.1f}%')\n    batch_loss = 0\n    total_t=0\n    correct_t=0\n    with torch.no_grad():\n        model.eval()\n        for data_t, target_t in (valid_data_loader):\n            data_t, target_t = data_t.to(device), target_t.to(device)# on GPU\n            outputs_t = model(data_t)\n            loss_t = criterion(outputs_t, target_t)\n            batch_loss += loss_t.item()\n            _,pred_t = torch.max(outputs_t, dim=1)\n            correct_t += torch.sum(pred_t==target_t).item()\n            total_t += target_t.size(0)\n        val_acc.append(100 * correct_t / total_t)\n        val_loss.append(batch_loss/len(valid_data_loader))\n\n        print(f'validation loss: {np.mean(val_loss):.3f}, validation acc: {(100 * correct_t / total_t):.1f}%')\n        torch.save(model.state_dict(), weights_filename)\n\n    model.train()","metadata":{"execution":{"iopub.status.busy":"2023-12-25T03:04:37.128837Z","iopub.execute_input":"2023-12-25T03:04:37.129536Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(12, 4))\n\nplt.subplot(1, 2, 1)\nplt.plot( train_loss, label='train')\nplt.plot( val_loss, label='validation')\nplt.title(\"Train - Validation Loss\")\nplt.xlabel('num_epochs', fontsize=12)\nplt.ylabel('loss', fontsize=12)\n\n\nplt.subplot(1, 2, 2)\nplt.plot(train_acc, label='train')\nplt.plot(val_acc, label='validation')\nplt.title(\"Train - Validation Accuracy\")\nplt.xlabel('num_epochs', fontsize=12)\nplt.ylabel('accuracy', fontsize=12)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Predicting on the Test Image","metadata":{}},{"cell_type":"code","source":"class TestImageLoader(Dataset):\n    def __init__(self, data, root_path, test_thumbnails, transform=None):\n        self.data = data\n        self.root_path = root_path\n        self.test_thumbnails = test_thumbnails\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n        img_name = self.data.iloc[idx, -1]\n        path = os.path.join(root_path, self.test_thumbnails, img_name)\n\n        image = Image.open(path).convert('RGB')\n        if self.transform:\n            image = self.transform(image)\n        \n        return image","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_data = TestImageLoader(test, root_path, test_thumbs, valid_transform)\ntest_data_loader = DataLoader(test_data, batch_size = 32, shuffle = False, num_workers = 4)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y_pred = []\nfor inputs in test_data_loader:\n    inputs = inputs.to(device)\n    output = model(inputs)\n    \n    output = (torch.max(torch.exp(output), 1)[1]).data.cpu().numpy()\n    y_pred.extend(output)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Our test data belongs to class: \", laben.inverse_transform(y_pred)[0])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Creating submission file\ntest['label'] = laben.inverse_transform(y_pred)[0]\ntest.drop(['image_width', 'image_id_path', 'image_height'], axis = 1, inplace=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test.to_csv('submission.csv', index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### If you enjoyed the notebook do upvote, and comment if you have any suggestions. Thank you!","metadata":{}}]}