{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":89850,"databundleVersionId":11256103,"sourceType":"competition"},{"sourceId":228774,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":195035,"modelId":216932},{"sourceId":228781,"sourceType":"modelInstanceVersion","isSourceIdPinned":true,"modelInstanceId":195042,"modelId":216938}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Official Starter notebook | Dinov2 Model Inference","metadata":{}},{"cell_type":"markdown","source":"## Introduction\nThe purpose of this notebook is to serve as a baseline entry for the competition, and as a guidance on how to infer the available trained models on quadrat test images. \nThe DinoV2-based plant identification model provided in the competition is applied to the entire image of each quadrat in the test set, which has been resized to the original input dimensions of 518x518 used to train DinoV2.\nThis notebook can be used as a starting point for further development. Feel free to leave comments on errors or for any improvement.","metadata":{}},{"cell_type":"markdown","source":"### Import libraries","metadata":{}},{"cell_type":"code","source":"import csv\nimport numpy as np \nimport pandas as pd\nimport timm \nimport torch\nfrom PIL import Image\nfrom torch.utils.data import DataLoader, Dataset\nimport logging\nimport time\nimport os\n\nlogging.basicConfig(\n    level=logging.INFO,\n    handlers=[logging.StreamHandler()])\n_logger = logging.getLogger('inference')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-03-11T10:50:09.787038Z","iopub.execute_input":"2025-03-11T10:50:09.787425Z","iopub.status.idle":"2025-03-11T10:50:15.361743Z","shell.execute_reply.started":"2025-03-11T10:50:09.787383Z","shell.execute_reply":"2025-03-11T10:50:15.360831Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class AverageMeter:\n    def __init__(self):\n        self.val = 0\n        self.avg = 0\n        self.sum = 0\n        self.count = 0\n\n    def update(self, val, n=1):\n        self.val = val\n        self.sum += val * n\n        self.count += n\n        self.avg = self.sum / self.count\n        \n\nclass TestDataset(Dataset):\n    def __init__(self, image_folder, transform=None):\n        self.image_folder = image_folder\n        self.image_paths = [os.path.join(image_folder, f) for f in os.listdir(image_folder)]\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.image_paths)\n\n    def __getitem__(self, idx):\n        image_path = self.image_paths[idx]\n        image = Image.open(image_path)\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, image_path ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-11T10:50:15.362900Z","iopub.execute_input":"2025-03-11T10:50:15.363216Z","iopub.status.idle":"2025-03-11T10:50:15.369240Z","shell.execute_reply.started":"2025-03-11T10:50:15.363188Z","shell.execute_reply":"2025-03-11T10:50:15.368403Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Load species competition metadata","metadata":{}},{"cell_type":"code","source":"df_species_ids = pd.read_csv('/kaggle/input/plantclef-2025/species_ids.csv')\n\ndf_metadata = pd.read_csv('/kaggle/input/plantclef-2025/PlantCLEF2024_single_plant_training_metadata.csv', sep=';', dtype={'partner': str})\nid_to_species = df_metadata[['species_id', 'species']].drop_duplicates().set_index('species_id')\n\ndf_metadata.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-11T10:50:15.371042Z","iopub.execute_input":"2025-03-11T10:50:15.371324Z","iopub.status.idle":"2025-03-11T10:50:36.112003Z","shell.execute_reply.started":"2025-03-11T10:50:15.371305Z","shell.execute_reply":"2025-03-11T10:50:36.111029Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Import provided model with timm library:","metadata":{}},{"cell_type":"code","source":"device = torch.device('cuda')\nmodel = timm.create_model('vit_base_patch14_reg4_dinov2.lvd142m',\n                          pretrained=False,\n                          num_classes=len(df_species_ids),\n                          checkpoint_path='/kaggle/input/dinov2_patch14_reg4_onlyclassifier_then_all/pytorch/default/3/model_best.pth.tar')\nmodel = model.to(device)\nmodel = model.eval()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-11T10:50:36.113201Z","iopub.execute_input":"2025-03-11T10:50:36.113496Z","iopub.status.idle":"2025-03-11T10:50:46.330638Z","shell.execute_reply.started":"2025-03-11T10:50:36.113474Z","shell.execute_reply":"2025-03-11T10:50:46.329954Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Load model configuration settings","metadata":{}},{"cell_type":"code","source":"data_config = timm.data.resolve_model_data_config(model)\ntransforms = timm.data.create_transform(**data_config, is_training=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-11T10:50:46.331368Z","iopub.execute_input":"2025-03-11T10:50:46.331578Z","iopub.status.idle":"2025-03-11T10:50:46.336606Z","shell.execute_reply.started":"2025-03-11T10:50:46.331560Z","shell.execute_reply":"2025-03-11T10:50:46.335753Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Set hyperparameters:\n* batch_size: size of batch of testing images\n* top_k: keep best top_k results for each image\n* min_score: keep only classes with a score higher than min_score","metadata":{}},{"cell_type":"code","source":"batch_size = 32\ntop_k = 15\nmin_score = 0.01 ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-11T10:50:46.337342Z","iopub.execute_input":"2025-03-11T10:50:46.337629Z","iopub.status.idle":"2025-03-11T10:50:46.363354Z","shell.execute_reply.started":"2025-03-11T10:50:46.337602Z","shell.execute_reply":"2025-03-11T10:50:46.362691Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Inference on Test Data","metadata":{}},{"cell_type":"code","source":"\nclass_map = df_species_ids['species_id'].to_dict()\ndataset = TestDataset(image_folder='/kaggle/input/plantclef-2025/PlantCLEF2025_test_images/PlantCLEF2025_test_images/',\n                      transform=timm.data.create_transform(**data_config, is_training=False))\ndataloader = DataLoader(dataset, batch_size=batch_size, shuffle=False)\n\nimage_predictions = {}\n\n# Initialize batch time tracking\nbatch_time = AverageMeter()\nend = time.time()\n\nwith torch.no_grad():\n    for batch_idx, (images, image_paths) in enumerate(dataloader):\n        images = images.to(device)\n        outputs = model(images)  # Perform inference on the batch\n        probabilities = torch.nn.functional.softmax(outputs, dim=1)\n\n        # Get the top-k values and their indices\n        values, indices = torch.topk(probabilities, top_k, dim=1)\n        \n        # Filter based on the probability threshold\n        values_np = values.cpu().numpy()\n        indices_np = indices.cpu().numpy()\n        \n        for i in range(values_np.shape[0]):\n            # Filtered class indices above the threshold\n            filtered_indices = indices_np[i][values_np[i] >= min_score]\n            \n            # Convert class indices to class labels\n            filtered_labels = [class_map.get(idx, 'Unknown') for idx in filtered_indices]\n\n            # Get the image name without the extension\n            image_name = os.path.splitext(os.path.basename(image_paths[i]))[0]\n\n            image_predictions[image_name] = filtered_labels\n        \n        batch_time.update(time.time() - end)\n        end = time.time()\n\n        # Log info at specified frequency\n        if batch_idx % 10 == 0:  # You can set your log frequency here\n            _logger.info(f'Predict: [{batch_idx}/{len(dataloader)}] '\n                         f'Time {batch_time.val:.3f} ({batch_time.avg:.3f})')  ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-11T10:56:05.518624Z","iopub.execute_input":"2025-03-11T10:56:05.518961Z","iopub.status.idle":"2025-03-11T11:09:57.912914Z","shell.execute_reply.started":"2025-03-11T10:56:05.518931Z","shell.execute_reply":"2025-03-11T11:09:57.912239Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Submit predictions","metadata":{}},{"cell_type":"code","source":"df_run = pd.DataFrame(list(image_predictions.items()), columns=['quadrat_id', 'species_ids'])\ndf_run['species_ids'] = df_run['species_ids'].apply(str)\ndf_run.to_csv(\"submission.csv\", sep=',', index=False, quoting=csv.QUOTE_ALL)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-11T11:21:13.696399Z","iopub.execute_input":"2025-03-11T11:21:13.696703Z","iopub.status.idle":"2025-03-11T11:21:13.713565Z","shell.execute_reply.started":"2025-03-11T11:21:13.696680Z","shell.execute_reply":"2025-03-11T11:21:13.712797Z"}},"outputs":[],"execution_count":null}]}