{"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 pandas as pd \nimport numpy as np\nfrom PIL import Image\nimport cv2\nimport matplotlib.pyplot as plt \nfrom tqdm import tqdm\nimport torch \nimport torchvision \nfrom torch.utils.data import Dataset, DataLoader\nimport torch.nn as nn\nimport torch.optim as optim","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-06-24T12:25:10.613218Z","iopub.execute_input":"2021-06-24T12:25:10.613564Z","iopub.status.idle":"2021-06-24T12:25:10.940309Z","shell.execute_reply.started":"2021-06-24T12:25:10.613529Z","shell.execute_reply":"2021-06-24T12:25:10.939505Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dir = \"../input/plant-pathology-2021-fgvc8/train_images\"\ntest_dir = \"../input/plant-pathology-2021-fgvc8/test_images\"","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:25:10.941809Z","iopub.execute_input":"2021-06-24T12:25:10.942163Z","iopub.status.idle":"2021-06-24T12:25:10.946108Z","shell.execute_reply.started":"2021-06-24T12:25:10.942127Z","shell.execute_reply":"2021-06-24T12:25:10.945171Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv(\"../input/plant-pathology-2021-fgvc8/train.csv\")\ndf.head()","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:25:10.948652Z","iopub.execute_input":"2021-06-24T12:25:10.949019Z","iopub.status.idle":"2021-06-24T12:25:11.006022Z","shell.execute_reply.started":"2021-06-24T12:25:10.948981Z","shell.execute_reply":"2021-06-24T12:25:11.004967Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Let's see number of classes ","metadata":{}},{"cell_type":"code","source":"classes = df[\"labels\"].unique()\nprint(classes, \"\\nTotal number of unique labels:\",len(classes))","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:25:11.007674Z","iopub.execute_input":"2021-06-24T12:25:11.008049Z","iopub.status.idle":"2021-06-24T12:25:11.016578Z","shell.execute_reply.started":"2021-06-24T12:25:11.008012Z","shell.execute_reply":"2021-06-24T12:25:11.015548Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"label_map = {}\nfor idx, j in enumerate(classes):\n    label_map.update({str(j):idx})\n    \nprint(label_map)\n#df.labels = df[]","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:25:11.018086Z","iopub.execute_input":"2021-06-24T12:25:11.018466Z","iopub.status.idle":"2021-06-24T12:25:11.026145Z","shell.execute_reply.started":"2021-06-24T12:25:11.018429Z","shell.execute_reply":"2021-06-24T12:25:11.025217Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = df.replace({\"labels\":label_map})","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:25:11.027634Z","iopub.execute_input":"2021-06-24T12:25:11.028326Z","iopub.status.idle":"2021-06-24T12:25:11.058685Z","shell.execute_reply.started":"2021-06-24T12:25:11.028288Z","shell.execute_reply":"2021-06-24T12:25:11.058009Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# parameters\nbatch_size = 32\nimage_size = 224\nepochs = 10\ndevice = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\ndevice","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:25:11.059940Z","iopub.execute_input":"2021-06-24T12:25:11.060286Z","iopub.status.idle":"2021-06-24T12:25:11.112306Z","shell.execute_reply.started":"2021-06-24T12:25:11.060252Z","shell.execute_reply":"2021-06-24T12:25:11.111490Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# plotting few sample data ","metadata":{}},{"cell_type":"code","source":"fig, ax = plt.subplots(4,3, figsize = (12, 10))\ni = 0\nfor row in range(4):\n    for col in range(3):\n        rand_idx = np.random.randint(len(df.image))\n        while df.labels[rand_idx] != label_map[classes[i]]:\n            rand_idx = np.random.randint(len(df.image))\n        img = Image.open(train_dir+\"/\"+df.image[rand_idx]).convert('RGB')\n        img = np.array(img)\n        ax[row, col].imshow(img)\n        ax[row, col].set_title(classes[i])\n        ax[row, col].set_axis_off()\n        i +=1 \nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:25:11.113718Z","iopub.execute_input":"2021-06-24T12:25:11.114418Z","iopub.status.idle":"2021-06-24T12:25:21.936955Z","shell.execute_reply.started":"2021-06-24T12:25:11.114381Z","shell.execute_reply":"2021-06-24T12:25:21.936193Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## augmentation and other stuff!","metadata":{}},{"cell_type":"code","source":"import albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\ndef get_train_transform():\n    return A.Compose([\n        A.Resize(width=300, height=300, p=1),\n        \n        A.RandomRotate90(),\n        A.Flip(),\n        A.Transpose(),\n        A.OneOf([\n            A.IAAAdditiveGaussianNoise(),\n            A.GaussNoise(),\n        ], p=0.2),\n        A.OneOf([\n            A.MotionBlur(p=.2),\n            A.MedianBlur(blur_limit=3, p=0.1),\n            A.Blur(blur_limit=3, p=0.1),\n        ], p=0.2),\n        A.ShiftScaleRotate(shift_limit=0.0625, scale_limit=0.2, rotate_limit=45, p=0.2),\n        A.OneOf([\n            A.OpticalDistortion(p=0.3),\n            A.GridDistortion(p=.1),\n            A.IAAPiecewiseAffine(p=0.3),\n        ], p=0.2),\n        A.OneOf([\n            A.CLAHE(clip_limit=2),\n            A.IAASharpen(),\n            A.IAAEmboss(),\n            A.RandomBrightnessContrast(),            \n        ], p=0.3),\n        A.HueSaturationValue(p=0.3),\n        ToTensorV2(p=1.0)\n    ])#https://albumentations.ai/docs/examples/example/\n\ndef get_valid_transform():\n    return A.Compose([\n        A.Resize(width=300, height=300, p=1),\n        ToTensorV2(p=1.0)\n    ])","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:25:21.939072Z","iopub.execute_input":"2021-06-24T12:25:21.939389Z","iopub.status.idle":"2021-06-24T12:25:23.802825Z","shell.execute_reply.started":"2021-06-24T12:25:21.939355Z","shell.execute_reply":"2021-06-24T12:25:23.801997Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class LeafDataset(Dataset):\n    def __init__(self, df, image_dir, transforms=None):\n        self.df = df\n        #slef.img_ids = df[\"image_id\"].unique()\n        self.image_dir=image_dir \n        self.transforms=transforms \n        \n    def __getitem__(self, index:int):\n        #image_id = self.img_ids[index] \n        img_id = self.df.image[index]\n        label = self.df.labels[index]\n#         image = cv2.imread(f\"{self.image_dir}/{image_id}\", cv2.IMREAD_COLOR)\n        image = Image.open(f\"{self.image_dir}/{img_id}\").convert('RGB')\n        image = np.array(image)\n        if self.transforms is not None:\n            image = self.transforms(image=image)['image']\n            \n        return image, label \n    \n    def __len__(self):\n        return len(self.df)\n        ","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:25:23.804266Z","iopub.execute_input":"2021-06-24T12:25:23.804579Z","iopub.status.idle":"2021-06-24T12:25:23.811579Z","shell.execute_reply.started":"2021-06-24T12:25:23.804544Z","shell.execute_reply":"2021-06-24T12:25:23.810428Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = LeafDataset(df, train_dir,transforms = get_train_transform() )\nvalid_dataset = LeafDataset(df, train_dir,transforms = get_valid_transform() )\n\nindices = torch.randperm(len(train_dataset)).tolist()\n\ntrain_dataset = torch.utils.data.Subset(train_dataset, indices[:-200])\nvalid_dataset = torch.utils.data.Subset(valid_dataset, indices[-200:])\n\ntrain_data_loader = DataLoader(\n    train_dataset, \n    batch_size = batch_size, \n    shuffle = True, \n    num_workers = 4\n)\nvalid_data_loader = DataLoader(\n    valid_dataset,\n    batch_size = batch_size, \n    shuffle = False,\n    num_workers = 4\n)\nprint(len(train_dataset), len(valid_dataset))","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:25:23.812987Z","iopub.execute_input":"2021-06-24T12:25:23.813757Z","iopub.status.idle":"2021-06-24T12:25:23.832199Z","shell.execute_reply.started":"2021-06-24T12:25:23.813706Z","shell.execute_reply":"2021-06-24T12:25:23.830941Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## let's plot few augmented ","metadata":{}},{"cell_type":"code","source":"def visualize(image, labels):\n    plt.figure(figsize=(20, 10))\n    plt.imshow(np.transpose(image.numpy(), (1, 2, 0)))\n    plt.title(labels.detach().numpy(), fontsize= 20)\n    plt.axis('off')\ndataiter = iter(train_data_loader)\nimages, labels = dataiter.next()    \nvisualize(torchvision.utils.make_grid(images), labels)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:25:23.833713Z","iopub.execute_input":"2021-06-24T12:25:23.834180Z","iopub.status.idle":"2021-06-24T12:25:46.910934Z","shell.execute_reply.started":"2021-06-24T12:25:23.834143Z","shell.execute_reply":"2021-06-24T12:25:46.909860Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# model tarining ","metadata":{}},{"cell_type":"code","source":"!pip install efficientnet_pytorch","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:25:46.913050Z","iopub.execute_input":"2021-06-24T12:25:46.913855Z","iopub.status.idle":"2021-06-24T12:26:09.192936Z","shell.execute_reply.started":"2021-06-24T12:25:46.913796Z","shell.execute_reply":"2021-06-24T12:26:09.191810Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from efficientnet_pytorch import EfficientNet\n\nmodel = EfficientNet.from_name('efficientnet-b4')\n# Unfreeze model weights\nfor param in model.parameters():\n    param.requires_grad = True\n    \nn_features =  model._fc.in_features\nmodel._fc = nn.Linear(n_features, len(classes))\nmodel.to(device)","metadata":{"scrolled":true,"execution":{"iopub.status.busy":"2021-06-24T12:26:09.194422Z","iopub.execute_input":"2021-06-24T12:26:09.196174Z","iopub.status.idle":"2021-06-24T12:26:15.855407Z","shell.execute_reply.started":"2021-06-24T12:26:09.196116Z","shell.execute_reply":"2021-06-24T12:26:15.854467Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss().to(device)\noptimizer = optim.Adam(model.parameters(), lr=0.0001, weight_decay = 1e-4 )\nscheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer, T_0=5, T_mult=1, eta_min=1e-6, last_epoch=-1, verbose=False)","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:26:15.856760Z","iopub.execute_input":"2021-06-24T12:26:15.857164Z","iopub.status.idle":"2021-06-24T12:26:15.883122Z","shell.execute_reply.started":"2021-06-24T12:26:15.857094Z","shell.execute_reply":"2021-06-24T12:26:15.882283Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train_one_epoch(model, epoch,train_data_loader, device, criterion, optimizer, scheduler=None,\n                    schd_batch_update=False ):\n    model.train()\n    running_loss = 0.0\n    sample_num = 0\n    pbar = tqdm(enumerate(train_data_loader), total=len(train_data_loader))\n    \n    for i, data in pbar:\n        # get the inputs; data is a list of [inputs, labels]\n        inputs, labels = data\n        inputs, labels = inputs.to(device).float(), labels.to(device).long()\n        # zero the parameter gradients\n        optimizer.zero_grad()\n        # forward + backward + optimize\n        outputs = model(inputs) \n        loss = criterion(outputs, labels)\n        loss.backward()\n#         optimizer.step()\n\n        # print statistics\n        running_loss += loss.item()\n        sample_num += labels.shape[0] \n        if ((i+1) % 2 == 0 ) or ((i+1)==len(train_data_loader)): \n            optimizer.step()\n            optimizer.zero_grad() \n            if scheduler is not None and not schd_batch_update:\n                scheduler.step()\n        if ((i+1) % 1 == 0 ) or ((i+1)==len(train_data_loader)):\n            description = f'epoch {epoch+1} loss: {running_loss/sample_num:.4f}'\n            pbar.set_description(description)\n            \n    if scheduler is not None and not schd_batch_update:\n                scheduler.step()\n    \ndef valid_one_epoch(model, epoch,valid_data_loader, device, criterian, optimizer, scheduler=None, \n                    schd_loss_update=False):\n    model.eval()\n    loss_sum = 0\n    sample_num = 0\n    all_predictions =[]\n    all_targets = []\n    pbar = tqdm(enumerate(valid_data_loader), total=len(valid_data_loader))\n    \n    for i , data in pbar:\n        inputs, labels = data\n        inputs, labels = inputs.to(device).float(), labels.to(device).long()\n        outputs = model(inputs) \n        all_predictions += [torch.argmax(outputs, 1).detach().cpu().numpy()]\n        all_targets += [labels.detach().cpu().numpy()]\n        loss = criterion(outputs, labels)\n        loss_sum += loss.item()*labels.shape[0]\n        sample_num += labels.shape[0] \n        if ((i+1) % 1 == 0) or ((i+1)==len(valid_data_loader)):    # print every 2000 mini-batches\n            description = f'epoch {epoch+1} loss: {loss_sum/sample_num:.4f}'\n            pbar.set_description(description)\n        \n    all_predictions= np.concatenate(all_predictions)\n    all_targets = np.concatenate(all_targets)\n    print('validation accuracy = {:.4f}'.format((all_predictions==all_targets).mean()))\n    \n    if scheduler is not None:\n        if schd_loss_update:\n            scheduler.step(loss_sum/sample_num)\n        else:\n            scheduler.step()\n        \n","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:56:28.679698Z","iopub.execute_input":"2021-06-24T12:56:28.680061Z","iopub.status.idle":"2021-06-24T12:56:28.694546Z","shell.execute_reply.started":"2021-06-24T12:56:28.680027Z","shell.execute_reply":"2021-06-24T12:56:28.693627Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for epoch in range(10):  # loop over the dataset multiple times\n    \n    train_one_epoch(model, epoch,train_data_loader, device, criterion, optimizer, scheduler=None, schd_batch_update = False)\n    with torch.no_grad():\n        valid_one_epoch(model, epoch, valid_data_loader, device, criterion, optimizer, scheduler=None, schd_loss_update = False)\n    torch.save(model.state_dict(),f'efficientnet-b4-{epoch}.pth')\n\nprint('Finished Training')","metadata":{"execution":{"iopub.status.busy":"2021-06-24T12:56:29.330721Z","iopub.execute_input":"2021-06-24T12:56:29.331097Z","iopub.status.idle":"2021-06-24T12:57:23.591313Z","shell.execute_reply.started":"2021-06-24T12:56:29.331062Z","shell.execute_reply":"2021-06-24T12:57:23.589351Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Continue ...","metadata":{}}]}