{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":19989,"databundleVersionId":1160143,"sourceType":"competition"}],"dockerImageVersionId":30732,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install lightning\n!pip install pycocotools\n!pip install hydra-core","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:46:13.277841Z","iopub.execute_input":"2024-07-02T10:46:13.278147Z","iopub.status.idle":"2024-07-02T10:46:59.267401Z","shell.execute_reply.started":"2024-07-02T10:46:13.278105Z","shell.execute_reply":"2024-07-02T10:46:59.265795Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n# Data Loading","metadata":{}},{"cell_type":"code","source":"import os \nimport pandas as pd\nimport numpy as np","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:25.744781Z","iopub.execute_input":"2024-07-02T10:47:25.745682Z","iopub.status.idle":"2024-07-02T10:47:26.113321Z","shell.execute_reply.started":"2024-07-02T10:47:25.745644Z","shell.execute_reply":"2024-07-02T10:47:26.112546Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"root = '/kaggle/input/global-wheat-detection'\nclasses = ['background', 'wheat']\nclass_to_idx_dict = dict(zip(classes, range(len(classes))))","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:26.114770Z","iopub.execute_input":"2024-07-02T10:47:26.115157Z","iopub.status.idle":"2024-07-02T10:47:26.119426Z","shell.execute_reply.started":"2024-07-02T10:47:26.115109Z","shell.execute_reply":"2024-07-02T10:47:26.118608Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv('/kaggle/input/global-wheat-detection/train.csv')\ndisplay(train_df)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:26.488329Z","iopub.execute_input":"2024-07-02T10:47:26.488733Z","iopub.status.idle":"2024-07-02T10:47:26.795462Z","shell.execute_reply.started":"2024-07-02T10:47:26.488705Z","shell.execute_reply":"2024-07-02T10:47:26.794624Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df = pd.read_csv('/kaggle/input/global-wheat-detection/sample_submission.csv')\ndisplay(submission_df)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:26.877816Z","iopub.execute_input":"2024-07-02T10:47:26.878823Z","iopub.status.idle":"2024-07-02T10:47:26.896378Z","shell.execute_reply.started":"2024-07-02T10:47:26.878785Z","shell.execute_reply":"2024-07-02T10:47:26.895562Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_coords_df = pd.DataFrame(list(train_df.bbox.apply(lambda x: (x[1:-1].split(','))).values), columns=['x1', 'y1', 'w', 'h'], dtype='float32')","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:27.711940Z","iopub.execute_input":"2024-07-02T10:47:27.712849Z","iopub.status.idle":"2024-07-02T10:47:28.088986Z","shell.execute_reply.started":"2024-07-02T10:47:27.712812Z","shell.execute_reply":"2024-07-02T10:47:28.088151Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"full_target_df = pd.concat([train_df, train_coords_df], axis=1)\nfull_target_df['x2'] = full_target_df['x1'] + full_target_df['w'] \nfull_target_df['y2'] = full_target_df['y1'] + full_target_df['h']\nfull_target_df['class'] = ['wheat']* full_target_df.shape[0]\nfull_target_df['label'] = full_target_df['class'].apply(lambda x: class_to_idx_dict[x]).values\nfull_target_df['area'] = (full_target_df['w'])* (full_target_df['h'])\nfull_target_df['iscrowd'] = 0\nfull_target_df.drop(['width', 'height', 'bbox', 'w', 'h'], axis=1, inplace=True)\ndisplay(full_target_df)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:29.916996Z","iopub.execute_input":"2024-07-02T10:47:29.917830Z","iopub.status.idle":"2024-07-02T10:47:30.068652Z","shell.execute_reply.started":"2024-07-02T10:47:29.917792Z","shell.execute_reply":"2024-07-02T10:47:30.067746Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.model_selection import StratifiedKFold\nskf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\nfull_image_df = full_target_df[['image_id']].copy()\nfull_image_df['bbox_count'] = 1\nfull_image_df = full_image_df.groupby('image_id', as_index=False).count()","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:31.639921Z","iopub.execute_input":"2024-07-02T10:47:31.640725Z","iopub.status.idle":"2024-07-02T10:47:32.252941Z","shell.execute_reply.started":"2024-07-02T10:47:31.640691Z","shell.execute_reply":"2024-07-02T10:47:32.252185Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"full_image_df['source'] = full_target_df[['image_id', 'source']].groupby('image_id').min()['source'].values\nfull_image_df.loc[:, 'stratify_group'] = np.char.add(\n    full_image_df['source'].values.astype(str),\n    full_image_df['bbox_count'].apply(lambda x: f'_{x // 15}').values.astype(str)\n)\nfull_image_df['image_path'] = root + \"/train/\" + full_image_df['image_id'] + \".jpg\"\nfull_image_df.loc[:, 'fold'] = 0\n\nfor fold_number, (train_index, val_index) in enumerate(skf.split(X=full_image_df.index, y=full_image_df['stratify_group'])):\n    full_image_df.loc[val_index, 'fold'] = fold_number","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:34.427502Z","iopub.execute_input":"2024-07-02T10:47:34.427871Z","iopub.status.idle":"2024-07-02T10:47:34.716144Z","shell.execute_reply.started":"2024-07-02T10:47:34.427842Z","shell.execute_reply":"2024-07-02T10:47:34.715206Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(full_image_df)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:34.846098Z","iopub.execute_input":"2024-07-02T10:47:34.846520Z","iopub.status.idle":"2024-07-02T10:47:34.859903Z","shell.execute_reply.started":"2024-07-02T10:47:34.846490Z","shell.execute_reply":"2024-07-02T10:47:34.858937Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_image_df = submission_df.copy(deep=True)\ntest_image_df['image_path'] = root + \"/test/\" + test_image_df['image_id'] + \".jpg\"\ndisplay(test_image_df)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:35.761470Z","iopub.execute_input":"2024-07-02T10:47:35.762324Z","iopub.status.idle":"2024-07-02T10:47:35.773904Z","shell.execute_reply.started":"2024-07-02T10:47:35.762290Z","shell.execute_reply":"2024-07-02T10:47:35.772934Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"full_target_df.to_csv('/kaggle/working/full_target.csv', index=False)\nfull_image_df.to_csv('/kaggle/working/full_image.csv', index=False)\ntest_image_df.to_csv('/kaggle/working/test_image.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:38.590468Z","iopub.execute_input":"2024-07-02T10:47:38.591109Z","iopub.status.idle":"2024-07-02T10:47:39.614324Z","shell.execute_reply.started":"2024-07-02T10:47:38.591074Z","shell.execute_reply":"2024-07-02T10:47:39.613400Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Libraries","metadata":{}},{"cell_type":"code","source":"# Standard libraries\nimport math\nimport os\nimport random\nimport time\nimport urllib.request\nfrom functools import partial\nfrom urllib.error import HTTPError\nfrom types import SimpleNamespace\nimport collections\nfrom omegaconf import DictConfig, OmegaConf\nfrom typing import Any, Dict, List\nimport importlib\n# PyTorch Lightning\nimport lightning as L\nfrom lightning.pytorch.loggers import TensorBoardLogger, CSVLogger\n\n# Plotting\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport matplotlib_inline.backend_inline\nimport seaborn as sns\n\nfrom mpl_toolkits.axes_grid1 import ImageGrid\nfrom PIL import Image\n\n# Libraries for ML\nimport numpy as np\nimport pandas as pd\nimport sklearn\nfrom sklearn.model_selection import KFold, GroupKFold, train_test_split\n\n# PyTorch\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nimport torch.utils.data as data\nfrom torch.utils.data import random_split, Dataset, DataLoader, Subset\nfrom torchvision.transforms import v2\nimport torchmetrics\n\n# Torchvision\nimport torchvision\nfrom lightning.pytorch.callbacks import LearningRateMonitor, ModelCheckpoint\nfrom torchvision import transforms\nfrom tqdm.notebook import tqdm\nimport timm\n\nimport cv2\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:39.615714Z","iopub.execute_input":"2024-07-02T10:47:39.615966Z","iopub.status.idle":"2024-07-02T10:47:48.633456Z","shell.execute_reply.started":"2024-07-02T10:47:39.615944Z","shell.execute_reply":"2024-07-02T10:47:48.632456Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Configuration","metadata":{}},{"cell_type":"code","source":"dict_cfg = {'general': {'workspace': 'erlemar', 'project_name': 'wheat'},\n       'training': {'lr': 0.0001, 'metric': 'val_map', 'first_run': True,\n                    'seed': 42, 'debug': False, 'mode': 'max', 'save_dir': '/kaggle/working',\n                    'save_name': '${general.project_name}', 'pretrained': False, 'pretrained_fname': ''},\n       'optimizer': {'class_name': 'torch.optim.AdamW', 'params': {'lr': '${training.lr}', 'weight_decay': 0.001}},\n       'scheduler': {'class_name': 'torch.optim.lr_scheduler.ReduceLROnPlateau', 'step': 'epoch', 'monitor': '${training.metric}', 'params': {'mode': '${training.mode}',                                                                                                                       'factor': 0.1, 'patience': 5}},\n       'model': {'backbone': {'class_name': 'torchvision.models.detection.fasterrcnn_resnet50_fpn_v2', 'params': {'weights': 'COCO_V1'}},\n                 'head': {'class_name': 'torchvision.models.detection.faster_rcnn.FastRCNNPredictor', 'params': {'num_classes': 2}}},\n       'data': {\n           'full_image_path': '/kaggle/working/full_image.csv',\n           'full_target_path': '/kaggle/working/full_target.csv',\n           'test_image_path': '/kaggle/working/test_image.csv',\n           'num_workers': 4, 'batch_size': 16, 'valid_train_ratio': 0.2,\n           'img_h': 512, 'img_w': 512\n       },\n       'augmentation': {\n           'train': {\n               'augs': [{'class_name': 'albumentations.HorizontalFlip', 'params': {'p': 0.6}},\n                       {'class_name': 'albumentations.RandomBrightnessContrast', 'params': {'p': 0.6}},\n                        {'class_name': 'albumentations.Resize', 'params': {'height': '${data.img_h}', 'width': '${data.img_w}', 'p': 1.0}},\n                       {'class_name': 'albumentations.pytorch.transforms.ToTensorV2', 'params': {'p': 1.0}}],\n                'bbox_params': {'format': 'pascal_voc', 'label_fields': ['labels']}},\n            'valid': {\n                'augs': [{'class_name': 'albumentations.Resize', 'params': {'height': '${data.img_h}', 'width': '${data.img_w}', 'p': 1.0}},\n                    {'class_name': 'albumentations.pytorch.transforms.ToTensorV2', 'params': {'p': 1.0}}],\n                'bbox_params': {'format': 'pascal_voc', 'label_fields': ['labels']}},\n           'test': {\n                'augs': [{'class_name': 'albumentations.Resize', 'params': {'height': '${data.img_h}', 'width': '${data.img_w}', 'p': 1.0}},\n                    {'class_name': 'albumentations.pytorch.transforms.ToTensorV2', 'params': {'p': 1.0}}]}\n       },\n      'metrics':[\n          {'class_name': 'torchmetrics.detection.mean_ap.MeanAveragePrecision', 'params': {'iou_thresholds': torch.linspace(0.5, 0.75, round((0.75 - 0.5) / 0.05) + 1).tolist(), 'average': 'macro'}}\n      ]}","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:48.634993Z","iopub.execute_input":"2024-07-02T10:47:48.635544Z","iopub.status.idle":"2024-07-02T10:47:48.659963Z","shell.execute_reply.started":"2024-07-02T10:47:48.635516Z","shell.execute_reply":"2024-07-02T10:47:48.659182Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dict_cfg['trainer'] = {'accelerator': 'auto', 'devices': 1, 'max_epochs': 15, 'log_every_n_steps': 50, 'precision': '16-mixed'}\n\ndict_cfg['loggers'] = [\n    {'class_name': 'lightning.pytorch.loggers.CSVLogger', \n     'params': {'save_dir': os.path.join('${training.save_dir}', '${training.save_name}'), 'name': 'logs', 'version': 'version_0'}},\n    {'class_name': 'lightning.pytorch.loggers.TensorBoardLogger',\n     'params': {'save_dir': os.path.join('${training.save_dir}', '${training.save_name}'), 'name': 'logs', 'version': 'version_0'}}\n]\n\ndict_cfg['callbacks'] = [{'class_name': 'lightning.pytorch.callbacks.ModelCheckpoint',\n                    'params': {'mode': '${training.mode}', 'monitor': '${training.metric}', 'verbose': True, 'save_last': True, 'save_top_k': 3}}]","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:48.661007Z","iopub.execute_input":"2024-07-02T10:47:48.661299Z","iopub.status.idle":"2024-07-02T10:47:48.668192Z","shell.execute_reply.started":"2024-07-02T10:47:48.661274Z","shell.execute_reply":"2024-07-02T10:47:48.667337Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cfg = OmegaConf.create(dict_cfg)\nprint(OmegaConf.to_yaml(cfg))","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:48.669798Z","iopub.execute_input":"2024-07-02T10:47:48.670064Z","iopub.status.idle":"2024-07-02T10:47:48.710332Z","shell.execute_reply.started":"2024-07-02T10:47:48.670040Z","shell.execute_reply":"2024-07-02T10:47:48.709462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Utils","metadata":{}},{"cell_type":"code","source":"BOX_COLOR = (255, 0, 0) # Red\nTEXT_COLOR = (255, 255, 255) # White\n\ndef draw_bbox(img, bbox, class_name, color=BOX_COLOR, thickness=2):\n    \"\"\"Visualizes a single bounding box on the image\"\"\"\n    x_min, y_min, x_max, y_max = bbox.astype('int')\n    cv2.rectangle(img, (x_min, y_min), (x_max, y_max), color=color, thickness=thickness)\n\n    ((text_width, text_height), _) = cv2.getTextSize(class_name, cv2.FONT_HERSHEY_SIMPLEX, 0.35, 1)\n    cv2.rectangle(img, (x_min, y_min - int(1.3 * text_height)), (x_min + text_width, y_min), BOX_COLOR, -1)\n    cv2.putText(\n        img,\n        text=class_name,\n        org=(x_min, y_min - int(0.3 * text_height)),\n        fontFace=cv2.FONT_HERSHEY_SIMPLEX,\n        fontScale=0.35,\n        color=TEXT_COLOR,\n        lineType=cv2.LINE_AA,\n    )\n    return img\n\ndef draw_bboxes(image, bboxes, labels, classes):\n    for bbox, label in zip(bboxes, labels):\n        class_name = classes[label]\n        image = draw_bbox(image, bbox, class_name)\n    return image\n\ndef load_obj(obj_path: str, default_obj_path: str = \"\") -> Any:\n    \"\"\"Extract an object from a given path.\n        Args:\n            obj_path: Path to an object to be extracted, including the object name.\n            default_obj_path: Default object path.\n        Returns:\n            Extracted object.\n        Raises:\n            AttributeError: When the object does not have the given named attribute.\n    \"\"\"\n    obj_path_list = obj_path.rsplit(\".\", 1)\n    obj_path = obj_path_list.pop(0) if len(obj_path_list) > 1 else default_obj_path\n    obj_name = obj_path_list[0]\n    module_obj = importlib.import_module(obj_path)\n    if not hasattr(module_obj, obj_name):\n        raise AttributeError(\n            f\"Object `{obj_name}` cannot be loaded from `{obj_path}`.\"\n        )\n    return getattr(module_obj, obj_name)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:57.798841Z","iopub.execute_input":"2024-07-02T10:47:57.799212Z","iopub.status.idle":"2024-07-02T10:47:57.810760Z","shell.execute_reply.started":"2024-07-02T10:47:57.799181Z","shell.execute_reply":"2024-07-02T10:47:57.809845Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def seed_worker(worker_id):\n    worker_seed = torch.initial_seed() % 2**32\n    np.random.seed(worker_seed)\n    random.seed(worker_seed)\n    \ndef plot_random_images(dataset, n_rows=2, n_cols=5, test=False):\n    fig = plt.figure(1, figsize=(n_rows*10, n_cols*10))\n    grid = ImageGrid(fig, 111, nrows_ncols=(n_rows, n_cols), axes_pad=(0.05, 0.5))\n    \n    for (plot_idx, dataset_idx) in enumerate(random.sample(range(len(dataset)), k=(n_rows*n_cols))):\n        if test:\n            image = dataset.get_raw_item(dataset_idx)      \n        else:\n            image, target = dataset.get_raw_item(dataset_idx)\n            image = draw_bboxes(image, target['boxes'], target['labels'], classes)\n        grid[plot_idx].imshow(image)\n        grid[plot_idx].set_axis_off()\n        grid[plot_idx].set_title(f\"H: {image.shape[0]}, W: {image.shape[1]}\")\n    \n    fig.tight_layout()\n    fig.show()","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:47:58.105354Z","iopub.execute_input":"2024-07-02T10:47:58.105712Z","iopub.status.idle":"2024-07-02T10:47:58.114067Z","shell.execute_reply.started":"2024-07-02T10:47:58.105683Z","shell.execute_reply":"2024-07-02T10:47:58.113165Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Dataset","metadata":{}},{"cell_type":"code","source":"class WheatDataset(Dataset):\n\n    def __init__(self, image_df, target_df, transform=None):\n        self.image_df = image_df.copy(deep=True)\n        self.target_df = target_df.copy(deep=True)\n        self.transform = transform\n\n    def __len__(self):\n        return self.image_df.shape[0]\n    \n    def get_raw_item(self, idx):\n        image_id = self.image_df.loc[idx, 'image_id']\n        image = cv2.imread(self.image_df.loc[idx, \"image_path\"])\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype('float32')\n        image /= 255.0\n        \n        mask = self.target_df.image_id == image_id\n        boxes = self.target_df.loc[mask, ['x1', 'y1', 'x2', 'y2']].values\n        labels = self.target_df.loc[mask, 'label'].values\n        area = self.target_df.loc[mask, 'area'].values\n        iscrowd = self.target_df.loc[mask, 'iscrowd'].values\n        \n        target = {\n            'boxes': boxes, \n            'labels': labels,\n            'area': area, \n            'iscrowd': iscrowd,\n            'image_id': np.array([idx])\n        }\n        return image, target\n    \n    def __getitem__(self, idx):\n        image, target = self.get_raw_item(idx)\n        if self.transform:\n            transformed = self.transform(\n                image=image,\n                bboxes=target['boxes'],\n                labels=target['labels']\n            )\n            image = transformed['image']\n            target['boxes'] = torch.as_tensor(transformed['bboxes'], dtype=torch.float32)\n            target['labels'] = torch.as_tensor(transformed['labels'], dtype=torch.int64)\n            target['area'] = torch.as_tensor(target['area'], dtype=torch.float32)\n            target['iscrowd'] = torch.as_tensor(target['iscrowd'], dtype=torch.int32)\n            target['image_id'] = torch.as_tensor(target['image_id'])\n        return image, target\n        \n","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:48:01.727064Z","iopub.execute_input":"2024-07-02T10:48:01.727725Z","iopub.status.idle":"2024-07-02T10:48:01.739872Z","shell.execute_reply.started":"2024-07-02T10:48:01.727692Z","shell.execute_reply":"2024-07-02T10:48:01.738889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Augmentation","metadata":{}},{"cell_type":"code","source":"def get_transform(augmentation_cfg):\n    augs_list = [load_obj(i.class_name)(**OmegaConf.to_container(i.params, resolve=True)) for i in augmentation_cfg.augs]\n    bbox_params = None\n    if 'bbox_params' in augmentation_cfg:\n        bbox_params = OmegaConf.to_container(augmentation_cfg.bbox_params, resolve=True)\n    transform = A.Compose(augs_list, bbox_params=bbox_params)\n    return transform","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:48:04.480374Z","iopub.execute_input":"2024-07-02T10:48:04.481002Z","iopub.status.idle":"2024-07-02T10:48:04.486667Z","shell.execute_reply.started":"2024-07-02T10:48:04.480971Z","shell.execute_reply":"2024-07-02T10:48:04.485635Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_ds = WheatDataset(full_image_df, full_target_df)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:48:05.513979Z","iopub.execute_input":"2024-07-02T10:48:05.514704Z","iopub.status.idle":"2024-07-02T10:48:05.547331Z","shell.execute_reply.started":"2024-07-02T10:48:05.514669Z","shell.execute_reply":"2024-07-02T10:48:05.546583Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_random_images(train_ds, 1, 1)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T15:17:01.140916Z","iopub.execute_input":"2024-07-02T15:17:01.141783Z","iopub.status.idle":"2024-07-02T15:17:02.405619Z","shell.execute_reply.started":"2024-07-02T15:17:01.141746Z","shell.execute_reply":"2024-07-02T15:17:02.404535Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Lightning Module","metadata":{}},{"cell_type":"code","source":"def collate_fn(batch):\n    return tuple(zip(*batch))\n\nclass LitWheatDataset(L.LightningDataModule):\n    def __init__(\n        self, augmentation_cfg, data_cfg, training_cfg,\n        fold_idx=0,\n    ):\n        super().__init__()\n        self.save_hyperparameters()\n\n    def prepare_data(self):\n        self.train_transform = get_transform(self.hparams.augmentation_cfg.train)\n        self.valid_transform = get_transform(self.hparams.augmentation_cfg.valid)\n        self.test_transform = get_transform(self.hparams.augmentation_cfg.test)\n        self.full_image_df = pd.read_csv(self.hparams.data_cfg.full_image_path)\n        self.full_target_df = pd.read_csv(self.hparams.data_cfg.full_target_path)\n        self.test_image_df = pd.read_csv(self.hparams.data_cfg.test_image_path)\n        pass\n\n    def setup(self, stage: str):\n        if stage == \"fit\":\n            train_image_df = self.full_image_df[self.full_image_df['fold'] != self.hparams.fold_idx]\n            valid_image_df = self.full_image_df[self.full_image_df['fold'] == self.hparams.fold_idx]\n    \n            train_image_df.reset_index(drop=True, inplace=True)\n            valid_image_df.reset_index(drop=True, inplace=True)\n            self.train_ds = WheatDataset(\n                train_image_df, self.full_target_df,\n                transform=self.train_transform\n            )\n            self.valid_ds = WheatDataset(\n                valid_image_df, self.full_target_df,\n                transform=self.valid_transform\n            )\n        elif stage == \"test\":\n            assert self.valid_ds, f\"No validation dataset is found\"\n            self.test_ds = self.valid_ds\n        elif stage == \"predict\":\n            self.predict_ds = TestWheatDataset(\n                self.test_image_df,\n                transform=self.test_transform\n            )\n\n    def train_dataloader(self):\n        return DataLoader(\n            self.train_ds, batch_size=self.hparams.data_cfg.batch_size,\n            shuffle=True, drop_last=False,\n            num_workers=self.hparams.data_cfg.num_workers,\n            pin_memory=True,\n            worker_init_fn=seed_worker,\n            generator=torch.Generator().manual_seed(self.hparams.training_cfg.seed),\\\n            persistent_workers=True,\n            collate_fn=collate_fn\n        )\n\n    def val_dataloader(self):\n        return DataLoader(\n            self.valid_ds, batch_size=self.hparams.data_cfg.batch_size,\n            shuffle=False, drop_last=False,\n            num_workers=self.hparams.data_cfg.num_workers,\n            pin_memory=True,\n            worker_init_fn=seed_worker,\n            generator=torch.Generator().manual_seed(self.hparams.training_cfg.seed),\\\n            persistent_workers=True,\n            collate_fn=collate_fn\n        )\n\n    def test_dataloader(self):\n        return DataLoader(\n            self.test_ds, batch_size=self.hparams.data_cfg.batch_size,\n            shuffle=False, drop_last=False,\n            num_workers=self.hparams.data_cfg.num_workers,\n            pin_memory=True,\n            worker_init_fn=seed_worker,\n            generator=torch.Generator().manual_seed(self.hparams.training_cfg.seed),\\\n            persistent_workers=True,\n            collate_fn=collate_fn\n        )\n\n    def predict_dataloader(self):\n        return DataLoader(\n            self.predict_ds, batch_size=self.hparams.data_cfg.batch_size,\n            shuffle=False, drop_last=False,\\\n            num_workers=self.hparams.data_cfg.num_workers,\n            pin_memory=True,\n            worker_init_fn=seed_worker,\n            generator=torch.Generator().manual_seed(self.hparams.training_cfg.seed),\\\n            persistent_workers=True\n        )","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:48:07.387119Z","iopub.execute_input":"2024-07-02T10:48:07.387485Z","iopub.status.idle":"2024-07-02T10:48:07.404948Z","shell.execute_reply.started":"2024-07-02T10:48:07.387457Z","shell.execute_reply":"2024-07-02T10:48:07.404015Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_detection_model(model_cfg):\n    model_class = load_obj(model_cfg.backbone.class_name)\n    model = model_class(**OmegaConf.to_container(model_cfg.backbone.params, resolve=True))\n\n    # get number of input features for the classifier\n    in_features = model.roi_heads.box_predictor.cls_score.in_features\n\n    # replace the pre-trained head with a new one\n    model.roi_heads.box_predictor = load_obj(model_cfg.head.class_name)(in_features, **OmegaConf.to_container(model_cfg.head.params, resolve=True))\n    return model\n\ndef create_example_input(data_cfg):\n    return torch.zeros((3, data_cfg.img_h, data_cfg.img_w), dtype=torch.float32).unsqueeze(0)\n","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:48:08.117467Z","iopub.execute_input":"2024-07-02T10:48:08.117800Z","iopub.status.idle":"2024-07-02T10:48:08.124335Z","shell.execute_reply.started":"2024-07-02T10:48:08.117774Z","shell.execute_reply":"2024-07-02T10:48:08.123270Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class LitWheatModel(L.LightningModule):\n    def __init__(self, model_cfg, optimizer_cfg, metrics_cfg, data_cfg, scheduler_cfg):\n        super().__init__()\n        # Exports the hyperparameters to a YAML file, and create \"self.hparams\" namespace\n        self.save_hyperparameters()\n        self.lr = optimizer_cfg.params.lr\n        # Create model\n        self.model = create_detection_model(model_cfg)\n        # Example input for visualizing the graph in Tensorboard\n        self.example_input_array = create_example_input(data_cfg)\n        self._configure_metrics()\n        \n    def _extract(self, metrics_value_dict):\n        for k in metrics_value_dict.keys():\n            if k[4:] == 'MeanAveragePrecision':\n                metrics_value_dict[k] = metrics_value_dict[k]['val_map']\n        return metrics_value_dict\n\n    def forward(self, x):\n        return self.model(x)\n\n    def training_step(self, batch, batch_idx):\n        inputs, targets = batch\n        targets = list(targets)\n        loss_dict = self.model(inputs, targets)\n        loss = sum(loss for loss in loss_dict.values())\n        \n        # By default logs it per step and epoch\n        self.log(\"train_loss\", loss, logger=True)\n        \n        return {'loss': loss}\n\n    def validation_step(self, batch, batch_idx):\n        inputs, targets = batch\n        targets = list(targets)\n        preds = self.model(inputs)\n        \n        self.valid_metrics.update(preds, targets)\n\n    def on_validation_epoch_end(self):\n        val_metrics_value_dict = self.valid_metrics.compute()\n        val_metrics_value_dict = self._extract(val_metrics_value_dict) \n        self.log_dict(val_metrics_value_dict, logger=True, prog_bar=True)\n        # remember to reset metrics at the end of the epoch\n        self.valid_metrics.reset()\n        \n    def test_step(self, batch, batch_idx):\n        inputs, targets = batch\n        targets = list(targets)\n        preds = self.model(inputs)\n        \n        self.test_metrics.update(preds, targets)\n        \n    def on_test_epoch_end(self):\n        test_metrics_value_dict = self.test_metrics.compute()\n        test_metrics_value_dict = self._extract(test_metrics_value_dict) \n        self.log_dict(test_metrics_value_dict, logger=True, prog_bar=True)\n        # remember to reset metrics at the end of the epoch\n        self.test_metrics.reset()\n    \n    def predict_step(self, batch, batch_idx):\n        images, image_ids = batch\n        preds = self.model(list(images))\n    \n        return preds\n\n    def _configure_metrics(self):\n        metric_lst = [load_obj(i.class_name)(**OmegaConf.to_container(i.params, resolve=True)) for i in self.hparams.metrics_cfg]\n        metrics = torchmetrics.MetricCollection(metric_lst)\n        self.valid_metrics = metrics.clone(prefix='val_')\n        self.test_metrics = metrics.clone(prefix='test_')\n\n    def configure_optimizers(self):\n        self.hparams.optimizer_cfg.params.lr = self.lr\n        \n        optimizer = load_obj(self.hparams.optimizer_cfg.class_name)(\n            filter(lambda p: p.requires_grad, self.model.parameters()),\n            **OmegaConf.to_container(self.hparams.optimizer_cfg.params, resolve=True)\n        )\n        scheduler = load_obj(self.hparams.scheduler_cfg.class_name)(\n            optimizer,\n            **OmegaConf.to_container(self.hparams.scheduler_cfg.params, resolve=True)\n        )\n        print(f'##############################')\n        print(OmegaConf.to_container(self.hparams.optimizer_cfg.params, resolve=True))\n        \n        \n        return [optimizer], [{\"scheduler\": scheduler,\n                              \"interval\": self.hparams.scheduler_cfg.step,\n                              'monitor': self.hparams.scheduler_cfg.monitor}]","metadata":{"execution":{"iopub.status.busy":"2024-07-02T15:22:15.748235Z","iopub.execute_input":"2024-07-02T15:22:15.749276Z","iopub.status.idle":"2024-07-02T15:22:15.769108Z","shell.execute_reply.started":"2024-07-02T15:22:15.749225Z","shell.execute_reply":"2024-07-02T15:22:15.768101Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_trainer(trainer_cfg, callbacks_cfg, loggers_cfg):\n    # Create a PyTorch Lightning trainer with the generation callback\n    logger_lst = [load_obj(i.class_name)(**OmegaConf.to_container(i.params, resolve=True)) for i in loggers_cfg]\n    callback_lst = [load_obj(i.class_name)(**OmegaConf.to_container(i.params, resolve=True)) for i in callbacks_cfg]\n    trainer = L.Trainer(\n        logger=logger_lst,\n        callbacks=callback_lst,\n        **trainer_cfg\n    )\n    return trainer","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:48:09.317990Z","iopub.execute_input":"2024-07-02T10:48:09.319061Z","iopub.status.idle":"2024-07-02T10:48:09.324802Z","shell.execute_reply.started":"2024-07-02T10:48:09.319024Z","shell.execute_reply":"2024-07-02T10:48:09.323743Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from lightning.pytorch.tuner import Tuner\ndef train_model(trainer, lit_data, lit_model_class, cfg, **kwargs):\n    # Check whether pretrained model exists. If yes, load it and skip training\n    \n    if cfg.training.pretrained:\n        pretrained_fpath = os.path.join(cfg.training.save_dir, cfg.training.pretrained_fname)\n        model = lit_model_class.load_from_checkpoint(pretrained_fpath)\n    else:\n        L.seed_everything(cfg.training.seed)  # To be reproducible\n        model = lit_model_class(cfg.model, cfg.optimizer, cfg.metrics, cfg.data, cfg.scheduler)\n        if cfg.training.first_run:\n            tuner = Tuner(trainer)\n            lr_finder = tuner.lr_find(model, datamodule=lit_data)\n            cfg.training.first_run = False\n        \n        trainer.fit(model, datamodule=lit_data, ckpt_path=\"last\")\n        model = lit_model_class.load_from_checkpoint(\n            trainer.checkpoint_callback.best_model_path\n        )  # Load best checkpoint after training\n\n    return model","metadata":{"execution":{"iopub.status.busy":"2024-07-02T10:48:10.137241Z","iopub.execute_input":"2024-07-02T10:48:10.137789Z","iopub.status.idle":"2024-07-02T10:48:10.145548Z","shell.execute_reply.started":"2024-07-02T10:48:10.137750Z","shell.execute_reply":"2024-07-02T10:48:10.144645Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cfg.trainer.max_epochs = 30","metadata":{"execution":{"iopub.status.busy":"2024-07-02T14:25:25.017921Z","iopub.execute_input":"2024-07-02T14:25:25.018310Z","iopub.status.idle":"2024-07-02T14:25:25.023452Z","shell.execute_reply.started":"2024-07-02T14:25:25.018277Z","shell.execute_reply":"2024-07-02T14:25:25.022481Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainer = create_trainer(cfg.trainer, cfg.callbacks, cfg.loggers)\nlit_wheat = LitWheatDataset(cfg.augmentation, cfg.data, cfg.training)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T15:22:20.896282Z","iopub.execute_input":"2024-07-02T15:22:20.896687Z","iopub.status.idle":"2024-07-02T15:22:20.968223Z","shell.execute_reply.started":"2024-07-02T15:22:20.896657Z","shell.execute_reply":"2024-07-02T15:22:20.967273Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = train_model(trainer, lit_wheat, LitWheatModel, cfg)","metadata":{"execution":{"iopub.status.busy":"2024-07-02T15:22:21.850716Z","iopub.execute_input":"2024-07-02T15:22:21.851098Z","iopub.status.idle":"2024-07-02T15:22:33.137814Z","shell.execute_reply.started":"2024-07-02T15:22:21.851068Z","shell.execute_reply":"2024-07-02T15:22:33.136927Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class TestWheatDataset(Dataset):\n\n    def __init__(self, image_df, transform=None):\n        self.image_df = image_df.copy(deep=True)\n        self.transform = transform\n\n    def __len__(self):\n        return self.image_df.shape[0]\n    \n    def get_raw_item(self, idx):\n        image_id = self.image_df.loc[idx, 'image_id']\n        image = cv2.imread(self.image_df.loc[idx, \"image_path\"])\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype('float32')\n        image /= 255.0\n        return image, image_id\n    \n    def __getitem__(self, idx):\n        image, image_id = self.get_raw_item(idx)\n        if self.transform:\n            transformed = self.transform(\n                image=image\n            )\n            image = transformed['image']\n        return image, image_id         ","metadata":{"execution":{"iopub.status.busy":"2024-07-02T15:19:32.615442Z","iopub.execute_input":"2024-07-02T15:19:32.616395Z","iopub.status.idle":"2024-07-02T15:19:32.625586Z","shell.execute_reply.started":"2024-07-02T15:19:32.616351Z","shell.execute_reply":"2024-07-02T15:19:32.624636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"outputs = trainer.predict(model, datamodule=lit_wheat, ckpt_path=\"last\")","metadata":{"execution":{"iopub.status.busy":"2024-07-02T15:22:57.905983Z","iopub.execute_input":"2024-07-02T15:22:57.906887Z","iopub.status.idle":"2024-07-02T15:23:00.148433Z","shell.execute_reply.started":"2024-07-02T15:22:57.906850Z","shell.execute_reply":"2024-07-02T15:23:00.147372Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"outputs[0][0]['labels']","metadata":{"execution":{"iopub.status.busy":"2024-07-02T15:23:49.600547Z","iopub.execute_input":"2024-07-02T15:23:49.601255Z","iopub.status.idle":"2024-07-02T15:23:49.607899Z","shell.execute_reply.started":"2024-07-02T15:23:49.601222Z","shell.execute_reply":"2024-07-02T15:23:49.607019Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"best_model_cp = torch.load(trainer.checkpoint_callback.best_model_path)\ntorch.save(best_model_cp['state_dict'], 'fasterrcnn_resnet50_fpn.pth')","metadata":{"execution":{"iopub.status.busy":"2024-07-02T12:52:45.542156Z","iopub.execute_input":"2024-07-02T12:52:45.542537Z","iopub.status.idle":"2024-07-02T12:52:45.970048Z","shell.execute_reply.started":"2024-07-02T12:52:45.542507Z","shell.execute_reply":"2024-07-02T12:52:45.969190Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainer.checkpoint_callback.best_model_path","metadata":{"execution":{"iopub.status.busy":"2024-07-02T15:46:02.347663Z","iopub.execute_input":"2024-07-02T15:46:02.348895Z","iopub.status.idle":"2024-07-02T15:46:02.355118Z","shell.execute_reply.started":"2024-07-02T15:46:02.348851Z","shell.execute_reply":"2024-07-02T15:46:02.354021Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Download ouput directory","metadata":{}},{"cell_type":"code","source":"os.listdir('/kaggle/working')","metadata":{"execution":{"iopub.status.busy":"2024-07-02T13:00:18.099635Z","iopub.execute_input":"2024-07-02T13:00:18.100001Z","iopub.status.idle":"2024-07-02T13:00:18.106716Z","shell.execute_reply.started":"2024-07-02T13:00:18.099970Z","shell.execute_reply":"2024-07-02T13:00:18.105810Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!zip -r file.zip /kaggle/working/wheat","metadata":{"execution":{"iopub.status.busy":"2024-07-02T12:54:42.214980Z","iopub.execute_input":"2024-07-02T12:54:42.215850Z","iopub.status.idle":"2024-07-02T12:56:27.317724Z","shell.execute_reply.started":"2024-07-02T12:54:42.215814Z","shell.execute_reply":"2024-07-02T12:56:27.316759Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"os.chdir(r'/kaggle/working')\nfrom IPython.display import FileLink ","metadata":{"execution":{"iopub.status.busy":"2024-07-02T13:00:00.443494Z","iopub.execute_input":"2024-07-02T13:00:00.444290Z","iopub.status.idle":"2024-07-02T13:00:00.448398Z","shell.execute_reply.started":"2024-07-02T13:00:00.444252Z","shell.execute_reply":"2024-07-02T13:00:00.447400Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"FileLink('fasterrcnn_resnet50_fpn.pth')","metadata":{"execution":{"iopub.status.busy":"2024-07-02T13:00:04.025553Z","iopub.execute_input":"2024-07-02T13:00:04.025904Z","iopub.status.idle":"2024-07-02T13:00:04.032115Z","shell.execute_reply.started":"2024-07-02T13:00:04.025877Z","shell.execute_reply":"2024-07-02T13:00:04.031180Z"},"trusted":true},"execution_count":null,"outputs":[]}]}