{"cells":[{"metadata":{},"cell_type":"markdown","source":"### Pytorch 1.6 was introduced with Automatic Mixed Precision. Though the model took a minute less while training with AMP, it comes at a cost.\n\n### There are number of issues and conflict while installing torch 1.6 so i am just sharing the results of the performance at the end for one epoch.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\nprint(torch.__version__)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# !pip3 install torch==1.6.0\n# !pip3 install torchvision==0.7.0\n# !pip3 install albumentations==0.4.5","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport cv2\nimport os\nimport re\nimport random\n\nfrom PIL import Image\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\nfrom albumentations.augmentations.transforms import Normalize, ShiftScaleRotate\n\nimport torch\nimport torchvision\n\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nfrom torchvision.models.detection.faster_rcnn import FasterRCNN\nfrom torchvision.models.detection.rpn import AnchorGenerator\n\nfrom torch.utils.data import DataLoader, Dataset\nfrom torch.utils.data.sampler import SequentialSampler\nfrom ast import literal_eval\nfrom torch.cuda.amp import GradScaler, autocast\n\n\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\nimport seaborn as sns\n\n#input_dir = \nDIR_train = '/kaggle/input/global-wheat-detection/train/'\nDIR_test = '/kaggle/input/global-wheat-detection/test/'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv(\"/kaggle/input/global-wheat-detection/train.csv\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(train.shape)\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def converter(x):\n    return literal_eval(x)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train[\"bbox\"] = train[\"bbox\"].apply(converter)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train[\"x\"] = -1\ntrain[\"y\"] = -1\ntrain[\"w\"] = -1\ntrain[\"h\"] = -1\ntrain[['x', 'y', 'w', 'h']] = np.stack(train[\"bbox\"])\ntrain['x'] = train['x'].astype(np.float)\ntrain['y'] = train['y'].astype(np.float)\ntrain['w'] = train['w'].astype(np.float)\ntrain['h'] = train['h'].astype(np.float)","execution_count":null,"outputs":[]},{"metadata":{"jupyter":{"source_hidden":true},"trusted":true},"cell_type":"code","source":"type(train[\"x\"][0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.drop(\"bbox\",inplace=True,axis=1)","execution_count":null,"outputs":[]},{"metadata":{"jupyter":{"source_hidden":true},"trusted":true},"cell_type":"code","source":"image_ids = train['image_id'].unique()\nprint(f'Total Number of Images: {len(image_ids)}')","execution_count":null,"outputs":[]},{"metadata":{"jupyter":{"source_hidden":true},"trusted":true},"cell_type":"code","source":"valid_ids = image_ids[-665:]\ntrain_ids = image_ids[:-665]\n\nprint(f'Number of Train Images: {len(train_ids)}')\nprint(f'Number of Validation Images: {len(valid_ids)}')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"valid = train[train['image_id'].isin(valid_ids)]\ntrain = train[train['image_id'].isin(train_ids)]","execution_count":null,"outputs":[]},{"metadata":{"jupyter":{"source_hidden":true},"trusted":true},"cell_type":"code","source":"valid.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = True\n\nseed_everything(42)\n\ndef get_all_bboxes(df, image_id):\n    image_bboxes = df[df.image_id == image_id]\n    \n    bboxes = []\n    for _,row in image_bboxes.iterrows():\n        bboxes.append((row.x, row.y, row.w, row.h))\n        \n    return bboxes\n\ndef plot_image_examples(df, rows=3, cols=3, title='Image examples'):\n    fig, axs = plt.subplots(rows, cols, figsize=(10,10))\n    for row in range(rows):\n        for col in range(cols):\n            idx = np.random.randint(len(df), size=1)[0]\n            img_id = df.iloc[idx].image_id\n            \n            img = cv2.imread(DIR_train + img_id + '.jpg',)\n            img = cv2.cvtColor(img, cv2.INTER_CUBIC)\n            axs[row, col].imshow(img)\n            \n            bboxes = get_all_bboxes(df, img_id)\n            \n            for bbox in bboxes:\n                rect = patches.Rectangle((bbox[0],bbox[1]),bbox[2],bbox[3],linewidth=1,edgecolor='r',facecolor='none')\n                axs[row, col].add_patch(rect)\n            \n            axs[row, col].axis('off')\n            \n    plt.suptitle(title)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_image_examples(train)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Creating Dataset Class","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class WheatDataset(Dataset):\n    \n    def __init__(self, df, IMG_DIR, transforms=None):\n        super().__init__()\n        self.image_ids = df[\"image_id\"].unique()\n        self.df = df\n        self.IMG_DIR = IMG_DIR\n        self.transforms = transforms\n        \n    def __getitem__(self, index:int):\n        image_id = self.image_ids[index]\n        row = self.df[self.df[\"image_id\"]==image_id]\n        \n        \"\"\"\n        Reading and processing the image using CV2\n        cv2.IMREAD_COLOR: It specifies to load a color image. \n        Any transparency of image will be neglected. It is the default flag. Alternatively, we can pass integer value 1 for this flag.\n        \"\"\"\n        image = cv2.imread(f'{self.IMG_DIR}/{image_id}.jpg', cv2.IMREAD_COLOR)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        #image/=255.0\n        boxes = row[['x', 'y', 'w', 'h']].values\n        boxes[:, 2] = boxes[:, 0] + boxes[:, 2]\n        boxes[:, 3] = boxes[:, 1] + boxes[:, 3]\n        \n        area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])\n        area = torch.as_tensor(area, dtype=torch.float32)\n\n        # there is only one class\n        labels = torch.ones((row.shape[0],), dtype=torch.int64)\n        \n        # suppose all instances are not crowd\n        iscrowd = torch.zeros((row.shape[0],), dtype=torch.int64)\n        \n        target = {}\n        target['boxes'] = boxes\n        target['labels'] = labels\n        target['image_id'] = torch.tensor([index])\n        target['area'] = area\n        target['iscrowd'] = iscrowd\n        \n        if self.transforms:\n            sample = {\n                'image': image,\n                'bboxes': target['boxes'],\n                'labels': labels\n            }\n            sample = self.transforms(**sample)\n            image = sample['image']\n            \n            target['boxes'] = torch.tensor(sample['bboxes']).float()\n            \n        return image, target, image_id\n    \n    \n    def __len__(self) -> int:\n        return self.image_ids.shape[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Albumentations\ndef get_train_transform():\n    return A.Compose([A.Flip(0.5),\n        Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),\n        ShiftScaleRotate(),\n        ToTensorV2(p=1.0)], 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":"def collate_fn(batch):\n    return tuple(zip(*batch))\n\ntrain_dataset = WheatDataset(train, DIR_train, get_train_transform())\nvalid_dataset = WheatDataset(valid, DIR_train, get_valid_transform())\n\n\n# split the dataset in train and test set\nindices = torch.randperm(len(train_dataset)).tolist()\n\ntrain_data_loader = DataLoader(\n    train_dataset,\n    batch_size=4,\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":{"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":"num_classes = 2  # 1 class (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":{"trusted":true},"cell_type":"code","source":"class Averager:\n    def __init__(self):\n        self.current_total = 0.0\n        self.iterations = 0.0\n\n    def send(self, value):\n        self.current_total += value\n        self.iterations += 1\n\n    @property\n    def value(self):\n        if self.iterations == 0:\n            return 0\n        else:\n            return 1.0 * self.current_total / self.iterations\n\n    def reset(self):\n        self.current_total = 0.0\n        self.iterations = 0.0","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"images, targets, image_ids = next(iter(train_data_loader))\nimages = list(image.to(device) for image in images)\ntargets = [{k: v.to(device) for k, v in t.items()} for t in targets]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#targets[0]['boxes'][0][0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# boxes = targets[2]['boxes'].cpu().numpy().astype(np.int32)\n# sample = images[2].permute(1,2,0).cpu().numpy()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.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 = 1","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Automatic Mixed Precision","execution_count":null},{"metadata":{"jupyter":{"source_hidden":true},"trusted":true},"cell_type":"code","source":"%%time\nloss_hist = Averager()\nitr = 1\nscaler = GradScaler()\nfor epoch in range(num_epochs):\n    loss_hist.reset()\n    with autocast():\n        for images, targets, image_ids in train_data_loader:\n            \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            loss_value = losses.item()\n\n            loss_hist.send(loss_value)\n\n            optimizer.zero_grad()\n            scaler.scale(losses).backward()\n            scaler.step(optimizer)\n            scaler.update()\n            if itr % 50 == 0:\n                print(f\"Iteration #{itr} loss: {loss_value}\")\n\n            itr += 1\n    \n    # update the learning rate\n    if lr_scheduler is not None:\n        lr_scheduler.step()\n\n    print(f\"Epoch #{epoch} loss: {loss_hist.value}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Without Automatic Mixed Precision","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\nloss_hist = Averager()\nitr = 1\n\nfor epoch in range(num_epochs):\n    loss_hist.reset()\n\n    for images, targets, image_ids in train_data_loader:\n\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        loss_value = losses.item()\n\n        loss_hist.send(loss_value)\n\n        optimizer.zero_grad()\n        losses.backward()\n        optimizer.step()\n        \n        if itr % 50 == 0:\n            print(f\"Iteration #{itr} loss: {loss_value}\")\n\n        itr += 1\n    \n    # update the learning rate\n    if lr_scheduler is not None:\n        lr_scheduler.step()\n\n    print(f\"Epoch #{epoch} loss: {loss_hist.value}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### First Trial Result\n\n### Without Automatic Mixed Precision\n\n```python\nIteration #50 loss: 1.0456067323684692\nIteration #100 loss: 1.147918701171875\nIteration #150 loss: 1.3456902503967285\nIteration #200 loss: 1.181681513786316\nIteration #250 loss: 1.2525768280029297\nIteration #300 loss: 1.3692357540130615\nIteration #350 loss: 1.2762341499328613\nIteration #400 loss: 0.9973544478416443\nIteration #450 loss: 0.9669315814971924\nIteration #500 loss: 1.0188997983932495\nIteration #550 loss: 1.2255617380142212\nIteration #600 loss: 1.1135486364364624\nIteration #650 loss: 1.0958911180496216\nEpoch #0 loss: 1.1444043681751854\nCPU times: user 5min 24s, sys: 3.79 s, total: 5min 28s\nWall time: 5min 31s\n```\n\n### With Automatic Mixed Precision\n\n```python\nIteration #50 loss: 2.2115015983581543\nIteration #100 loss: 2.2777822017669678\nIteration #150 loss: 2.2091197967529297\nIteration #200 loss: 2.1065731048583984\nIteration #250 loss: 2.188401460647583\nIteration #300 loss: 1.940766453742981\nIteration #350 loss: 2.7645530700683594\nIteration #400 loss: 2.2159934043884277\nIteration #450 loss: 2.3759047985076904\nIteration #500 loss: 2.2256269454956055\nIteration #550 loss: 2.5738768577575684\nIteration #600 loss: 2.2025678157806396\nIteration #650 loss: 2.856794595718384\nEpoch #0 loss: 2.2858444737542856\nCPU times: user 3min 58s, sys: 4.79 s, total: 4min 3s\nWall time: 4min 6s\n```","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"### References\n\n[Kaggle Notebook for EDA](https://www.kaggle.com/aleksandradeis/globalwheatdetection-eda)\n\n[Getting started with Object Detection](https://www.kaggle.com/pestipeti/pytorch-starter-fasterrcnn-train)","execution_count":null},{"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}