{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","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":30528,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"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","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:09:14.816829Z","iopub.execute_input":"2025-01-01T12:09:14.817169Z","iopub.status.idle":"2025-01-01T12:09:21.874876Z","shell.execute_reply.started":"2025-01-01T12:09:14.817144Z","shell.execute_reply":"2025-01-01T12:09:21.874061Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# ***Install and import necessary libraries***","metadata":{}},{"cell_type":"code","source":"import cv2\nimport os\nimport copy\nimport re\nimport torch\nimport torchvision\nfrom torchvision import transforms \nfrom torch.utils.data import Dataset,DataLoader\nfrom torch.utils.data import Subset\nimport torch.optim as optim\nfrom torchvision.models.detection import fasterrcnn_resnet50_fpn\nfrom torchvision.models.detection import FasterRCNN_ResNet50_FPN_Weights\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nfrom sklearn.model_selection import ShuffleSplit\nfrom matplotlib import pyplot as plt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:09:38.737352Z","iopub.execute_input":"2025-01-01T12:09:38.737977Z","iopub.status.idle":"2025-01-01T12:09:42.942566Z","shell.execute_reply.started":"2025-01-01T12:09:38.737945Z","shell.execute_reply":"2025-01-01T12:09:42.941824Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"np.random.seed(42)\ntorch.manual_seed(42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:09:46.735172Z","iopub.execute_input":"2025-01-01T12:09:46.735706Z","iopub.status.idle":"2025-01-01T12:09:46.746022Z","shell.execute_reply.started":"2025-01-01T12:09:46.735663Z","shell.execute_reply":"2025-01-01T12:09:46.745092Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"***Explore the csv file***","metadata":{}},{"cell_type":"code","source":"data=pd.read_csv(\"/kaggle/input/global-wheat-detection/train.csv\")\ndata.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:09:51.527774Z","iopub.execute_input":"2025-01-01T12:09:51.528581Z","iopub.status.idle":"2025-01-01T12:09:51.795201Z","shell.execute_reply.started":"2025-01-01T12:09:51.528551Z","shell.execute_reply":"2025-01-01T12:09:51.794299Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"***Break down the bbox into x0,y0,w,h***","metadata":{}},{"cell_type":"code","source":"def bbox_(string):\n    ls = string.split()\n    bbox = []\n    for i in range(4):\n        bbox.append(np.float32(ls[i].strip(\"[],\")))\n        \n    return bbox","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:09:55.5163Z","iopub.execute_input":"2025-01-01T12:09:55.517069Z","iopub.status.idle":"2025-01-01T12:09:55.521806Z","shell.execute_reply.started":"2025-01-01T12:09:55.517038Z","shell.execute_reply":"2025-01-01T12:09:55.520755Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data['bbox'] = data['bbox'].apply(bbox_)\n\ndata[\"x0\"] = data[\"bbox\"].apply(lambda x:x[0])\ndata[\"y0\"] = data[\"bbox\"].apply(lambda x:x[1])\ndata[\"w\"] = data[\"bbox\"].apply(lambda x:x[2])\ndata[\"h\"] = data[\"bbox\"].apply(lambda x:x[3])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:09:58.928713Z","iopub.execute_input":"2025-01-01T12:09:58.929321Z","iopub.status.idle":"2025-01-01T12:09:59.859251Z","shell.execute_reply.started":"2025-01-01T12:09:58.929293Z","shell.execute_reply":"2025-01-01T12:09:59.858561Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data.drop(columns=[\"bbox\",\"source\"],axis=1,inplace=True)\ndata.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:10:03.246078Z","iopub.execute_input":"2025-01-01T12:10:03.246386Z","iopub.status.idle":"2025-01-01T12:10:03.263633Z","shell.execute_reply.started":"2025-01-01T12:10:03.246361Z","shell.execute_reply":"2025-01-01T12:10:03.262731Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"data[\"image_id\"].nunique() # Check number of unique images ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:10:07.259127Z","iopub.execute_input":"2025-01-01T12:10:07.259893Z","iopub.status.idle":"2025-01-01T12:10:07.276078Z","shell.execute_reply.started":"2025-01-01T12:10:07.259853Z","shell.execute_reply":"2025-01-01T12:10:07.275297Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dataframe=data\nimage_dir = \"/kaggle/input/global-wheat-detection/train\"\ntest_dir = \"/kaggle/input/global-wheat-detection/test\"\nmodel_weights_file = \"model.pth\"\n\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n#Put Hyperparameters \nbatch_size = 4\nlearning_rate = 3e-5\nepochs = 100\n\nthreshold=0.5\niou_threshold=0.8","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:10:10.888184Z","iopub.execute_input":"2025-01-01T12:10:10.888852Z","iopub.status.idle":"2025-01-01T12:10:10.916003Z","shell.execute_reply.started":"2025-01-01T12:10:10.888823Z","shell.execute_reply":"2025-01-01T12:10:10.914926Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# ***Customizing our dataset***","metadata":{}},{"cell_type":"code","source":"\"\"\"The input to the model is expected to be a list of tensors, each of shape [c, h, w], \n   one for each image, and should be in 0-1 range. Different images can have different \n   sizes.\"\"\"\n   \n   \nclass WheatDataset(Dataset):\n    def __init__(self,dataframe,image_dir):\n        super().__init__()\n        self.dataframe=dataframe\n        self.img_list=sorted(self.dataframe[\"image_id\"].unique())\n        self.img_dir=image_dir\n        \n    def __len__(self):\n        return len(self.img_list)\n    \n    def __getitem__(self,idx):\n        img_name=self.img_list[idx]+\".jpg\"\n        img_path=os.path.join(self.img_dir,img_name)\n        img=cv2.imread(img_path)\n        img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n        img=transforms.functional.to_tensor(img)\n        \n        inter=self.dataframe[self.dataframe[\"image_id\"]==self.img_list[idx]]\n        boxes=inter[[\"x0\",\"y0\",\"w\",\"h\"]].values\n        area=boxes[:,2]*boxes[:,3]\n        \n        # converting bounding box from x0y0wh format to x0y0x1y1 format\n        boxes[:,2]=boxes[:,0]+boxes[:,2]\n        boxes[:,3]=boxes[:,1]+boxes[:,3]\n        \n        labels=torch.ones((boxes.shape[0]),dtype=torch.int64)\n        iscrowd=torch.zeros((boxes.shape[0]),dtype=torch.uint8)\n        \n        target={}\n        target[\"boxes\"]=torch.as_tensor(boxes,dtype=torch.float32)\n        target[\"area\"]=torch.as_tensor(area,dtype=torch.float32)\n        target[\"labels\"]=labels\n        target[\"iscrowd\"]=iscrowd\n        target[\"id\"]=torch.tensor(idx)\n        \n        return img,target","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:10:14.993348Z","iopub.execute_input":"2025-01-01T12:10:14.994016Z","iopub.status.idle":"2025-01-01T12:10:15.00244Z","shell.execute_reply.started":"2025-01-01T12:10:14.993987Z","shell.execute_reply":"2025-01-01T12:10:15.001508Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_ds=WheatDataset(dataframe,image_dir)\nval_ds=WheatDataset(dataframe,image_dir)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:10:19.530281Z","iopub.execute_input":"2025-01-01T12:10:19.530848Z","iopub.status.idle":"2025-01-01T12:10:19.550166Z","shell.execute_reply.started":"2025-01-01T12:10:19.530818Z","shell.execute_reply":"2025-01-01T12:10:19.549435Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Shuffle and Split data to 20% test and 80% Train\nss=ShuffleSplit(n_splits=1,test_size=0.2,random_state=1)\n\nindexs=range(len(train_ds))\nfor train_idx,val_idx in ss.split(indexs):\n    print(f\"Train dataset length: {len(train_idx)}\")\n    print(f\"Validation dataset length: {len(val_idx)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:10:22.804973Z","iopub.execute_input":"2025-01-01T12:10:22.805582Z","iopub.status.idle":"2025-01-01T12:10:22.81106Z","shell.execute_reply.started":"2025-01-01T12:10:22.805554Z","shell.execute_reply":"2025-01-01T12:10:22.810228Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_ds=Subset(train_ds,train_idx)\nval_ds=Subset(val_ds,val_idx)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:10:26.586041Z","iopub.execute_input":"2025-01-01T12:10:26.58682Z","iopub.status.idle":"2025-01-01T12:10:26.590561Z","shell.execute_reply.started":"2025-01-01T12:10:26.586791Z","shell.execute_reply":"2025-01-01T12:10:26.589631Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# ***Visualize some images with bounded boxes***","metadata":{}},{"cell_type":"code","source":"def show(img,boxes):\n    boxes=boxes.detach().numpy().astype(np.int32)\n    sample=img.permute(1,2,0).numpy().copy()\n    \n    for box in boxes:\n        cv2.rectangle(sample,(box[0], box[1]),(box[2], box[3]),(220, 0, 0), 3)\n    \n    plt.axis(\"off\");\n    plt.imshow(sample);","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:10:30.788316Z","iopub.execute_input":"2025-01-01T12:10:30.788969Z","iopub.status.idle":"2025-01-01T12:10:30.793848Z","shell.execute_reply.started":"2025-01-01T12:10:30.788931Z","shell.execute_reply":"2025-01-01T12:10:30.792845Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(8,8))\n\nimg,target=next(iter(train_ds))\nshow(img,target[\"boxes\"])\nplt.savefig(\"1.png\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:10:37.107231Z","iopub.execute_input":"2025-01-01T12:10:37.108036Z","iopub.status.idle":"2025-01-01T12:10:38.022129Z","shell.execute_reply.started":"2025-01-01T12:10:37.108007Z","shell.execute_reply":"2025-01-01T12:10:38.02122Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(8,8))\n\nimg,target=next(iter(val_ds))\nshow(img,target[\"boxes\"])\nplt.savefig(\"2.png\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:10:46.040144Z","iopub.execute_input":"2025-01-01T12:10:46.0408Z","iopub.status.idle":"2025-01-01T12:10:46.789694Z","shell.execute_reply.started":"2025-01-01T12:10:46.040774Z","shell.execute_reply":"2025-01-01T12:10:46.788924Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### ***PyTorch Data Loader***","metadata":{}},{"cell_type":"code","source":"def collate_fn(batch):\n    return tuple(zip(*batch))\n\ntrain_dl=DataLoader(train_ds,batch_size=batch_size,shuffle=True,num_workers=2,\n                    pin_memory=True if torch.cuda.is_available else False,\n                    collate_fn=collate_fn)\nval_dl=DataLoader(val_ds,batch_size=batch_size,shuffle=False,num_workers=2,\n                  pin_memory=True if torch.cuda.is_available else False,\n                  collate_fn=collate_fn)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:10:52.128856Z","iopub.execute_input":"2025-01-01T12:10:52.129215Z","iopub.status.idle":"2025-01-01T12:10:52.134658Z","shell.execute_reply.started":"2025-01-01T12:10:52.129192Z","shell.execute_reply":"2025-01-01T12:10:52.133787Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# ***Modeling***","metadata":{}},{"cell_type":"markdown","source":"#### We want to start from a pre-trained model, and just finetune the last layer.","metadata":{}},{"cell_type":"code","source":"# load a model pre-trained on COCO\nweights = FasterRCNN_ResNet50_FPN_Weights.DEFAULT\nmodel = fasterrcnn_resnet50_fpn(weights=weights)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:10:55.681035Z","iopub.execute_input":"2025-01-01T12:10:55.682044Z","iopub.status.idle":"2025-01-01T12:10:56.894056Z","shell.execute_reply.started":"2025-01-01T12:10:55.682013Z","shell.execute_reply":"2025-01-01T12:10:56.893391Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### The model considers class 0 as background. If the dataset does not contain the background class, we should not have 0 in your labels. For example, assuming you have just two classes, cat and dog, you can define 1 (not 0) to represent cats and 2 to represent dogs. So, for instance, if one of the images has booth classes, your labels tensor should look like [1,2].\n\n#### In our case number of classes are 2, class 0 for background and class 1 for wheat.","metadata":{}},{"cell_type":"markdown","source":"https://pytorch.org/vision/main/models/generated/torchvision.models.detection.fasterrcnn_resnet50_fpn.html","metadata":{}},{"cell_type":"code","source":"num_classes = 2 \n\n# get number of input features for the classifier\nin_features=model.roi_heads.box_predictor.cls_score.in_features\n\n\n# replace the pre-trained head with a new one\nmodel.roi_heads.box_predictor=FastRCNNPredictor(in_channels=in_features, num_classes=num_classes)\n\nmodel.to(device)","metadata":{"_kg_hide-output":true,"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:11:00.231314Z","iopub.execute_input":"2025-01-01T12:11:00.231616Z","iopub.status.idle":"2025-01-01T12:11:00.433259Z","shell.execute_reply.started":"2025-01-01T12:11:00.231594Z","shell.execute_reply":"2025-01-01T12:11:00.43252Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# access the direct child modules of the model.\nfor params in model.children():\n    print(params)","metadata":{"_kg_hide-output":true,"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:11:06.528152Z","iopub.execute_input":"2025-01-01T12:11:06.528842Z","iopub.status.idle":"2025-01-01T12:11:06.534363Z","shell.execute_reply.started":"2025-01-01T12:11:06.528813Z","shell.execute_reply":"2025-01-01T12:11:06.533646Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# ***Fine Tuning the model***","metadata":{}},{"cell_type":"markdown","source":"#### ConvNet as fixed feature extractor: Here, we will freeze the weights for the backbone of\n####   the network (resnet50 with feature pyramid network). The Regional Proposal network and \n####   Region of Interest heads will be fine tuned using transfer learning.","metadata":{}},{"cell_type":"code","source":"classification_head = list(model.children())[-2:]\n\nfor children in list(model.children())[:-2]:\n    for params in children.parameters():\n        params.requires_grad=False\n        \nparameters = []\nfor heads in classification_head:\n    for params in heads.parameters():\n        parameters.append(params)\n        \n# Optimization using Adam \noptimizer = optim.Adam(parameters,lr=learning_rate)    \n\n# scheduling learning rate  https://pytorch.org/docs/stable/optim.html#how-to-adjust-learning-rate\nlr_scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer,mode='min',\n                                                    factor=0.1, \n                                                    patience=9,\n                                                    threshold=0.0001)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:11:13.453571Z","iopub.execute_input":"2025-01-01T12:11:13.454366Z","iopub.status.idle":"2025-01-01T12:11:13.460098Z","shell.execute_reply.started":"2025-01-01T12:11:13.454334Z","shell.execute_reply":"2025-01-01T12:11:13.459369Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(classification_head)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:11:17.093635Z","iopub.execute_input":"2025-01-01T12:11:17.09445Z","iopub.status.idle":"2025-01-01T12:11:17.099023Z","shell.execute_reply.started":"2025-01-01T12:11:17.09442Z","shell.execute_reply":"2025-01-01T12:11:17.098061Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# ***Training the fine tuned model***","metadata":{}},{"cell_type":"markdown","source":"These Notebooks helped me a lot! https://www.kaggle.com/code/pestipeti/pytorch-starter-fasterrcnn-train/notebook\n\nhttps://www.kaggle.com/code/sudhanshu2198/wheat-head-detection-using-faster-rcnn-pytorch\n","metadata":{}},{"cell_type":"code","source":"def get_lr(optimizer):\n    for params in optimizer.param_groups:\n        return params[\"lr\"]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:11:22.992898Z","iopub.execute_input":"2025-01-01T12:11:22.993219Z","iopub.status.idle":"2025-01-01T12:11:22.997277Z","shell.execute_reply.started":"2025-01-01T12:11:22.993196Z","shell.execute_reply":"2025-01-01T12:11:22.996342Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"loss_history={\"training_loss\":[],\n              \"validation_loss\":[]}\n\ntrain_len=len(train_dl.dataset)\nval_len=len(val_dl.dataset)\n\nbest_validation_loss=np.inf\nbest_weights=copy.deepcopy(model.state_dict())\n\nfor epoch in range(epochs):\n    \n    training_loss=0.0\n    validation_loss=0.0\n    current_lr=get_lr(optimizer)\n    \n    #During training, the model expects both the input tensors, as well as a targets \n    model.train()\n    for imgs,targets in train_dl:\n        imgs=[img.to(device) for img in imgs]\n        targets=[{k:v.to(device) for (k,v) in d.items()} for d in targets]\n        \n        \"\"\"The model returns a Dict[Tensor] during training, containing the classification\n           and regression losses for both the RPN and the R-CNN.\"\"\"\n        \n        loss_dict=model(imgs,targets)\n        losses=sum(loss for loss in loss_dict.values())\n        training_loss+=losses.item()\n        \n        optimizer.zero_grad()\n        losses.backward()\n        optimizer.step()\n        \n    with torch.no_grad():\n        for imgs,targets in val_dl:\n            imgs=[img.to(device) for img in imgs]\n            targets=[{k:v.to(device) for (k,v) in d.items()} for d in targets]\n            \n            \"\"\"The model returns a Dict[Tensor] during training, containing the classification\n               and regression losses for both the RPN and the R-CNN.\"\"\"\n            \n            loss_dict=model(imgs,targets)\n            losses=sum(loss for loss in loss_dict.values())\n            validation_loss+=losses.item()\n            \n            \n    lr_scheduler.step(validation_loss)\n    if current_lr!=get_lr(optimizer):\n        print(\"Loading best Model weights\")\n        model.load_state_dict(best_weights)\n    \n    if validation_loss<best_validation_loss:\n        best_validation_loss=validation_loss\n        best_weights=copy.deepcopy(model.state_dict())\n        print(\"Updating Best Model weights\")\n        \n    \n    loss_history[\"training_loss\"].append(training_loss/train_len)\n    loss_history[\"validation_loss\"].append(validation_loss/val_len)\n            \n    print(f\"\\n{epoch+1}/{epochs}\")\n    print(f\"Training Loss: {training_loss/train_len}\")\n    print(f\"Validation_loss: {validation_loss/val_len}\")\n    print(\"\\n\"+\"*\"*40)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T12:11:27.420414Z","iopub.execute_input":"2025-01-01T12:11:27.421056Z","iopub.status.idle":"2025-01-01T17:25:20.207159Z","shell.execute_reply.started":"2025-01-01T12:11:27.421026Z","shell.execute_reply":"2025-01-01T17:25:20.206143Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"***Plot training and validation loss***","metadata":{}},{"cell_type":"code","source":"import seaborn as sns\n\nsns.lineplot(x=range(epochs),y=loss_history[\"training_loss\"],label=\"Train Losses\");\nsns.lineplot(x=range(epochs),y=loss_history[\"validation_loss\"],label=\"Validation Losses\");\nplt.title(\"Training Validation Datasets Losses\");\nplt.legend();\nplt.savefig(\"3.jpg\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T17:27:35.61958Z","iopub.execute_input":"2025-01-01T17:27:35.620234Z","iopub.status.idle":"2025-01-01T17:27:36.308464Z","shell.execute_reply.started":"2025-01-01T17:27:35.620194Z","shell.execute_reply":"2025-01-01T17:27:36.30768Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# ***Save the Model***","metadata":{}},{"cell_type":"code","source":"torch.save(best_weights,model_weights_file)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T17:31:30.679009Z","iopub.execute_input":"2025-01-01T17:31:30.679647Z","iopub.status.idle":"2025-01-01T17:31:31.114748Z","shell.execute_reply.started":"2025-01-01T17:31:30.679622Z","shell.execute_reply":"2025-01-01T17:31:31.113737Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"torch.save(model.state_dict(), 'fasterrcnn_resnet50.pth')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T17:42:36.509268Z","iopub.execute_input":"2025-01-01T17:42:36.50958Z","iopub.status.idle":"2025-01-01T17:42:36.933772Z","shell.execute_reply.started":"2025-01-01T17:42:36.509556Z","shell.execute_reply":"2025-01-01T17:42:36.932832Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# ***Inference***","metadata":{}},{"cell_type":"code","source":"model.load_state_dict(torch.load(model_weights_file))\nmodel.to(device)","metadata":{"_kg_hide-output":true,"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T17:42:58.999655Z","iopub.execute_input":"2025-01-01T17:42:59.000573Z","iopub.status.idle":"2025-01-01T17:42:59.172745Z","shell.execute_reply.started":"2025-01-01T17:42:59.000534Z","shell.execute_reply":"2025-01-01T17:42:59.171837Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def show(img,boxes,ax,color=(255,0,0)):\n    boxes=boxes.detach().cpu().numpy().astype(np.int32)\n    sample=img.permute(1,2,0).numpy().copy()\n    \n    for box in boxes:\n        cv2.rectangle(sample,(box[0], box[1]),(box[2], box[3]),color, 3)\n    \n    ax.axis(\"off\");\n    ax.imshow(sample);","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T17:43:05.566527Z","iopub.execute_input":"2025-01-01T17:43:05.566818Z","iopub.status.idle":"2025-01-01T17:43:05.57228Z","shell.execute_reply.started":"2025-01-01T17:43:05.566797Z","shell.execute_reply":"2025-01-01T17:43:05.571437Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"During inference, the model requires only the input tensors, and returns the \n   post-processed predictions as a List[Dict[Tensor]], one for each input image. The fields\n   of the Dict are as follows:\n   - boxes (FloatTensor[N, 4]): the predicted boxes in [x0, y0, x1, y1] format\n   - labels (Int64Tensor[N]): the predicted labels for each image\n   - scores (Tensor[N]): the scores or each prediction\"\"\"\n\nfig,axes=plt.subplots(4,2,figsize=(8,16))\nplt.subplots_adjust(wspace=0.1,hspace=0.1)\n\nimgs,targets=next(iter(train_dl))\nmodel.eval()\noutput=model([img.to(device) for img in imgs])\n\nfor i,idx in enumerate(range(len(imgs))):\n    img=imgs[idx]\n    predictions=output[idx]\n    \n    #real bounding boxes\n    show(img,targets[idx][\"boxes\"],axes[i,0],color=(0,255,0))\n    \n    #non-max suppression\n    #threshold=0.5\n    #iou_threshold=0.8\n    \"\"\"Non-max suppression is the final step of these object detection algorithms and is \n       used to select the most appropriate bounding box for the object.\n       The NMS takes two things into account\n        -The objectiveness score is given by the model\n        -The overlap or IOU of the bounding boxes\"\"\"\n    \n    pp_boxes=predictions[\"boxes\"][predictions[\"scores\"]>=threshold]\n    scores=predictions[\"scores\"][predictions[\"scores\"]>=threshold]\n    nms=torchvision.ops.nms(pp_boxes,scores,iou_threshold=iou_threshold)\n    pp_boxes=pp_boxes[nms]\n\n    \n    show(img,pp_boxes,axes[i,1]);\n    \nplt.savefig(\"4.png\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T17:43:09.3341Z","iopub.execute_input":"2025-01-01T17:43:09.334436Z","iopub.status.idle":"2025-01-01T17:43:12.826139Z","shell.execute_reply.started":"2025-01-01T17:43:09.334408Z","shell.execute_reply":"2025-01-01T17:43:12.825056Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torchvision.ops import box_iou\n\n\ndef calculate_mAP(pred_boxes, true_boxes):\n    iou = box_iou(pred_boxes, true_boxes)\n    precision = torch.mean(torch.max(iou, dim=1).values)\n    return precision.item()\n\nmean_iou = 0\nmean_ap = 0\ncount = 0\n\nmodel.eval()\nwith torch.no_grad():\n    for imgs, targets in val_dl:\n        imgs = [img.to(device) for img in imgs]\n        outputs = model(imgs)\n        \n        for i, output in enumerate(outputs):\n            pred_boxes = output['boxes'].cpu()\n            true_boxes = targets[i]['boxes']\n            \n            if len(pred_boxes) > 0 and len(true_boxes) > 0:\n                iou = torch.mean(box_iou(pred_boxes, true_boxes))\n                ap = calculate_mAP(pred_boxes, true_boxes)\n                mean_iou += iou\n                mean_ap += ap\n                count += 1\n                \nmean_iou /= count\nmean_ap /= count\n\nloss_history['mAP'].append(mean_ap)\nloss_history['IoU'].append(mean_iou)\n\nprint(f\"Mean Average Precision (mAP): {mean_ap:.4f}\")\nprint(f\"Intersection over Union (IoU): {mean_iou:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T17:46:05.53564Z","iopub.execute_input":"2025-01-01T17:46:05.536302Z","iopub.status.idle":"2025-01-01T17:46:37.824731Z","shell.execute_reply.started":"2025-01-01T17:46:05.536262Z","shell.execute_reply":"2025-01-01T17:46:37.823688Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"img, target = val_ds[50]  # Prend une image de validation\nmodel.eval()  # Mode évaluation\n\nwith torch.no_grad():\n    prediction = model([img.to(device)])[0]  # Prédiction sur l'image\n\nprint('Predicted #boxes: ', len(prediction['boxes']))\nprint('Real #boxes: ', len(target['boxes']))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T18:14:29.538915Z","iopub.execute_input":"2025-01-01T18:14:29.539549Z","iopub.status.idle":"2025-01-01T18:14:29.655672Z","shell.execute_reply.started":"2025-01-01T18:14:29.539521Z","shell.execute_reply":"2025-01-01T18:14:29.654764Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib\nbbox = target['boxes'].numpy()\nfig,ax = plt.subplots(1,figsize=(18,10))\nax.imshow(img.permute(1,2,0).cpu().numpy())\nfor i in range(len(bbox)):\n    box = bbox[i]\n    x,y,w,h = box[0], box[1], box[2]-box[0], box[3]-box[1]\n    rect = matplotlib.patches.Rectangle((x,y),w,h,linewidth=2,edgecolor='r',facecolor='none',)\n    ax.text(*box[:2], \"wheat\", verticalalignment='top', color='red', fontsize=13, weight='bold')\n    ax.add_patch(rect)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T18:22:57.33253Z","iopub.execute_input":"2025-01-01T18:22:57.333157Z","iopub.status.idle":"2025-01-01T18:22:58.123216Z","shell.execute_reply.started":"2025-01-01T18:22:57.333128Z","shell.execute_reply":"2025-01-01T18:22:58.122419Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# the function takes the original prediction and the iou threshold.\ndef apply_nms(orig_prediction, iou_thresh=0.2):\n    \n    # torchvision returns the indices of the bboxes to keep\n    keep = torchvision.ops.nms(orig_prediction['boxes'], orig_prediction['scores'], iou_thresh)\n    \n    final_prediction = orig_prediction\n    final_prediction['boxes'] = final_prediction['boxes'][keep]\n    final_prediction['scores'] = final_prediction['scores'][keep]\n    final_prediction['labels'] = final_prediction['labels'][keep]\n    \n    return final_prediction","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T18:32:22.588457Z","iopub.execute_input":"2025-01-01T18:32:22.589107Z","iopub.status.idle":"2025-01-01T18:32:22.594078Z","shell.execute_reply.started":"2025-01-01T18:32:22.589053Z","shell.execute_reply":"2025-01-01T18:32:22.593263Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torchvision.ops import nms\ndef plot_valid(img,prediction,nms=True,detect_thresh=0.5):\n    fig,ax = plt.subplots(figsize=(18,10))\n    val_img = img.permute(1,2,0).cpu().numpy()\n    ax.imshow(val_img)\n    nms_prediction = apply_nms(prediction, iou_thresh=0.2) if nms else prediction\n    val_scores = nms_prediction['scores'].cpu().detach().numpy()\n    bbox = nms_prediction['boxes'].cpu().detach().numpy()\n    for i in range(len(bbox)):\n        if val_scores[i]>=detect_thresh:\n            box = bbox[i]\n            x,y,w,h = box[0], box[1], box[2]-box[0], box[3]-box[1]\n            rect = matplotlib.patches.Rectangle((x,y),w,h,linewidth=2 ,edgecolor='r',facecolor='none',)\n            ax.text(*box[:2], \"wheat {0:.3f}\".format(val_scores[i]), verticalalignment='top', color='white', fontsize=12, weight='bold')\n            ax.add_patch(rect)\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T18:32:28.862143Z","iopub.execute_input":"2025-01-01T18:32:28.862466Z","iopub.status.idle":"2025-01-01T18:32:28.869428Z","shell.execute_reply.started":"2025-01-01T18:32:28.862441Z","shell.execute_reply":"2025-01-01T18:32:28.868445Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_valid(img,prediction)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T18:32:32.585227Z","iopub.execute_input":"2025-01-01T18:32:32.585844Z","iopub.status.idle":"2025-01-01T18:32:33.419318Z","shell.execute_reply.started":"2025-01-01T18:32:32.585818Z","shell.execute_reply":"2025-01-01T18:32:33.418463Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.read_csv('../input/global-wheat-detection/sample_submission.csv')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T18:33:12.04285Z","iopub.execute_input":"2025-01-01T18:33:12.04322Z","iopub.status.idle":"2025-01-01T18:33:12.059795Z","shell.execute_reply.started":"2025-01-01T18:33:12.043195Z","shell.execute_reply":"2025-01-01T18:33:12.059177Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class TestDataset(object):\n    def __init__(self, df, IMG_DIR, transforms=None):\n        # select only those classes that have boxes\n        \n        self.df = df\n        self.img_dir = IMG_DIR\n        self.transforms = transforms\n        self.image_ids = self.df['image_id'].tolist()\n        \n    def __len__(self):\n        return len(self.image_ids)\n    \n    def __getitem__(self, idx):\n        \n        image_id = self.image_ids[idx]\n        image = cv2.imread(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        \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","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T18:33:48.943158Z","iopub.execute_input":"2025-01-01T18:33:48.944015Z","iopub.status.idle":"2025-01-01T18:33:48.949966Z","shell.execute_reply.started":"2025-01-01T18:33:48.943986Z","shell.execute_reply":"2025-01-01T18:33:48.94906Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\ndef get_test_transform(IMG_SIZE=(512,512)):\n    return A.Compose([\n         A.Normalize(mean=(0, 0, 0), std=(1, 1, 1), max_pixel_value=255.0, p=1.0),\n        A.Resize(*IMG_SIZE),\n        ToTensorV2(p=1.0)\n    ])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T18:36:39.643704Z","iopub.execute_input":"2025-01-01T18:36:39.644515Z","iopub.status.idle":"2025-01-01T18:36:40.681817Z","shell.execute_reply.started":"2025-01-01T18:36:39.644485Z","shell.execute_reply":"2025-01-01T18:36:40.680927Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_img_dir = '../input/global-wheat-detection/test/'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T18:36:43.550338Z","iopub.execute_input":"2025-01-01T18:36:43.550666Z","iopub.status.idle":"2025-01-01T18:36:43.554727Z","shell.execute_reply.started":"2025-01-01T18:36:43.550637Z","shell.execute_reply":"2025-01-01T18:36:43.553906Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMG_SIZE = (512,512)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T18:36:47.154107Z","iopub.execute_input":"2025-01-01T18:36:47.154773Z","iopub.status.idle":"2025-01-01T18:36:47.158405Z","shell.execute_reply.started":"2025-01-01T18:36:47.154747Z","shell.execute_reply":"2025-01-01T18:36:47.157518Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_dataset = TestDataset(submission, test_img_dir ,get_test_transform(IMG_SIZE))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T18:36:58.557133Z","iopub.execute_input":"2025-01-01T18:36:58.557848Z","iopub.status.idle":"2025-01-01T18:36:58.561951Z","shell.execute_reply.started":"2025-01-01T18:36:58.557821Z","shell.execute_reply":"2025-01-01T18:36:58.561029Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for j in range(submission.shape[0]):\n    img,_ = test_dataset[j]\n    # put the model in evaluation mode\n    model.eval()\n    with torch.no_grad():\n        prediction = model([img.to(device)])[0]\n    plot_valid(img,prediction)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-01T18:37:56.214993Z","iopub.execute_input":"2025-01-01T18:37:56.215321Z","iopub.status.idle":"2025-01-01T18:38:03.80744Z","shell.execute_reply.started":"2025-01-01T18:37:56.2153Z","shell.execute_reply":"2025-01-01T18:38:03.806456Z"}},"outputs":[],"execution_count":null}]}