{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"#!pip freeze","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 第一次参加比赛落下帷幕，使用的是FasterRCNN50，图像增强使用的是某位大佬提供的notebook，不算伪代码的话训练了50epoch左右，但是从最后的结果来看，哪怕再增加训练的轮数也已经不能够得到更好的LB了。\n### 观看其他大佬的notebook和discussion，需要学习的有几点：\n1. 额外数据的寻找和导入\n2. 混合模型的使用（虽然并不是很懂这一个概念）或者说应该尝试更多的方法，当然这一次是因为加入之后剩余的时间本来就不多了\n3. EDA的执行（这次并没有很好的观察数据与图像本身）\n4. 整体框架copy，整合了其他的图像增强的方式，但是其中部分方法并没有很好的理解，需要后续仔细。另外应当深入到图像增强的源代码当中  \n下面先将notebook中不懂的部分标出，留待后续解决\n不过好歹也算往入门更近了一步，另外，非常感谢[Jacob](https://www.kaggle.com/creatrol)大佬 [gzYou](https://www.kaggle.com/guizengyou)大佬不厌其烦的回复和解疑，虽然问的问题有一点点(to be honest a lot)愚蠢。\n非常喜欢这一个氛围，希望能够完成更多的比赛学习到更多新的东西。","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"After Wheat competition, I'd like to record something incluing the problems and the inspirations.  \nmodel：FasterRCNN50(after COCO pretrain)  \nepoch:50\nexternal way:Mosaic and Pseudo label\nThe question is in the main body and the inspiration is below:  \n1. Search and import of additional data\n2. The use of the hybrid model (although not very familiar with this concept) or should try more methods, of course, this time because there is not much time left after joining\n3. EDA implementation (there is no good observation data and image itself this time)\n4. The whole framework copy integrates other image enhancement methods, but some of them are not well understood and need to be followed up carefully. In addition, we should go deep into the source code of image enhancement\n\nNow I'll mark the part that I don't understand in the notebook, and I'll leave it for later solution. But somehow, it's a step closer to the beginning. Thank you very much for your untiring reply and answering questions, [Jacob](https://www.kaggle.com/creatrol) and [gzYou](https://www.kaggle.com/guizengyou)，although the question I asked is a little bit stupid.   \nI like Kaggle’s atmosphere very much. I hope I can finish more competitions and learn more new things.\n","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import glob\nfrom tqdm import notebook","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport cv2\nimport os\nimport re\n\nfrom PIL import Image\n\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\n\nimport torch\nimport torchvision\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\n\nfrom matplotlib import pyplot as plt\n\nDIR_INPUT = '../input/global-wheat-detection'\nDIR_TRAIN = f'{DIR_INPUT}/train'\nDIR_TEST = f'{DIR_INPUT}/test'\n\nDIR_WEIGHTS = '../input/lasttry/fasterrcnn_resnet50_fpn12nd.pth'\n\nWEIGHTS_FILE = f'{DIR_WEIGHTS}' ","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"test_df = pd.read_csv(f'{DIR_INPUT}/sample_submission.csv')\n#test_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class WheatTestDataset(Dataset):\n\n    def __init__(self, dataframe, image_dir, transforms=None):\n        super().__init__()\n\n        #self.image_ids = dataframe['image_id'].unique()\n        self.image_ids=np.array([path.split('/')[-1][:-4]for path in glob.glob(f'{image_dir}/*.jpg')])\n        self.df = dataframe\n        self.image_dir = image_dir\n        self.transforms = transforms\n\n    def __getitem__(self, index: int):\n\n        image_id = self.image_ids[index]\n        #records = self.df[self.df['image_id'] == image_id]\n\n        image = cv2.imread(f'{self.image_dir}/{image_id}.jpg', cv2.IMREAD_COLOR)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        image /= 255.0\n\n        if self.transforms:\n            sample = {\n                'image': image,\n            }\n            sample = self.transforms(**sample)\n            image = sample['image']\n\n        return image, image_id\n\n    def __len__(self) -> int:\n        return self.image_ids.shape[0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"1.从目录中读取图片名称而不是从csv中读取，这样就能够对接在submit时的隐藏test，避免出现提交错误  \n2.cv库读取的图片格式在大部分情况下都需要使用RGB转换，相对于其他图片读取库而言，cv的速度更快速，这使得它在深度学习大规模图片读取中应用非常广泛，当然，存在需要转换RGB格式这一个无关紧要的缺陷  \n3.对于我们这样的初学者而言，需要注意的反而是图像导入转化过程中格式的变化，如pandas、numpy、tensor几个数据格式之间的转换，当然还有tuple，根据我浅薄的见识，tuple因为不能更改更加安全","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"1. Read the image name from the directory instead of from the CSV. In this way, you can dock the hidden test in submit and avoid submission errors\n2. In most cases, the image format read by CV library needs to use RGB conversion. Compared with other image reading libraries, the speed of CV is faster, which makes it widely used in large-scale image reading of deep learning. Of course, there is an insignificant defect of converting RGB format\n3. For beginners like us, what we need to pay attention to is the change of format in the process of image import and transformation, such as the conversion between pandas, numpy, tensor, and of course, tuple. According to my shallow knowledge, tuple is safer because it can't be changed","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# Albumentations\ndef get_train_transform():\n    return A.Compose([\n        A.Flip(0.5),\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']})\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"~~目前并没有很好的使用的数据增强方法，使用的手段是非常简易的翻转~~\n后来还是使用了数据增强，但是也是copy的，虽然也花了我不少时间去调试格式的问题，其实想做的是将所有方式全部使用AL库的compose进行整合，毕竟其提供了加入自定义函数的方法，不过目前有点力所不逮，\n但是我看一个notebook讲masico的方式循环十轮左右就满足了，我似乎训练的有点多了。\n下面是从AL文档中copy过来的参数解释：\ntransforms (list) – list of transformations to compose.  \nbbox_params (BboxParams) – Parameters for bounding boxes transforms","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"~~At present, there is no good data enhancement method, and the method used is very simple flip~~  \nLater, data enhancement was used, but it was also copy. Although it took me a lot of time to debug the format problem, what I wanted to do was to integrate all the methods using the compose of Al library.   \nAfter all, it provided a way to add custom functions, but at present, it was a little difficult,But I see a notebook to talk about masico, and it's enough to cycle about ten rounds.   \nI seem to have trained a little bit more.\nThe following is an explanation of the parameters copied from the Al document:  \ntransforms (list) – list of transformations to compose.  \nbbox_ params (BboxParams) – Parameters for bounding boxes transforms","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# Albumentations\ndef get_test_transform():\n    return A.Compose([\n        # A.Resize(512, 512),\n        ToTensorV2(p=1.0)\n    ])\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# load a model; pre-trained on COCO\nmodel = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=False, pretrained_backbone=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"raw","source":"导入经过COCO预训练的残差网络，在此基础上进行训练，应该算是迁移训练，不过我目前并没有很好的了解过迁移训练的原理","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"The introduction of residual network pre trained by coco and training on this basis should be regarded as migration training.   \nHowever, I have no good understanding of the principle of migration training","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\n\nnum_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)\n\n# Load the trained weights\nmodel.load_state_dict(torch.load(WEIGHTS_FILE))\nmodel.eval()\n\nx = model.to(device)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"虽然这一步就是简单的模型导入，但是不得不承认，对于in_feature的导入这一句我还是有点疑问的，后面还得抽空去看Pytorch的文档。","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Although this step is a simple model import, I have to admit that for in_ I still have some doubts about the import of feature.  \nI have to take time to read the document of Pytorch.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def collate_fn(batch):\n    return tuple(zip(*batch))\n\ntest_dataset = WheatTestDataset(test_df, DIR_TEST, get_test_transform())\n\ntest_data_loader = DataLoader(\n    test_dataset,\n    batch_size=8,\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":"其实def collate_fn(batch):这一个语句我还是有点没有理解，虽然我去查看了DataLoader的参数","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Actually, def collate_ FN (batch): I still don't understand this statement, although I went to check the parameters of dataloader","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def format_prediction_string(boxes, scores):\n    pred_strings = []\n    for j in zip(scores, boxes):\n        pred_strings.append(\"{0:.4f} {1} {2} {3} {4}\".format(j[0], j[1][0], j[1][1], j[1][2], j[1][3]))\n\n    return \" \".join(pred_strings)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# # **Detection and make Pseudo labels for test dataset**","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"detection_threshold = 0.5\nresults = []\n\ntestdf_psuedo = []\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        for box in boxes:\n            #print(box)\n            result = {\n                'image_id': 'nvnn'+image_id,\n                'width': 1024,\n                'height': 1024,\n                'source': 'nvnn',\n                'x': box[0],\n                'y': box[1],\n                'w': box[2],\n                'h': box[3]\n            }\n            testdf_psuedo.append(result)\n            \n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"其实我这里很想尝试一下，不同的detection_threshold会带来的变化，因此先码在这里。\n另一个就是开始的时候我还有疑问就是为什么这里的数据还要导入到cpu，后来才知道要运算的话只能转换，这大概就是基础不扎实吧。","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"In fact, I'd like to try different detection methods here_ Threshold will bring changes, so code here first.  \nThe other is that at the beginning, I still had a question about why the data here has to be imported into the CPU.   \nLater, I learned that if you want to calculate, you can only convert it. This is probably because my foundation is not solid.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df_pseudo = pd.DataFrame(testdf_psuedo, columns=['image_id', 'width', 'height', 'source', 'x', 'y', 'w', 'h'])\ntest_df_pseudo.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# # **Retrain model with pseudo labels","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = pd.read_csv(f'{DIR_INPUT}/train.csv')\ntrain_df['x'] = -1\ntrain_df['y'] = -1\ntrain_df['w'] = -1\ntrain_df['h'] = -1\n\ndef expand_bbox(x):\n    r = np.array(re.findall(\"([0-9]+[.]?[0-9]*)\", x))\n    if len(r) == 0:\n        r = [-1, -1, -1, -1]\n    return r\n\ntrain_df[['x', 'y', 'w', 'h']] = np.stack(train_df['bbox'].apply(lambda x: expand_bbox(x)))\ntrain_df.drop(columns=['bbox'], inplace=True)\ntrain_df['x'] = train_df['x'].astype(np.float)\ntrain_df['y'] = train_df['y'].astype(np.float)\ntrain_df['w'] = train_df['w'].astype(np.float)\ntrain_df['h'] = train_df['h'].astype(np.float)\n\ntrain_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"正则的语句和方法真是看了忘看了忘，只好copy了","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"The regular statements and methods are really forgotten, so I have to copy them","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"image_ids = train_df['image_id'].unique()\nvalid_ids = image_ids[-665:]\ntrain_ids = image_ids #[:-665]\n\nvalid_df = train_df[train_df['image_id'].isin(valid_ids)]\n#train_df = train_df[train_df['image_id'].isin(train_ids)]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"frames = [train_df, test_df_pseudo]\n\ntrain_df = pd.concat(frames)\ntrain_df.tail()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# # **Wheat dataset for Training**","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class WheatDataset(Dataset):\n\n    def __init__(self, dataframe, image_dir, transforms=None):\n        super().__init__()\n\n        self.image_ids = dataframe['image_id'].unique()\n        self.df = dataframe\n        self.image_dir = image_dir\n        self.transforms = transforms\n\n    def __getitem__(self, index: int):\n\n        image_id = self.image_ids[index]\n        records = self.df[self.df['image_id'] == image_id]\n        if 'nvnn' in image_id:\n            image_id = image_id[4:]\n            image = cv2.imread(f'{DIR_TEST}/{image_id}.jpg', cv2.IMREAD_COLOR)\n        else:\n            image = cv2.imread(f'{self.image_dir}/{image_id}.jpg', cv2.IMREAD_COLOR)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        image /= 255.0\n\n        boxes = records[['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((records.shape[0],), dtype=torch.int64)\n        \n        # suppose all instances are not crowd\n        iscrowd = torch.zeros((records.shape[0],), dtype=torch.int64)\n        \n        target = {}\n        target['boxes'] = boxes\n        target['labels'] = labels\n        # target['masks'] = None\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.stack(tuple(map(torch.tensor, zip(*sample['bboxes'])))).permute(1, 0)\n\n        return image, target, image_id\n\n    def __len__(self) -> int:\n        return self.image_ids.shape[0]\n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"说实话，这个数据导入函数我已经翻来覆去看来很多遍了，但是总觉得还差点意思，比如target['boxes'] = torch.stack(tuple(map(torch.tensor, zip(*sample['bboxes'])))).permute(1, 0)这一句，，这个几重转换都要把我搞晕了，，不知道有没有大佬可以解答一下\n还有就是这个面积计算我总感觉有那么一点怪","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"To tell you the truth, I've looked over and over for this data import function many times, but I always think it's almost meaningless,  \nsuch as target ['boxes']= torch.stack (tuple(map( torch.tensor , zip (* sample ['bboxes']))). Permute (1, 0)   \nThis several conversions will make me confused. I wonder if there is a big guy who can solve it.  \nThere is also the area calculation, I always feel a little strange,is it necessary？","execution_count":null},{"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\n        \n        \ndef collate_fn(batch):\n    return tuple(zip(*batch))\n\ntrain_dataset = WheatDataset(train_df, DIR_TRAIN, get_train_transform())\nvalid_dataset = WheatDataset(valid_df, 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=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)\n\ndevice = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# **Show a sample**","execution_count":null},{"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]\n\nboxes = targets[2]['boxes'].cpu().numpy().astype(np.int32)\nsample = images[2].permute(1,2,0).cpu().numpy()\n\nfig, ax = plt.subplots(1, 1, figsize=(16, 8))\n\nfor box in boxes:\n    cv2.rectangle(sample,\n                  (box[0], box[1]),\n                  (box[2], box[3]),\n                  (220, 0, 0), 3)\n    \nax.set_axis_off()\nax.imshow(sample)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"这里有一个小小的疑问就是在数据导入时已经做了均一化，即/255了，这里并没有做任何其他操作便进行display了，是不是cv本身自带的对应的补全方法","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"There is a small question here, that is, / 255 has been homogenized when data is imported. Display is performed without any other operation.  \nIs it the corresponding completion method of CV itself？","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# # ****retrain Faster RCNN****","execution_count":null},{"metadata":{"scrolled":true,"trusted":true},"cell_type":"code","source":"model.train()\nmodel.to(device)\nparams = [p for p in model.parameters() if p.requires_grad]\noptimizer = torch.optim.SGD(params, lr=0.01, momentum=0.9, weight_decay=0.0001)\nlr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=1, gamma=0.5)\n#lr_scheduler = None\n\nnum_epochs =30\n\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        images = list(image.to(device) for image in images)\n        targets = [{k: v.long().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}\")\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"加tqdm包，，这样的话训练的过程更赏心悦目","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"In fact，When I run the code in my own computer,I add tqdm which I think let the running time more interesting.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"torch.save(model.state_dict(), 'fasterrcnn_resnet50_fpn3nd.pth')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"对于参数保存的版本下次一定得做好记录，，我几次训练之后，，就乱套了。","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"I need to pay attention to the records of parameters version which confused me this time.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# **final Inference and submission","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"model.eval()\ndetection_threshold=0.2\nresults=[]\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).clip(min=0,max=1023)\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        \n            \n        result = {\n            'image_id': image_id,\n            'PredictionString': format_prediction_string(boxes, scores)\n        }\n\n        \n        results.append(result)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df = pd.DataFrame(results, columns=['image_id', 'PredictionString'])\ntest_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample = images[1].permute(1,2,0).cpu().numpy()\nboxes = outputs[1]['boxes'].data.cpu().numpy()\nscores = outputs[1]['scores'].data.cpu().numpy()\n\nboxes = boxes[scores >= detection_threshold].astype(np.int32)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1, 1, figsize=(16, 8))\n\nfor box in boxes:\n    cv2.rectangle(sample,\n                  (box[0], box[1]),\n                  (box[2], box[3]),\n                  (220, 0, 0), 2)\n    \nax.set_axis_off()\nax.imshow(sample)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"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}