{"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"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport time\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom tqdm.auto import tqdm\n\nfrom PIL import Image\n\nfrom skimage.transform import resize\nfrom sklearn.metrics import confusion_matrix\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nimport torchvision.transforms as transforms\nimport torchvision.transforms.functional as TF\nimport torchvision.models as models","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-07-07T05:03:26.326280Z","iopub.execute_input":"2021-07-07T05:03:26.326749Z","iopub.status.idle":"2021-07-07T05:03:28.647618Z","shell.execute_reply.started":"2021-07-07T05:03:26.326662Z","shell.execute_reply":"2021-07-07T05:03:28.646711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"root = \"/kaggle/input/compsiimmodified\"\n\ndf = pd.read_csv(f\"{root}/train.csv\", index_col=0, dtype={\"label\": \"category\"})\n\ndf[\"boxes\"] = df[\"boxes\"].apply(lambda x: eval(x))\n\n# Remove rows that do not contain the bounding boxes for atypical,\n# indeterminate or typical.\n#df_train = df_train[(df_train[\"boxes\"].apply(lambda x: bool(x))) | (df_train[\"label\"] == \"negative\")]\n\ndf","metadata":{"execution":{"iopub.status.busy":"2021-07-07T05:03:28.649085Z","iopub.execute_input":"2021-07-07T05:03:28.649427Z","iopub.status.idle":"2021-07-07T05:03:28.841346Z","shell.execute_reply.started":"2021-07-07T05:03:28.649390Z","shell.execute_reply":"2021-07-07T05:03:28.840359Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_test = pd.read_csv(f\"{root}/test.csv\", index_col=0)\ndf_test","metadata":{"execution":{"iopub.status.busy":"2021-07-07T05:03:28.843180Z","iopub.execute_input":"2021-07-07T05:03:28.843555Z","iopub.status.idle":"2021-07-07T05:03:28.867753Z","shell.execute_reply.started":"2021-07-07T05:03:28.843517Z","shell.execute_reply":"2021-07-07T05:03:28.866809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Model(nn.Module):\n    def __init__(self):\n        super().__init__()\n        \n        self.gradients = None\n        \n        # Convolutional layers\n        self.conv = models.vgg16(pretrained=True).features\n        \n        # Freeze the parameters of the pretrained network except for the \n        # last few layers\n        for i, param in enumerate(self.conv.parameters()):\n            if i < 22:\n                param.requires_grad = False\n            \n        # Fully connected layers\n        self.dense = nn.Sequential(\n            nn.Linear(25088, 1024),\n            nn.ReLU(),\n            nn.Linear(1024, 1024),\n            nn.ReLU(),\n            nn.Linear(1024, 4),\n            nn.Softmax()\n        )\n        \n    # hook for the gradients of the activations\n    def activations_hook(self, grad):\n        self.gradients = grad\n    \n    # method for the gradient extraction\n    def get_activations_gradient(self):\n        return self.gradients\n    \n    # method for the activation exctraction\n    def get_activations(self, x):\n        return self.conv(x)\n\n    def forward(self, x):\n        x = self.conv(x)\n        x.register_hook(self.activations_hook)\n        x = self.dense(torch.flatten(x, 1))\n        return x\n\n\nmodel = Model()\n\nfor param_tensor in model.state_dict():\n    print(param_tensor, \"\\t\", model.state_dict()[param_tensor].size())","metadata":{"execution":{"iopub.status.busy":"2021-07-07T06:30:19.555441Z","iopub.execute_input":"2021-07-07T06:30:19.555780Z","iopub.status.idle":"2021-07-07T06:30:21.306402Z","shell.execute_reply.started":"2021-07-07T06:30:19.555748Z","shell.execute_reply":"2021-07-07T06:30:21.305440Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sum([p.numel() for p in model.parameters()])","metadata":{"execution":{"iopub.status.busy":"2021-07-07T06:30:21.309679Z","iopub.execute_input":"2021-07-07T06:30:21.309958Z","iopub.status.idle":"2021-07-07T06:30:21.316535Z","shell.execute_reply.started":"2021-07-07T06:30:21.309929Z","shell.execute_reply":"2021-07-07T06:30:21.315284Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if not torch.cuda.is_available():  \n    raise AssertionError(\"Turn on GPU\")\nprint(\"Using GPU\")","metadata":{"execution":{"iopub.status.busy":"2021-07-07T05:41:32.259452Z","iopub.execute_input":"2021-07-07T05:41:32.259699Z","iopub.status.idle":"2021-07-07T05:41:32.263971Z","shell.execute_reply.started":"2021-07-07T05:41:32.259675Z","shell.execute_reply":"2021-07-07T05:41:32.263100Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class DataGenerator:\n    def __init__(self, df, batch_size, im_shape, augment):\n        self.df = df\n        self.indexes = df.index.values\n        self.df[\"code\"] = self.df[\"label\"].cat.codes\n        self.batch_size = batch_size\n        self.im_shape = im_shape\n        self.augment = augment\n        self.inputs = torch.empty((batch_size, *im_shape), dtype=torch.float32)\n        self.targets = torch.empty(batch_size, dtype=torch.long)\n        \n    def get_image(self, image_id, label):\n        filepath = f\"{root}/train/{label}/{image_id}.png\"\n        with Image.open(filepath) as f:\n            im = TF.to_tensor(f)  # (1, height, width)\n\n        im = TF.resize(im, self.im_shape[1:])  # Resize\n        \n        if self.augment:\n            # Affine transformations\n            angle, translate, scale, shear = transforms.RandomAffine.get_params(\n                [-30, 30], [0.05, 0.05], [0.8, 1.2], [-10, 10], im.shape[1:])\n            im = TF.affine(im, angle, translate, scale, shear)\n\n        im = im.repeat(3, 1, 1)  # Change grayscale to rgb by repeating the channel\n        return im        \n        \n    def __iter__(self):\n        \"\"\"Initialize the iterator\"\"\"\n        np.random.shuffle(self.indexes)  # Shuffle the input\n        self.i = 0  # Index for going through the dataframe.\n        \n        # Move the tensors to GPU\n        self.inputs = self.inputs.to(\"cuda:0\")\n        self.targets = self.targets.to(\"cuda:0\")\n\n        return self\n    \n    def __next__(self):\n        while self.i < len(self):\n            # Add an element to the batch.\n            index = self.indexes[self.i]\n            self.inputs[self.i % self.batch_size], self.targets[self.i % self.batch_size] = self[index]\n\n            self.i += 1\n            # If the batch is full, give it away\n            if self.i % self.batch_size == 0:\n                return self.inputs, self.targets\n\n        # Move the tensors to back to CPU to release memory\n        # TODO: Does this actually release memory from the GPU?\n        self.inputs = self.inputs.to(\"cpu\")\n        self.targets = self.targets.to(\"cpu\")\n\n        raise StopIteration\n        \n    def __getitem__(self, index):\n        \"\"\"Get an input and its target.\"\"\"\n        row = self.df.loc[index]\n        return self.get_image(row[\"image_id\"], row[\"label\"]), row[\"code\"]\n    \n    def __len__(self):\n        return len(self.df)","metadata":{"execution":{"iopub.status.busy":"2021-07-07T06:30:29.603981Z","iopub.execute_input":"2021-07-07T06:30:29.604309Z","iopub.status.idle":"2021-07-07T06:30:29.620210Z","shell.execute_reply.started":"2021-07-07T06:30:29.604276Z","shell.execute_reply":"2021-07-07T06:30:29.619308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# TESTING\ndata_generator = DataGenerator(df, 32, (3, 224, 224), augment=True)\n\nfig, axs = plt.subplots(1, 5, figsize=(20, 10))\n\nfor i in range(5):\n    im, target = data_generator[i]\n    axs[i].imshow(im.permute(1, 2, 0), cmap=plt.cm.bone)\n    axs[i].set_title(df[\"label\"].cat.categories[target])\n    \ndel data_generator","metadata":{"execution":{"iopub.status.busy":"2021-07-07T06:30:30.155024Z","iopub.execute_input":"2021-07-07T06:30:30.155386Z","iopub.status.idle":"2021-07-07T06:30:30.884844Z","shell.execute_reply.started":"2021-07-07T06:30:30.155344Z","shell.execute_reply":"2021-07-07T06:30:30.883961Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Initialize\nbatch_size_train = 32\nbatch_size_val = 1\n\nindexes_train = df.sample(frac=0.9, random_state=42).index.values\nindexes_val = df.drop(indexes_train).index.values\ndf_train = df.loc[indexes_train]\ndf_val = df.loc[indexes_val]\n\n#min_count = df[\"label\"].value_counts().min()\n#n_samples_val = 20\n#n_samples_train = min_count - n_samples_val\n#df_train = pd.concat(df[df[\"label\"] == label].sample(n_samples_train, random_state=42) for label in df[\"label\"].cat.categories)\n#df_rest = df.drop(df_train.index)\n#df_val = pd.concat(df_rest[df_rest[\"label\"] == label].sample(n_samples_val, random_state=42) for label in df[\"label\"].cat.categories)\n\ndata_generator_train = DataGenerator(df_train, batch_size_train, (3, 224, 224), augment=True)\ndata_generator_val = DataGenerator(df_val, batch_size_val, (3, 224, 224), augment=False)\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)","metadata":{"execution":{"iopub.status.busy":"2021-07-07T06:31:27.532587Z","iopub.execute_input":"2021-07-07T06:31:27.532923Z","iopub.status.idle":"2021-07-07T06:31:27.549249Z","shell.execute_reply.started":"2021-07-07T06:31:27.532892Z","shell.execute_reply":"2021-07-07T06:31:27.548282Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Train the model\nepochs = 100\n\nlosses_train = []\nlosses_val = []\naccuracies_val = []\n\nn_iterations = epochs * (len(data_generator_train) // batch_size_train * batch_size_train + len(data_generator_val) // batch_size_val * batch_size_val)\nprogress_bar = tqdm(range(n_iterations))\n\nmodel = model.to(\"cuda:0\")\n\nfor epoch in range(epochs):\n    model.train()  # Training mode\n    losses_train.append([])  # Initialize list for losses for this epoch\n    for inputs, targets in data_generator_train:\n        optimizer.zero_grad()  # zero the parameter gradients\n        outputs = model(inputs)  # Forward pass\n        loss = criterion(outputs, targets)  # Calculate the loss\n        loss.backward()  # Calculate the gradients\n        optimizer.step()  # Optimize based on the gradients\n        losses_train[-1].append(loss.item())  # Store the loss\n        progress_bar.update(batch_size_train)\n\n    model.eval()  # Set the model to evaluation mode.\n    losses_val.append(0)  # Initialize the total loss for this epoch\n    correct = 0\n    for inputs_val, targets_val in data_generator_val:\n        outputs_val = model(inputs_val)  # Forward pass\n        losses_val[-1] += criterion(outputs_val, targets_val).item()  # Calculate the loss\n        correct += outputs_val.max(1)[1][0].item() == targets_val.item()\n        progress_bar.update(batch_size_val)\n    \n    # Calculate the metrics for this epoch\n    loss_train_epoch = sum(losses_train[-1]) * batch_size_train / len(data_generator_train)\n    loss_val_epoch = losses_val[-1] / len(data_generator_val)\n    accuracy_epoch = correct / len(data_generator_val)\n    accuracies_val.append(accuracy_epoch)\n    \n    # Save the model\n    filepath = f\"/kaggle/working/{epoch}.pt\"\n    torch.save(model.state_dict(), filepath)\n        \n    print(f\"{epoch} Training loss: {loss_train_epoch}, Validation loss: {loss_val_epoch}, Validation accuracy: {accuracy_epoch}\")\n    \nmodel = model.to(\"cpu\")\n\nprint('Finished Training')","metadata":{"execution":{"iopub.status.busy":"2021-07-07T06:31:30.838764Z","iopub.execute_input":"2021-07-07T06:31:30.839105Z","iopub.status.idle":"2021-07-07T07:54:07.770363Z","shell.execute_reply.started":"2021-07-07T06:31:30.839075Z","shell.execute_reply":"2021-07-07T07:54:07.769013Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load the best model\nbest_epoch = np.argmin([value / len(data_generator_val) for value in losses_val])\nmodel.load_state_dict(torch.load(f\"/kaggle/working/{best_epoch}.pt\"))","metadata":{"execution":{"iopub.status.busy":"2021-07-07T06:22:54.086648Z","iopub.execute_input":"2021-07-07T06:22:54.086979Z","iopub.status.idle":"2021-07-07T06:22:54.112271Z","shell.execute_reply.started":"2021-07-07T06:22:54.086948Z","shell.execute_reply":"2021-07-07T06:22:54.109912Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y_true = []\ny_pred = []\ntmp = []\n\nmodel = model.to(\"cuda:0\")\nmodel.eval()  # Set the model to evaluation mode.\n\nprogress_bar = tqdm(range(len(data_generator_val)))\nfor inputs_val, targets_val in data_generator_val:\n    outputs_val = model(inputs_val)\n    tmp.append(outputs_val)\n    y_true.append(targets_val.item())\n    y_pred.append(outputs_val.max(1)[1][0].item())\n    \n    progress_bar.update(1)\n    \ncm = confusion_matrix(y_true, y_pred)\n\nlabels = df_train[\"label\"].cat.categories.values\nplt.matshow(cm, cmap=plt.cm.coolwarm)\nplt.colorbar()\ntick_marks = np.arange(len(labels))\nplt.xticks(tick_marks, labels, rotation=45)\nplt.yticks(tick_marks, labels)\n#plt.tight_layout()\nplt.ylabel(\"True\")\nplt.xlabel(\"Predicted\")\n\nfor (i, j), z in np.ndenumerate(cm):\n    plt.text(j, i, str(z), ha='center', va='center')","metadata":{"execution":{"iopub.status.busy":"2021-07-07T07:55:37.063085Z","iopub.execute_input":"2021-07-07T07:55:37.063425Z","iopub.status.idle":"2021-07-07T07:55:47.767691Z","shell.execute_reply.started":"2021-07-07T07:55:37.063393Z","shell.execute_reply":"2021-07-07T07:55:47.766788Z"},"trusted":true},"execution_count":null,"outputs":[]}]}