{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Intro\n\nThis notebook is about using one of the famous CNN architecture to solve an image classification task on Cassava Leaf Disease dataset.\nIt contains a simple code to try training InceptionNet model from torchvision.models and finetune it on this dataset in order to get better result.\nFinetuning is concept of transfer learning that uses a pretrained model on huge dataset and try to adapt its weights on the dataset of a specified task. You can either finetune only the last classifier layer, or last N layers or retrain the whole model on the specified dataset.\n\nIn this notebook I used a variable train_CNN (False/True) to tell the model if we want to train all CNN model or only the last classifier layer\nThere are also some parameters you can tweak to get other results (better or worse) but you need to try!!"},{"metadata":{},"cell_type":"markdown","source":"# Libraries"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import torch\n# Creating Dataset\nfrom torch.utils.data import Dataset\nimport pandas as pd\nimport os\nfrom PIL import Image\n\n# For creating CNN model and training\nimport torch.nn as nn\nimport torchvision.models as models\nfrom torch.utils.data import DataLoader\nimport torchvision.transforms as transforms\nfrom tqdm import tqdm","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Creating our Dataset"},{"metadata":{"trusted":true},"cell_type":"code","source":"class LeafDiseaseDataset(Dataset):\n    def __init__(self, root_dir, annotation_file, transform=None):\n        self.root_dir = root_dir\n        self.annotations = pd.read_csv(annotation_file)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.annotations)\n\n    def __getitem__(self, index):\n        img_id = self.annotations.iloc[index, 0]\n        img = Image.open(os.path.join(self.root_dir, img_id)).convert(\"RGB\")\n        y_label = torch.tensor(float(self.annotations.iloc[index, 1]))\n\n        if self.transform is not None:\n            img = self.transform(img)\n\n        return (img, y_label)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Precise data augmentation (Images transformation)"},{"metadata":{"trusted":true},"cell_type":"code","source":"transform = transforms.Compose(\n        [\n            transforms.Resize((356, 356)),\n            transforms.RandomCrop((299, 299)),\n            transforms.RandomRotation(50),\n            transforms.RandomVerticalFlip(0.4), \n            transforms.RandomHorizontalFlip(0.4), \n            transforms.ColorJitter(brightness=0.1, contrast=0.2, saturation=0, hue=0),\n            transforms.ToTensor(),\n            transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),\n        ]\n    )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Define the CNN arcitecture (based on InceptionNet)"},{"metadata":{"trusted":true},"cell_type":"code","source":"class CNN(nn.Module):\n    def __init__(self, train_CNN=False, num_classes=5):\n        super(CNN, self).__init__()\n        self.train_CNN = train_CNN\n        self.inception = models.inception_v3(pretrained=True, aux_logits=False)\n        self.inception.fc = nn.Linear(self.inception.fc.in_features, num_classes)\n        #self.relu = nn.ReLU()\n        self.dropout = nn.Dropout(0.5)\n        #self.softmax = nn.Softmax()\n\n    def forward(self, images):\n        features = self.inception(images)\n        return self.dropout(features)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Initializations"},{"metadata":{"trusted":true},"cell_type":"code","source":"num_epochs = 14\nlearning_rate = 0.0001\ntrain_CNN = True\nbatch_size = 32\nshuffle = True\npin_memory = True\nnum_workers = 1\n\ndevice = (\"cuda\" if torch.cuda.is_available() else \"cpu\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Starting ..."},{"metadata":{"trusted":true},"cell_type":"code","source":"images_path = \"../input/cassava-leaf-disease-classification/train_images\"\ntraining_csv_file = \"../input/cassava-leaf-disease-classification/train.csv\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset = LeafDiseaseDataset(images_path,training_csv_file,transform=transform)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(dataset)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv(\"../input/cassava-leaf-disease-classification/train.csv\")\nprint(df.head())\nprint(df.label.unique())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_set, validation_set = torch.utils.data.random_split(dataset,[17000,4397])\ntrain_loader = DataLoader(dataset=train_set, shuffle=shuffle, batch_size=batch_size,num_workers=num_workers,pin_memory=pin_memory)\nvalidation_loader = DataLoader(dataset=validation_set, shuffle=shuffle, batch_size=batch_size,num_workers=num_workers, pin_memory=pin_memory)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#We only use this in the training of the model \n# model = CNN().to(device)\n\n# criterion = nn.CrossEntropyLoss()\n# optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)\n\n# for name, param in model.inception.named_parameters():\n#     if \"fc.weight\" in name or \"fc.bias\" in name:\n#         param.requires_grad = True\n#     else:\n#         param.requires_grad = train_CNN","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def check_accuracy(loader, model):\n    if loader == train_loader:\n        print(\"Checking accuracy on training data\")\n    else:\n        print(\"Checking accuracy on validation data\")\n\n    num_correct = 0\n    num_samples = 0\n    model.eval()\n\n    with torch.no_grad():\n        for x, y in loader:\n            x = x.to(device=device)\n            y = y.to(device=device)\n\n            scores = model(x)\n            predictions = torch.tensor(scores).to(device)\n            _, predictions = torch.max(predictions, dim=1)\n            num_correct += (predictions == y).sum()\n            num_samples += predictions.size(0)\n        model.train()\n    return f\"{float(num_correct)/float(num_samples)*100:.2f}\"   ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def train():\n    model.train()\n    for epoch in range(num_epochs):\n        losses = []\n        for imgs, labels in train_loader:\n            imgs = imgs.to(device)\n            labels = labels.to(device, dtype=torch.long)\n            outputs = model(imgs)\n            predictions = torch.tensor(outputs).to(device)\n            loss = criterion(outputs, labels)\n            optimizer.zero_grad()\n            loss.backward()\n            optimizer.step()\n            losses.append(loss.item())\n        if epoch % 2 == 0:\n            val_acc = check_accuracy(validation_loader, model)\n            print(val_acc)\n        print(f\"Epoch [{epoch}/{num_epochs}] ===> \", sum(losses)/len(losses))\n\n# We will not train it here, I will use my pretrained model insteasd\n# if __name__ == \"__main__\":\n#     train()\n\n# PATH = \"CassavleafDiseaseClassificationPretrainedInceptionV3.pkl\"\n# #Store the model after training it in a path to be used in the testing phase\n# torch.save(model, PATH)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Apply the trained CNN model (Inception) on the test data"},{"metadata":{"trusted":true},"cell_type":"code","source":"PATH = \"../input/casavadiseaseincetionv3model/CassavleafDiseaseClassificationPretrainedInceptionV3.pkl\"\nmodel = torch.load(PATH)\nmodel.eval()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_images_path = \"../input/cassava-leaf-disease-classification/test_images\"\nsubmission_file = \"../input/cassava-leaf-disease-classification/sample_submission.csv\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class TestLeafDiseaseDataset(Dataset):\n    def __init__(self, root_dir, image_names_file, transform=None):\n        self.root_dir = root_dir\n        self.image_names = pd.read_csv(image_names_file)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.image_names)\n\n    def __getitem__(self, index):\n        img_id = self.image_names.iloc[index, 0]\n        img = Image.open(os.path.join(self.root_dir, img_id)).convert(\"RGB\")\n\n        if self.transform is not None:\n            img = self.transform(img)\n\n        return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_set = TestLeafDiseaseDataset(test_images_path, submission_file, transform=transform)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(test_set)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_loader = DataLoader(dataset=test_set, batch_size=batch_size,num_workers=num_workers,pin_memory=pin_memory)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Create the submission file"},{"metadata":{"trusted":true},"cell_type":"code","source":"def test(loader, model):\n    model.eval()\n    predicted = []\n    with torch.no_grad():\n        for x in loader:\n            x = x.to(device=device)\n\n            scores = model(x)\n            predictions = torch.tensor(scores).to(device)\n            _, predictions = torch.max(predictions, dim=1)\n            predicted.extend(predictions.cpu().numpy())\n    return predicted","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pred = test(test_loader, model)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = pd.read_csv(\"../input/cassava-leaf-disease-classification/sample_submission.csv\")\nsubmission.label = pred","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","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}