{"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":"markdown","source":"#### **Import the libraries**","metadata":{"id":"eoQUQIBVkAVk"}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nimport cv2\nfrom torchvision import transforms\nfrom torchvision.transforms import ToTensor\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nfrom tqdm import trange","metadata":{"id":"jhVKEx4DIgKT"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Set the random seed**","metadata":{"id":"ydDc504-akhk"}},{"cell_type":"code","source":"np.random.seed(0)\ntorch.manual_seed(0)","metadata":{"id":"IgoqGPBPdGBf","outputId":"bf577ba9-067c-48c6-b821-a29cbcf9549d"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Set GPU**","metadata":{"id":"E2bidiHXawdT"}},{"cell_type":"code","source":"# Make sure you're using cuda (GPU) by checking the hardware accelerator under Runtime -> Change runtime type\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(\"We're using:\", device)","metadata":{"id":"5lI0V_BYa3LP","outputId":"a8e9384f-c5d2-442c-ca89-a5b446bfacf8"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from google.colab import drive\ndrive.mount('/content/drive/')","metadata":{"id":"obZjTKcSl92V","outputId":"ac130a51-d8cf-44c8-9551-56617755bca1"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%cd /content/drive/MyDrive/CIS519 Project/\nif not os.path.exists(\"Dataset\"):\n    !yes N | unzip \"/content/drive/MyDrive/CIS519 Project/cassava-leaf-disease-classification.zip\" -d \"/content/drive/MyDrive/CIS519 Project/Dataset\"","metadata":{"id":"CYJiCztELu9B","outputId":"6815c8da-8738-45c6-8f91-741614fe8fab"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"os.listdir(\"./Dataset\")","metadata":{"id":"R-XtWEzxPHOi","outputId":"1f58bca7-92b1-4c03-d59e-5bac15277357"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"label_map = pd.read_json(\"./Dataset/label_num_to_disease_map.json\",orient='index')\nprint(len(label_map))\ndisplay(label_map)","metadata":{"id":"wUDdNplkP1tq","outputId":"b57b5aea-992b-4ce8-d557-3a08c3fc65c6"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = pd.read_csv(\"./Dataset/train.csv\")\ntrain_1 = train.groupby(['label'],as_index=False).count().rename(columns={'image_id':'Count'})\ntrain_1","metadata":{"id":"1jpe0el0PniB","outputId":"a641822a-2a55-4d98-c3bf-c8859a8a0f43"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train['label']","metadata":{"id":"D5_13ykdCdSd","outputId":"2d675689-842d-4ecf-c04a-60ecd29c0201"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import seaborn as sns\nax=sns.histplot(train['label'],bins=5,kde=False)\n# ax.set_xticklabels(labels=[\"Cassava Bacterial Blight (CBB)\",\n#                            \"Cassava Brown Streak Disease (CBSD)\",\n#                            \"Cassava Green Mottle (CGM)\",\n#                            \"Cassava Mosaic Disease (CMD)\",\n#                            \"Healthy\"],\n#                    rotation=45)","metadata":{"id":"9_wXex7tRjZC","outputId":"866300ed-b0cb-49e6-bc27-1ebe63611e19"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ax=sns.barplot(x=train_1['label'],y=train_1['Count'])\n# ax.set_xticklabels(labels=[\"Cassava Bacterial Blight (CBB)\",\n#                            \"Cassava Brown Streak Disease (CBSD)\",\n#                            \"Cassava Green Mottle (CGM)\",\n#                            \"Cassava Mosaic Disease (CMD)\",\n#                            \"Healthy\"],\n#                    rotation=45)\nplt.tight_layout()","metadata":{"id":"5JoovN5zCuEW","outputId":"839c3f85-0b82-4b27-a2ee-eaf3cb988db8"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y = train_1['Count']\n\nplt.pie(y,\n        labels=train_1['label'],\n        colors=[\"#d5695d\", \"#5d8ca8\", \"#65a479\", \"#a564c9\",'y'], \n        explode=(0, 0, 0, 0.2, 0), \n        autopct='%.2f%%', \n       )\nplt.title(\"Data Pie Chart\")\nplt.show()","metadata":{"id":"b_3-0aaJEArK","outputId":"932aedb6-3c08-469e-a538-5e1b6e74a2cd"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## **Dataset class implementation**","metadata":{"id":"J9aK47H-3ERJ"}},{"cell_type":"code","source":"ENCODING_TO_LABELS = {0: \"Cassava Bacterial Blight (CBB)\",\n                      1: \"Cassava Brown Streak Disease (CBSD)\",\n                      2: \"Cassava Green Mottle (CGM)\",\n                      3: \"Cassava Mosaic Disease (CMD)\",\n                      4: \"Healthy\"}\n\nLABELS_TO_ENCODING = {\"Cassava Bacterial Blight (CBB)\": 0,\n                      \"Cassava Brown Streak Disease (CBSD)\": 1,\n                      \"Cassava Green Mottle (CGM)\": 2,\n                      \"Cassava Mosaic Disease (CMD)\": 3,\n                      \"Healthy\": 4}","metadata":{"id":"SHfzymjbxnl4"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CassDataset(Dataset):\n    \n    def __init__(self, image_path, subset=(0,len(train)), transform=None, dim=64):\n        \"\"\"\n        subset(tuple): dataset from where to where\n        \"\"\"\n        self.image_path = image_path\n        self.subset = subset\n        self.label_idx = pd.read_csv(image_path + \"/train.csv\")[subset[0]:subset[1]] \n        self.transform = transform\n        self.dim = dim\n\n    def __len__(self):\n            \n        # STUDENT TODO START: Return the number of samples in the dataset\n        return len(self.label_idx)\n        # STUDENT TODO END\n\n    def __getitem__(self, idx):\n            \n        if torch.is_tensor(idx):\n            idx = idx.tolist()\n\n        # STUDENT TODO START: Create the path to each image by joining the root path with the name of the file as found in labels.csv\n        img_name = './' + self.image_path + '/train_images/' + self.label_idx.iloc[idx]['image_id']\n        # STUDENT TODO END\n\n        # Read the image from the file path\n        image = Image.open(img_name)\n        width, height = image.size\n        # Setting the points for randomly-cropped image\n        left = np.random.randint(0,width-dim)\n        top = np.random.randint(0,height-dim)\n        right = np.random.randint(left,width)\n        bottom = np.random.randint(top,height)\n        # Cropped image of above dimension\n        # (It will not change original image)\n        im = image.crop((left, top, right, bottom))\n        newsize = (self.dim, self.dim)\n        im = im.resize(newsize)\n\n        # Transform the image using self.transform\n        if self.transform:\n            im = self.transform(im)\n\n        if \"label\" in self.label_idx.columns:\n            # STUDENT TODO START: Extract label name and encode it using the LABELS_TO_ENCODING dictionary\n            label_idx_1 = self.label_idx.iloc[idx]['label']\n            # STUDENT TODO END\n            sample = (im, label_idx_1)\n        else:\n            sample = (im)\n        return sample","metadata":{"id":"ukqAyzwfI850"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])\ndim=300\ndataset = CassDataset(image_path=\"Dataset\", transform=transform, dim=dim)\ntr_dataset = CassDataset(image_path=\"Dataset\", subset=(0,2048), transform=transform, dim=dim) # 取前1024个图作为 training set\ntr_dataloader  = DataLoader(tr_dataset, 128, shuffle=True)\nval_dataset = CassDataset(image_path=\"Dataset\", subset=(2048,2304), transform=transform, dim=dim) # 取第1024向后256个图为 validation set\nval_dataloader  = DataLoader(val_dataset, 128, shuffle=True)","metadata":{"id":"GWZodsyC3UOE"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(len(dataset), len(tr_dataset),len(val_dataset))","metadata":{"id":"UJO2v5M-vnhY","outputId":"c525e49c-fe8e-44f3-bcbf-01f38c6caf33"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Visualization**\n\nThe following cell visualizes the data as a sanity check for your implementation of the `CassDataset` class.","metadata":{"id":"9xU1vMHZTZqA"}},{"cell_type":"code","source":"figure = plt.figure(figsize=(15, 12))\ncols, rows = 3, 3\ntorch.manual_seed(0)\nfor i in range(1, cols * rows + 1):\n    sample_idx = torch.randint(len(dataset), size=(1,)).item()\n    img, label_idx = dataset[sample_idx]\n    figure.add_subplot(rows, cols, i)\n    plt.title(ENCODING_TO_LABELS[label_idx])\n    plt.axis(\"off\")\n    plt.imshow(img.permute(1, 2, 0)*0.5 + 0.5)\nplt.show()","metadata":{"id":"NovDuS3C3xSX","outputId":"1c53f5ca-6ab6-437c-9748-284fb57b6717"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"num_epochs = 10","metadata":{"id":"pM-c_mAjHsbQ"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## **CNN architecture**","metadata":{"id":"DcdY5uHVd1Rt"}},{"cell_type":"code","source":"# Create a neural net class\nclass Net(nn.Module):\n    \n    \n    # Defining the Constructor\n    def __init__(self, num_classes):\n        super(Net, self).__init__()\n        \n        # In the init function, we define each layer we will use in our model\n        \n        # Our images are RGB, so we have input channels = 3. \n        # We will apply 8 filters in the first convolutional layer\n        self.conv1 = nn.Conv2d(in_channels=3, out_channels=8, kernel_size=3, stride=1, padding=1)\n        \n        # A second convolutional layer takes 8 input channels, and generates 32 outputs\n        self.conv2 = nn.Conv2d(in_channels=8, out_channels=32, kernel_size=3, stride=1, padding=1)\n\n        # A third convolutional layer takes 32 input channels, and generates 64 outputs\n        self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1, padding=1)\n\n        # A fourth convolutional layer takes 64 input channels, and generates 128 outputs\n        self.conv4 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1)\n\n        # A fifth convolutional layer takes 128 input channels, and generates 256 outputs\n        self.conv5 = nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1)\n\n        # A sixth convolutional layer takes 256 input channels, and generates 512 outputs\n        self.conv6 = nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1)\n        \n        # Apply max pooling with a kernel size of 2\n        self.pool = nn.MaxPool2d(kernel_size=2)\n        \n        # We need to flatten these in order to feed them to a fully-connected layer\n        self.fc = nn.Linear(in_features= int(4**2*512), out_features=num_classes)\n\n    def forward(self, x):\n        # In the forward function, pass the data through the layers we defined in the init function\n        \n        # Use a ReLU activation function after layer 1 (convolution 1 and pool)\n        x = F.relu(self.pool(self.conv1(x))) \n        \n        # Use a ReLU activation function after layer 2\n        x = F.relu(self.pool(self.conv2(x)))  \n\n        # Use a ReLU activation function after layer 3\n        x = F.relu(self.pool(self.conv3(x))) \n\n        # Use a ReLU activation function after layer 4\n        x = F.relu(self.pool(self.conv4(x))) \n\n        # Use a ReLU activation function after layer 5\n        x = F.relu(self.pool(self.conv5(x))) \n\n        # Use a ReLU activation function after layer 6\n        x = F.relu(self.pool(self.conv6(x))) \n        \n        # Flatten\n        # x = x.view(-1, 25 * 25 * 24)\n        x = torch.flatten(x, 1)\n        # Feed to fully-connected layer to predict class\n        x = self.fc(x)\n        # Return class probabilities via a softmax function \n        return torch.softmax(x, dim=1)","metadata":{"id":"_CJJEh5rBRmQ"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# STUDENT TODO START: Create an instance of Net and move it to the GPU\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nmodel = Net(num_classes=len(label_map))\nmodel = model.to(device)\n# STUDENT TODO END","metadata":{"id":"izZHbzmJd2AM"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## **Training, validation, and testing**","metadata":{"id":"PS8OWjf2uc29"}},{"cell_type":"code","source":"# 1. Set the criterion to be cross entropy loss\ncriterion = nn.CrossEntropyLoss()\n\n# 2. Experiment with different optimizers\noptimizer = optim.Adam(model.parameters(), lr=2e-5)\n","metadata":{"id":"uwcE5_u_uc3E"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_loss, batch_train_loss, validation_loss, batch_validation_loss = [], [], [], []\ntrain_acc, batch_train_acc, validation_acc = [], [], []\nepoch_nums,batch_nums = [],[]\n# Note that we have set the number of epochs to be 10. You can choose to increase or decrease the number of epochs.\n\nbatch_num_i = 0\nprint('Training on', device)\n\nfor epoch in trange(num_epochs):\n        \n    model.train()\n    epoch_nums.append(epoch)\n    running_loss = 0.\n    correct, total = 0, 0 \n        \n    for i, data in enumerate(tr_dataloader, 0):\n        \n        batch_num_i +=1\n        batch_nums.append(batch_num_i)\n        inputs, labels = data\n        # 1. Store the inputs and labels in the GPU\n        inputs = inputs.to(device)\n        labels = labels.to(device)\n            \n        # 2. Get the model predictions\n        predictions = model(inputs)\n\n        # 3. Zero the gradients out\n        optimizer.zero_grad()\n\n        # 4. Get the loss\n        loss = criterion(predictions, labels)\n\n        # 5. Calculate the gradients\n        loss.backward()\n\n        # 6. Update the weights\n        optimizer.step()\n\n        # STUDENT TODO END \n        running_loss += loss.item()\n        print('\\tTraining batch {} Loss: {:.6f}'.format(i + 1, loss.item()))\n            \n        _, predicted = torch.max(predictions, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n        batch_train_loss.append(running_loss / len(tr_dataloader))\n        \n    train_loss.append(running_loss / len(tr_dataloader))\n    train_acc.append(correct/total)\n                \n    model.eval()\n    running_loss = 0.\n    correct, total = 0, 0 \n        \n    for i, data in enumerate(val_dataloader, 0):\n\n        inputs, labels = data\n        # STUDENT TODO START:\n        # 1. Store the inputs and labels in the GPU\n        inputs = inputs.to(device)\n        labels = labels.to(device)\n            \n        # 2. Get the model predictions\n        predictions = model(inputs)\n\n        # 3. Get the loss\n        loss = criterion(predictions, labels)\n\n        # STUDENT TODO END \n\n        running_loss += loss.item()\n            \n        _, predicted = torch.max(predictions, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n        batch_validation_loss.append(running_loss / len(val_dataloader))\n            \n    validation_loss.append(running_loss / len(val_dataloader))\n    validation_acc.append(correct/total)\n\n    print(f\"Epoch {epoch+1}:\")\n    print(f\"Training Loss:\", round(train_loss[epoch], 4))\n    print(f\"Validation Loss:\", round(validation_loss[epoch], 4))\n    print(f\"Training Accuracy:\", round(train_acc[epoch], 4))\n    print(f\"Validation Accuracy:\", round(validation_acc[epoch], 4))\n    print(\"------------------------------\")","metadata":{"id":"1jacD4Qxuc3F","outputId":"6beab40b-91ec-4197-faba-77f213f85139"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(10,7))\nplt.plot(batch_nums, batch_train_loss, label='Training')\n# plt.plot(batch_nums, batch_validation_loss, label='Validation')\nplt.title(\"CNN loss vs batch\")\nplt.xlabel('Batch')\nplt.ylabel('Loss')\n#plt.yscale('log')\nplt.legend()\nplt.show()","metadata":{"id":"8nwUdpwYuc3F","outputId":"1df79fb4-f32c-4ee6-edc5-69f4bcdaaef1"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(10,7))\nplt.plot(epoch_nums, train_loss, label='Training')\nplt.plot(epoch_nums, validation_loss, label='Validation')\nplt.xticks(np.arange(0, num_epochs, 1))\nplt.title(\"CNN loss vs Epoch\")\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\n#plt.yscale('log')\nplt.legend()\nplt.show()","metadata":{"outputId":"8ebfd8d4-1fd9-45fd-99a0-88842871c59d","id":"OKxSxMxNtQnF"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"nb_classes = 5\n\nconfusion_matrix = torch.zeros(nb_classes, nb_classes)\nmodel.eval()\nwith torch.no_grad():\n    for i, (inputs, classes) in enumerate(val_dataloader):\n        inputs = inputs.to(device)\n        classes = classes.to(device)\n        #print(classes)\n        outputs = model(inputs)\n        _, preds = torch.max(outputs, 1)\n        for t, p in zip(classes.view(-1), preds.view(-1)):\n                confusion_matrix[t.long(), p.long()] += 1\n\nprint(confusion_matrix)","metadata":{"id":"USfqaLoJH1v0","outputId":"5e4a3f38-5817-4ec4-9581-fc5448b902f3"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import itertools\n\ndef plot_confusion_matrix(cm, classes,\n                          normalize=True,\n                          title='Confusion matrix',\n                          cmap=plt.cm.Blues):\n    \"\"\"\n    This function prints and plots the confusion matrix.\n    Normalization can be applied by setting `normalize=True`.\n    \"\"\"\n    if normalize:\n        cm = cm/ np.sum(cm)\n\n    plt.imshow(cm, interpolation='nearest', cmap=cmap)\n    plt.title(title)\n    plt.colorbar()\n    tick_marks = np.arange(len(classes))\n    plt.xticks(tick_marks, classes, rotation=45)\n    plt.yticks(tick_marks, classes)\n\n    fmt = '.2%' #if normalize else 'd'\n    thresh = cm.max() / 2.\n    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n        plt.text(j, i, format(cm[i, j], fmt),\n                 horizontalalignment=\"center\",\n                 color=\"white\" if cm[i, j] > thresh else \"black\")\n\n    plt.ylabel('True label')\n    plt.xlabel('Predicted label')\n    plt.tight_layout()","metadata":{"id":"t2K7tVwZJXjW"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure()\nnum_classes=5\nM = confusion_matrix.numpy()\nplot_confusion_matrix(M, classes=np.arange(num_classes), normalize=True)\nprint(\"Accuracy:\",np.trace(M/np.sum(M)))","metadata":{"id":"6-PLhFF-JbON","outputId":"8640e332-7b7c-4c59-c96b-85ceb399f7d0"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## **CNN-modified model architecture**","metadata":{"id":"jbduA3nNte9t"}},{"cell_type":"code","source":"# Create a neural net class\nclass CNN_1(nn.Module):\n    \n    # Defining the Constructor\n    def __init__(self, num_classes):\n        super(CNN_1, self).__init__()\n        \n        # In the init function, we define each layer we will use in our model\n        \n        # Our images are RGB, so we have input channels = 3. \n        # We will apply 8 filters in the first convolutional layer\n        self.conv1 = nn.Conv2d(in_channels=3, out_channels=8, kernel_size=3, stride=1, padding=1)\n        \n        # A second convolutional layer takes 8 input channels, and generates 32 outputs\n        self.conv2 = nn.Conv2d(in_channels=8, out_channels=32, kernel_size=3, stride=1, padding=1)\n\n        # A third convolutional layer takes 32 input channels, and generates 64 outputs\n        self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1, padding=1)\n\n        # Batch normalization\n        self.batchnorm1 = nn.BatchNorm2d(64)\n\n        # A drop layer deletes 20% of the features to help prevent overfitting\n        self.drop1 = nn.Dropout2d(p=0.2)\n\n        # A fourth convolutional layer takes 64 input channels, and generates 128 outputs\n        self.conv4 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1)\n\n        # A fifth convolutional layer takes 128 input channels, and generates 256 outputs\n        self.conv5 = nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1)\n\n        # A sixth convolutional layer takes 256 input channels, and generates 512 outputs\n        self.conv6 = nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1)\n        \n        # Apply max pooling with a kernel size of 2\n        self.pool = nn.MaxPool2d(kernel_size=2)\n        \n        # We need to flatten these in order to feed them to a fully-connected layer\n        self.fc = nn.Linear(in_features=int(4**2*512), out_features=num_classes)\n\n    def forward(self, x):\n        # In the forward function, pass the data through the layers we defined in the init function\n        \n        # Use a ReLU activation function after layer 1 (convolution 1 and pool)\n        x = F.relu(self.pool(self.conv1(x))) \n        \n        # Use a ReLU activation function after layer 2\n        x = F.relu(self.pool(self.conv2(x)))  \n\n        # Use a ReLU activation function after layer 3\n        x = F.relu(self.pool(self.conv3(x))) \n\n        # Batch norm 1\n        outputs = self.batchnorm1(x)\n\n        # Select some features to drop to prevent overfitting (only drop during training)\n        x = F.dropout(self.drop1(x), training=self.training)\n\n        # Use a ReLU activation function after layer 4\n        x = F.relu(self.pool(self.conv4(x))) \n\n        # Use a ReLU activation function after layer 5\n        x = F.relu(self.pool(self.conv5(x)))  \n\n        # Use a ReLU activation function after layer 6\n        x = F.relu(self.pool(self.conv6(x))) \n        \n        # Flatten\n        # x = x.view(-1, 25 * 25 * 24)\n        x = torch.flatten(x, 1)\n        # Feed to fully-connected layer to predict class\n        x = self.fc(x)\n        # Return class probabilities via a softmax function \n        return torch.softmax(x, dim=1)","metadata":{"id":"7pYDrs9Lte9u"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# STUDENT TODO START: Create an instance of Net and move it to the GPU\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nmodel_1 = CNN_1(num_classes=len(label_map))\nmodel_1 = model_1.to(device)\n# STUDENT TODO END","metadata":{"id":"6Lxkg2jDte9v"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## **Training, validation, and testing**","metadata":{"id":"Ezwmjeg5te9v"}},{"cell_type":"code","source":"# 1. Set the criterion to be cross entropy loss\ncriterion = nn.CrossEntropyLoss()\n\n# 2. Experiment with different optimizers\noptimizer = optim.Adam(model.parameters(), lr=2e-5)\n","metadata":{"id":"rwSzgHF4te9v"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_loss, batch_train_loss, validation_loss, batch_validation_loss = [], [], [], []\ntrain_acc, batch_train_acc, validation_acc = [], [], []\nepoch_nums,batch_nums = [],[]\n# Note that we have set the number of epochs to be 10. You can choose to increase or decrease the number of epochs.\n# num_epochs = 20\nbatch_num_i = 0\nprint('Training on', device)\n\nfor epoch in trange(num_epochs):\n        \n    model_1.train()\n    epoch_nums.append(epoch)\n    running_loss = 0.\n    correct, total = 0, 0 \n        \n    for i, data in enumerate(tr_dataloader, 0):\n        \n        batch_num_i +=1\n        batch_nums.append(batch_num_i)\n        inputs, labels = data\n        # 1. Store the inputs and labels in the GPU\n        inputs = inputs.to(device)\n        labels = labels.to(device)\n            \n        # 2. Get the model predictions\n        predictions = model_1(inputs)\n\n        # 3. Zero the gradients out\n        optimizer.zero_grad()\n\n        # 4. Get the loss\n        loss = criterion(predictions, labels)\n\n        # 5. Calculate the gradients\n        loss.backward()\n\n        # 6. Update the weights\n        optimizer.step()\n\n        # STUDENT TODO END \n        running_loss += loss.item()\n        print('\\tTraining batch {} Loss: {:.6f}'.format(i + 1, loss.item()))\n            \n        _, predicted = torch.max(predictions, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n        batch_train_loss.append(running_loss / len(tr_dataloader))\n        \n    train_loss.append(running_loss / len(tr_dataloader))\n    train_acc.append(correct/total)\n                \n    model_1.eval()\n    running_loss = 0.\n    correct, total = 0, 0 \n        \n    for i, data in enumerate(val_dataloader, 0):\n\n        inputs, labels = data\n        # STUDENT TODO START:\n        # 1. Store the inputs and labels in the GPU\n        inputs = inputs.to(device)\n        labels = labels.to(device)\n            \n        # 2. Get the model predictions\n        predictions = model_1(inputs)\n\n        # 3. Get the loss\n        loss = criterion(predictions, labels)\n\n        # STUDENT TODO END \n\n        running_loss += loss.item()\n            \n        _, predicted = torch.max(predictions, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n        batch_validation_loss.append(running_loss / len(val_dataloader))\n            \n    validation_loss.append(running_loss / len(val_dataloader))\n    validation_acc.append(correct/total)\n\n    print(f\"Epoch {epoch+1}:\")\n    print(f\"Training Loss:\", round(train_loss[epoch], 4))\n    print(f\"Validation Loss:\", round(validation_loss[epoch], 4))\n    print(f\"Training Accuracy:\", round(train_acc[epoch], 4))\n    print(f\"Validation Accuracy:\", round(validation_acc[epoch], 4))\n    print(\"------------------------------\")","metadata":{"id":"-dECXr5bCz-6","outputId":"4ea5637a-4a44-4823-dedb-6c347e327692"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(10,7))\nplt.plot(batch_nums, batch_train_loss, label='Training')\n#plt.plot(batch_nums, batch_validation_loss, label='Validation')\nplt.title(\"CNN+Dropout+Batchnorm loss vs batch\")\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\n#plt.yscale('log')\nplt.legend()\nplt.show()","metadata":{"id":"3k4aBZTLBUzo","outputId":"03f1d154-cdcc-468e-a5d8-bc779a1f3006"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(10,7))\nplt.plot(epoch_nums, train_loss, label='Training')\nplt.plot(epoch_nums, validation_loss, label='Validation')\nplt.xticks(np.arange(0, len(epoch_nums), 1))\nplt.title(\"CNN+Dropout+Batchnorm loss vs Epoch\")\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\n#plt.yscale('log')\nplt.legend()\nplt.show()","metadata":{"id":"3Gfs8OESHfyT","outputId":"028b9bb7-97a5-4e80-bf50-0c66c32bc28a"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"nb_classes = 5\n\nconfusion_matrix = torch.zeros(nb_classes, nb_classes)\nmodel.eval()\nwith torch.no_grad():\n    for i, (inputs, classes) in enumerate(val_dataloader):\n        inputs = inputs.to(device)\n        classes = classes.to(device)\n        #print(classes)\n        outputs = model_1(inputs)\n        _, preds = torch.max(outputs, 1)\n        for t, p in zip(classes.view(-1), preds.view(-1)):\n                confusion_matrix[t.long(), p.long()] += 1\n\nprint(confusion_matrix)","metadata":{"outputId":"5379a959-b7ae-4a29-d4b6-3d649d0c9abb","id":"sqZ70klUM9vG"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure()\nnum_classes=5\nM = confusion_matrix.numpy()\nplot_confusion_matrix(M, classes=np.arange(num_classes), normalize=True)\nprint(\"Accuracy:\",np.trace(M/np.sum(M)))","metadata":{"outputId":"b7d0cc1a-76c9-4779-b8f2-cccbc1d9caca","id":"fIGe7Gg-M9vN"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"id":"DsjNR21G5lkg"},"execution_count":null,"outputs":[]}]}