{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Data Preprocessing"},{"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\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","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom matplotlib import pyplot as plt\n\n#from tqdm import tqdm_notebook as tqdm\n#from tqdm import tqdm \nfrom tqdm.notebook import tqdm as tqdm\n\nimport cv2\nimport os\nimport re\n\nimport random\n\nfrom PIL import Image\n\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\n\nimport torch\nimport torchvision\n\nimport ast\n\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nfrom torchvision.models.detection import FasterRCNN\nfrom torchvision.models.detection.rpn import AnchorGenerator\n\nfrom torch.utils.data import DataLoader, Dataset\nfrom torch.utils.data.sampler import SequentialSampler","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"DATA_PATH = \"../input/global-wheat-detection/\"\nTRAIN_DIR = \"../input/global-wheat-detection/train\"\nTEST_DIR = \"../input/global-wheat-detection/test\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv(os.path.join(DATA_PATH, \"train.csv\"))\ndf.head(5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def extract_bbox(DataFrame):\n    DataFrame[\"x\"] = [np.float(ast.literal_eval(i)[0]) for i in DataFrame[\"bbox\"]]\n    DataFrame[\"y\"] = [np.float(ast.literal_eval(i)[1]) for i in DataFrame[\"bbox\"]]\n    DataFrame[\"w\"] = [np.float(ast.literal_eval(i)[2]) for i in DataFrame[\"bbox\"]]\n    DataFrame[\"h\"] = [np.float(ast.literal_eval(i)[3]) for i in DataFrame[\"bbox\"]]\n    \nextract_bbox(df)\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_split = 0.8\nimages_id   = df[\"image_id\"].unique() \ntrain_ids   = images_id[:int(len(images_id)*train_split)]\nvalid_ids   = images_id[int(len(images_id)*train_split):]\n\nprint(f'Total Images Number: {len(images_id)}')\nprint(f'Number of training images: {len(train_ids)}')\nprint(f'Number of Valid images: {len(valid_ids)}')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = df[df[\"image_id\"].isin(train_ids)]\nvalid_df = df[df[\"image_id\"].isin(valid_ids)]\n\nprint(f'Shape of train_df: {train_df.shape}')\nprint(f'Shape of valid_df: {valid_df.shape}')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Here I have added some more argmentations to see if we can improve the validation loss."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Data Transform - Albumentation\ndef get_train_transform():\n    return A.Compose([\n        A.HorizontalFlip(p=0.5),\n        A.RandomBrightnessContrast(p=0.2),\n        A.Blur(p=1),\n        ToTensorV2(p=1.0)\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})\n\ndef get_valid_transform():\n    return A.Compose([\n        ToTensorV2(p=1.0)\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class WheatDataset(Dataset):\n    def __init__(self, dataframe, image_dir, transform=None):\n        super().__init__()\n        self.dataframe = dataframe\n        self.image_dir = image_dir\n        self.transform = transform\n        self.image_ids = dataframe[\"image_id\"].unique()\n        \n    def __getitem__(self, idx):\n        #Load images and details\n        image_id = self.image_ids[idx]\n        details = self.dataframe[self.dataframe[\"image_id\"]==image_id]\n        img_path = os.path.join(TRAIN_DIR, image_id)+\".jpg\"\n        image = cv2.imread(img_path, cv2.IMREAD_COLOR)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        image /= 255.0\n        \n        #Row of Dataframe of a particular index.\n        boxes = details[['x', 'y', 'w', 'h']].values\n        boxes[:, 2] = boxes[:, 0] + boxes[:, 2]\n        boxes[:, 3] = boxes[:, 1] + boxes[:, 3]\n        \n        #To find area\n        area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])\n        \n        #Convert it into tensor dataType\n        area = torch.as_tensor(area, dtype=torch.float32)\n        \n        # there is only one class\n        labels = torch.ones((details.shape[0],), dtype=torch.int64)\n        \n        # suppose all instances are not crowd\n        iscrowd = torch.zeros((details.shape[0],), dtype=torch.int64)\n        \n        target = {}\n        target['boxes'] = boxes\n        target['labels'] = labels\n        target['image_id'] = torch.tensor(idx) ### <------------ New change list has been removed\n        target['area'] = area\n        target['iscrowd'] = iscrowd\n        \n        if self.transform:\n            sample = {\n                'image': image,\n                'bboxes': target['boxes'],\n                'labels': labels\n            }\n            \n            sample = self.transform(**sample)\n            image = sample['image']\n            target['boxes'] = torch.stack(tuple(map(torch.tensor, zip(*sample['bboxes'])))).permute(1, 0)\n            target[\"boxes\"] = torch.as_tensor(target[\"boxes\"], dtype=torch.long)\n        \n        return image, target     #, image_id\n    \n    def __len__(self) -> int:\n        return len(self.image_ids)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def collate_fn(batch):\n    return tuple(zip(*batch))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dataset = WheatDataset(train_df, TRAIN_DIR, get_train_transform())\nvalid_dataset = WheatDataset(valid_df, TRAIN_DIR, get_valid_transform())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"indices = torch.randperm(len(train_dataset)).tolist()\n\ntrain_data_loader = DataLoader(\n    train_dataset,\n    batch_size=8,\n    shuffle=False,\n    num_workers=4,\n    collate_fn=collate_fn\n)\n\nvalid_data_loader = DataLoader(\n    valid_dataset,\n    batch_size=8,\n    shuffle=False,\n    num_workers=4,\n    collate_fn=collate_fn\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained = True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(model)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"num_classes = 2  # wheat + background\n\n# get number of input features for the classifier\nin_features = model.roi_heads.box_predictor.cls_score.in_features\n\n# replace the pre-trained head with a new one\nmodel.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Reduced the number of epochs to 10 as on the previous model overfitting occured."},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\nmodel.to(device)\nparams = [p for p in model.parameters() if p.requires_grad]\noptimizer = torch.optim.SGD(params, lr=0.005, momentum=0.9, weight_decay=0.0005)\n# lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1)\nlr_scheduler = None\n\nnum_epochs = 9","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import time\n\nitr=1\n\ntotal_train_loss = []\ntotal_valid_loss = []\n\nlosses_value = 0\nfor epoch in range(num_epochs):\n  \n    start_time = time.time()\n    train_loss = []\n    model.train()\n    \n #<-----------Training Loop---------------------------->\n    pbar = tqdm(train_data_loader, desc = 'description')\n    for images, targets in pbar:\n        \n        images = list(image.to(device) for image in images)\n        targets = [{k: v.to(device) for k, v in t.items()} for t in targets]\n\n        loss_dict = model(images, targets)\n        \n        losses = sum(loss for loss in loss_dict.values())\n        losses_value = losses.item()\n        train_loss.append(losses_value)        \n        optimizer.zero_grad()\n        losses.backward()\n        optimizer.step()\n        \n        pbar.set_description(f\"Epoch: {epoch+1}, Batch: {itr}, loss: {losses_value}\")\n        itr+=1\n\n    epoch_train_loss = np.mean(train_loss)\n    total_train_loss.append(epoch_train_loss)\n    \n    \n    #<---------------Validation Loop---------------------->\n    with torch.no_grad():\n        valid_loss = []\n\n        for images, targets in valid_data_loader:\n            images = list(image.to(device) for image in images)\n            targets = [{k: v.to(device) for k, v in t.items()} for t in targets]\n            \n            \n            # If you need validation losses\n            model.train()\n            # Calculate validation losses\n            loss_dict = model(images, targets)\n            losses = sum(loss for loss in loss_dict.values())\n            loss_value = losses.item()\n            valid_loss.append(loss_value)\n            \n    epoch_valid_loss = np.mean(valid_loss)\n    total_valid_loss.append(epoch_valid_loss)\n    \n    print(f\"Epoch Completed: {epoch+1}/{num_epochs}, Time: {time.time()-start_time},\\\n    Train Loss: {epoch_train_loss}, Valid Loss: {epoch_valid_loss}\")   \n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import seaborn as sns\nplt.figure(figsize=(8,5))\nsns.set_style(style=\"whitegrid\")\nsns.lineplot(range(1, len(total_train_loss)+1), total_train_loss, label=\"Training Loss\")\nsns.lineplot(range(1, len(total_train_loss)+1), total_valid_loss, label=\"Valid Loss\")\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Loss\")\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Training data"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Data Transform - Test Albumentation\ndef get_test_transform():\n    return A.Compose([\n        ToTensorV2(p=1.0)\n    ])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class WheatTestDatasetTest(Dataset):\n    def __init__(self, dataframe, image_dir, transform=None):\n        super().__init__()\n        self.dataframe = dataframe\n        self.image_dir = image_dir\n        self.transform = transform\n        self.image_ids = dataframe[\"image_id\"].unique()\n        \n    def __getitem__(self, idx):\n        image_id = self.image_ids[idx]\n        details = self.dataframe[self.dataframe[\"image_id\"]==image_id]\n        img_path = os.path.join(TEST_DIR, image_id)+\".jpg\"\n        image = cv2.imread(img_path, cv2.IMREAD_COLOR)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        image /= 255.0\n        \n        if self.transform:\n            sample = {\n                'image': image,\n            }\n            \n            sample = self.transform(**sample)\n            image = sample['image']\n        \n        return image, image_id\n    \n    def __len__(self) -> int:\n        return len(self.image_ids)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_test = pd.read_csv(os.path.join(DATA_PATH, \"sample_submission.csv\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def collate_fn(batch):\n    return tuple(zip(*batch))\n\ntest_dataset = WheatTestDatasetTest(df_test, TEST_DIR, get_test_transform())\nprint(f\"Length of test dataset: {len(test_dataset)}\")\n\ntest_data_loader = DataLoader(\n    test_dataset,\n    batch_size=4,\n    shuffle=False,\n    num_workers=4,\n    drop_last=False,\n    collate_fn=collate_fn\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Evaluate model "},{"metadata":{"trusted":true},"cell_type":"code","source":"model.eval()\nx = model.to(device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"detection_threshold = 0.5\noutput_list = []\n\nfor images, image_ids in test_data_loader:\n\n    images = list(image.to(device) for image in images)\n    outputs = model(images)\n\n    for i, image in enumerate(images):\n\n        boxes = outputs[i]['boxes'].data.cpu().numpy()\n        scores = outputs[i]['scores'].data.cpu().numpy()\n        \n        boxes = boxes[scores >= detection_threshold].astype(np.int32)\n        scores = scores[scores >= detection_threshold]\n        image_id = image_ids[i]\n        \n        boxes[:, 2] = boxes[:, 2] - boxes[:, 0]\n        boxes[:, 3] = boxes[:, 3] - boxes[:, 1]\n        \n        output_dict = {\n            'image_id': image_ids[i],\n            'boxes': outputs[i]['boxes'].data.cpu().numpy(),\n            'scores': outputs[i]['scores'].data.cpu().numpy()\n        }\n        output_list.append(output_dict)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Prediction images "},{"metadata":{"trusted":true},"cell_type":"code","source":"## Plot image prediction\n\ndef predict_images(n_num, random_selection=True):\n    '''Plot N Number of Predicted Images'''\n    if random_selection:\n        index = random.sample(range(0, len(df_test[\"image_id\"].unique())), n_num)\n    else:\n        index = range(0, n_num)\n        \n    plt.figure(figsize=(15,15))\n    fig_no = 1\n    \n    for i in index:\n        images, image_id = test_dataset.__getitem__(i)\n        sample = images.permute(1,2,0).cpu().numpy()\n        boxes = output_list[i]['boxes']\n        scores = output_list[i]['scores']\n        boxes = boxes[scores >= detection_threshold].astype(np.int32)\n        #Plot figure/image\n        for box in boxes:\n            cv2.rectangle(sample,(box[0], box[1]),(box[2], box[3]),(255,223,0), 2)\n        plt.subplot(n_num/2, n_num/2, fig_no)\n        plt.imshow(sample)\n        fig_no+=1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def print_image(i):\n  plt.figure(figsize=(6,6))\n  images, image_id = test_dataset.__getitem__(i)\n  sample = images.permute(1,2,0).cpu().numpy()\n  boxes = output_list[i]['boxes']\n  scores = output_list[i]['scores']\n  boxes = boxes[scores >= detection_threshold].astype(np.int32)\n  #Plot figure/image\n  for box in boxes:\n      cv2.rectangle(sample,(box[0], box[1]),(box[2], box[3]),(255,223,0), 2)\n  # plt.subplot(n_num/2, n_num/2, fig_no)\n  plt.imshow(sample)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"predict_images(4, True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print_image(0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"torch.save(model.state_dict(), 'fasterrcnn_best_resnet50.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}