{
  "id": 663026,
  "title": "3rd place solution",
  "url": "/competitions/MABe-mouse-behavior-detection/writeups/3rd-place-solution",
  "author_name": "",
  "post_date": "2025-12-16T00:02:14.027Z",
  "votes": 52,
  "comment_count": 10,
  "views": 0,
  "content": "<p>Here is a link to my final script:</p>\n<p><a href=\"https://www.kaggle.com/code/ymatioun/mabe-blend-ym\" target=\"_blank\">https://www.kaggle.com/code/ymatioun/mabe-blend-ym</a></p>\n<p>It works for both training and submission, based on settings in “nn_mode” and “CFG.mode”. But the training was done offline, with pre-trained models used for inference – for runtime reduction and simplicity.</p>\n<p>My solution is a blend of neural network and XGBoost. Several versions of NN and XGB were trained with slightly different settings and blended together.</p>\n<p>Neural network is LSTM with several dense layers. Output is “softmax” for all allowed actions. Not allowed actions are masked out. “Laboratory code” runs through categorical embedding layer and is added to the inputs.</p>\n<p>Most effort went into feature engineering. I ended up with around 60 features – mostly distances, angles, speeds, etc. Some metadata features.</p>\n<p>Input data is combined for all labs, selecting 5 body parts present in most of the data. This way the model is able to learn data patterns in all the labs.</p>\n<p>Training was done with small batches (batch size of16 or 32 ), using “CosineDecayRestarts” schedule.</p>\n<p>XGBoost model is based on the best public notebook, with many changes. Here are some of them:\n    • added new features\n    • removed features with low importance\n    • early stopping based on F1 metric\n    • smooth predicted probabilities before using them\n    • optimize model hyper-parameters</p>\n<p>Post processing: blended predicted probabilities turned into binary prediction when probability exceed a threshold. Threshold varies by lab and action; it was optimized by grid search on out-of-fold predictions. When multiple actions are predicted for the same frame, the one with the highest ratio of probability to threshold is kept.</p>\n<p>Exploiting data properties: I looked into it, but could not get much benefit from it. But there are some potentially exploitable data properties out there:\n    • for some lab “AdaptableSnail” videos, actions have been mislabeled, and organizers did not fix this issue – this was discussed in <a href=\"https://www.kaggle.com/competitions/MABe-mouse-behavior-detection/discussion/612531\" target=\"_blank\">https://www.kaggle.com/competitions/MABe-mouse-behavior-detection/discussion/612531</a> \n    • for lab “BoisterousParrot”, most actions have length as a multiple of 15 frames\n    • for lab “CautiousGiraffe”, many actions have length of 9 frames, with mouse 1 and mouse 2 alternating attacks\nI suspect that these (and possibly other) data properties could lead to “magic” solutions exploiting them, as happened in the past in many other competitions – will see if that happened here (I’m writing this before looking at other solution write-ups, so don’t have the answer to this question yet).</p>",
  "messages": [
    {
      "id": "3377220",
      "postDate": "12/16/2025 00:02:07",
      "content": "<p>Here is a link to my final script:</p>\n<p><a href=\"https://www.kaggle.com/code/ymatioun/mabe-blend-ym\" target=\"_blank\">https://www.kaggle.com/code/ymatioun/mabe-blend-ym</a></p>\n<p>It works for both training and submission, based on settings in “nn_mode” and “CFG.mode”. But the training was done offline, with pre-trained models used for inference – for runtime reduction and simplicity.</p>\n<p>My solution is a blend of neural network and XGBoost. Several versions of NN and XGB were trained with slightly different settings and blended together.</p>\n<p>Neural network is LSTM with several dense layers. Output is “softmax” for all allowed actions. Not allowed actions are masked out. “Laboratory code” runs through categorical embedding layer and is added to the inputs.</p>\n<p>Most effort went into feature engineering. I ended up with around 60 features – mostly distances, angles, speeds, etc. Some metadata features.</p>\n<p>Input data is combined for all labs, selecting 5 body parts present in most of the data. This way the model is able to learn data patterns in all the labs.</p>\n<p>Training was done with small batches (batch size of16 or 32 ), using “CosineDecayRestarts” schedule.</p>\n<p>XGBoost model is based on the best public notebook, with many changes. Here are some of them:\n    • added new features\n    • removed features with low importance\n    • early stopping based on F1 metric\n    • smooth predicted probabilities before using them\n    • optimize model hyper-parameters</p>\n<p>Post processing: blended predicted probabilities turned into binary prediction when probability exceed a threshold. Threshold varies by lab and action; it was optimized by grid search on out-of-fold predictions. When multiple actions are predicted for the same frame, the one with the highest ratio of probability to threshold is kept.</p>\n<p>Exploiting data properties: I looked into it, but could not get much benefit from it. But there are some potentially exploitable data properties out there:\n    • for some lab “AdaptableSnail” videos, actions have been mislabeled, and organizers did not fix this issue – this was discussed in <a href=\"https://www.kaggle.com/competitions/MABe-mouse-behavior-detection/discussion/612531\" target=\"_blank\">https://www.kaggle.com/competitions/MABe-mouse-behavior-detection/discussion/612531</a> \n    • for lab “BoisterousParrot”, most actions have length as a multiple of 15 frames\n    • for lab “CautiousGiraffe”, many actions have length of 9 frames, with mouse 1 and mouse 2 alternating attacks\nI suspect that these (and possibly other) data properties could lead to “magic” solutions exploiting them, as happened in the past in many other competitions – will see if that happened here (I’m writing this before looking at other solution write-ups, so don’t have the answer to this question yet).</p>",
      "rawMarkdown": "Here is a link to my final script:\n\nhttps://www.kaggle.com/code/ymatioun/mabe-blend-ym\n\nIt works for both training and submission, based on settings in “nn_mode” and “CFG.mode”. But the training was done offline, with pre-trained models used for inference – for runtime reduction and simplicity.\n\n\nMy solution is a blend of neural network and XGBoost. Several versions of NN and XGB were trained with slightly different settings and blended together.\n\n\nNeural network is LSTM with several dense layers. Output is “softmax” for all allowed actions. Not allowed actions are masked out. “Laboratory code” runs through categorical embedding layer and is added to the inputs.\n\nMost effort went into feature engineering. I ended up with around 60 features – mostly distances, angles, speeds, etc. Some metadata features.\n\nInput data is combined for all labs, selecting 5 body parts present in most of the data. This way the model is able to learn data patterns in all the labs.\n\nTraining was done with small batches (batch size of16 or 32 ), using “CosineDecayRestarts” schedule.\n\n\nXGBoost model is based on the best public notebook, with many changes. Here are some of them:\n    • added new features\n    • removed features with low importance\n    • early stopping based on F1 metric\n    • smooth predicted probabilities before using them\n    • optimize model hyper-parameters\n\n\nPost processing: blended predicted probabilities turned into binary prediction when probability exceed a threshold. Threshold varies by lab and action; it was optimized by grid search on out-of-fold predictions. When multiple actions are predicted for the same frame, the one with the highest ratio of probability to threshold is kept.\n\n\nExploiting data properties: I looked into it, but could not get much benefit from it. But there are some potentially exploitable data properties out there:\n    • for some lab “AdaptableSnail” videos, actions have been mislabeled, and organizers did not fix this issue – this was discussed in https://www.kaggle.com/competitions/MABe-mouse-behavior-detection/discussion/612531 \n    • for lab “BoisterousParrot”, most actions have length as a multiple of 15 frames\n    • for lab “CautiousGiraffe”, many actions have length of 9 frames, with mouse 1 and mouse 2 alternating attacks\nI suspect that these (and possibly other) data properties could lead to “magic” solutions exploiting them, as happened in the past in many other competitions – will see if that happened here (I’m writing this before looking at other solution write-ups, so don’t have the answer to this question yet).",
      "votes": null
    },
    {
      "id": "3377234",
      "postDate": "12/16/2025 00:16:03",
      "content": "<p>A work worth learning</p>",
      "rawMarkdown": "A work worth learning",
      "votes": null
    },
    {
      "id": "3377238",
      "postDate": "12/16/2025 00:22:38",
      "content": "<p>Nice work! </p>",
      "rawMarkdown": "Nice work!",
      "votes": null
    },
    {
      "id": "3377299",
      "postDate": "12/16/2025 03:34:37",
      "content": "<p>Much appreciated!</p>",
      "rawMarkdown": "Much appreciated!",
      "votes": null
    },
    {
      "id": "3377320",
      "postDate": "12/16/2025 04:44:19",
      "content": "<p>Congratulations dude, It's really appreciative. Keep going👍</p>",
      "rawMarkdown": "Congratulations dude, It's really appreciative. Keep going👍",
      "votes": null
    },
    {
      "id": "3377346",
      "postDate": "12/16/2025 06:40:06",
      "content": "<p>Congrats regarding the 3rd place! 🥉</p>\n<p>In an era dominated by Transformers, seeing an LSTM based solution perform this well is refreshing and impressive. It proves that with strong feature engineering (your 60+ handcrafted features), classic recurrent architectures are still SOTA for this type of behavioral time-series data.</p>\n<p>Also, the threshold optimization logic is very smart. Well done!</p>",
      "rawMarkdown": "Congrats regarding the 3rd place! 🥉\n\nIn an era dominated by Transformers, seeing an LSTM based solution perform this well is refreshing and impressive. It proves that with strong feature engineering (your 60+ handcrafted features), classic recurrent architectures are still SOTA for this type of behavioral time-series data.\n\nAlso, the threshold optimization logic is very smart. Well done!",
      "votes": null
    },
    {
      "id": "3377531",
      "postDate": "12/16/2025 14:15:58",
      "content": "<p>Congratulations <a href=\"https://www.kaggle.com/ymatioun\" target=\"_blank\">@ymatioun</a> winning cash gold solo 3rd place! I'm exciting to see you finished in the top3 because I saw that you worked hard from the beginning of the comp and were among the top in public LB for most of the comp. Thanks for sharing your writeup and code.</p>\n<blockquote>\n  <p>When multiple actions are predicted for the same frame, the one with the highest ratio of probability to threshold is kept.</p>\n</blockquote>\n<p>That is smart post process. I also noticed that using simple argmax did not always pick the best action when multiple actions passed threshold.</p>",
      "rawMarkdown": "Congratulations @ymatioun winning cash gold solo 3rd place! I'm exciting to see you finished in the top3 because I saw that you worked hard from the beginning of the comp and were among the top in public LB for most of the comp. Thanks for sharing your writeup and code.\n\n> When multiple actions are predicted for the same frame, the one with the highest ratio of probability to threshold is kept.\n\nThat is smart post process. I also noticed that using simple argmax did not always pick the best action when multiple actions passed threshold.",
      "votes": null
    },
    {
      "id": "3377772",
      "postDate": "12/16/2025 23:27:28",
      "content": "<p>Youri, can you share the datasets attached to your solution notebook <a href=\"https://www.kaggle.com/code/ymatioun/mabe-blend-ym\" target=\"_blank\">here</a>? I would like to run some experiments. Thanks for sharing your code.</p>",
      "rawMarkdown": "Youri, can you share the datasets attached to your solution notebook [here][1]? I would like to run some experiments. Thanks for sharing your code.\n\n[1]: https://www.kaggle.com/code/ymatioun/mabe-blend-ym",
      "votes": null
    },
    {
      "id": "3377784",
      "postDate": "12/17/2025 00:00:25",
      "content": "<p>Chris: thanks!</p>\n<p>I made the datasets public.</p>\n<p>By the way, i saw a potentially better approach in 15th place solution write-up: \"Fair tie-breaking when multiple classes exceed thresholds: argmax() is not fair under class imbalance, so I used a Z-score–based margin:\n    z = (prob - threshold) / sigma, where sigma is the standard deviation of OOF predictions.\n    Selected the class with the highest z-score.\"</p>\n<p>I tried many alternatives for this logic, but did not think of doing this.</p>",
      "rawMarkdown": "Chris: thanks!\n\nI made the datasets public.\n\nBy the way, i saw a potentially better approach in 15th place solution write-up: \"Fair tie-breaking when multiple classes exceed thresholds: argmax() is not fair under class imbalance, so I used a Z-score–based margin:\n    z = (prob - threshold) / sigma, where sigma is the standard deviation of OOF predictions.\n    Selected the class with the highest z-score.\"\n\nI tried many alternatives for this logic, but did not think of doing this.",
      "votes": null
    },
    {
      "id": "3378101",
      "postDate": "12/17/2025 14:44:36",
      "content": "<p>explain the F1 metric in sample word's </p>",
      "rawMarkdown": "explain the F1 metric in sample word's",
      "votes": null
    },
    {
      "id": "3379555",
      "postDate": "12/20/2025 05:16:25",
      "content": "<p>Congratulations. Great work.</p>",
      "rawMarkdown": "Congratulations. Great work.",
      "votes": null
    }
  ],
  "comments": [
    {
      "id": 3377234,
      "author_name": "aizealotwu",
      "author_url": "",
      "post_date": "12/16/2025 00:16:03",
      "content": "<p>A work worth learning</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3377238,
      "author_name": "evanarlenhandy",
      "author_url": "",
      "post_date": "12/16/2025 00:22:38",
      "content": "<p>Nice work! </p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3377299,
      "author_name": "masterbao",
      "author_url": "",
      "post_date": "12/16/2025 03:34:37",
      "content": "<p>Much appreciated!</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3377320,
      "author_name": "mani787060",
      "author_url": "",
      "post_date": "12/16/2025 04:44:19",
      "content": "<p>Congratulations dude, It's really appreciative. Keep going👍</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3377346,
      "author_name": "musapeker",
      "author_url": "",
      "post_date": "12/16/2025 06:40:06",
      "content": "<p>Congrats regarding the 3rd place! 🥉</p>\n<p>In an era dominated by Transformers, seeing an LSTM based solution perform this well is refreshing and impressive. It proves that with strong feature engineering (your 60+ handcrafted features), classic recurrent architectures are still SOTA for this type of behavioral time-series data.</p>\n<p>Also, the threshold optimization logic is very smart. Well done!</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3377531,
      "author_name": "cdeotte",
      "author_url": "",
      "post_date": "12/16/2025 14:15:58",
      "content": "<p>Congratulations <a href=\"https://www.kaggle.com/ymatioun\" target=\"_blank\">@ymatioun</a> winning cash gold solo 3rd place! I'm exciting to see you finished in the top3 because I saw that you worked hard from the beginning of the comp and were among the top in public LB for most of the comp. Thanks for sharing your writeup and code.</p>\n<blockquote>\n  <p>When multiple actions are predicted for the same frame, the one with the highest ratio of probability to threshold is kept.</p>\n</blockquote>\n<p>That is smart post process. I also noticed that using simple argmax did not always pick the best action when multiple actions passed threshold.</p>",
      "votes": null,
      "replies": [
        {
          "id": 3377772,
          "author_name": "cdeotte",
          "author_url": "",
          "post_date": "12/16/2025 23:27:28",
          "content": "<p>Youri, can you share the datasets attached to your solution notebook <a href=\"https://www.kaggle.com/code/ymatioun/mabe-blend-ym\" target=\"_blank\">here</a>? I would like to run some experiments. Thanks for sharing your code.</p>",
          "votes": null,
          "replies": [
            {
              "id": 3377784,
              "author_name": "ymatioun",
              "author_url": "",
              "post_date": "12/17/2025 00:00:25",
              "content": "<p>Chris: thanks!</p>\n<p>I made the datasets public.</p>\n<p>By the way, i saw a potentially better approach in 15th place solution write-up: \"Fair tie-breaking when multiple classes exceed thresholds: argmax() is not fair under class imbalance, so I used a Z-score–based margin:\n    z = (prob - threshold) / sigma, where sigma is the standard deviation of OOF predictions.\n    Selected the class with the highest z-score.\"</p>\n<p>I tried many alternatives for this logic, but did not think of doing this.</p>",
              "votes": null,
              "replies": []
            }
          ]
        }
      ]
    },
    {
      "id": 3378101,
      "author_name": "ajabkhan21",
      "author_url": "",
      "post_date": "12/17/2025 14:44:36",
      "content": "<p>explain the F1 metric in sample word's </p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3379555,
      "author_name": "merinsujanaandrews",
      "author_url": "",
      "post_date": "12/20/2025 05:16:25",
      "content": "<p>Congratulations. Great work.</p>",
      "votes": null,
      "replies": []
    }
  ],
  "raw_markdown_by_id": {
    "3377220": "Here is a link to my final script:\n\nhttps://www.kaggle.com/code/ymatioun/mabe-blend-ym\n\nIt works for both training and submission, based on settings in “nn_mode” and “CFG.mode”. But the training was done offline, with pre-trained models used for inference – for runtime reduction and simplicity.\n\n\nMy solution is a blend of neural network and XGBoost. Several versions of NN and XGB were trained with slightly different settings and blended together.\n\n\nNeural network is LSTM with several dense layers. Output is “softmax” for all allowed actions. Not allowed actions are masked out. “Laboratory code” runs through categorical embedding layer and is added to the inputs.\n\nMost effort went into feature engineering. I ended up with around 60 features – mostly distances, angles, speeds, etc. Some metadata features.\n\nInput data is combined for all labs, selecting 5 body parts present in most of the data. This way the model is able to learn data patterns in all the labs.\n\nTraining was done with small batches (batch size of16 or 32 ), using “CosineDecayRestarts” schedule.\n\n\nXGBoost model is based on the best public notebook, with many changes. Here are some of them:\n    • added new features\n    • removed features with low importance\n    • early stopping based on F1 metric\n    • smooth predicted probabilities before using them\n    • optimize model hyper-parameters\n\n\nPost processing: blended predicted probabilities turned into binary prediction when probability exceed a threshold. Threshold varies by lab and action; it was optimized by grid search on out-of-fold predictions. When multiple actions are predicted for the same frame, the one with the highest ratio of probability to threshold is kept.\n\n\nExploiting data properties: I looked into it, but could not get much benefit from it. But there are some potentially exploitable data properties out there:\n    • for some lab “AdaptableSnail” videos, actions have been mislabeled, and organizers did not fix this issue – this was discussed in https://www.kaggle.com/competitions/MABe-mouse-behavior-detection/discussion/612531 \n    • for lab “BoisterousParrot”, most actions have length as a multiple of 15 frames\n    • for lab “CautiousGiraffe”, many actions have length of 9 frames, with mouse 1 and mouse 2 alternating attacks\nI suspect that these (and possibly other) data properties could lead to “magic” solutions exploiting them, as happened in the past in many other competitions – will see if that happened here (I’m writing this before looking at other solution write-ups, so don’t have the answer to this question yet).",
    "3377234": "A work worth learning",
    "3377238": "Nice work!",
    "3377299": "Much appreciated!",
    "3377320": "Congratulations dude, It's really appreciative. Keep going👍",
    "3377346": "Congrats regarding the 3rd place! 🥉\n\nIn an era dominated by Transformers, seeing an LSTM based solution perform this well is refreshing and impressive. It proves that with strong feature engineering (your 60+ handcrafted features), classic recurrent architectures are still SOTA for this type of behavioral time-series data.\n\nAlso, the threshold optimization logic is very smart. Well done!",
    "3377531": "Congratulations @ymatioun winning cash gold solo 3rd place! I'm exciting to see you finished in the top3 because I saw that you worked hard from the beginning of the comp and were among the top in public LB for most of the comp. Thanks for sharing your writeup and code.\n\n> When multiple actions are predicted for the same frame, the one with the highest ratio of probability to threshold is kept.\n\nThat is smart post process. I also noticed that using simple argmax did not always pick the best action when multiple actions passed threshold.",
    "3377772": "Youri, can you share the datasets attached to your solution notebook [here][1]? I would like to run some experiments. Thanks for sharing your code.\n\n[1]: https://www.kaggle.com/code/ymatioun/mabe-blend-ym",
    "3377784": "Chris: thanks!\n\nI made the datasets public.\n\nBy the way, i saw a potentially better approach in 15th place solution write-up: \"Fair tie-breaking when multiple classes exceed thresholds: argmax() is not fair under class imbalance, so I used a Z-score–based margin:\n    z = (prob - threshold) / sigma, where sigma is the standard deviation of OOF predictions.\n    Selected the class with the highest z-score.\"\n\nI tried many alternatives for this logic, but did not think of doing this.",
    "3378101": "explain the F1 metric in sample word's",
    "3379555": "Congratulations. Great work."
  },
  "source": "meta"
}