{
  "id": 663073,
  "title": "15th place solution",
  "url": "/competitions/MABe-mouse-behavior-detection/writeups/15th-place-solution",
  "author_name": "",
  "post_date": "2025-12-16T07:41:41.513Z",
  "votes": 18,
  "comment_count": 3,
  "views": 0,
  "content": "<p>I built on this excellent baseline and made the following changes:</p>\n<p><a href=\"https://www.kaggle.com/code/ravaghi/social-action-recognition-in-mice-xgboost\" target=\"_blank\">https://www.kaggle.com/code/ravaghi/social-action-recognition-in-mice-xgboost</a></p>\n<p>Thank you <a href=\"https://www.kaggle.com/ravaghi\" target=\"_blank\">@ravaghi</a>.</p>\n<ul>\n<li><p><strong>Excluded non-test datasets from training</strong>: removed <strong>CalMS21</strong>, <strong>MABe22</strong>, and <strong>CRIM13</strong> since they do not appear in the test set. (This made CV correlate better with LB.)</p></li>\n<li><p><strong>Trained models per lab</strong>: trained <strong>XGBoost</strong> and <strong>CatBoost</strong> separately for each lab. (LightGBM performed worse for some reason.)</p></li>\n<li><p><strong>Removed AdaptableSnail 25 fps from training</strong>.</p></li>\n<li><p><strong>Bidirectional EMA smoothing</strong>: applied EMA in both forward and backward directions, and searched for the best smoothing timescale.</p></li>\n<li><p><strong>Ego-centric features</strong>: used <strong>body_center</strong> as the origin (for labs without body_center, I used the mean of the annotation coordinates), rotated coordinates so the motion direction aligns with the <strong>positive Y-axis</strong>, and generated ego-centric features.</p></li>\n<li><p><strong>Feature pruning</strong>: removed features whose importance was <strong>zero in both CatBoost and XGBoost</strong>.</p></li>\n<li><p><strong>Fair tie-breaking when multiple classes exceed thresholds</strong>: <code>argmax()</code> is not fair under class imbalance, so I used a Z-score–based margin:</p>\n<ul>\n<li><code>z = (prob - threshold) / sigma</code>, where <strong>sigma is the standard deviation of OOF predictions</strong>.</li>\n<li>Selected the class with the highest z-score.</li></ul></li>\n<li><p><strong>Ensembling</strong>: averaged <strong>EMA-smoothed probabilities</strong> across models and used the <strong>average threshold</strong> for decisions; when multiple classes competed, selected the one with the highest <strong>mean Z-score across models</strong>.</p></li>\n</ul>\n<h3>What didn’t help (for me)</h3>\n<ul>\n<li>Post-processing after inference: filling with <code>max_gap</code> and removing spikes via <code>min_duration</code>.</li>\n<li>Left-right flip augmentation for GBDT (<strong>CV↑ but LB↓</strong>).</li>\n<li>Handling AdaptableSnail 25 fps based on discussion ideas (scaling frames by <strong>1.2×</strong>, removing <code>rear</code>, <code>submit</code>, <code>approach</code>).</li>\n</ul>",
  "messages": [
    {
      "id": "3377383",
      "postDate": "12/16/2025 07:39:31",
      "content": "<p>I built on this excellent baseline and made the following changes:</p>\n<p><a href=\"https://www.kaggle.com/code/ravaghi/social-action-recognition-in-mice-xgboost\" target=\"_blank\">https://www.kaggle.com/code/ravaghi/social-action-recognition-in-mice-xgboost</a></p>\n<p>Thank you <a href=\"https://www.kaggle.com/ravaghi\" target=\"_blank\">@ravaghi</a>.</p>\n<ul>\n<li><p><strong>Excluded non-test datasets from training</strong>: removed <strong>CalMS21</strong>, <strong>MABe22</strong>, and <strong>CRIM13</strong> since they do not appear in the test set. (This made CV correlate better with LB.)</p></li>\n<li><p><strong>Trained models per lab</strong>: trained <strong>XGBoost</strong> and <strong>CatBoost</strong> separately for each lab. (LightGBM performed worse for some reason.)</p></li>\n<li><p><strong>Removed AdaptableSnail 25 fps from training</strong>.</p></li>\n<li><p><strong>Bidirectional EMA smoothing</strong>: applied EMA in both forward and backward directions, and searched for the best smoothing timescale.</p></li>\n<li><p><strong>Ego-centric features</strong>: used <strong>body_center</strong> as the origin (for labs without body_center, I used the mean of the annotation coordinates), rotated coordinates so the motion direction aligns with the <strong>positive Y-axis</strong>, and generated ego-centric features.</p></li>\n<li><p><strong>Feature pruning</strong>: removed features whose importance was <strong>zero in both CatBoost and XGBoost</strong>.</p></li>\n<li><p><strong>Fair tie-breaking when multiple classes exceed thresholds</strong>: <code>argmax()</code> is not fair under class imbalance, so I used a Z-score–based margin:</p>\n<ul>\n<li><code>z = (prob - threshold) / sigma</code>, where <strong>sigma is the standard deviation of OOF predictions</strong>.</li>\n<li>Selected the class with the highest z-score.</li></ul></li>\n<li><p><strong>Ensembling</strong>: averaged <strong>EMA-smoothed probabilities</strong> across models and used the <strong>average threshold</strong> for decisions; when multiple classes competed, selected the one with the highest <strong>mean Z-score across models</strong>.</p></li>\n</ul>\n<h3>What didn’t help (for me)</h3>\n<ul>\n<li>Post-processing after inference: filling with <code>max_gap</code> and removing spikes via <code>min_duration</code>.</li>\n<li>Left-right flip augmentation for GBDT (<strong>CV↑ but LB↓</strong>).</li>\n<li>Handling AdaptableSnail 25 fps based on discussion ideas (scaling frames by <strong>1.2×</strong>, removing <code>rear</code>, <code>submit</code>, <code>approach</code>).</li>\n</ul>",
      "rawMarkdown": "I built on this excellent baseline and made the following changes:\n\nhttps://www.kaggle.com/code/ravaghi/social-action-recognition-in-mice-xgboost\n\nThank you @ravaghi.\n\n\n* **Excluded non-test datasets from training**: removed **CalMS21**, **MABe22**, and **CRIM13** since they do not appear in the test set. (This made CV correlate better with LB.)\n* **Trained models per lab**: trained **XGBoost** and **CatBoost** separately for each lab. (LightGBM performed worse for some reason.)\n* **Removed AdaptableSnail 25 fps from training**.\n* **Bidirectional EMA smoothing**: applied EMA in both forward and backward directions, and searched for the best smoothing timescale.\n* **Ego-centric features**: used **body_center** as the origin (for labs without body_center, I used the mean of the annotation coordinates), rotated coordinates so the motion direction aligns with the **positive Y-axis**, and generated ego-centric features.\n* **Feature pruning**: removed features whose importance was **zero in both CatBoost and XGBoost**.\n* **Fair tie-breaking when multiple classes exceed thresholds**: `argmax()` is not fair under class imbalance, so I used a Z-score–based margin:\n\n  * `z = (prob - threshold) / sigma`, where **sigma is the standard deviation of OOF predictions**.\n  * Selected the class with the highest z-score.\n* **Ensembling**: averaged **EMA-smoothed probabilities** across models and used the **average threshold** for decisions; when multiple classes competed, selected the one with the highest **mean Z-score across models**.\n\n### What didn’t help (for me)\n\n* Post-processing after inference: filling with `max_gap` and removing spikes via `min_duration`.\n* Left-right flip augmentation for GBDT (**CV↑ but LB↓**).\n* Handling AdaptableSnail 25 fps based on discussion ideas (scaling frames by **1.2×**, removing `rear`, `submit`, `approach`).",
      "votes": null
    },
    {
      "id": "3377409",
      "postDate": "12/16/2025 09:11:16",
      "content": "<p>\"Congrats <a href=\"https://www.kaggle.com/shu01\" target=\"_blank\">@shu01</a>! I'm really curious, how much of a boost did you get from the ego-centric features?</p>\n<p>Also, I managed to improve my score by nearly 0.02 using max_gap and min_duration for post-processing, so I'm wondering if you could share the details of how you handled this part?\"</p>",
      "rawMarkdown": "\"Congrats @shu01! I'm really curious, how much of a boost did you get from the ego-centric features?\n\nAlso, I managed to improve my score by nearly 0.02 using max_gap and min_duration for post-processing, so I'm wondering if you could share the details of how you handled this part?\"",
      "votes": null
    },
    {
      "id": "3377423",
      "postDate": "12/16/2025 09:38:55",
      "content": "<p>The egocentric features gave about a +0.005 gain on both CV and LB. (I double-checked they were implemented correctly using egocentric coordinates.)</p>\n<p>As for post-processing: after converting frame-wise predictions into segments, I used OOF predictions to grid-search action-specific values of max_gap and min_duration (in seconds). However, applying this post-processing did not change the LB score at all. I suspect it was largely overfitting to CV. I originally introduced it because I noticed the sample submission had more rows than I expected given the video durations, but since I was already applying EMA smoothing, this extra step might have been unnecessary (or even too aggressive).</p>",
      "rawMarkdown": "The egocentric features gave about a +0.005 gain on both CV and LB. (I double-checked they were implemented correctly using egocentric coordinates.)\n\nAs for post-processing: after converting frame-wise predictions into segments, I used OOF predictions to grid-search action-specific values of max_gap and min_duration (in seconds). However, applying this post-processing did not change the LB score at all. I suspect it was largely overfitting to CV. I originally introduced it because I noticed the sample submission had more rows than I expected given the video durations, but since I was already applying EMA smoothing, this extra step might have been unnecessary (or even too aggressive).",
      "votes": null
    },
    {
      "id": "3377436",
      "postDate": "12/16/2025 10:09:16",
      "content": "<p>\"Thanks for the answer. The relative coordinate system is indeed an impressive feature extraction method.</p>\n<p>Perhaps the key difference lies in how these parameters were derived: my min_gap and min_duration were based on statistics from the ground truth labels of the entire training set, and I used frames as the unit.\"</p>",
      "rawMarkdown": "\"Thanks for the answer. The relative coordinate system is indeed an impressive feature extraction method.\n\nPerhaps the key difference lies in how these parameters were derived: my min_gap and min_duration were based on statistics from the ground truth labels of the entire training set, and I used frames as the unit.\"",
      "votes": null
    }
  ],
  "comments": [
    {
      "id": 3377409,
      "author_name": "ybzjdbc",
      "author_url": "",
      "post_date": "12/16/2025 09:11:16",
      "content": "<p>\"Congrats <a href=\"https://www.kaggle.com/shu01\" target=\"_blank\">@shu01</a>! I'm really curious, how much of a boost did you get from the ego-centric features?</p>\n<p>Also, I managed to improve my score by nearly 0.02 using max_gap and min_duration for post-processing, so I'm wondering if you could share the details of how you handled this part?\"</p>",
      "votes": null,
      "replies": [
        {
          "id": 3377423,
          "author_name": "shu01jp",
          "author_url": "",
          "post_date": "12/16/2025 09:38:55",
          "content": "<p>The egocentric features gave about a +0.005 gain on both CV and LB. (I double-checked they were implemented correctly using egocentric coordinates.)</p>\n<p>As for post-processing: after converting frame-wise predictions into segments, I used OOF predictions to grid-search action-specific values of max_gap and min_duration (in seconds). However, applying this post-processing did not change the LB score at all. I suspect it was largely overfitting to CV. I originally introduced it because I noticed the sample submission had more rows than I expected given the video durations, but since I was already applying EMA smoothing, this extra step might have been unnecessary (or even too aggressive).</p>",
          "votes": null,
          "replies": [
            {
              "id": 3377436,
              "author_name": "ybzjdbc",
              "author_url": "",
              "post_date": "12/16/2025 10:09:16",
              "content": "<p>\"Thanks for the answer. The relative coordinate system is indeed an impressive feature extraction method.</p>\n<p>Perhaps the key difference lies in how these parameters were derived: my min_gap and min_duration were based on statistics from the ground truth labels of the entire training set, and I used frames as the unit.\"</p>",
              "votes": null,
              "replies": []
            }
          ]
        }
      ]
    }
  ],
  "raw_markdown_by_id": {
    "3377383": "I built on this excellent baseline and made the following changes:\n\nhttps://www.kaggle.com/code/ravaghi/social-action-recognition-in-mice-xgboost\n\nThank you @ravaghi.\n\n\n* **Excluded non-test datasets from training**: removed **CalMS21**, **MABe22**, and **CRIM13** since they do not appear in the test set. (This made CV correlate better with LB.)\n* **Trained models per lab**: trained **XGBoost** and **CatBoost** separately for each lab. (LightGBM performed worse for some reason.)\n* **Removed AdaptableSnail 25 fps from training**.\n* **Bidirectional EMA smoothing**: applied EMA in both forward and backward directions, and searched for the best smoothing timescale.\n* **Ego-centric features**: used **body_center** as the origin (for labs without body_center, I used the mean of the annotation coordinates), rotated coordinates so the motion direction aligns with the **positive Y-axis**, and generated ego-centric features.\n* **Feature pruning**: removed features whose importance was **zero in both CatBoost and XGBoost**.\n* **Fair tie-breaking when multiple classes exceed thresholds**: `argmax()` is not fair under class imbalance, so I used a Z-score–based margin:\n\n  * `z = (prob - threshold) / sigma`, where **sigma is the standard deviation of OOF predictions**.\n  * Selected the class with the highest z-score.\n* **Ensembling**: averaged **EMA-smoothed probabilities** across models and used the **average threshold** for decisions; when multiple classes competed, selected the one with the highest **mean Z-score across models**.\n\n### What didn’t help (for me)\n\n* Post-processing after inference: filling with `max_gap` and removing spikes via `min_duration`.\n* Left-right flip augmentation for GBDT (**CV↑ but LB↓**).\n* Handling AdaptableSnail 25 fps based on discussion ideas (scaling frames by **1.2×**, removing `rear`, `submit`, `approach`).",
    "3377409": "\"Congrats @shu01! I'm really curious, how much of a boost did you get from the ego-centric features?\n\nAlso, I managed to improve my score by nearly 0.02 using max_gap and min_duration for post-processing, so I'm wondering if you could share the details of how you handled this part?\"",
    "3377423": "The egocentric features gave about a +0.005 gain on both CV and LB. (I double-checked they were implemented correctly using egocentric coordinates.)\n\nAs for post-processing: after converting frame-wise predictions into segments, I used OOF predictions to grid-search action-specific values of max_gap and min_duration (in seconds). However, applying this post-processing did not change the LB score at all. I suspect it was largely overfitting to CV. I originally introduced it because I noticed the sample submission had more rows than I expected given the video durations, but since I was already applying EMA smoothing, this extra step might have been unnecessary (or even too aggressive).",
    "3377436": "\"Thanks for the answer. The relative coordinate system is indeed an impressive feature extraction method.\n\nPerhaps the key difference lies in how these parameters were derived: my min_gap and min_duration were based on statistics from the ground truth labels of the entire training set, and I used frames as the unit.\""
  },
  "source": "meta"
}