{
  "id": 673934,
  "title": "4th place solution",
  "url": "/competitions/dal-shemagh-detection-challenge/writeups/4th-place-solution",
  "author_name": "",
  "post_date": "2026-02-17T23:48:50.087Z",
  "votes": 7,
  "comment_count": 2,
  "views": 0,
  "content": "<p>السلام عليكم ورحمة الله وبركاته, رمضان مبارك جميعا</p>\n<p>First I want to thank  <a href=\"https://www.kaggle.com/mohammad2012191\" target=\"_blank\">@mohammad2012191</a> and all the organizers for this great competition, and want to thank my teammates <a href=\"https://www.kaggle.com/mohammedkhurd\" target=\"_blank\">@mohammedkhurd</a> and <a href=\"https://www.kaggle.com/ahmedmsr\" target=\"_blank\">@ahmedmsr</a>,  it seems  that Dal competitions always comes during my exams and while I  was pushing  for a gold in  the Vesuvius Challenge which was supposed to get to end 4 days before this one where I should've performed my best, unfortunately it got extended for 2 weeks🥲, anyways the world doesn't care  about excuses you either achieve it or you don't, so back to action💪</p>\n<h1>EDA</h1>\n<p>looking at the data there are many things that I noticed</p>\n<p>1) it seems  for some objects in train to have multiple boxes for  the same shemagh and  same face, an example is that for people with beards there are 2 boxes, and also there are some face boxes where there is no face  at all which could be because the images where cropped out, this is not a big issue but could introduce noise to training and similar patters could exist in test so I didn't touch the labels in train and didn't try to clean it</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F756fc84c3fa35916f0dc4f0a9c75ef15%2FScreenshot%202026-02-18%20003527.png?generation=1771364150387260&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F2a13984343130afafe3805cd76e7e19c%2FScreenshot%202026-02-18%20003026.png?generation=1771363843935348&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F9fa53b8b60f21f337dfaf077e9e69f32%2FScreenshot%202026-02-18%20020143.png?generation=1771369361763571&amp;alt=media\" alt=\"\"></p>\n<p>and looking in test, there is a huge shift due to color augmentations so people had to look for it</p>\n<h6>1) greyscale  images exist hugely in test while not existing in train</h6>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F9fec262f7dfd7edc5b4e85b366763fae%2FScreenshot%202026-02-18%20003743.png?generation=1771364297513855&amp;alt=media\" alt=\"\"></p>\n<h6>2) Also in test there is shifting in the channel order from RGB to GRB and  from RGB to BGR in some images</h6>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2Fdd4df6997cb472ebaca3ffe4f32ef1f9%2F52.jpg?generation=1771364641383922&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F403ace39fe916cefe966c4c3cf87b6db%2FWhatsApp%20Image%202026-02-10%20at%201.51.21%20AM.jpeg?generation=1771364666859929&amp;alt=media\" alt=\"\"></p>\n<h1>Preprocessing(Main points only)</h1>\n<p>while it is easy to detect greyscale image by code, It is not  that straightforward easy to find the correct channel order in colored image , since I was reading more about the information theory  and  Claude  Shannon the same day while noticing this I had a plan in inference where I pass the test image to  early layers of a Resnet model in all possible  6 orders( RGB, BGR, GRB, RBG, GBR, BRG) and choosing the order with least entropy because it is more expected for the model to see  those channel values variation (if greyscale image I don't put it in this process since in greyscale images all channels have relatively equal values R = G = B) even though I expected this method to be trash  and not efficient  but I wanted to try it because it was really cool, but surprisingly did really well, It performed even better than a  DINO model trained specifically to classify the correct  channel order🙂</p>\n<p>color augmentations for such cases didn't seem to give the perfect performance, so I thought about training another identical models for such images,\nfor grayscale the Idea was that I trained a normal object detection model , and  then converted all  train images to grayscale and train another similar object detection model to detect objects in greyscale images, if in test a greyscale image was encountered i choose the other model trained on greyscale only</p>\n<p>I was  also supposed to train 2 other models for GRB  BGR images or at least  add augmentations for such cases but looking at the code again it seems that I was too busy and forget about adding these points</p>\n<p>for right place  classification model which is  DINOv2 instead of feeding the  image itself, I averaged embeddings from 4 transformations  with the original image to make it ready for possible channel shifts and greyscale images, I will get into more details about this in the model section</p>\n<pre><code>def extract_features(image_paths, model):\n    print(f\"Extracting DINOv2 features for {len(image_paths)} images...\")\n    mean = [0.485, 0.456, 0.406]\n    std = [0.229, 0.224, 0.225]\n\n    transforms_list = [\n        # Original\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Horizontal flip\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.RandomHorizontalFlip(p=1.0),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Grayscale (3-channel)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.Grayscale(num_output_channels=3),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Channel flip (RGB -&gt; BGR)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(2, 1, 0)),\n                    T.Normalize(mean, std)]),\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(1, 0, 2)),\n                    T.Normalize(mean, std)]),\n    ]\n</code></pre>\n<h1>Model architecture and training (Farewell CNNs)</h1>\n<p>It's becoming really clear that SSL pretrained transformers specially with  constrative-learning techniques  seems to perform way better than CNNs in all tasks, just switching  from YOLO baseline to RF-DETR(detection transformer with DINO encoder) moved the score from 0.79 to 0.88 in the public leaderboard which is an insane jump from just a model switch and It converge really fast (just 10 epochs for all data)</p>\n<pre><code># Train RF-DETR on color images\nrfdetr_color = RFDETRBase()\nrfdetr_color.train(\n    dataset_dir=coco_color_dir,\n    epochs=10,\n    batch_size=2,\n    grad_accumulation_steps=8,\n    lr=1e-4,\n    resolution=560,\n    multi_scale=False,\n    gradient_checkpointing=True,\n    output_dir='/kaggle/working/rfdetr_output_color',\n)\nprint(\"RF-DETR (COLOR) training complete.\")\n</code></pre>\n<p>and for the  people who wore shemagh the right way there are only 5  positive samples in train so it is impossible to train a good classification model based on train data only, so used a condition where if a face box is almost  is contained completely inside a shemagh box  then consider this guy to be wearing it the correct way, then add these poistive samples from test as pseudo labels(positive samples only) and add it with train and then train a classification model which is DINOv2 to classify the right place label again, dino was  so perfect that it even outperformed  the ground truth🙂, thats by only only extracting the embeddings and training  GBDTs on top of it</p>\n<h3>code for right place  pseudo labelling  condition</h3>\n<pre><code>  # Pseudo-label: person box mostly contained inside shemagh box\n    right_place = 0\n    if person_boxes and shemagh_boxes:\n        for p_box in person_boxes:\n            for s_box in shemagh_boxes:\n                if containment_ratio(p_box, s_box) &gt;= CFG.containment_threshold:\n                    right_place = 1\n                    break\n            if right_place == 1:\n                break\n</code></pre>\n<h3>dino classification code</h3>\n<pre><code># ############################################################\n# PART 3: DINOv2 FEATURE EXTRACTION\n# ############################################################\nprint(\"\\n\" + \"=\" * 60)\nprint(\"PART 3: DINOv2 Feature Extraction\")\nprint(\"=\" * 60)\n\n\nclass ImageDataset(torch.utils.data.Dataset):\n    def __init__(self, image_paths, transform=None):\n        self.image_paths = image_paths\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.image_paths)\n\n    def __getitem__(self, idx):\n        try:\n            image = Image.open(self.image_paths[idx]).convert(\"RGB\")\n        except Exception:\n            image = Image.new(\"RGB\", (256, 256))\n        if self.transform:\n            image = self.transform(image)\n        return image\n\n\nclass ChannelFlip:\n    \"\"\"Swap RGB channels, e.g. RGB -&gt; BGR.\"\"\"\n    def __init__(self, order=(2, 1, 0)):\n        self.order = order\n\n    def __call__(self, tensor):\n        return tensor[list(self.order), :, :]\n\n\ndef get_dinov2_model():\n    for path in CFG.dinov2_paths:\n        if os.path.exists(path):\n            print(f\"Loading DINOv2 from: {path}\")\n            try:\n                model = AutoModel.from_pretrained(path, local_files_only=True)\n                return model.to(CFG.device).eval()\n            except Exception:\n                pass\n    print(\"Trying torch.hub...\")\n    try:\n        torch.hub.set_dir('/kaggle/working/torch_hub_cache')\n        model = torch.hub.load('facebookresearch/dinov2', 'dinov2_vitl14', pretrained=True)\n        return model.to(CFG.device).eval()\n    except Exception:\n        pass\n    try:\n        model = AutoModel.from_pretrained('facebook/dinov2-large')\n        return model.to(CFG.device).eval()\n    except Exception:\n        pass\n    raise RuntimeError(\"Could not load DINOv2 model.\")\n\n\ndef extract_features(image_paths, model):\n    print(f\"Extracting DINOv2 features for {len(image_paths)} images...\")\n    mean = [0.485, 0.456, 0.406]\n    std = [0.229, 0.224, 0.225]\n\n    transforms_list = [\n        # Original\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Horizontal flip\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.RandomHorizontalFlip(p=1.0),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Grayscale (3-channel)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.Grayscale(num_output_channels=3),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Channel flip (RGB -&gt; BGR)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(2, 1, 0)),\n                    T.Normalize(mean, std)]),\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(1, 0, 2)),\n                    T.Normalize(mean, std)]),\n    ]\n\n    is_transformer_model = hasattr(model, 'embeddings')\n    feats_tta = []\n\n    for i, transform in enumerate(transforms_list):\n        ds = ImageDataset(image_paths, transform=transform)\n        dl = torch.utils.data.DataLoader(ds, batch_size=CFG.batch_size,\n                                         shuffle=False, num_workers=2)\n        feats_epoch = []\n        with torch.no_grad():\n            for imgs in tqdm(dl, desc=f\"TTA Round {i + 1}/{len(transforms_list)}\"):\n                imgs = imgs.to(CFG.device)\n                if is_transformer_model:\n                    outputs = model(imgs)\n                    last_hidden = outputs.last_hidden_state\n                    emb = torch.cat([\n                        last_hidden[:, 0, :],\n                        last_hidden[:, 1:, :].mean(dim=1),\n                        last_hidden[:, 1:, :].max(dim=1)[0],\n                    ], dim=1)\n                else:\n                    outputs = model.forward_features(imgs)\n                    if isinstance(outputs, dict):\n                        emb = torch.cat([\n                            outputs['x_norm_clstoken'],\n                            outputs['x_norm_patchtokens'].mean(dim=1),\n                            outputs['x_norm_patchtokens'].max(dim=1)[0],\n                        ], dim=1)\n                    else:\n                        emb = outputs\n                feats_epoch.append(emb.cpu().numpy())\n        feats_tta.append(np.concatenate(feats_epoch))\n\n    return np.mean(feats_tta, axis=0)\n\n\n# Extract\ndino_model = get_dinov2_model()\nall_features = extract_features(all_images, dino_model)\n\ndel dino_model\ntorch.cuda.empty_cache()\ngc.collect()\n\nn_train = len(train_images)\nX_train_dino = all_features[:n_train]\nX_test_dino = all_features[n_train:]\nprint(f\"Feature shapes — Train: {X_train_dino.shape}, Test: {X_test_dino.shape}\")\n\n\n# ############################################################\n# PART 4: BUILD TRAINING SET + TRAIN GBDT CLASSIFIERS\n# ############################################################\nprint(\"\\n\" + \"=\" * 60)\nprint(\"PART 4: Training GBDT Classifiers for right_place\")\nprint(\"=\" * 60)\n\ntrain_filenames = [os.path.basename(p) for p in train_images]\ntest_filenames = [os.path.basename(p) for p in test_images]\n\n# --- Train labels ---\ntrain_labels = np.array([rfdetr_data[fn]['right_place'] for fn in train_filenames])\n\n# --- Pseudo-positive samples from test ---\npseudo_pos_indices = [i for i, fn in enumerate(test_filenames)\n                      if rfdetr_data[fn]['right_place'] == 1]\n\nprint(f\"Original train: {len(train_labels)} samples ({train_labels.sum()} positive)\")\nprint(f\"Pseudo-positive from test: {len(pseudo_pos_indices)} samples\")\n\n# Combine\nif pseudo_pos_indices:\n    X_combined = np.vstack([X_train_dino, X_test_dino[pseudo_pos_indices]])\n    y_combined = np.concatenate([train_labels, np.ones(len(pseudo_pos_indices), dtype=int)])\nelse:\n    X_combined = X_train_dino\n    y_combined = train_labels\n\nprint(f\"Combined training set: {len(y_combined)} samples ({y_combined.sum()} positive)\")\n\n# Scale features\nscaler = RobustScaler()\nX_train_scaled = scaler.fit_transform(X_combined)\nX_test_scaled = scaler.transform(X_test_dino)\n\n# --- LightGBM ---\nlgbm_params = {\n    'n_estimators': 300,\n    'learning_rate': 0.05,\n    'num_leaves': 31,\n    'random_state': CFG.seed,\n    'n_jobs': -1,\n    'verbose': -1,\n    'objective': 'binary',\n    'is_unbalance': True,\n}\nif torch.cuda.is_available():\n    lgbm_params['device'] = 'gpu'\n\nlgbm_model = lgb.LGBMClassifier(**lgbm_params)\nlgbm_model.fit(X_train_scaled, y_combined)\nprint(\"LightGBM trained.\")\n\n# --- XGBoost ---\nscale_pos = (len(y_combined) - y_combined.sum()) / max(y_combined.sum(), 1)\nxgb_params = {\n    'n_estimators': 300,\n    'learning_rate': 0.05,\n    'max_depth': 6,\n    'subsample': 0.8,\n    'colsample_bytree': 0.8,\n    'objective': 'binary:logistic',\n    'scale_pos_weight': scale_pos,\n    'n_jobs': -1,\n    'random_state': CFG.seed,\n    'tree_method': 'hist',\n    'device': 'cuda' if torch.cuda.is_available() else 'cpu',\n}\n\nxgb_model = xgb.XGBClassifier(**xgb_params)\nxgb_model.fit(X_train_scaled, y_combined)\nprint(\"XGBoost trained.\")\n\n# --- Ensemble: average probabilities ---\nprob_lgbm = lgbm_model.predict_proba(X_test_scaled)[:, 1]\nprob_xgb = xgb_model.predict_proba(X_test_scaled)[:, 1]\nprob_avg = (prob_lgbm + prob_xgb) / 2.0\ntest_preds = (prob_avg &gt;= CFG.cls_threshold).astype(int)\n\nprint(f\"Predicted right_place=1 for {test_preds.sum()} / {len(test_preds)} test images\")\n</code></pre>\n<p>by probing the leaderboard and submitting empty string for object detection to get 0 intentionally  in the mAP@[0.5:0.95] metric, my score in the public leaderboard was 0.5 which means my model was 100% perfect in the classfication metric in the public leaderboard, and  0.952 in the private which mostly because of some little mistakes  in the ground truth values  itself😃(although there are some very rare mistakes by the model still)</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F445dbdf16768b2d56581e0d16dca83d3%2FScreenshot%202026-02-18%20015346.png?generation=1771368845667692&amp;alt=media\" alt=\"\"></p>\n<p>after having a small look in the train set, It seems that if a person's face  is not included  in the image(for example he is giving his back to camera) the class would be false even if he actually wore it the right way</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F0e593828d6f6ac85a45362d314f4acab%2FScreenshot%202026-02-18%20020042.png?generation=1771369540855203&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F17aa7ee2301c63cefb7589e37aa88c20%2FScreenshot%202026-02-18%20020732.png?generation=1771370111003211&amp;alt=media\" alt=\"\"></p>\n<p>but in test  DINO classified  such cases as True even though it have not met a single positive case where the face is not appearing in the image!!!, DINO seems to have managed to figure out clearly what it means to wear the shemagh the right way 🙂, so I forcefully set  the class to be false for images where there is no face predictions to match such cases in test because probably it was  labeled the same way</p>\n<h1>what could've increased the score further:</h1>\n<p>1) ensembling different models, even merging my RFDETR Base with RFDETR large could've improved the score significantly specially that my score was lacking in the object detection metric and not classification, and Detectron2 would probably do even better since we are ensembling different architechtures</p>\n<p>2)dealing better with   BGR and GRB, I already mentioned this  point above in detail</p>\n<p>3)more focus on overlapping boxes, I think that I should've spent more time in NMS logic and try to fix the overlapping boxes in train to stabilize the model more but didn't have time to analyze the errors when optimizing  it and depending on the  change in the 15% public lb risks overfitting besides that i didn't have enough subs in the last day</p>\n<p>time to roll back to the  Vesuvius Challenge as am being outworked by my teammate there wish us good luck💪</p>",
  "messages": [
    {
      "id": "3407246",
      "postDate": "02/17/2026 23:48:42",
      "content": "<p>السلام عليكم ورحمة الله وبركاته, رمضان مبارك جميعا</p>\n<p>First I want to thank  <a href=\"https://www.kaggle.com/mohammad2012191\" target=\"_blank\">@mohammad2012191</a> and all the organizers for this great competition, and want to thank my teammates <a href=\"https://www.kaggle.com/mohammedkhurd\" target=\"_blank\">@mohammedkhurd</a> and <a href=\"https://www.kaggle.com/ahmedmsr\" target=\"_blank\">@ahmedmsr</a>,  it seems  that Dal competitions always comes during my exams and while I  was pushing  for a gold in  the Vesuvius Challenge which was supposed to get to end 4 days before this one where I should've performed my best, unfortunately it got extended for 2 weeks🥲, anyways the world doesn't care  about excuses you either achieve it or you don't, so back to action💪</p>\n<h1>EDA</h1>\n<p>looking at the data there are many things that I noticed</p>\n<p>1) it seems  for some objects in train to have multiple boxes for  the same shemagh and  same face, an example is that for people with beards there are 2 boxes, and also there are some face boxes where there is no face  at all which could be because the images where cropped out, this is not a big issue but could introduce noise to training and similar patters could exist in test so I didn't touch the labels in train and didn't try to clean it</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F756fc84c3fa35916f0dc4f0a9c75ef15%2FScreenshot%202026-02-18%20003527.png?generation=1771364150387260&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F2a13984343130afafe3805cd76e7e19c%2FScreenshot%202026-02-18%20003026.png?generation=1771363843935348&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F9fa53b8b60f21f337dfaf077e9e69f32%2FScreenshot%202026-02-18%20020143.png?generation=1771369361763571&amp;alt=media\" alt=\"\"></p>\n<p>and looking in test, there is a huge shift due to color augmentations so people had to look for it</p>\n<h6>1) greyscale  images exist hugely in test while not existing in train</h6>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F9fec262f7dfd7edc5b4e85b366763fae%2FScreenshot%202026-02-18%20003743.png?generation=1771364297513855&amp;alt=media\" alt=\"\"></p>\n<h6>2) Also in test there is shifting in the channel order from RGB to GRB and  from RGB to BGR in some images</h6>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2Fdd4df6997cb472ebaca3ffe4f32ef1f9%2F52.jpg?generation=1771364641383922&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F403ace39fe916cefe966c4c3cf87b6db%2FWhatsApp%20Image%202026-02-10%20at%201.51.21%20AM.jpeg?generation=1771364666859929&amp;alt=media\" alt=\"\"></p>\n<h1>Preprocessing(Main points only)</h1>\n<p>while it is easy to detect greyscale image by code, It is not  that straightforward easy to find the correct channel order in colored image , since I was reading more about the information theory  and  Claude  Shannon the same day while noticing this I had a plan in inference where I pass the test image to  early layers of a Resnet model in all possible  6 orders( RGB, BGR, GRB, RBG, GBR, BRG) and choosing the order with least entropy because it is more expected for the model to see  those channel values variation (if greyscale image I don't put it in this process since in greyscale images all channels have relatively equal values R = G = B) even though I expected this method to be trash  and not efficient  but I wanted to try it because it was really cool, but surprisingly did really well, It performed even better than a  DINO model trained specifically to classify the correct  channel order🙂</p>\n<p>color augmentations for such cases didn't seem to give the perfect performance, so I thought about training another identical models for such images,\nfor grayscale the Idea was that I trained a normal object detection model , and  then converted all  train images to grayscale and train another similar object detection model to detect objects in greyscale images, if in test a greyscale image was encountered i choose the other model trained on greyscale only</p>\n<p>I was  also supposed to train 2 other models for GRB  BGR images or at least  add augmentations for such cases but looking at the code again it seems that I was too busy and forget about adding these points</p>\n<p>for right place  classification model which is  DINOv2 instead of feeding the  image itself, I averaged embeddings from 4 transformations  with the original image to make it ready for possible channel shifts and greyscale images, I will get into more details about this in the model section</p>\n<pre><code>def extract_features(image_paths, model):\n    print(f\"Extracting DINOv2 features for {len(image_paths)} images...\")\n    mean = [0.485, 0.456, 0.406]\n    std = [0.229, 0.224, 0.225]\n\n    transforms_list = [\n        # Original\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Horizontal flip\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.RandomHorizontalFlip(p=1.0),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Grayscale (3-channel)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.Grayscale(num_output_channels=3),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Channel flip (RGB -&gt; BGR)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(2, 1, 0)),\n                    T.Normalize(mean, std)]),\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(1, 0, 2)),\n                    T.Normalize(mean, std)]),\n    ]\n</code></pre>\n<h1>Model architecture and training (Farewell CNNs)</h1>\n<p>It's becoming really clear that SSL pretrained transformers specially with  constrative-learning techniques  seems to perform way better than CNNs in all tasks, just switching  from YOLO baseline to RF-DETR(detection transformer with DINO encoder) moved the score from 0.79 to 0.88 in the public leaderboard which is an insane jump from just a model switch and It converge really fast (just 10 epochs for all data)</p>\n<pre><code># Train RF-DETR on color images\nrfdetr_color = RFDETRBase()\nrfdetr_color.train(\n    dataset_dir=coco_color_dir,\n    epochs=10,\n    batch_size=2,\n    grad_accumulation_steps=8,\n    lr=1e-4,\n    resolution=560,\n    multi_scale=False,\n    gradient_checkpointing=True,\n    output_dir='/kaggle/working/rfdetr_output_color',\n)\nprint(\"RF-DETR (COLOR) training complete.\")\n</code></pre>\n<p>and for the  people who wore shemagh the right way there are only 5  positive samples in train so it is impossible to train a good classification model based on train data only, so used a condition where if a face box is almost  is contained completely inside a shemagh box  then consider this guy to be wearing it the correct way, then add these poistive samples from test as pseudo labels(positive samples only) and add it with train and then train a classification model which is DINOv2 to classify the right place label again, dino was  so perfect that it even outperformed  the ground truth🙂, thats by only only extracting the embeddings and training  GBDTs on top of it</p>\n<h3>code for right place  pseudo labelling  condition</h3>\n<pre><code>  # Pseudo-label: person box mostly contained inside shemagh box\n    right_place = 0\n    if person_boxes and shemagh_boxes:\n        for p_box in person_boxes:\n            for s_box in shemagh_boxes:\n                if containment_ratio(p_box, s_box) &gt;= CFG.containment_threshold:\n                    right_place = 1\n                    break\n            if right_place == 1:\n                break\n</code></pre>\n<h3>dino classification code</h3>\n<pre><code># ############################################################\n# PART 3: DINOv2 FEATURE EXTRACTION\n# ############################################################\nprint(\"\\n\" + \"=\" * 60)\nprint(\"PART 3: DINOv2 Feature Extraction\")\nprint(\"=\" * 60)\n\n\nclass ImageDataset(torch.utils.data.Dataset):\n    def __init__(self, image_paths, transform=None):\n        self.image_paths = image_paths\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.image_paths)\n\n    def __getitem__(self, idx):\n        try:\n            image = Image.open(self.image_paths[idx]).convert(\"RGB\")\n        except Exception:\n            image = Image.new(\"RGB\", (256, 256))\n        if self.transform:\n            image = self.transform(image)\n        return image\n\n\nclass ChannelFlip:\n    \"\"\"Swap RGB channels, e.g. RGB -&gt; BGR.\"\"\"\n    def __init__(self, order=(2, 1, 0)):\n        self.order = order\n\n    def __call__(self, tensor):\n        return tensor[list(self.order), :, :]\n\n\ndef get_dinov2_model():\n    for path in CFG.dinov2_paths:\n        if os.path.exists(path):\n            print(f\"Loading DINOv2 from: {path}\")\n            try:\n                model = AutoModel.from_pretrained(path, local_files_only=True)\n                return model.to(CFG.device).eval()\n            except Exception:\n                pass\n    print(\"Trying torch.hub...\")\n    try:\n        torch.hub.set_dir('/kaggle/working/torch_hub_cache')\n        model = torch.hub.load('facebookresearch/dinov2', 'dinov2_vitl14', pretrained=True)\n        return model.to(CFG.device).eval()\n    except Exception:\n        pass\n    try:\n        model = AutoModel.from_pretrained('facebook/dinov2-large')\n        return model.to(CFG.device).eval()\n    except Exception:\n        pass\n    raise RuntimeError(\"Could not load DINOv2 model.\")\n\n\ndef extract_features(image_paths, model):\n    print(f\"Extracting DINOv2 features for {len(image_paths)} images...\")\n    mean = [0.485, 0.456, 0.406]\n    std = [0.229, 0.224, 0.225]\n\n    transforms_list = [\n        # Original\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Horizontal flip\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.RandomHorizontalFlip(p=1.0),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Grayscale (3-channel)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.Grayscale(num_output_channels=3),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Channel flip (RGB -&gt; BGR)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(2, 1, 0)),\n                    T.Normalize(mean, std)]),\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(1, 0, 2)),\n                    T.Normalize(mean, std)]),\n    ]\n\n    is_transformer_model = hasattr(model, 'embeddings')\n    feats_tta = []\n\n    for i, transform in enumerate(transforms_list):\n        ds = ImageDataset(image_paths, transform=transform)\n        dl = torch.utils.data.DataLoader(ds, batch_size=CFG.batch_size,\n                                         shuffle=False, num_workers=2)\n        feats_epoch = []\n        with torch.no_grad():\n            for imgs in tqdm(dl, desc=f\"TTA Round {i + 1}/{len(transforms_list)}\"):\n                imgs = imgs.to(CFG.device)\n                if is_transformer_model:\n                    outputs = model(imgs)\n                    last_hidden = outputs.last_hidden_state\n                    emb = torch.cat([\n                        last_hidden[:, 0, :],\n                        last_hidden[:, 1:, :].mean(dim=1),\n                        last_hidden[:, 1:, :].max(dim=1)[0],\n                    ], dim=1)\n                else:\n                    outputs = model.forward_features(imgs)\n                    if isinstance(outputs, dict):\n                        emb = torch.cat([\n                            outputs['x_norm_clstoken'],\n                            outputs['x_norm_patchtokens'].mean(dim=1),\n                            outputs['x_norm_patchtokens'].max(dim=1)[0],\n                        ], dim=1)\n                    else:\n                        emb = outputs\n                feats_epoch.append(emb.cpu().numpy())\n        feats_tta.append(np.concatenate(feats_epoch))\n\n    return np.mean(feats_tta, axis=0)\n\n\n# Extract\ndino_model = get_dinov2_model()\nall_features = extract_features(all_images, dino_model)\n\ndel dino_model\ntorch.cuda.empty_cache()\ngc.collect()\n\nn_train = len(train_images)\nX_train_dino = all_features[:n_train]\nX_test_dino = all_features[n_train:]\nprint(f\"Feature shapes — Train: {X_train_dino.shape}, Test: {X_test_dino.shape}\")\n\n\n# ############################################################\n# PART 4: BUILD TRAINING SET + TRAIN GBDT CLASSIFIERS\n# ############################################################\nprint(\"\\n\" + \"=\" * 60)\nprint(\"PART 4: Training GBDT Classifiers for right_place\")\nprint(\"=\" * 60)\n\ntrain_filenames = [os.path.basename(p) for p in train_images]\ntest_filenames = [os.path.basename(p) for p in test_images]\n\n# --- Train labels ---\ntrain_labels = np.array([rfdetr_data[fn]['right_place'] for fn in train_filenames])\n\n# --- Pseudo-positive samples from test ---\npseudo_pos_indices = [i for i, fn in enumerate(test_filenames)\n                      if rfdetr_data[fn]['right_place'] == 1]\n\nprint(f\"Original train: {len(train_labels)} samples ({train_labels.sum()} positive)\")\nprint(f\"Pseudo-positive from test: {len(pseudo_pos_indices)} samples\")\n\n# Combine\nif pseudo_pos_indices:\n    X_combined = np.vstack([X_train_dino, X_test_dino[pseudo_pos_indices]])\n    y_combined = np.concatenate([train_labels, np.ones(len(pseudo_pos_indices), dtype=int)])\nelse:\n    X_combined = X_train_dino\n    y_combined = train_labels\n\nprint(f\"Combined training set: {len(y_combined)} samples ({y_combined.sum()} positive)\")\n\n# Scale features\nscaler = RobustScaler()\nX_train_scaled = scaler.fit_transform(X_combined)\nX_test_scaled = scaler.transform(X_test_dino)\n\n# --- LightGBM ---\nlgbm_params = {\n    'n_estimators': 300,\n    'learning_rate': 0.05,\n    'num_leaves': 31,\n    'random_state': CFG.seed,\n    'n_jobs': -1,\n    'verbose': -1,\n    'objective': 'binary',\n    'is_unbalance': True,\n}\nif torch.cuda.is_available():\n    lgbm_params['device'] = 'gpu'\n\nlgbm_model = lgb.LGBMClassifier(**lgbm_params)\nlgbm_model.fit(X_train_scaled, y_combined)\nprint(\"LightGBM trained.\")\n\n# --- XGBoost ---\nscale_pos = (len(y_combined) - y_combined.sum()) / max(y_combined.sum(), 1)\nxgb_params = {\n    'n_estimators': 300,\n    'learning_rate': 0.05,\n    'max_depth': 6,\n    'subsample': 0.8,\n    'colsample_bytree': 0.8,\n    'objective': 'binary:logistic',\n    'scale_pos_weight': scale_pos,\n    'n_jobs': -1,\n    'random_state': CFG.seed,\n    'tree_method': 'hist',\n    'device': 'cuda' if torch.cuda.is_available() else 'cpu',\n}\n\nxgb_model = xgb.XGBClassifier(**xgb_params)\nxgb_model.fit(X_train_scaled, y_combined)\nprint(\"XGBoost trained.\")\n\n# --- Ensemble: average probabilities ---\nprob_lgbm = lgbm_model.predict_proba(X_test_scaled)[:, 1]\nprob_xgb = xgb_model.predict_proba(X_test_scaled)[:, 1]\nprob_avg = (prob_lgbm + prob_xgb) / 2.0\ntest_preds = (prob_avg &gt;= CFG.cls_threshold).astype(int)\n\nprint(f\"Predicted right_place=1 for {test_preds.sum()} / {len(test_preds)} test images\")\n</code></pre>\n<p>by probing the leaderboard and submitting empty string for object detection to get 0 intentionally  in the mAP@[0.5:0.95] metric, my score in the public leaderboard was 0.5 which means my model was 100% perfect in the classfication metric in the public leaderboard, and  0.952 in the private which mostly because of some little mistakes  in the ground truth values  itself😃(although there are some very rare mistakes by the model still)</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F445dbdf16768b2d56581e0d16dca83d3%2FScreenshot%202026-02-18%20015346.png?generation=1771368845667692&amp;alt=media\" alt=\"\"></p>\n<p>after having a small look in the train set, It seems that if a person's face  is not included  in the image(for example he is giving his back to camera) the class would be false even if he actually wore it the right way</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F0e593828d6f6ac85a45362d314f4acab%2FScreenshot%202026-02-18%20020042.png?generation=1771369540855203&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F17aa7ee2301c63cefb7589e37aa88c20%2FScreenshot%202026-02-18%20020732.png?generation=1771370111003211&amp;alt=media\" alt=\"\"></p>\n<p>but in test  DINO classified  such cases as True even though it have not met a single positive case where the face is not appearing in the image!!!, DINO seems to have managed to figure out clearly what it means to wear the shemagh the right way 🙂, so I forcefully set  the class to be false for images where there is no face predictions to match such cases in test because probably it was  labeled the same way</p>\n<h1>what could've increased the score further:</h1>\n<p>1) ensembling different models, even merging my RFDETR Base with RFDETR large could've improved the score significantly specially that my score was lacking in the object detection metric and not classification, and Detectron2 would probably do even better since we are ensembling different architechtures</p>\n<p>2)dealing better with   BGR and GRB, I already mentioned this  point above in detail</p>\n<p>3)more focus on overlapping boxes, I think that I should've spent more time in NMS logic and try to fix the overlapping boxes in train to stabilize the model more but didn't have time to analyze the errors when optimizing  it and depending on the  change in the 15% public lb risks overfitting besides that i didn't have enough subs in the last day</p>\n<p>time to roll back to the  Vesuvius Challenge as am being outworked by my teammate there wish us good luck💪</p>",
      "rawMarkdown": "السلام عليكم ورحمة الله وبركاته, رمضان مبارك جميعا\n\nFirst I want to thank  @mohammad2012191 and all the organizers for this great competition, and want to thank my teammates @mohammedkhurd and @ahmedmsr,  it seems  that Dal competitions always comes during my exams and while I  was pushing  for a gold in  the Vesuvius Challenge which was supposed to get to end 4 days before this one where I should've performed my best, unfortunately it got extended for 2 weeks🥲, anyways the world doesn't care  about excuses you either achieve it or you don't, so back to action💪\n\n\n\n# EDA\n\n\nlooking at the data there are many things that I noticed\n\n1) it seems  for some objects in train to have multiple boxes for  the same shemagh and  same face, an example is that for people with beards there are 2 boxes, and also there are some face boxes where there is no face  at all which could be because the images where cropped out, this is not a big issue but could introduce noise to training and similar patters could exist in test so I didn't touch the labels in train and didn't try to clean it\n\n\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F756fc84c3fa35916f0dc4f0a9c75ef15%2FScreenshot%202026-02-18%20003527.png?generation=1771364150387260&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F2a13984343130afafe3805cd76e7e19c%2FScreenshot%202026-02-18%20003026.png?generation=1771363843935348&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F9fa53b8b60f21f337dfaf077e9e69f32%2FScreenshot%202026-02-18%20020143.png?generation=1771369361763571&alt=media)\n\n\nand looking in test, there is a huge shift due to color augmentations so people had to look for it\n###### 1) greyscale  images exist hugely in test while not existing in train\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F9fec262f7dfd7edc5b4e85b366763fae%2FScreenshot%202026-02-18%20003743.png?generation=1771364297513855&alt=media)\n\n\n###### 2) Also in test there is shifting in the channel order from RGB to GRB and  from RGB to BGR in some images\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2Fdd4df6997cb472ebaca3ffe4f32ef1f9%2F52.jpg?generation=1771364641383922&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F403ace39fe916cefe966c4c3cf87b6db%2FWhatsApp%20Image%202026-02-10%20at%201.51.21%20AM.jpeg?generation=1771364666859929&alt=media)\n\n\n# Preprocessing(Main points only)\n\n\nwhile it is easy to detect greyscale image by code, It is not  that straightforward easy to find the correct channel order in colored image , since I was reading more about the information theory  and  Claude  Shannon the same day while noticing this I had a plan in inference where I pass the test image to  early layers of a Resnet model in all possible  6 orders( RGB, BGR, GRB, RBG, GBR, BRG) and choosing the order with least entropy because it is more expected for the model to see  those channel values variation (if greyscale image I don't put it in this process since in greyscale images all channels have relatively equal values R = G = B) even though I expected this method to be trash  and not efficient  but I wanted to try it because it was really cool, but surprisingly did really well, It performed even better than a  DINO model trained specifically to classify the correct  channel order🙂\n\ncolor augmentations for such cases didn't seem to give the perfect performance, so I thought about training another identical models for such images,\nfor grayscale the Idea was that I trained a normal object detection model , and  then converted all  train images to grayscale and train another similar object detection model to detect objects in greyscale images, if in test a greyscale image was encountered i choose the other model trained on greyscale only\n\nI was  also supposed to train 2 other models for GRB  BGR images or at least  add augmentations for such cases but looking at the code again it seems that I was too busy and forget about adding these points\n\nfor right place  classification model which is  DINOv2 instead of feeding the  image itself, I averaged embeddings from 4 transformations  with the original image to make it ready for possible channel shifts and greyscale images, I will get into more details about this in the model section\n\n```python\ndef extract_features(image_paths, model):\n    print(f\"Extracting DINOv2 features for {len(image_paths)} images...\")\n    mean = [0.485, 0.456, 0.406]\n    std = [0.229, 0.224, 0.225]\n\n    transforms_list = [\n        # Original\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Horizontal flip\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.RandomHorizontalFlip(p=1.0),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Grayscale (3-channel)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.Grayscale(num_output_channels=3),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Channel flip (RGB -> BGR)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(2, 1, 0)),\n                    T.Normalize(mean, std)]),\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(1, 0, 2)),\n                    T.Normalize(mean, std)]),\n    ]\n```\n\n# Model architecture and training (Farewell CNNs)\n\nIt's becoming really clear that SSL pretrained transformers specially with  constrative-learning techniques  seems to perform way better than CNNs in all tasks, just switching  from YOLO baseline to RF-DETR(detection transformer with DINO encoder) moved the score from 0.79 to 0.88 in the public leaderboard which is an insane jump from just a model switch and It converge really fast (just 10 epochs for all data)\n\n```python\n# Train RF-DETR on color images\nrfdetr_color = RFDETRBase()\nrfdetr_color.train(\n    dataset_dir=coco_color_dir,\n    epochs=10,\n    batch_size=2,\n    grad_accumulation_steps=8,\n    lr=1e-4,\n    resolution=560,\n    multi_scale=False,\n    gradient_checkpointing=True,\n    output_dir='/kaggle/working/rfdetr_output_color',\n)\nprint(\"RF-DETR (COLOR) training complete.\")\n```\n\n\n\nand for the  people who wore shemagh the right way there are only 5  positive samples in train so it is impossible to train a good classification model based on train data only, so used a condition where if a face box is almost  is contained completely inside a shemagh box  then consider this guy to be wearing it the correct way, then add these poistive samples from test as pseudo labels(positive samples only) and add it with train and then train a classification model which is DINOv2 to classify the right place label again, dino was  so perfect that it even outperformed  the ground truth🙂, thats by only only extracting the embeddings and training  GBDTs on top of it\n\n### code for right place  pseudo labelling  condition\n```python\n  # Pseudo-label: person box mostly contained inside shemagh box\n    right_place = 0\n    if person_boxes and shemagh_boxes:\n        for p_box in person_boxes:\n            for s_box in shemagh_boxes:\n                if containment_ratio(p_box, s_box) >= CFG.containment_threshold:\n                    right_place = 1\n                    break\n            if right_place == 1:\n                break\n```\n\n### dino classification code\n\n```python\n# ############################################################\n# PART 3: DINOv2 FEATURE EXTRACTION\n# ############################################################\nprint(\"\\n\" + \"=\" * 60)\nprint(\"PART 3: DINOv2 Feature Extraction\")\nprint(\"=\" * 60)\n\n\nclass ImageDataset(torch.utils.data.Dataset):\n    def __init__(self, image_paths, transform=None):\n        self.image_paths = image_paths\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.image_paths)\n\n    def __getitem__(self, idx):\n        try:\n            image = Image.open(self.image_paths[idx]).convert(\"RGB\")\n        except Exception:\n            image = Image.new(\"RGB\", (256, 256))\n        if self.transform:\n            image = self.transform(image)\n        return image\n\n\nclass ChannelFlip:\n    \"\"\"Swap RGB channels, e.g. RGB -> BGR.\"\"\"\n    def __init__(self, order=(2, 1, 0)):\n        self.order = order\n\n    def __call__(self, tensor):\n        return tensor[list(self.order), :, :]\n\n\ndef get_dinov2_model():\n    for path in CFG.dinov2_paths:\n        if os.path.exists(path):\n            print(f\"Loading DINOv2 from: {path}\")\n            try:\n                model = AutoModel.from_pretrained(path, local_files_only=True)\n                return model.to(CFG.device).eval()\n            except Exception:\n                pass\n    print(\"Trying torch.hub...\")\n    try:\n        torch.hub.set_dir('/kaggle/working/torch_hub_cache')\n        model = torch.hub.load('facebookresearch/dinov2', 'dinov2_vitl14', pretrained=True)\n        return model.to(CFG.device).eval()\n    except Exception:\n        pass\n    try:\n        model = AutoModel.from_pretrained('facebook/dinov2-large')\n        return model.to(CFG.device).eval()\n    except Exception:\n        pass\n    raise RuntimeError(\"Could not load DINOv2 model.\")\n\n\ndef extract_features(image_paths, model):\n    print(f\"Extracting DINOv2 features for {len(image_paths)} images...\")\n    mean = [0.485, 0.456, 0.406]\n    std = [0.229, 0.224, 0.225]\n\n    transforms_list = [\n        # Original\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Horizontal flip\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.RandomHorizontalFlip(p=1.0),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Grayscale (3-channel)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.Grayscale(num_output_channels=3),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Channel flip (RGB -> BGR)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(2, 1, 0)),\n                    T.Normalize(mean, std)]),\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(1, 0, 2)),\n                    T.Normalize(mean, std)]),\n    ]\n\n    is_transformer_model = hasattr(model, 'embeddings')\n    feats_tta = []\n\n    for i, transform in enumerate(transforms_list):\n        ds = ImageDataset(image_paths, transform=transform)\n        dl = torch.utils.data.DataLoader(ds, batch_size=CFG.batch_size,\n                                         shuffle=False, num_workers=2)\n        feats_epoch = []\n        with torch.no_grad():\n            for imgs in tqdm(dl, desc=f\"TTA Round {i + 1}/{len(transforms_list)}\"):\n                imgs = imgs.to(CFG.device)\n                if is_transformer_model:\n                    outputs = model(imgs)\n                    last_hidden = outputs.last_hidden_state\n                    emb = torch.cat([\n                        last_hidden[:, 0, :],\n                        last_hidden[:, 1:, :].mean(dim=1),\n                        last_hidden[:, 1:, :].max(dim=1)[0],\n                    ], dim=1)\n                else:\n                    outputs = model.forward_features(imgs)\n                    if isinstance(outputs, dict):\n                        emb = torch.cat([\n                            outputs['x_norm_clstoken'],\n                            outputs['x_norm_patchtokens'].mean(dim=1),\n                            outputs['x_norm_patchtokens'].max(dim=1)[0],\n                        ], dim=1)\n                    else:\n                        emb = outputs\n                feats_epoch.append(emb.cpu().numpy())\n        feats_tta.append(np.concatenate(feats_epoch))\n\n    return np.mean(feats_tta, axis=0)\n\n\n# Extract\ndino_model = get_dinov2_model()\nall_features = extract_features(all_images, dino_model)\n\ndel dino_model\ntorch.cuda.empty_cache()\ngc.collect()\n\nn_train = len(train_images)\nX_train_dino = all_features[:n_train]\nX_test_dino = all_features[n_train:]\nprint(f\"Feature shapes — Train: {X_train_dino.shape}, Test: {X_test_dino.shape}\")\n\n\n# ############################################################\n# PART 4: BUILD TRAINING SET + TRAIN GBDT CLASSIFIERS\n# ############################################################\nprint(\"\\n\" + \"=\" * 60)\nprint(\"PART 4: Training GBDT Classifiers for right_place\")\nprint(\"=\" * 60)\n\ntrain_filenames = [os.path.basename(p) for p in train_images]\ntest_filenames = [os.path.basename(p) for p in test_images]\n\n# --- Train labels ---\ntrain_labels = np.array([rfdetr_data[fn]['right_place'] for fn in train_filenames])\n\n# --- Pseudo-positive samples from test ---\npseudo_pos_indices = [i for i, fn in enumerate(test_filenames)\n                      if rfdetr_data[fn]['right_place'] == 1]\n\nprint(f\"Original train: {len(train_labels)} samples ({train_labels.sum()} positive)\")\nprint(f\"Pseudo-positive from test: {len(pseudo_pos_indices)} samples\")\n\n# Combine\nif pseudo_pos_indices:\n    X_combined = np.vstack([X_train_dino, X_test_dino[pseudo_pos_indices]])\n    y_combined = np.concatenate([train_labels, np.ones(len(pseudo_pos_indices), dtype=int)])\nelse:\n    X_combined = X_train_dino\n    y_combined = train_labels\n\nprint(f\"Combined training set: {len(y_combined)} samples ({y_combined.sum()} positive)\")\n\n# Scale features\nscaler = RobustScaler()\nX_train_scaled = scaler.fit_transform(X_combined)\nX_test_scaled = scaler.transform(X_test_dino)\n\n# --- LightGBM ---\nlgbm_params = {\n    'n_estimators': 300,\n    'learning_rate': 0.05,\n    'num_leaves': 31,\n    'random_state': CFG.seed,\n    'n_jobs': -1,\n    'verbose': -1,\n    'objective': 'binary',\n    'is_unbalance': True,\n}\nif torch.cuda.is_available():\n    lgbm_params['device'] = 'gpu'\n\nlgbm_model = lgb.LGBMClassifier(**lgbm_params)\nlgbm_model.fit(X_train_scaled, y_combined)\nprint(\"LightGBM trained.\")\n\n# --- XGBoost ---\nscale_pos = (len(y_combined) - y_combined.sum()) / max(y_combined.sum(), 1)\nxgb_params = {\n    'n_estimators': 300,\n    'learning_rate': 0.05,\n    'max_depth': 6,\n    'subsample': 0.8,\n    'colsample_bytree': 0.8,\n    'objective': 'binary:logistic',\n    'scale_pos_weight': scale_pos,\n    'n_jobs': -1,\n    'random_state': CFG.seed,\n    'tree_method': 'hist',\n    'device': 'cuda' if torch.cuda.is_available() else 'cpu',\n}\n\nxgb_model = xgb.XGBClassifier(**xgb_params)\nxgb_model.fit(X_train_scaled, y_combined)\nprint(\"XGBoost trained.\")\n\n# --- Ensemble: average probabilities ---\nprob_lgbm = lgbm_model.predict_proba(X_test_scaled)[:, 1]\nprob_xgb = xgb_model.predict_proba(X_test_scaled)[:, 1]\nprob_avg = (prob_lgbm + prob_xgb) / 2.0\ntest_preds = (prob_avg >= CFG.cls_threshold).astype(int)\n\nprint(f\"Predicted right_place=1 for {test_preds.sum()} / {len(test_preds)} test images\")\n\n\n```\n\n\n\nby probing the leaderboard and submitting empty string for object detection to get 0 intentionally  in the mAP@[0.5:0.95] metric, my score in the public leaderboard was 0.5 which means my model was 100% perfect in the classfication metric in the public leaderboard, and  0.952 in the private which mostly because of some little mistakes  in the ground truth values  itself😃(although there are some very rare mistakes by the model still)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F445dbdf16768b2d56581e0d16dca83d3%2FScreenshot%202026-02-18%20015346.png?generation=1771368845667692&alt=media)\n\n\nafter having a small look in the train set, It seems that if a person's face  is not included  in the image(for example he is giving his back to camera) the class would be false even if he actually wore it the right way\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F0e593828d6f6ac85a45362d314f4acab%2FScreenshot%202026-02-18%20020042.png?generation=1771369540855203&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F17aa7ee2301c63cefb7589e37aa88c20%2FScreenshot%202026-02-18%20020732.png?generation=1771370111003211&alt=media)\n\nbut in test  DINO classified  such cases as True even though it have not met a single positive case where the face is not appearing in the image!!!, DINO seems to have managed to figure out clearly what it means to wear the shemagh the right way 🙂, so I forcefully set  the class to be false for images where there is no face predictions to match such cases in test because probably it was  labeled the same way\n\n\n\n# what could've increased the score further:\n\n1) ensembling different models, even merging my RFDETR Base with RFDETR large could've improved the score significantly specially that my score was lacking in the object detection metric and not classification, and Detectron2 would probably do even better since we are ensembling different architechtures\n\n2)dealing better with   BGR and GRB, I already mentioned this  point above in detail\n\n3)more focus on overlapping boxes, I think that I should've spent more time in NMS logic and try to fix the overlapping boxes in train to stabilize the model more but didn't have time to analyze the errors when optimizing  it and depending on the  change in the 15% public lb risks overfitting besides that i didn't have enough subs in the last day\n\n\ntime to roll back to the  Vesuvius Challenge as am being outworked by my teammate there wish us good luck💪",
      "votes": null
    },
    {
      "id": "3407298",
      "postDate": "02/18/2026 04:11:54",
      "content": "<p>Mashallah pure skills my friend :)</p>",
      "rawMarkdown": "Mashallah pure skills my friend :)",
      "votes": null
    },
    {
      "id": "3407431",
      "postDate": "02/18/2026 10:41:04",
      "content": "<p>Mashallah incredible way of thinking </p>",
      "rawMarkdown": "Mashallah incredible way of thinking",
      "votes": null
    }
  ],
  "comments": [
    {
      "id": 3407298,
      "author_name": "mohammad2012191",
      "author_url": "",
      "post_date": "02/18/2026 04:11:54",
      "content": "<p>Mashallah pure skills my friend :)</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3407431,
      "author_name": "abdullahhalees",
      "author_url": "",
      "post_date": "02/18/2026 10:41:04",
      "content": "<p>Mashallah incredible way of thinking </p>",
      "votes": null,
      "replies": []
    }
  ],
  "raw_markdown_by_id": {
    "3407246": "السلام عليكم ورحمة الله وبركاته, رمضان مبارك جميعا\n\nFirst I want to thank  @mohammad2012191 and all the organizers for this great competition, and want to thank my teammates @mohammedkhurd and @ahmedmsr,  it seems  that Dal competitions always comes during my exams and while I  was pushing  for a gold in  the Vesuvius Challenge which was supposed to get to end 4 days before this one where I should've performed my best, unfortunately it got extended for 2 weeks🥲, anyways the world doesn't care  about excuses you either achieve it or you don't, so back to action💪\n\n\n\n# EDA\n\n\nlooking at the data there are many things that I noticed\n\n1) it seems  for some objects in train to have multiple boxes for  the same shemagh and  same face, an example is that for people with beards there are 2 boxes, and also there are some face boxes where there is no face  at all which could be because the images where cropped out, this is not a big issue but could introduce noise to training and similar patters could exist in test so I didn't touch the labels in train and didn't try to clean it\n\n\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F756fc84c3fa35916f0dc4f0a9c75ef15%2FScreenshot%202026-02-18%20003527.png?generation=1771364150387260&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F2a13984343130afafe3805cd76e7e19c%2FScreenshot%202026-02-18%20003026.png?generation=1771363843935348&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F9fa53b8b60f21f337dfaf077e9e69f32%2FScreenshot%202026-02-18%20020143.png?generation=1771369361763571&alt=media)\n\n\nand looking in test, there is a huge shift due to color augmentations so people had to look for it\n###### 1) greyscale  images exist hugely in test while not existing in train\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F9fec262f7dfd7edc5b4e85b366763fae%2FScreenshot%202026-02-18%20003743.png?generation=1771364297513855&alt=media)\n\n\n###### 2) Also in test there is shifting in the channel order from RGB to GRB and  from RGB to BGR in some images\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2Fdd4df6997cb472ebaca3ffe4f32ef1f9%2F52.jpg?generation=1771364641383922&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F403ace39fe916cefe966c4c3cf87b6db%2FWhatsApp%20Image%202026-02-10%20at%201.51.21%20AM.jpeg?generation=1771364666859929&alt=media)\n\n\n# Preprocessing(Main points only)\n\n\nwhile it is easy to detect greyscale image by code, It is not  that straightforward easy to find the correct channel order in colored image , since I was reading more about the information theory  and  Claude  Shannon the same day while noticing this I had a plan in inference where I pass the test image to  early layers of a Resnet model in all possible  6 orders( RGB, BGR, GRB, RBG, GBR, BRG) and choosing the order with least entropy because it is more expected for the model to see  those channel values variation (if greyscale image I don't put it in this process since in greyscale images all channels have relatively equal values R = G = B) even though I expected this method to be trash  and not efficient  but I wanted to try it because it was really cool, but surprisingly did really well, It performed even better than a  DINO model trained specifically to classify the correct  channel order🙂\n\ncolor augmentations for such cases didn't seem to give the perfect performance, so I thought about training another identical models for such images,\nfor grayscale the Idea was that I trained a normal object detection model , and  then converted all  train images to grayscale and train another similar object detection model to detect objects in greyscale images, if in test a greyscale image was encountered i choose the other model trained on greyscale only\n\nI was  also supposed to train 2 other models for GRB  BGR images or at least  add augmentations for such cases but looking at the code again it seems that I was too busy and forget about adding these points\n\nfor right place  classification model which is  DINOv2 instead of feeding the  image itself, I averaged embeddings from 4 transformations  with the original image to make it ready for possible channel shifts and greyscale images, I will get into more details about this in the model section\n\n```python\ndef extract_features(image_paths, model):\n    print(f\"Extracting DINOv2 features for {len(image_paths)} images...\")\n    mean = [0.485, 0.456, 0.406]\n    std = [0.229, 0.224, 0.225]\n\n    transforms_list = [\n        # Original\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Horizontal flip\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.RandomHorizontalFlip(p=1.0),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Grayscale (3-channel)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.Grayscale(num_output_channels=3),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Channel flip (RGB -> BGR)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(2, 1, 0)),\n                    T.Normalize(mean, std)]),\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(1, 0, 2)),\n                    T.Normalize(mean, std)]),\n    ]\n```\n\n# Model architecture and training (Farewell CNNs)\n\nIt's becoming really clear that SSL pretrained transformers specially with  constrative-learning techniques  seems to perform way better than CNNs in all tasks, just switching  from YOLO baseline to RF-DETR(detection transformer with DINO encoder) moved the score from 0.79 to 0.88 in the public leaderboard which is an insane jump from just a model switch and It converge really fast (just 10 epochs for all data)\n\n```python\n# Train RF-DETR on color images\nrfdetr_color = RFDETRBase()\nrfdetr_color.train(\n    dataset_dir=coco_color_dir,\n    epochs=10,\n    batch_size=2,\n    grad_accumulation_steps=8,\n    lr=1e-4,\n    resolution=560,\n    multi_scale=False,\n    gradient_checkpointing=True,\n    output_dir='/kaggle/working/rfdetr_output_color',\n)\nprint(\"RF-DETR (COLOR) training complete.\")\n```\n\n\n\nand for the  people who wore shemagh the right way there are only 5  positive samples in train so it is impossible to train a good classification model based on train data only, so used a condition where if a face box is almost  is contained completely inside a shemagh box  then consider this guy to be wearing it the correct way, then add these poistive samples from test as pseudo labels(positive samples only) and add it with train and then train a classification model which is DINOv2 to classify the right place label again, dino was  so perfect that it even outperformed  the ground truth🙂, thats by only only extracting the embeddings and training  GBDTs on top of it\n\n### code for right place  pseudo labelling  condition\n```python\n  # Pseudo-label: person box mostly contained inside shemagh box\n    right_place = 0\n    if person_boxes and shemagh_boxes:\n        for p_box in person_boxes:\n            for s_box in shemagh_boxes:\n                if containment_ratio(p_box, s_box) >= CFG.containment_threshold:\n                    right_place = 1\n                    break\n            if right_place == 1:\n                break\n```\n\n### dino classification code\n\n```python\n# ############################################################\n# PART 3: DINOv2 FEATURE EXTRACTION\n# ############################################################\nprint(\"\\n\" + \"=\" * 60)\nprint(\"PART 3: DINOv2 Feature Extraction\")\nprint(\"=\" * 60)\n\n\nclass ImageDataset(torch.utils.data.Dataset):\n    def __init__(self, image_paths, transform=None):\n        self.image_paths = image_paths\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.image_paths)\n\n    def __getitem__(self, idx):\n        try:\n            image = Image.open(self.image_paths[idx]).convert(\"RGB\")\n        except Exception:\n            image = Image.new(\"RGB\", (256, 256))\n        if self.transform:\n            image = self.transform(image)\n        return image\n\n\nclass ChannelFlip:\n    \"\"\"Swap RGB channels, e.g. RGB -> BGR.\"\"\"\n    def __init__(self, order=(2, 1, 0)):\n        self.order = order\n\n    def __call__(self, tensor):\n        return tensor[list(self.order), :, :]\n\n\ndef get_dinov2_model():\n    for path in CFG.dinov2_paths:\n        if os.path.exists(path):\n            print(f\"Loading DINOv2 from: {path}\")\n            try:\n                model = AutoModel.from_pretrained(path, local_files_only=True)\n                return model.to(CFG.device).eval()\n            except Exception:\n                pass\n    print(\"Trying torch.hub...\")\n    try:\n        torch.hub.set_dir('/kaggle/working/torch_hub_cache')\n        model = torch.hub.load('facebookresearch/dinov2', 'dinov2_vitl14', pretrained=True)\n        return model.to(CFG.device).eval()\n    except Exception:\n        pass\n    try:\n        model = AutoModel.from_pretrained('facebook/dinov2-large')\n        return model.to(CFG.device).eval()\n    except Exception:\n        pass\n    raise RuntimeError(\"Could not load DINOv2 model.\")\n\n\ndef extract_features(image_paths, model):\n    print(f\"Extracting DINOv2 features for {len(image_paths)} images...\")\n    mean = [0.485, 0.456, 0.406]\n    std = [0.229, 0.224, 0.225]\n\n    transforms_list = [\n        # Original\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Horizontal flip\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.RandomHorizontalFlip(p=1.0),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Grayscale (3-channel)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.Grayscale(num_output_channels=3),\n                    T.ToTensor(), T.Normalize(mean, std)]),\n        # Channel flip (RGB -> BGR)\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(2, 1, 0)),\n                    T.Normalize(mean, std)]),\n        T.Compose([T.Resize(CFG.img_size), T.CenterCrop(CFG.img_size),\n                    T.ToTensor(), ChannelFlip(order=(1, 0, 2)),\n                    T.Normalize(mean, std)]),\n    ]\n\n    is_transformer_model = hasattr(model, 'embeddings')\n    feats_tta = []\n\n    for i, transform in enumerate(transforms_list):\n        ds = ImageDataset(image_paths, transform=transform)\n        dl = torch.utils.data.DataLoader(ds, batch_size=CFG.batch_size,\n                                         shuffle=False, num_workers=2)\n        feats_epoch = []\n        with torch.no_grad():\n            for imgs in tqdm(dl, desc=f\"TTA Round {i + 1}/{len(transforms_list)}\"):\n                imgs = imgs.to(CFG.device)\n                if is_transformer_model:\n                    outputs = model(imgs)\n                    last_hidden = outputs.last_hidden_state\n                    emb = torch.cat([\n                        last_hidden[:, 0, :],\n                        last_hidden[:, 1:, :].mean(dim=1),\n                        last_hidden[:, 1:, :].max(dim=1)[0],\n                    ], dim=1)\n                else:\n                    outputs = model.forward_features(imgs)\n                    if isinstance(outputs, dict):\n                        emb = torch.cat([\n                            outputs['x_norm_clstoken'],\n                            outputs['x_norm_patchtokens'].mean(dim=1),\n                            outputs['x_norm_patchtokens'].max(dim=1)[0],\n                        ], dim=1)\n                    else:\n                        emb = outputs\n                feats_epoch.append(emb.cpu().numpy())\n        feats_tta.append(np.concatenate(feats_epoch))\n\n    return np.mean(feats_tta, axis=0)\n\n\n# Extract\ndino_model = get_dinov2_model()\nall_features = extract_features(all_images, dino_model)\n\ndel dino_model\ntorch.cuda.empty_cache()\ngc.collect()\n\nn_train = len(train_images)\nX_train_dino = all_features[:n_train]\nX_test_dino = all_features[n_train:]\nprint(f\"Feature shapes — Train: {X_train_dino.shape}, Test: {X_test_dino.shape}\")\n\n\n# ############################################################\n# PART 4: BUILD TRAINING SET + TRAIN GBDT CLASSIFIERS\n# ############################################################\nprint(\"\\n\" + \"=\" * 60)\nprint(\"PART 4: Training GBDT Classifiers for right_place\")\nprint(\"=\" * 60)\n\ntrain_filenames = [os.path.basename(p) for p in train_images]\ntest_filenames = [os.path.basename(p) for p in test_images]\n\n# --- Train labels ---\ntrain_labels = np.array([rfdetr_data[fn]['right_place'] for fn in train_filenames])\n\n# --- Pseudo-positive samples from test ---\npseudo_pos_indices = [i for i, fn in enumerate(test_filenames)\n                      if rfdetr_data[fn]['right_place'] == 1]\n\nprint(f\"Original train: {len(train_labels)} samples ({train_labels.sum()} positive)\")\nprint(f\"Pseudo-positive from test: {len(pseudo_pos_indices)} samples\")\n\n# Combine\nif pseudo_pos_indices:\n    X_combined = np.vstack([X_train_dino, X_test_dino[pseudo_pos_indices]])\n    y_combined = np.concatenate([train_labels, np.ones(len(pseudo_pos_indices), dtype=int)])\nelse:\n    X_combined = X_train_dino\n    y_combined = train_labels\n\nprint(f\"Combined training set: {len(y_combined)} samples ({y_combined.sum()} positive)\")\n\n# Scale features\nscaler = RobustScaler()\nX_train_scaled = scaler.fit_transform(X_combined)\nX_test_scaled = scaler.transform(X_test_dino)\n\n# --- LightGBM ---\nlgbm_params = {\n    'n_estimators': 300,\n    'learning_rate': 0.05,\n    'num_leaves': 31,\n    'random_state': CFG.seed,\n    'n_jobs': -1,\n    'verbose': -1,\n    'objective': 'binary',\n    'is_unbalance': True,\n}\nif torch.cuda.is_available():\n    lgbm_params['device'] = 'gpu'\n\nlgbm_model = lgb.LGBMClassifier(**lgbm_params)\nlgbm_model.fit(X_train_scaled, y_combined)\nprint(\"LightGBM trained.\")\n\n# --- XGBoost ---\nscale_pos = (len(y_combined) - y_combined.sum()) / max(y_combined.sum(), 1)\nxgb_params = {\n    'n_estimators': 300,\n    'learning_rate': 0.05,\n    'max_depth': 6,\n    'subsample': 0.8,\n    'colsample_bytree': 0.8,\n    'objective': 'binary:logistic',\n    'scale_pos_weight': scale_pos,\n    'n_jobs': -1,\n    'random_state': CFG.seed,\n    'tree_method': 'hist',\n    'device': 'cuda' if torch.cuda.is_available() else 'cpu',\n}\n\nxgb_model = xgb.XGBClassifier(**xgb_params)\nxgb_model.fit(X_train_scaled, y_combined)\nprint(\"XGBoost trained.\")\n\n# --- Ensemble: average probabilities ---\nprob_lgbm = lgbm_model.predict_proba(X_test_scaled)[:, 1]\nprob_xgb = xgb_model.predict_proba(X_test_scaled)[:, 1]\nprob_avg = (prob_lgbm + prob_xgb) / 2.0\ntest_preds = (prob_avg >= CFG.cls_threshold).astype(int)\n\nprint(f\"Predicted right_place=1 for {test_preds.sum()} / {len(test_preds)} test images\")\n\n\n```\n\n\n\nby probing the leaderboard and submitting empty string for object detection to get 0 intentionally  in the mAP@[0.5:0.95] metric, my score in the public leaderboard was 0.5 which means my model was 100% perfect in the classfication metric in the public leaderboard, and  0.952 in the private which mostly because of some little mistakes  in the ground truth values  itself😃(although there are some very rare mistakes by the model still)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F445dbdf16768b2d56581e0d16dca83d3%2FScreenshot%202026-02-18%20015346.png?generation=1771368845667692&alt=media)\n\n\nafter having a small look in the train set, It seems that if a person's face  is not included  in the image(for example he is giving his back to camera) the class would be false even if he actually wore it the right way\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F0e593828d6f6ac85a45362d314f4acab%2FScreenshot%202026-02-18%20020042.png?generation=1771369540855203&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18473244%2F17aa7ee2301c63cefb7589e37aa88c20%2FScreenshot%202026-02-18%20020732.png?generation=1771370111003211&alt=media)\n\nbut in test  DINO classified  such cases as True even though it have not met a single positive case where the face is not appearing in the image!!!, DINO seems to have managed to figure out clearly what it means to wear the shemagh the right way 🙂, so I forcefully set  the class to be false for images where there is no face predictions to match such cases in test because probably it was  labeled the same way\n\n\n\n# what could've increased the score further:\n\n1) ensembling different models, even merging my RFDETR Base with RFDETR large could've improved the score significantly specially that my score was lacking in the object detection metric and not classification, and Detectron2 would probably do even better since we are ensembling different architechtures\n\n2)dealing better with   BGR and GRB, I already mentioned this  point above in detail\n\n3)more focus on overlapping boxes, I think that I should've spent more time in NMS logic and try to fix the overlapping boxes in train to stabilize the model more but didn't have time to analyze the errors when optimizing  it and depending on the  change in the 15% public lb risks overfitting besides that i didn't have enough subs in the last day\n\n\ntime to roll back to the  Vesuvius Challenge as am being outworked by my teammate there wish us good luck💪",
    "3407298": "Mashallah pure skills my friend :)",
    "3407431": "Mashallah incredible way of thinking"
  },
  "source": "meta"
}