{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":12631,"databundleVersionId":682729,"sourceType":"competition"}],"dockerImageVersionId":30636,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"from __future__ import print_function\nfrom __future__ import division\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision\nfrom torchvision import datasets, models, transforms\nfrom torch.utils.data.dataset import Dataset\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom pathlib import Path\nimport time\nimport os\nimport copy\nfrom PIL import *\n# Function for shuffling the dataset\nfrom sklearn.utils import shuffle\nfrom tqdm import tqdm_notebook as tqdm\n\nprint(\"PyTorch Version: \",torch.__version__)\nprint(\"Torchvision Version: \",torchvision.__version__)","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:23:53.946572Z","iopub.execute_input":"2024-01-22T06:23:53.947613Z","iopub.status.idle":"2024-01-22T06:23:53.959219Z","shell.execute_reply.started":"2024-01-22T06:23:53.947570Z","shell.execute_reply":"2024-01-22T06:23:53.958018Z"},"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\"); device","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:23:56.920241Z","iopub.execute_input":"2024-01-22T06:23:56.921126Z","iopub.status.idle":"2024-01-22T06:23:56.927049Z","shell.execute_reply.started":"2024-01-22T06:23:56.921091Z","shell.execute_reply":"2024-01-22T06:23:56.926106Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!unzip -q /kaggle/input/hbku2019/imgs.zip ","metadata":{"execution":{"iopub.status.busy":"2024-01-22T05:51:10.672654Z","iopub.execute_input":"2024-01-22T05:51:10.673333Z","iopub.status.idle":"2024-01-22T05:52:29.535412Z","shell.execute_reply.started":"2024-01-22T05:51:10.673296Z","shell.execute_reply":"2024-01-22T05:52:29.534312Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!unzip -q /kaggle/input/hbku2019/labels.zip","metadata":{"execution":{"iopub.status.busy":"2024-01-22T05:52:29.537877Z","iopub.execute_input":"2024-01-22T05:52:29.538187Z","iopub.status.idle":"2024-01-22T05:52:30.626603Z","shell.execute_reply.started":"2024-01-22T05:52:29.538159Z","shell.execute_reply":"2024-01-22T05:52:30.625401Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DIR = Path(\"./\").absolute()\nDATA_DIR = DIR / 'labels'\nIMG_DIR_TRAIN = DIR / 'imgs/train/'\nIMG_DIR_TEST = DIR / 'imgs/test/'\ndata_csv= DATA_DIR / 'labels_train.csv'","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:23:59.392828Z","iopub.execute_input":"2024-01-22T06:23:59.393161Z","iopub.status.idle":"2024-01-22T06:23:59.398130Z","shell.execute_reply.started":"2024-01-22T06:23:59.393137Z","shell.execute_reply":"2024-01-22T06:23:59.397239Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data","metadata":{}},{"cell_type":"code","source":"cats = pd.read_csv(DATA_DIR / \"categories.csv\", header=None)\ncats = list(cats[0]); cats","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:23:59.999221Z","iopub.execute_input":"2024-01-22T06:23:59.999575Z","iopub.status.idle":"2024-01-22T06:24:00.011925Z","shell.execute_reply.started":"2024-01-22T06:23:59.999548Z","shell.execute_reply":"2024-01-22T06:24:00.010745Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data = pd.read_csv(data_csv)","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:00.348980Z","iopub.execute_input":"2024-01-22T06:24:00.349286Z","iopub.status.idle":"2024-01-22T06:24:01.213280Z","shell.execute_reply.started":"2024-01-22T06:24:00.349263Z","shell.execute_reply":"2024-01-22T06:24:01.212244Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data.head()","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:01.215184Z","iopub.execute_input":"2024-01-22T06:24:01.216056Z","iopub.status.idle":"2024-01-22T06:24:01.249587Z","shell.execute_reply.started":"2024-01-22T06:24:01.216014Z","shell.execute_reply":"2024-01-22T06:24:01.248514Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Custom Dataset Object\nWe need to define a custom dataset object to define how raw img files will be loaded into the system for training/testing","metadata":{}},{"cell_type":"code","source":"class CustomDatasetFromCSV(Dataset):\n    def __init__(self, csv_path, transformations, train):\n        \"\"\"\n        Args:\n            csv_path (string): path to csv file\n            transformations: pytorch transforms for transforms and tensor conversion\n            train: flag to determine if train or val set\n        \"\"\"\n        # Transforms\n        self.transforms = transformations\n        # Read the csv file\n        self.data_info = pd.read_csv(csv_path)\n        \n        # IMPORTANT: dataset needs to be shuffled because by default,\n        # the loaded dataset has an ordering which may cause examples of \n        # certain classes from being missed in the training set if following \n        # the below method to create a train/val split\n        self.data_info = shuffle(self.data_info)\n        \n        # First 90k images becomes the training set and the rest becomes\n        # validation set\n        if train:\n            self.image_arr = (self.data_info.iloc[:90000, 0])\n        else:\n            self.image_arr = (self.data_info.iloc[90000:, 0])\n  \n        self.image_arr = np.asarray(self.image_arr)\n        \n        # Second column is the labels\n        if train:\n            self.label_arr = np.asarray(self.data_info.iloc[:90000, 1:])\n        else:\n            self.label_arr = np.asarray(self.data_info.iloc[90000:, 1:])\n\n        # Calculate len\n        self.data_len = len(self.label_arr)\n\n    def __getitem__(self, index):\n        # Get image name from the pandas df\n        single_image_name = self.image_arr[index]\n        \n        # Open image\n        img_as_img = Image.open(IMG_DIR_TRAIN / single_image_name).convert('RGB')\n\n        if self.transforms is not None:\n            img_as_tensor = self.transforms(img_as_img)\n        \n        single_image_label = self.label_arr[index]\n\n        return (img_as_tensor, single_image_label)\n\n    def __len__(self):\n        return self.data_len","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:01.741526Z","iopub.execute_input":"2024-01-22T06:24:01.741889Z","iopub.status.idle":"2024-01-22T06:24:01.751936Z","shell.execute_reply.started":"2024-01-22T06:24:01.741860Z","shell.execute_reply":"2024-01-22T06:24:01.751069Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_name = \"resnet\"\n\n# Batch size for training (change depending on how much memory you have)\nbatch_size = 128\n\n# Number of epochs to train for\nnum_epochs = 10\n\n# Flag for feature extracting. When False, we finetune the whole model\nfeature_extract = True\n\n# Number of classes\nnum_classes = 80\n\n# Img model input size\nim_size = 224","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:02.108170Z","iopub.execute_input":"2024-01-22T06:24:02.108524Z","iopub.status.idle":"2024-01-22T06:24:02.115581Z","shell.execute_reply.started":"2024-01-22T06:24:02.108495Z","shell.execute_reply":"2024-01-22T06:24:02.114526Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Transformations on the data\ntransformations = transforms.Compose([transforms.Resize((im_size, im_size)),\n                                      transforms.ToTensor(),\n                                      transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n                                     ])\n\n# Define a dictionary of dataset\n# one for train and one for validation4\nimage_datasets = {'train': CustomDatasetFromCSV(data_csv, transformations, True), \n                 'val': CustomDatasetFromCSV(data_csv, transformations, False)}\n\n# Define data loader [set num_workers to 0 or 1 if on windows]\ndataloaders_dict = {x: torch.utils.data.DataLoader(image_datasets[x], \n                                                   batch_size=batch_size, \n                                                   shuffle=True, \n                                                   num_workers=6) for x in ['train', 'val']}","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:02.384087Z","iopub.execute_input":"2024-01-22T06:24:02.384408Z","iopub.status.idle":"2024-01-22T06:24:04.186933Z","shell.execute_reply.started":"2024-01-22T06:24:02.384365Z","shell.execute_reply":"2024-01-22T06:24:04.185794Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model","metadata":{}},{"cell_type":"code","source":"# Function for calculating accuracy of the model\ndef calc_accuracy(preds, labels):\n    return (np.sum(np.around(preds.cpu().detach().numpy()) == labels.cpu().detach().numpy()))\n\n# Function for calculating MEAN Average Precision(MAP) score\ndef calc_map(preds, labels):\n    preds = np.around(preds.cpu().detach().numpy())\n    labels = labels.cpu().detach().numpy()             \n    pred = []\n    for i in preds:\n        cats = np.nonzero(list(i))[0]\n        pred.append(list(cats))\n    label = []\n    for i in labels:\n        cats = np.nonzero(list(i))[0]\n        label.append(list(cats))\n    return metrics.mapk(label, pred)","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:04.188775Z","iopub.execute_input":"2024-01-22T06:24:04.189101Z","iopub.status.idle":"2024-01-22T06:24:04.197428Z","shell.execute_reply.started":"2024-01-22T06:24:04.189073Z","shell.execute_reply":"2024-01-22T06:24:04.196407Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train_model(model, dataloaders, criterion, optimizer, num_epochs=25):\n    since = time.time()\n\n    val_acc_history = []\n\n    best_model_wts = copy.deepcopy(model.state_dict())\n    best_acc = 0.0\n    for epoch in range(num_epochs):\n        print('Epoch {}/{}'.format(epoch, num_epochs - 1))\n        print('-' * 10)\n\n        # Each epoch has a training and validation phase\n        for phase in ['train', 'val']:\n            if phase == 'train':\n                model.train()  # Set model to training mode\n            else:\n                model.eval()   # Set model to evaluate mode\n\n            running_loss = 0.0\n            running_corrects = 0\n            running_mapk = 0.0\n            # Iterate over data.\n            for inputs, labels in tqdm(dataloaders[phase]):\n                inputs = inputs.to(device)\n                labels = labels.to(device)\n                labels = labels.float()\n                # zero the parameter gradients\n                optimizer.zero_grad()\n\n                # forward\n                # track history if only in train\n                with torch.set_grad_enabled(phase == 'train'):\n                    # Get model outputs and calculate loss\n                    outputs = model(inputs)\n                    # For multi-label\n                    outputs = torch.sigmoid(outputs)\n                    loss = criterion(outputs, labels)\n\n                    preds = outputs\n\n                    # backward + optimize only if in training phase\n                    if phase == 'train':\n                        loss.backward()\n                        optimizer.step()\n                # statistics\n                running_loss += loss.item() * inputs.size(0)\n                running_corrects += calc_accuracy(preds, labels.data)\n                running_mapk += calc_map(preds, labels.data)\n\n            epoch_loss = running_loss / len(dataloaders[phase].dataset)\n            epoch_acc = running_corrects / len(dataloaders[phase].dataset)\n            \n            epoch_mapk = running_mapk / len(dataloaders[phase])\n            print('{} Loss: {:.4f} Acc: {:.4f} MAP: {:.4f}'.format(phase, epoch_loss, epoch_acc, epoch_mapk))\n\n            # deep copy the model [TODO: change to save based on mapk]\n            if phase == 'val' and epoch_acc > best_acc:\n                best_acc = epoch_acc\n                best_model_wts = copy.deepcopy(model.state_dict())\n            if phase == 'val':\n                val_acc_history.append(epoch_acc)\n\n        print()\n\n    time_elapsed = time.time() - since\n    print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))\n    print('Best val Acc: {:4f}'.format(best_acc))\n\n    # load best model weights\n    model.load_state_dict(best_model_wts)\n    return model, val_acc_history","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:04.959412Z","iopub.execute_input":"2024-01-22T06:24:04.959785Z","iopub.status.idle":"2024-01-22T06:24:04.973417Z","shell.execute_reply.started":"2024-01-22T06:24:04.959759Z","shell.execute_reply":"2024-01-22T06:24:04.972358Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Set which parts of the model should be trained\ndef set_parameter_requires_grad(model, feature_extracting):\n    if feature_extracting:\n        for param in model.parameters():\n            param.requires_grad = False","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:08.415985Z","iopub.execute_input":"2024-01-22T06:24:08.416352Z","iopub.status.idle":"2024-01-22T06:24:08.421861Z","shell.execute_reply.started":"2024-01-22T06:24:08.416324Z","shell.execute_reply":"2024-01-22T06:24:08.420946Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CNNS(nn.Module):\n    def __init__(self, num_classes=num_classes):\n        super(CNNS, self).__init__()\n        self.features = nn.Sequential(\n            nn.Conv2d(3, 32, kernel_size=5, stride=2, padding=2),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(kernel_size=3, stride=2),\n            nn.Conv2d(32, 64, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(kernel_size=3, stride=2),\n            nn.Conv2d(64, 128, kernel_size=3, padding=1),\n            nn.ReLU(inplace=True),\n            nn.MaxPool2d(kernel_size=3, stride=2),\n        )\n        self.avgpool = nn.AdaptiveAvgPool2d((6, 6))\n        self.classifier = nn.Sequential(\n            nn.Dropout(),\n            nn.Linear(128 * 6 * 6, 512),\n            nn.ReLU(inplace=True),\n            nn.Dropout(),\n            nn.Linear(512, num_classes),\n        )\n\n    def forward(self, x):\n        x = self.features(x)\n        x = self.avgpool(x)\n        x = torch.flatten(x, 1)\n        x = self.classifier(x)\n        return x","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:08.668085Z","iopub.execute_input":"2024-01-22T06:24:08.668440Z","iopub.status.idle":"2024-01-22T06:24:08.678651Z","shell.execute_reply.started":"2024-01-22T06:24:08.668406Z","shell.execute_reply":"2024-01-22T06:24:08.677723Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = CNNS(num_classes=num_classes)","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:09.411978Z","iopub.execute_input":"2024-01-22T06:24:09.412348Z","iopub.status.idle":"2024-01-22T06:24:09.436208Z","shell.execute_reply.started":"2024-01-22T06:24:09.412320Z","shell.execute_reply":"2024-01-22T06:24:09.435448Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def initialize_model(model, num_classes, feature_extract, use_pretrained=True):\n    return model, im_size\nmodel_ft, input_size = initialize_model(model, num_classes, feature_extract, use_pretrained=True)","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:10.400633Z","iopub.execute_input":"2024-01-22T06:24:10.400983Z","iopub.status.idle":"2024-01-22T06:24:10.406844Z","shell.execute_reply.started":"2024-01-22T06:24:10.400958Z","shell.execute_reply":"2024-01-22T06:24:10.405759Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Send the model to GPU\nmodel_ft = model_ft.to(device)","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:11.342527Z","iopub.execute_input":"2024-01-22T06:24:11.342899Z","iopub.status.idle":"2024-01-22T06:24:11.350936Z","shell.execute_reply.started":"2024-01-22T06:24:11.342870Z","shell.execute_reply":"2024-01-22T06:24:11.350060Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Gather the parameters to be optimized/updated in this run. If we are\n# finetuning we will be updating all parameters. However, if we are\n# doing feature extract method, we will only update the parameters\n# that we have just initialized, i.e. the parameters with requires_grad\n# is True.\nparams_to_update = model_ft.parameters()\nprint(\"Params to learn:\")\nif feature_extract:\n    params_to_update = []\n    for name,param in model_ft.named_parameters():\n        if param.requires_grad == True:\n            params_to_update.append(param)\n            print(\"\\t\",name)\nelse:\n    for name,param in model_ft.named_parameters():\n        if param.requires_grad == True:\n            print(\"\\t\",name)","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:13.144360Z","iopub.execute_input":"2024-01-22T06:24:13.144762Z","iopub.status.idle":"2024-01-22T06:24:13.152190Z","shell.execute_reply.started":"2024-01-22T06:24:13.144734Z","shell.execute_reply":"2024-01-22T06:24:13.151027Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Setting optimizer\n#optimizer_ft = optim.SGD(params_to_update, lr=0.01, momentum=0.9)\noptimizer_ft = optim.Adam(params_to_update, lr=0.0001)\n","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:14.150301Z","iopub.execute_input":"2024-01-22T06:24:14.150747Z","iopub.status.idle":"2024-01-22T06:24:14.155427Z","shell.execute_reply.started":"2024-01-22T06:24:14.150714Z","shell.execute_reply":"2024-01-22T06:24:14.154492Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install -U \"setuptools<58\"\n!pip install ml_metrics","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:03:33.702442Z","iopub.execute_input":"2024-01-22T06:03:33.702828Z","iopub.status.idle":"2024-01-22T06:04:09.134498Z","shell.execute_reply.started":"2024-01-22T06:03:33.702800Z","shell.execute_reply":"2024-01-22T06:04:09.133419Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import ml_metrics as metrics","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:19.929469Z","iopub.execute_input":"2024-01-22T06:24:19.930190Z","iopub.status.idle":"2024-01-22T06:24:19.934108Z","shell.execute_reply.started":"2024-01-22T06:24:19.930154Z","shell.execute_reply":"2024-01-22T06:24:19.933213Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Binary Cross Entropy loss function\ncriterion = torch.nn.BCELoss()","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:20.351236Z","iopub.execute_input":"2024-01-22T06:24:20.351592Z","iopub.status.idle":"2024-01-22T06:24:20.355981Z","shell.execute_reply.started":"2024-01-22T06:24:20.351564Z","shell.execute_reply":"2024-01-22T06:24:20.355071Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_ft, hist = train_model(model_ft, dataloaders_dict, criterion, optimizer_ft, num_epochs=num_epochs)","metadata":{"execution":{"iopub.status.busy":"2024-01-22T06:24:21.225472Z","iopub.execute_input":"2024-01-22T06:24:21.225882Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}