{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n#for dirname, _, filenames in os.walk('/kaggle/input'):\n #   for filename in filenames:\n  #      print(os.path.join(dirname, filename))\n        \n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"#Create a string containing the trainig csv file path\ntrain_path = \"/kaggle/input/cassava-leaf-disease-classification/train.csv\"\n\n#Read this csv into a Pandas dataframe\ntrain_file = pd.read_csv(train_path)\n\n#Our images are not saved in the csv so they will have a different file path.\n#This file path has mostly the same folder structure apart from the actual file name. Therefore, we can use this as a base and append the file path later.\ntrain_image_path = f'/kaggle/input/cassava-leaf-disease-classification/train_images/'\n\n\n#Create a list of boolean values up to the length of the dataframe\nmsk = np.random.rand(len(train_file)) <= 0.7\n\n#Select all the rows from train_file that correspond to True in msk.\ntrain_data = train_file[msk].reset_index()\n\n#Select all the rows from train_file that correspond to False in msk.\nvalidation_data = train_file[~msk].reset_index()\n\n\nimport matplotlib.pyplot as plt\n#Select a random image and display to check the file path is working correctly.\nimg = plt.imread((train_image_path + \"3770952591.jpg\"))\nplt.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import torchvision\nfrom torchvision import transforms\n\n#The bare minimum transform we need for our network to work is to transform the images - which are currently stored in numpy arrays - into tensors.\ndef train_transform():\n    transforms.Compose([\n        transforms.ToTensor(),\n    ])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch.utils.data import Dataset, DataLoader\nimport cv2\n\n# All classes for NN will need an __init__ function, a __len__ function, and a __getitem__ function.\nclass DataCreation(Dataset):\n    #__init__ creates the structure for our class\n    def __init__(self, data, transforms = train_transform()):\n        super().__init__()\n\n        self.transform = transforms\n        self.image_id = data[\"image_id\"]\n        self.label = data[\"label\"]\n\n    def __len__(self):\n        return len(self.image_id)\n    \n    #__getitem__ does most of the work we care about. In this case in reads in the row id using self, then using that row id it finds the...\n    #...corresponding label, and the image - using the previously discussed file_path and appends the image_id.\n    def __getitem__(self,idx : int):\n        image_id = self.image_id[idx]\n        label = self.label[idx]\n        image = cv2.imread(train_image_path + image_id, cv2.IMREAD_COLOR)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        image /= 255.0\n        \n        #This is almost always the case where we have a transform set, but to avoid future errors we will include it in an if statement.\n        if self.transform:\n            #First we send our batch of images to the transform function, which outputs it as a tensor.\n            augmented = self.transform(image=image)\n            image = augmented[\"image\"]\n        \n        #Return 2 outputs. Note, when you assign this function to 2 variables, it will assign them in this order.\n        return label, image\n\n\n#Call the DataCreation function on our training data, making sure to pass each item through the transformer.\ntrain_dataset = DataCreation(train_data, train_transform())\n#Batch multiple items from the DataCreation function ready for passing to the network.\ntrain_loader = DataLoader(train_dataset, batch_size = 32)\n\n#Do the same for validation data.\nvalidation_dataset = DataCreation(validation_data, train_transform())\nvalidation_loader = DataLoader(validation_dataset, batch_size = 32)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\nfrom torch import nn\nfrom torch import optim\nimport torch.nn.functional as F\nfrom torchvision import datasets, transforms, models\n\n#If GPU is available, use it, otherwise use CPU.\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n#Use a pretrained model to save us a lot of time during training. These pretrained models have proved to be very good on the majority of tasks...\n#... and only in edge cases are they unsuitable.\nmodel = models.resnet50(pretrained=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for param in model.parameters():\n    param.requires_grad = False\n\n#The pretrained model is likely to have a different number of output classes than other models. Therefore, we will create a new \"head\".\n#Make sure thart the input size to this head is the same as the output size of the last layer in the original network.\nmodel.fc = nn.Sequential(nn.Linear(2048, 512),\n                                 nn.ReLU(),\n                                 nn.Dropout(0.2),\n                                 nn.Linear(512, 5), #Set the number of output classes, in this case 5.\n                                 nn.LogSoftmax(dim=1))\ncriterion = nn.NLLLoss()\noptimizer = optim.Adam(model.fc.parameters(), lr=0.003)\nmodel.to(device) #Send the model to the current device, either GPU or CPU.","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"epochs = 1\nsteps = 0\nrunning_loss = 0\nprint_every = 100\ntrain_losses, test_losses = [], []\nfor epoch in range(epochs):\n    for label, image in train_loader:\n        #Note: image and image_id are a batch, so their lengths are the same as the batch_size.\n        steps += 1\n        \n        #The network takes inputs as N, C, W, H which is not the default output characteristic of the train_loader.\n        #...Therefore, we must permute the tensors order.\n        image = image.permute(0,3,1,2)\n        label = label.to(device)\n        image = image.to(device)\n        optimizer.zero_grad()\n        logps = model.forward(image)\n        loss = criterion(logps, label)\n        loss.backward()\n        optimizer.step()\n        running_loss += loss.item()\n        \n        if steps % print_every == 0:\n            test_loss = 0\n            accuracy = 0\n            model.eval()\n            with torch.no_grad():\n                for labels, inputs in validation_loader:\n                    inputs, labels = inputs.to(device), labels.to(device)\n                    inputs = inputs.permute(0,3,1,2)\n                    logps = model.forward(inputs)\n                    batch_loss = criterion(logps, labels)\n                    test_loss += batch_loss.item()\n                    \n                    ps = torch.exp(logps)\n                    top_p, top_class = ps.topk(1, dim=1)\n                    equals = top_class == labels.view(*top_class.shape)\n                    accuracy += torch.mean(equals.type(torch.FloatTensor)).item()\n            train_losses.append(running_loss/len(train_loader))\n            test_losses.append(test_loss/len(validation_loader))                    \n            print(f\"Epoch {epoch+1}/{epochs}.. \"\n                  f\"Train loss: {running_loss/print_every:.3f}.. \"\n                  f\"Test loss: {test_loss/len(validation_loader):.3f}.. \"\n                  f\"Test accuracy: {accuracy/len(validation_loader):.3f}\")\n            running_loss = 0\n            model.train()\ntorch.save(model, 'Diseasemodel2.pth')","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}