{"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 random\nimport os\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport warnings\nfrom sklearn.model_selection import train_test_split\nimport torch\nfrom torch.utils.data import DataLoader, Dataset\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor, fasterrcnn_resnet50_fpn\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nfrom tqdm import tqdm_notebook as tqdm\n\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'\nwarnings.filterwarnings('ignore')","metadata":{"execution":{"iopub.status.busy":"2022-08-25T09:49:15.438062Z","iopub.execute_input":"2022-08-25T09:49:15.438423Z","iopub.status.idle":"2022-08-25T09:49:15.447494Z","shell.execute_reply.started":"2022-08-25T09:49:15.438393Z","shell.execute_reply":"2022-08-25T09:49:15.446540Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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","metadata":{"execution":{"iopub.status.busy":"2022-08-25T09:49:15.543948Z","iopub.execute_input":"2022-08-25T09:49:15.544315Z","iopub.status.idle":"2022-08-25T09:49:15.550453Z","shell.execute_reply.started":"2022-08-25T09:49:15.544283Z","shell.execute_reply":"2022-08-25T09:49:15.549204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- The dataset seems to have a lot of duplicated image ids. This is because there are multiple boxes for the same image.\n- Therefore I have decided to take only the unique images for the train_test_split and I combined all the boxes of a unique image together in a dictionary.","metadata":{}},{"cell_type":"code","source":"def get_data(data_path):\n    df = pd.read_csv(data_path)\n    bboxs = np.stack(df['bbox'].apply(lambda x: np.fromstring(x[1:-1], sep=',')))\n    df['xmin'],df['ymin'],df['w'],df['h'] = bboxs[:,0], bboxs[:,1], bboxs[:,2], bboxs[:,3]    \n    df['xmax'] = df['xmin'] + df['w']\n    df['ymax'] = df['ymin'] + df['h']\n    df['area'] = df['w'] * df['h']\n    df.drop(['source', 'width', 'height', 'w', 'h','bbox'], axis = 1, inplace = True)\n    bb = []\n    for i in df.values:\n        bb.append((i[1:-1]))\n    df['bbox'] = bb\n    unique_images = df['image_id'].unique()\n    img_bbox = {}\n    img_area = {}\n    for img in tqdm(unique_images):\n        bbs = []\n        ar = []\n        for row in df.values:\n            if row[0] == img:\n                bbs.append(list(row[-1]))\n                ar.append(row[-2])\n        img_area[img] = ar\n        img_bbox[img] = bbs\n    df['image_id'] = df['image_id'].drop_duplicates()\n    df.dropna(inplace=True)\n    df.reset_index(drop=True, inplace = True)\n    train_df, val_df = train_test_split(df, test_size=20)\n    train_df.reset_index(drop=True,inplace=True)\n    val_df.reset_index(drop=True,inplace=True)\n    return train_df, val_df, img_bbox, img_area","metadata":{"execution":{"iopub.status.busy":"2022-08-25T09:49:15.737853Z","iopub.execute_input":"2022-08-25T09:49:15.738712Z","iopub.status.idle":"2022-08-25T09:49:15.749846Z","shell.execute_reply.started":"2022-08-25T09:49:15.738677Z","shell.execute_reply":"2022-08-25T09:49:15.748945Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class WheatDataset(Dataset):\n    def __init__(self, df, img_dir, transforms=False):\n        self.df = df\n        self.img_dir = img_dir\n        self.transforms = transforms\n        \n    def __len__(self):\n        return self.df.shape[0]\n    \n    def __getitem__(self, idx):\n        img_name = self.df['image_id'][idx]\n        img_path = self.img_dir + '/' + img_name + '.jpg'\n        img_vector = cv2.imread(img_path)\n        img_vector = cv2.cvtColor(img_vector, cv2.COLOR_BGR2RGB)\n        if self.transforms:\n            trans_output = self.transforms(image=img_vector)\n        img_vector = trans_output['image']\n        bbox = torch.tensor(bbox_dict[img_name], dtype=torch.float)\n        area = torch.tensor(area_dict[img_name], dtype=torch.float)\n        labels=torch.ones((bbox.shape[0]),dtype=torch.int64)\n        iscrowd=torch.zeros((bbox.shape[0],), dtype=torch.int64)      \n        \n        target = {}\n        target['boxes'] = bbox\n        target['labels'] = labels\n        target['image_id'] = torch.tensor([idx])\n        target['area'] = area\n        target['iscrowd'] = iscrowd\n        return img_vector.float() / 255, target\n    \ndef collate_fn(batch):\n    return tuple(zip(*batch))","metadata":{"execution":{"iopub.status.busy":"2022-08-25T09:49:15.752807Z","iopub.execute_input":"2022-08-25T09:49:15.753678Z","iopub.status.idle":"2022-08-25T09:49:15.764081Z","shell.execute_reply.started":"2022-08-25T09:49:15.753640Z","shell.execute_reply":"2022-08-25T09:49:15.763076Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class LossLogger:\n    def __init__(self):\n        self.classifier_loss = 0\n        self.box_loss = 0\n        self.objectiveness_loss = 0\n        self.rpn_box_loss = 0\n        self.total_loss = 0\n        self.count = 0\n        \n    def log(self, loss_dict):\n        self.classifier_loss += loss_dict['loss_classifier'].detach().tolist()\n        self.box_loss += loss_dict['loss_box_reg'].detach().tolist()\n        self.objectiveness_loss += loss_dict['loss_objectness'].detach().tolist()\n        self.rpn_box_loss += loss_dict['loss_rpn_box_reg'].detach().tolist()\n        self.total_loss += sum(loss for loss in loss_dict.values()).detach().tolist()\n        self.count += 1\n        \n    def reset(self):\n        self.classifier_loss = 0\n        self.box_loss = 0\n        self.objectiveness_loss = 0\n        self.rpn_box_loss = 0\n        self.total_loss = 0\n        self.count = 0\n    \n    def get(self):\n        self.classifier_loss = self.classifier_loss / self.count\n        self.box_loss = self.box_loss / self.count\n        self.objectiveness_loss = self.objectiveness_loss / self.count\n        self.rpn_box_loss = self.rpn_box_loss / self.count\n        self.total_loss = self.total_loss / self.count\n        losses = {\n            'Classifier Loss' : self.classifier_loss,\n            'Box Loss' : self.box_loss,\n            'Objectiveness Loss' : self.objectiveness_loss,\n            'RPN Box Loss' : self.rpn_box_loss,\n            'Total Loss' : self.total_loss\n        }\n        return losses","metadata":{"execution":{"iopub.status.busy":"2022-08-25T09:49:16.196258Z","iopub.execute_input":"2022-08-25T09:49:16.196864Z","iopub.status.idle":"2022-08-25T09:49:16.208461Z","shell.execute_reply.started":"2022-08-25T09:49:16.196825Z","shell.execute_reply":"2022-08-25T09:49:16.207385Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train(model, loader, logger):\n    model.train()\n    logger.reset()\n    for images,targets  in tqdm(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        loss_dict = model(images, targets)\n        logger.log(loss_dict)\n        losses = sum(loss for loss in loss_dict.values())\n        optimizer.zero_grad()\n        losses.backward()\n        optimizer.step()\n        scheduler.step()\n    return model\n","metadata":{"execution":{"iopub.status.busy":"2022-08-25T09:49:16.210501Z","iopub.execute_input":"2022-08-25T09:49:16.210918Z","iopub.status.idle":"2022-08-25T09:49:16.223145Z","shell.execute_reply.started":"2022-08-25T09:49:16.210880Z","shell.execute_reply":"2022-08-25T09:49:16.221872Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def validate(model, loader, logger):\n    logger.reset()\n    for images,targets  in tqdm(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        with torch.no_grad():\n            loss_dict = model(images, targets)\n        logger.log(loss_dict)","metadata":{"execution":{"iopub.status.busy":"2022-08-25T09:49:16.224969Z","iopub.execute_input":"2022-08-25T09:49:16.225558Z","iopub.status.idle":"2022-08-25T09:49:16.233399Z","shell.execute_reply.started":"2022-08-25T09:49:16.225523Z","shell.execute_reply":"2022-08-25T09:49:16.232517Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_report(epoch, logger, train = True):\n    loop = 'Train' if train else 'Validation'\n    print(f'Epoch {epoch} :')\n    print(f'{loop} Losses :')\n    loss = logger.get()\n    for k,v in loss.items():\n        print(f'{k} : {v}')\n    print('\\n\\n')","metadata":{"execution":{"iopub.status.busy":"2022-08-25T09:49:16.237004Z","iopub.execute_input":"2022-08-25T09:49:16.239153Z","iopub.status.idle":"2022-08-25T09:49:16.246952Z","shell.execute_reply.started":"2022-08-25T09:49:16.239124Z","shell.execute_reply":"2022-08-25T09:49:16.245937Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_path = '../input/global-wheat-detection/train.csv'\nIMG_DIR = '../input/global-wheat-detection/train'\n\nconfig = {\n    'learning_rate' : 1e-3,\n    'n_epochs' : 12,\n    'batch_size' : 16,\n    'num_classes' : 2,\n    'seed' : 42\n}","metadata":{"execution":{"iopub.status.busy":"2022-08-25T09:49:16.248470Z","iopub.execute_input":"2022-08-25T09:49:16.249101Z","iopub.status.idle":"2022-08-25T09:49:16.256972Z","shell.execute_reply.started":"2022-08-25T09:49:16.249066Z","shell.execute_reply":"2022-08-25T09:49:16.255912Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"seed_everything(config['seed'])\n\ntrain_df, val_df, bbox_dict, area_dict = get_data(train_path)\ntrain_aug = A.Compose([ToTensorV2()])\n\ntrainset = WheatDataset(train_df, IMG_DIR, transforms=train_aug)\nvalset = WheatDataset(val_df, IMG_DIR, transforms=train_aug)\n\ntrain_load = DataLoader(trainset, batch_size = config['batch_size'], collate_fn = collate_fn, num_workers = 2, pin_memory = True)\nval_load = DataLoader(valset, batch_size = config['batch_size'], collate_fn = collate_fn, num_workers = 2, pin_memory = True)","metadata":{"execution":{"iopub.status.busy":"2022-08-25T09:49:16.259356Z","iopub.execute_input":"2022-08-25T09:49:16.261739Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = fasterrcnn_resnet50_fpn(pretrained=True)\nin_features = model.roi_heads.box_predictor.cls_score.in_features\nmodel.roi_heads.box_predictor = FastRCNNPredictor(in_features, config['num_classes'])\nmodel = model.to(device)\n\noptimizer = torch.optim.AdamW(model.parameters(), lr = config['learning_rate'])\nscheduler = torch.optim.lr_scheduler.CyclicLR(optimizer, base_lr = 0.4 * config['learning_rate'], \n                                              max_lr = config['learning_rate'], \n                                              step_size_up = 315,\n                                              step_size_down = 315, \n                                              cycle_momentum = False)\nlogger = LossLogger()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i in range(1, config['n_epochs'] + 1):\n    model = train(model, train_load, logger)\n    get_report(i, logger, train = True)\n    torch.cuda.empty_cache()\n    validate(model, val_load, logger)\n    get_report(i, logger, train = False)\n    torch.cuda.empty_cache()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}