{"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":"# 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\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\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","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\ndata_path = '../input/cassava-leaf-disease-classification'\nprint(len(os.listdir('../input/cassava-leaf-disease-classification/train_images')))\nprint(len(os.listdir('../input/cassava-leaf-disease-classification/test_images')))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\ntrain_df = pd.read_csv('../input/cassava-leaf-disease-classification/train.csv')\ntrain_df.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import json\nimport seaborn as sns\nwith open('../input/cassava-leaf-disease-classification/label_num_to_disease_map.json') as file:\n    print(json.dumps(json.loads(file.read()), indent=4))\nsns.countplot(x = 'label', data = train_df)\ntrain_df['label'].value_counts()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import cv2\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\n# def get_img(path):\n#     im_bgr = cv2.imread(path)\n#     im_rgb = im_bgr[:, :, ::-1]\n#     return im_rgb\n\n# a = get_img('../input/cassava-leaf-disease-classification/train_images/1000015157.jpg')\nImage.open('../input/cassava-leaf-disease-classification/train_images/1000015157.jpg')\n# print(a.shape)\n# plt.imshow(a)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\ndata_list = pd.read_csv('../input/cassava-leaf-disease-classification/train.csv')\ntrain_list, valid_list = train_test_split(data_list, test_size=0.2, random_state=42)\nprint(data_list)\nprint(train_list.shape)\nprint(valid_list.shape)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nfrom torch.utils.data import Dataset,DataLoader\nfrom PIL import Image\nclass CassavaDataset(Dataset):\n    \n    def __init__(self,  df, transforms=None, target_transform=None):\n        super().__init__()\n        self.data_root = \"../input/cassava-leaf-disease-classification/train_images\"\n        self.df = df.reset_index(drop=True).copy()\n        self.transforms = transforms\n        self.target_transform = target_transform\n \n    def __getitem__(self, index: int):\n        img_path = \"{}/{}\".format(self.data_root, self.df.iloc[index]['image_id'])\n#         img  = get_img(img_path)\n        img = Image.open(img_path).convert('RGB')\n        \n        target = self.df.iloc[index]['label']\n\n        if self.transforms is not None:\n            img = self.transforms(img)\n            \n        if self.target_transform is not None:\n            target = self.target_transform(target)\n\n        return img, target\n    \n    def __len__(self):\n        return len(self.df)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport torchvision\nimport torchvision.transforms as transforms\n\n\ndef Get_Dataloader(train_list, valid_list):\n    \n    # data augmentation is more abundant for training data than validation data\n    train_transform = transforms.Compose([\n            transforms.Resize((256, 256)),\n            transforms.CenterCrop(224), # we will use resnet34, which requires the size of input to be 224*224*3, you can also change the network structure to accomodate the original image size\n            transforms.ColorJitter(0.2, 0.2, 0.2),\n            transforms.RandomHorizontalFlip(p=0.5),\n            transforms.ToTensor(),\n            transforms.Normalize(mean = [0.5,0.5,0.5], std = [0.5,0.5,0.5]),\n        ])\n    \n    valid_transform = transforms.Compose([\n            transforms.Resize((224, 224)),\n            transforms.ToTensor(),\n            transforms.Normalize(mean = [0.5,0.5,0.5], std = [0.5,0.5,0.5]),\n        ])\n \n    # training and validation datasets\n    train_data = CassavaDataset(train_list, transforms=train_transform, target_transform=None)\n    valid_data = CassavaDataset(valid_list, transforms=valid_transform, target_transform=None)\n    \n    # training and validation dataloaders\n    train_loader = torch.utils.data.DataLoader(\n        train_data,\n        batch_size=64,\n        num_workers=2,\n        shuffle=True, \n        pin_memory=True,\n    )\n\n    valid_loader = torch.utils.data.DataLoader(\n        valid_data, \n        batch_size=64,\n        num_workers=2,\n        shuffle=False,\n        pin_memory=True,\n    )\n    \n    return train_loader, valid_loader\n\ntrain_loader, valid_loader = Get_Dataloader(train_list, valid_list)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class AverageMeter(object):\n    \"\"\"Computes and stores the average and current value\"\"\"\n    def __init__(self):\n        self.reset()\n\n    def reset(self):\n        self.val = 0\n        self.avg = 0\n        self.sum = 0\n        self.count = 0\n\n    def update(self, val, n=1):\n        self.val = val\n        self.sum += val * n\n        self.count += n\n        self.avg = self.sum / self.count\n        \ndef save_model(model, save_file):\n    state = {\n        'model': model.state_dict(),\n    }\n    torch.save(state, save_file)\n    del state\n    \ndef adjust_learning_rate(optimizer, epoch, lr, milestones):\n    '''decrease learning rate lr to 0.1*lr when train for milestone[0]th epoch, and decrease it to 0.01*lr when comes to milestone[1]th epoch\n    '''\n    lr_1 = lr*(0.1**(epoch>=milestones[0]))*(0.1**(epoch>=milestones[1]))\n\n    for param_group in optimizer.param_groups:\n        param_group['lr'] = lr_1\n\n    return lr_1\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nfrom sklearn.metrics import accuracy_score\ndef train(model, criterion, optimizer, train_loader, device, epoch):\n    losses = AverageMeter()\n    accs = AverageMeter()\n    \n    model.train()\n    for idx, (images, labels) in enumerate(train_loader):\n        images = images.to(device)\n        labels = labels.to(device)\n        bsz = labels.shape[0]\n        \n        output = model(images)\n        loss = criterion(output, labels)\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n        \n        _, predicted = torch.max(output.data, 1)\n        acc = accuracy_score(labels.cpu(), predicted.cpu())\n        \n        accs.update(acc)\n        losses.update(loss.item())\n        \n    return accs.avg, losses.avg","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def valid(model, criterion, valid_loader, device, epoch):\n    losses = AverageMeter()\n    accs = AverageMeter()\n    \n    model.eval()\n    with torch.no_grad():\n        for idx, (images, labels) in enumerate(valid_loader):\n            images = images.to(device)\n            labels = labels.to(device)\n            bsz = labels.shape[0]\n        \n            output = model(images)\n            loss = criterion(output, labels)\n        \n            _, predicted = torch.max(output.data, 1)\n            acc = accuracy_score(labels.cpu(), predicted.cpu())\n        \n            accs.update(acc)\n            losses.update(loss.item())\n      \n    return accs.avg, losses.avg","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport torch\n\nclass EarlyStopping:\n    \"\"\"Early stops the training if validation loss doesn't improve after a given patience.\"\"\"\n    def __init__(self, patience=7, verbose=False, delta=0, path='checkpoint.pt', trace_func=print):\n        \"\"\"\n        Args:\n            patience (int): How long to wait after last time validation loss improved.\n                            Default: 7\n            verbose (bool): If True, prints a message for each validation loss improvement. \n                            Default: False\n            delta (float): Minimum change in the monitored quantity to qualify as an improvement.\n                            Default: 0\n            path (str): Path for the checkpoint to be saved to.\n                            Default: 'checkpoint.pt'\n            trace_func (function): trace print function.\n                            Default: print            \n        \"\"\"\n        self.patience = patience\n        self.verbose = verbose\n        self.counter = 0\n        self.best_score = None\n        self.early_stop = False\n        self.val_loss_min = np.Inf\n        self.delta = delta\n        self.path = path\n        self.trace_func = trace_func\n    def __call__(self, val_loss, model):\n\n        score = -val_loss\n\n        if self.best_score is None:\n            self.best_score = score\n            self.save_checkpoint(val_loss, model)\n        elif score < self.best_score + self.delta:\n            self.counter += 1\n            self.trace_func(f'EarlyStopping counter: {self.counter} out of {self.patience}')\n            if self.counter >= self.patience:\n                self.early_stop = True\n        else:\n            self.best_score = score\n            self.save_checkpoint(val_loss, model)\n            self.counter = 0\n\n    def save_checkpoint(self, val_loss, model):\n        '''Saves model when validation loss decrease.'''\n        if self.verbose:\n            self.trace_func(f'Validation loss decreased ({self.val_loss_min:.6f} --> {val_loss:.6f}).  Saving model ...')\n        torch.save(model.state_dict(), self.path)\n        self.val_loss_min = val_loss","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torchvision import models\n\nnum_classes = 5\nmodel = models.resnet50() # we use resnet34 as the backbone\n#model = SwinTransformer(\n#    num_classes = 5\n#)\nmodel.fc = nn.Linear(model.fc.in_features, num_classes) # we should change the last fc layer of the predefined resnet34 network to accomodate our classification problem\nlr = 0.1\noptimizer = optim.SGD(model.parameters(), lr=lr)\ncriterion = nn.CrossEntropyLoss()\nn_epochs = 80 # just a demo\nmilestones = [30, 40]\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n\nmodel.to(device)\ntrain_loader, valid_loader = Get_Dataloader(train_list, valid_list)\nos.makedirs('cassava/model')\n\ntrain_losses = []\ntrain_acces = []\neval_losses = []\neval_acces = []\n\n\nscheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.2, \npatience=3, verbose=True, threshold=0.0001, threshold_mode='rel', cooldown=0, min_lr=0.0001, eps=1e-08)\n\n\nearly_stopping = EarlyStopping(patience=8, verbose=True)\n\nbest_acc = 0\nfor epoch in range(1, n_epochs+1):\n    #lr_now = adjust_learning_rate(optimizer, epoch, lr, milestones) # adjust learning rate\n    acc_train, loss_train = train(model, criterion, optimizer, train_loader, device, epoch)\n    #print(\"Epoch[{}], Train acc: {:.3f}, Train loss: {:.3f}, lr {:.3f}.\".format(epoch, acc_train, loss_train, lr_now))\n    print(\"Epoch[{}], Train acc: {:.3f}, Train loss: {:.3f}.\".format(epoch, acc_train, loss_train))\n    acc_val, loss_val = valid(model, criterion, valid_loader, device, epoch)\n    scheduler.step(epoch)\n    print(\"Epoch[{}], Valid acc: {:.3f}, Valid loss: {:.3f}.\".format(epoch, acc_val, loss_val))\n    train_losses.append(loss_train)\n    train_acces.append(acc_train)\n    eval_losses.append(loss_val)\n    eval_acces.append(acc_val)\n    if acc_val > best_acc:\n        save_model(model, 'cassava/model/best.pth') # save model\n        best_acc = acc_val\n    early_stopping(loss_val, model)\n        \n    if early_stopping.early_stop:\n        print(\"Early stopping\")\n        break","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.plot(np.arange(len(train_losses)), train_losses,label=\"train loss\")\nplt.xlabel('epoches')\nplt.title('train loss')\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nplt.plot(np.arange(len(train_acces)), train_acces, label=\"train acc\")\n\nplt.legend()\nplt.xlabel('epoches')\nplt.title('train acc')\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nplt.plot(np.arange(len(eval_losses)), eval_losses, label=\"valid loss\")\n\nplt.legend()\nplt.xlabel('epoches')\nplt.title('valid loss')\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nplt.plot(np.arange(len(eval_acces)), eval_acces, label=\"valid acc\")\nplt.legend()\nplt.xlabel('epoches')\nplt.title('valid acc')\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from glob import glob\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nfrom torchvision import models\nfrom torch.utils.data import Dataset,DataLoader\nfrom PIL import Image\nimport torchvision.transforms as transforms\n\n# def a new dataset function\nclass CassavaDataset_test(Dataset):\n    \n    def __init__(self,  df, transforms=None, target_transform=None):\n        super().__init__()\n        self.data_root = \"/kaggle/input/cassava-leaf-disease-classification/test_images\"\n        self.df = df.reset_index(drop=True).copy()\n        self.transforms = transforms\n        self.target_transform = target_transform\n \n    def __getitem__(self, index: int):\n        img_path = \"{}/{}\".format(self.data_root, self.df.iloc[index]['image_id'])\n#         img  = get_img(img_path)\n        img = Image.open(img_path).convert('RGB')\n        \n        img_id = self.df.iloc[index]['image_id']      # add\n        target = self.df.iloc[index]['label']\n\n        if self.transforms is not None:\n            img = self.transforms(img)\n            \n        if self.target_transform is not None:\n            target = self.target_transform(target)\n\n        return img_id, img, target                   # add\n    \n    def __len__(self):\n        return len(self.df)\n\n    \ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n\n# load model\nmodel_file = 'cassava/model/best.pth'\nmodel = models.resnet50()\nmodel.fc = nn.Linear(model.fc.in_features, 5)\n#model = SwinTransformer(\n#    num_classes = 5\n#)\ncheckpoint = torch.load(model_file)\nstate = checkpoint['model']\nmodel.load_state_dict(state)\nmodel.to(device)\n\n\n# test image loader\nresult = {}\ntest_images = glob('/kaggle/input/cassava-leaf-disease-classification/test_images/*.jpg')\nfor img in test_images:\n    img_name = img.split('/')[-1]\n    if img_name not in result:\n        result[img_name] = 99\ntest_list = pd.DataFrame.from_dict(result, orient='index', columns=['label']).reset_index().rename(columns={'index': 'image_id'})\n\nvalid_transform = transforms.Compose([transforms.Resize((224, 224)),transforms.ToTensor(), transforms.Normalize(mean = [0.5,0.5,0.5], std = [0.5,0.5,0.5]),])\ntest_data = CassavaDataset_test(test_list, transforms=valid_transform, target_transform=None)\ntest_loader = torch.utils.data.DataLoader(test_data,batch_size=16,num_workers=2,shuffle=False,pin_memory=True)\n\n#  use model to test image and produce a 'submission.csv' file\nmodel.eval()\nwith torch.no_grad():\n    for idx, (image_id, images, _) in enumerate(test_loader):\n        images = images.to(device)\n        bsz = images.shape[0]\n        output = model(images)\n        _, predicted = torch.max(output.data, 1)\n        for i in range(bsz):\n            result[image_id[i]] = predicted[i].item()  # record predicted label\n        \nresult = pd.DataFrame.from_dict(result, orient='index', columns=['label']).reset_index().rename(columns={'index': 'image_id'})\nresult.to_csv('submission.csv', index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}