{
  "id": 462228,
  "title": "86th Place Solution for the Child Mind Institute - Detect Sleep States Competition",
  "url": "/competitions/child-mind-institute-detect-sleep-states/writeups/michal-86th-place-solution-for-the-child-mind-inst",
  "author_name": "",
  "post_date": "2023-12-18T23:00:48.042153100Z",
  "votes": 4,
  "comment_count": 1,
  "views": 0,
  "content": "<p>First, big thanks to the organizers for preparing such an interesting competition. I really enjoyed working on it and learned a lot. I was quite surprised how well the NN models worked in this competition and how easily they surpassed the handcrafted features and decision tree models.</p>\n<p>Also big thanks to all the people who shared their knowledge in discussions and code. Especially to:</p>\n<ul>\n<li><a href=\"https://www.kaggle.com/tubotubo\" target=\"_blank\">213tubo</a> for <a href=\"https://www.kaggle.com/code/tubotubo/cmi-submit\" target=\"_blank\">CMI-submit</a></li>\n<li><a href=\"https://www.kaggle.com/danielphalen\" target=\"_blank\">Daniel Phalen</a> for <a href=\"https://www.kaggle.com/code/danielphalen/cmss-grunet-infer\" target=\"_blank\">CMSS-GRUNET</a></li>\n<li><a href=\"https://www.kaggle.com/werus23\" target=\"_blank\">werus23</a> for <a href=\"https://www.kaggle.com/code/werus23/sleep-critical-point-infer\" target=\"_blank\">Sleep - Critical point</a></li>\n<li><a href=\"https://www.kaggle.com/lccburk\" target=\"_blank\">Lucas Burke</a> for <a href=\"https://www.kaggle.com/code/lccburk/feature-engineering-and-random-forest-prediction\" target=\"_blank\">Feature Engineering and Random Forest Prediction</a></li>\n</ul>\n<p>Even though my best solution was based on code published by <a href=\"https://www.kaggle.com/tubotubo\" target=\"_blank\">213tubo</a> in <a href=\"https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940\" target=\"_blank\">Detecting sleep using UNet(2D) post</a>, I decided to publish the write up to show some simple ways how to improve on it and that it's possible to get good results using only limited resources. All training was done on Google Colab using T4 GPU instance.</p>\n<h1>Context</h1>\n<p>Business Context - <a href=\"https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/overview\" target=\"_blank\">https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/overview</a><br>\nData Context - <a href=\"https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/data\" target=\"_blank\">https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/data</a></p>\n<h1>Overview of the Approach</h1>\n<p>My best scoring pipeline is based on code published by <a href=\"https://www.kaggle.com/tubotubo\" target=\"_blank\">213tubo</a> in <a href=\"https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940\" target=\"_blank\">Detecting sleep using UNet(2D) post</a>.</p>\n<h2>Model</h2>\n<p>As described by in the original post the model consist of feature extractor, encoder and decoder.</p>\n<ul>\n<li><strong>Feature extractor</strong> takes raw input and transforms it into wavegram of image like shape. 3 Conv1d layers with different kernel sizes were used as feature extractors.</li>\n<li><strong>Encoder</strong> takes the wavegram and creates a two dimensional heatmap. Unet and FPN models with resnet34 as backbone from <a href=\"https://github.com/qubvel/segmentation_models.pytorch\" target=\"_blank\">PyTorch Segmentation models library</a> were used as encoders.</li>\n<li><strong>Decoder</strong> creates probabilities of sleep, onset and wakeup from the heatmap generated by encoder. 1d Unet and TransformerEncoder were used as decoders. Only onset and wakeup predictions are used during inference.</li>\n</ul>\n<p>Model input: 8 hour window (5760 samples) with 4 features: normalized anglez, normalized enmo, hour sin and hour cos</p>\n<p>Used around 20% of data for validation and trained on remaining 80%.</p>\n<h2>Additional improvements that boosted the score:</h2>\n<p>1) Train more models<br>\n2) Ensemble using average output of multiple models<br>\n3) Use timeframes with overlap during inference, average out predictions from overlapping segments<br>\n4) Tune threshold and peak gap - lower threshold with 5 minute gap between events produced the best results</p>\n<h1>Details of the submission</h1>\n<p>Model published in <a href=\"https://www.kaggle.com/code/tubotubo/cmi-submit\" target=\"_blank\">CMI-submit</a></p>\n<table>\n<thead>\n<tr>\n<th>Base model</th>\n<th>CV Score</th>\n<th>Public Score</th>\n<th>Private Score</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Base model</td>\n<td>0.7305</td>\n<td>0.707</td>\n<td>0.728</td>\n</tr>\n</tbody>\n</table>\n<h2>Experiment with different models, architectures, settings</h2>\n<p>I trained around 30 models with different encoders, feature extractors, downsampling rate, losses, architectures and sizes. Most of the models were trained with downsample rate 4 which sped up the training and did not decrease performance. From those models I selected best 6 for the final ensemble based on their score.</p>\n<table>\n<thead>\n<tr>\n<th>Feature extractor base filters</th>\n<th>Encoder architecture</th>\n<th>Decoder</th>\n<th>downsample rate</th>\n<th>CV Score</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>256</td>\n<td>Unet</td>\n<td>UNet1DDecoder</td>\n<td>4</td>\n<td>0.7545</td>\n</tr>\n<tr>\n<td>64</td>\n<td>Unet</td>\n<td>TransformerCNNDecoder</td>\n<td>4</td>\n<td>0.7526</td>\n</tr>\n<tr>\n<td>128</td>\n<td>Unet</td>\n<td>UNet1DDecoder</td>\n<td>4</td>\n<td>0.7525</td>\n</tr>\n<tr>\n<td>128</td>\n<td>Unet</td>\n<td>UNet1DDecoder</td>\n<td>2</td>\n<td>0.7519</td>\n</tr>\n<tr>\n<td>128</td>\n<td>Unet</td>\n<td>UNet1DDecoder</td>\n<td>3</td>\n<td>0.7510</td>\n</tr>\n<tr>\n<td>128</td>\n<td>FPN</td>\n<td>UNet1DDecoder</td>\n<td>4</td>\n<td>0.7486</td>\n</tr>\n</tbody>\n</table>\n<h2>Ensemble</h2>\n<p>Average output of models was used as prediction. I tested mean, max, min and some simple weighted average based on CV score but simple average gave best results.</p>\n<table>\n<thead>\n<tr>\n<th>Ensemble</th>\n<th>Ensemble method</th>\n<th>CV Score</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>6 model ensemble</td>\n<td>mean</td>\n<td>0.7717</td>\n</tr>\n</tbody>\n</table>\n<h2>Timeframe with overlaps during inference</h2>\n<p>Inference was made on 8 hour windows with 4 hour step, overlapping segments were avereged out. Bigger overlap gave better results but unfortunatelly finished few minutes after competition ended. Taking average from overlapping segments was better than taking minimum prediction, using maximum was giving the worst results.</p>\n<table>\n<thead>\n<tr>\n<th>Overlap during inference</th>\n<th>CV Score</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>4 hours</td>\n<td>0.7818</td>\n</tr>\n</tbody>\n</table>\n<h2>Threshold and peak gap tuning</h2>\n<p>Lower threshold with bigger gap between events produced better score. Missing on event seems to be punished more than extra prediction but too many predicted events led to submission scoring error. Best score I was able to get was using 0.001 threshold and 60 sample distance (5 minutes) between predicted events.</p>\n<table>\n<thead>\n<tr>\n<th>Threshold</th>\n<th>Min event distance</th>\n<th>CV Score</th>\n<th>Public Score</th>\n<th>Private Score</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0.001</td>\n<td>60</td>\n<td>0.7871</td>\n<td>0.740</td>\n<td>0.782</td>\n</tr>\n</tbody>\n</table>\n<h1>Sources</h1>\n<ul>\n<li><a href=\"https://www.kaggle.com/code/tubotubo/cmi-submit\" target=\"_blank\">https://www.kaggle.com/code/tubotubo/cmi-submit</a></li>\n<li><a href=\"https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940\" target=\"_blank\">https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940</a></li>\n<li><a href=\"https://github.com/tubo213/kaggle-child-mind-institute-detect-sleep-states/tree/main\" target=\"_blank\">https://github.com/tubo213/kaggle-child-mind-institute-detect-sleep-states/tree/main</a></li>\n</ul>\n<h1>Ending note - trust your CV or not</h1>\n<p>I started with my own pipeline, using handcrafted features and decision tree models and later moved to NNs. Even though the models were getting really good CV score (over 0.8) the leaderboard score was always about 0.15 lower. I tried multiple CV strategies, random split, split based on length, number of nights without labels etc. and could not get a good CV strategy. I checked my code multiple times for leaks and was not able to find out what could be the cause. Getting discouraged by the low leaderboard score I abandoned my pipeline and switched to 213tubo's amazing solution about a week before the competition end. After the private leaderboard was revealed I found out that my pipeline was working just as well and only the public score was low. Lesson learned to trust your CV even though it's hard sometimes.</p>",
  "messages": [
    {
      "id": "2566523",
      "postDate": "12/18/2023 23:00:48",
      "content": "<p>First, big thanks to the organizers for preparing such an interesting competition. I really enjoyed working on it and learned a lot. I was quite surprised how well the NN models worked in this competition and how easily they surpassed the handcrafted features and decision tree models.</p>\n<p>Also big thanks to all the people who shared their knowledge in discussions and code. Especially to:</p>\n<ul>\n<li><a href=\"https://www.kaggle.com/tubotubo\" target=\"_blank\">213tubo</a> for <a href=\"https://www.kaggle.com/code/tubotubo/cmi-submit\" target=\"_blank\">CMI-submit</a></li>\n<li><a href=\"https://www.kaggle.com/danielphalen\" target=\"_blank\">Daniel Phalen</a> for <a href=\"https://www.kaggle.com/code/danielphalen/cmss-grunet-infer\" target=\"_blank\">CMSS-GRUNET</a></li>\n<li><a href=\"https://www.kaggle.com/werus23\" target=\"_blank\">werus23</a> for <a href=\"https://www.kaggle.com/code/werus23/sleep-critical-point-infer\" target=\"_blank\">Sleep - Critical point</a></li>\n<li><a href=\"https://www.kaggle.com/lccburk\" target=\"_blank\">Lucas Burke</a> for <a href=\"https://www.kaggle.com/code/lccburk/feature-engineering-and-random-forest-prediction\" target=\"_blank\">Feature Engineering and Random Forest Prediction</a></li>\n</ul>\n<p>Even though my best solution was based on code published by <a href=\"https://www.kaggle.com/tubotubo\" target=\"_blank\">213tubo</a> in <a href=\"https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940\" target=\"_blank\">Detecting sleep using UNet(2D) post</a>, I decided to publish the write up to show some simple ways how to improve on it and that it's possible to get good results using only limited resources. All training was done on Google Colab using T4 GPU instance.</p>\n<h1>Context</h1>\n<p>Business Context - <a href=\"https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/overview\" target=\"_blank\">https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/overview</a><br>\nData Context - <a href=\"https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/data\" target=\"_blank\">https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/data</a></p>\n<h1>Overview of the Approach</h1>\n<p>My best scoring pipeline is based on code published by <a href=\"https://www.kaggle.com/tubotubo\" target=\"_blank\">213tubo</a> in <a href=\"https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940\" target=\"_blank\">Detecting sleep using UNet(2D) post</a>.</p>\n<h2>Model</h2>\n<p>As described by in the original post the model consist of feature extractor, encoder and decoder.</p>\n<ul>\n<li><strong>Feature extractor</strong> takes raw input and transforms it into wavegram of image like shape. 3 Conv1d layers with different kernel sizes were used as feature extractors.</li>\n<li><strong>Encoder</strong> takes the wavegram and creates a two dimensional heatmap. Unet and FPN models with resnet34 as backbone from <a href=\"https://github.com/qubvel/segmentation_models.pytorch\" target=\"_blank\">PyTorch Segmentation models library</a> were used as encoders.</li>\n<li><strong>Decoder</strong> creates probabilities of sleep, onset and wakeup from the heatmap generated by encoder. 1d Unet and TransformerEncoder were used as decoders. Only onset and wakeup predictions are used during inference.</li>\n</ul>\n<p>Model input: 8 hour window (5760 samples) with 4 features: normalized anglez, normalized enmo, hour sin and hour cos</p>\n<p>Used around 20% of data for validation and trained on remaining 80%.</p>\n<h2>Additional improvements that boosted the score:</h2>\n<p>1) Train more models<br>\n2) Ensemble using average output of multiple models<br>\n3) Use timeframes with overlap during inference, average out predictions from overlapping segments<br>\n4) Tune threshold and peak gap - lower threshold with 5 minute gap between events produced the best results</p>\n<h1>Details of the submission</h1>\n<p>Model published in <a href=\"https://www.kaggle.com/code/tubotubo/cmi-submit\" target=\"_blank\">CMI-submit</a></p>\n<table>\n<thead>\n<tr>\n<th>Base model</th>\n<th>CV Score</th>\n<th>Public Score</th>\n<th>Private Score</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Base model</td>\n<td>0.7305</td>\n<td>0.707</td>\n<td>0.728</td>\n</tr>\n</tbody>\n</table>\n<h2>Experiment with different models, architectures, settings</h2>\n<p>I trained around 30 models with different encoders, feature extractors, downsampling rate, losses, architectures and sizes. Most of the models were trained with downsample rate 4 which sped up the training and did not decrease performance. From those models I selected best 6 for the final ensemble based on their score.</p>\n<table>\n<thead>\n<tr>\n<th>Feature extractor base filters</th>\n<th>Encoder architecture</th>\n<th>Decoder</th>\n<th>downsample rate</th>\n<th>CV Score</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>256</td>\n<td>Unet</td>\n<td>UNet1DDecoder</td>\n<td>4</td>\n<td>0.7545</td>\n</tr>\n<tr>\n<td>64</td>\n<td>Unet</td>\n<td>TransformerCNNDecoder</td>\n<td>4</td>\n<td>0.7526</td>\n</tr>\n<tr>\n<td>128</td>\n<td>Unet</td>\n<td>UNet1DDecoder</td>\n<td>4</td>\n<td>0.7525</td>\n</tr>\n<tr>\n<td>128</td>\n<td>Unet</td>\n<td>UNet1DDecoder</td>\n<td>2</td>\n<td>0.7519</td>\n</tr>\n<tr>\n<td>128</td>\n<td>Unet</td>\n<td>UNet1DDecoder</td>\n<td>3</td>\n<td>0.7510</td>\n</tr>\n<tr>\n<td>128</td>\n<td>FPN</td>\n<td>UNet1DDecoder</td>\n<td>4</td>\n<td>0.7486</td>\n</tr>\n</tbody>\n</table>\n<h2>Ensemble</h2>\n<p>Average output of models was used as prediction. I tested mean, max, min and some simple weighted average based on CV score but simple average gave best results.</p>\n<table>\n<thead>\n<tr>\n<th>Ensemble</th>\n<th>Ensemble method</th>\n<th>CV Score</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>6 model ensemble</td>\n<td>mean</td>\n<td>0.7717</td>\n</tr>\n</tbody>\n</table>\n<h2>Timeframe with overlaps during inference</h2>\n<p>Inference was made on 8 hour windows with 4 hour step, overlapping segments were avereged out. Bigger overlap gave better results but unfortunatelly finished few minutes after competition ended. Taking average from overlapping segments was better than taking minimum prediction, using maximum was giving the worst results.</p>\n<table>\n<thead>\n<tr>\n<th>Overlap during inference</th>\n<th>CV Score</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>4 hours</td>\n<td>0.7818</td>\n</tr>\n</tbody>\n</table>\n<h2>Threshold and peak gap tuning</h2>\n<p>Lower threshold with bigger gap between events produced better score. Missing on event seems to be punished more than extra prediction but too many predicted events led to submission scoring error. Best score I was able to get was using 0.001 threshold and 60 sample distance (5 minutes) between predicted events.</p>\n<table>\n<thead>\n<tr>\n<th>Threshold</th>\n<th>Min event distance</th>\n<th>CV Score</th>\n<th>Public Score</th>\n<th>Private Score</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0.001</td>\n<td>60</td>\n<td>0.7871</td>\n<td>0.740</td>\n<td>0.782</td>\n</tr>\n</tbody>\n</table>\n<h1>Sources</h1>\n<ul>\n<li><a href=\"https://www.kaggle.com/code/tubotubo/cmi-submit\" target=\"_blank\">https://www.kaggle.com/code/tubotubo/cmi-submit</a></li>\n<li><a href=\"https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940\" target=\"_blank\">https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940</a></li>\n<li><a href=\"https://github.com/tubo213/kaggle-child-mind-institute-detect-sleep-states/tree/main\" target=\"_blank\">https://github.com/tubo213/kaggle-child-mind-institute-detect-sleep-states/tree/main</a></li>\n</ul>\n<h1>Ending note - trust your CV or not</h1>\n<p>I started with my own pipeline, using handcrafted features and decision tree models and later moved to NNs. Even though the models were getting really good CV score (over 0.8) the leaderboard score was always about 0.15 lower. I tried multiple CV strategies, random split, split based on length, number of nights without labels etc. and could not get a good CV strategy. I checked my code multiple times for leaks and was not able to find out what could be the cause. Getting discouraged by the low leaderboard score I abandoned my pipeline and switched to 213tubo's amazing solution about a week before the competition end. After the private leaderboard was revealed I found out that my pipeline was working just as well and only the public score was low. Lesson learned to trust your CV even though it's hard sometimes.</p>",
      "rawMarkdown": "First, big thanks to the organizers for preparing such an interesting competition. I really enjoyed working on it and learned a lot. I was quite surprised how well the NN models worked in this competition and how easily they surpassed the handcrafted features and decision tree models.\n\nAlso big thanks to all the people who shared their knowledge in discussions and code. Especially to:\n- [213tubo](https://www.kaggle.com/tubotubo) for [CMI-submit](https://www.kaggle.com/code/tubotubo/cmi-submit)\n- [Daniel Phalen](https://www.kaggle.com/danielphalen) for [CMSS-GRUNET](https://www.kaggle.com/code/danielphalen/cmss-grunet-infer)\n- [werus23](https://www.kaggle.com/werus23) for [Sleep - Critical point](https://www.kaggle.com/code/werus23/sleep-critical-point-infer)\n- [Lucas Burke](https://www.kaggle.com/lccburk) for [Feature Engineering and Random Forest Prediction](https://www.kaggle.com/code/lccburk/feature-engineering-and-random-forest-prediction)\n\nEven though my best solution was based on code published by [213tubo](https://www.kaggle.com/tubotubo) in [Detecting sleep using UNet(2D) post](https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940), I decided to publish the write up to show some simple ways how to improve on it and that it's possible to get good results using only limited resources. All training was done on Google Colab using T4 GPU instance.\n\n#Context\nBusiness Context - https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/overview\nData Context - https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/data\n\n#Overview of the Approach\nMy best scoring pipeline is based on code published by [213tubo](https://www.kaggle.com/tubotubo) in [Detecting sleep using UNet(2D) post](https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940).\n\n## Model\nAs described by in the original post the model consist of feature extractor, encoder and decoder.\n- **Feature extractor** takes raw input and transforms it into wavegram of image like shape. 3 Conv1d layers with different kernel sizes were used as feature extractors.\n- **Encoder** takes the wavegram and creates a two dimensional heatmap. Unet and FPN models with resnet34 as backbone from [PyTorch Segmentation models library](https://github.com/qubvel/segmentation_models.pytorch) were used as encoders.\n- **Decoder** creates probabilities of sleep, onset and wakeup from the heatmap generated by encoder. 1d Unet and TransformerEncoder were used as decoders. Only onset and wakeup predictions are used during inference.\n\nModel input: 8 hour window (5760 samples) with 4 features: normalized anglez, normalized enmo, hour sin and hour cos\n\nUsed around 20% of data for validation and trained on remaining 80%.\n\n## Additional improvements that boosted the score:\n1) Train more models\n2) Ensemble using average output of multiple models\n3) Use timeframes with overlap during inference, average out predictions from overlapping segments\n4) Tune threshold and peak gap - lower threshold with 5 minute gap between events produced the best results\n\n#Details of the submission\nModel published in [CMI-submit](https://www.kaggle.com/code/tubotubo/cmi-submit)\n|Base model|CV Score|Public Score|Private Score|\n|---|---|---|---|\n|Base model|0.7305|0.707|0.728|\n\n## Experiment with different models, architectures, settings\nI trained around 30 models with different encoders, feature extractors, downsampling rate, losses, architectures and sizes. Most of the models were trained with downsample rate 4 which sped up the training and did not decrease performance. From those models I selected best 6 for the final ensemble based on their score.\n\n|Feature extractor base filters|Encoder architecture|Decoder|downsample rate|CV Score|\n|---|---|---|---|---|\n|256|Unet|UNet1DDecoder|4|0.7545|\n|64|Unet|TransformerCNNDecoder|4|0.7526|\n|128|Unet|UNet1DDecoder|4|0.7525|\n|128|Unet|UNet1DDecoder|2|0.7519|\n|128|Unet|UNet1DDecoder|3|0.7510|\n|128| FPN|UNet1DDecoder|4|0.7486|\n\n## Ensemble\nAverage output of models was used as prediction. I tested mean, max, min and some simple weighted average based on CV score but simple average gave best results.\n|Ensemble|Ensemble method|CV Score|\n|---|---|---|\n|6 model ensemble|mean|0.7717|\n\n## Timeframe with overlaps during inference\nInference was made on 8 hour windows with 4 hour step, overlapping segments were avereged out. Bigger overlap gave better results but unfortunatelly finished few minutes after competition ended. Taking average from overlapping segments was better than taking minimum prediction, using maximum was giving the worst results.\n|Overlap during inference|CV Score|\n|---|---|\n|4 hours|0.7818|\n\n## Threshold and peak gap tuning\nLower threshold with bigger gap between events produced better score. Missing on event seems to be punished more than extra prediction but too many predicted events led to submission scoring error. Best score I was able to get was using 0.001 threshold and 60 sample distance (5 minutes) between predicted events.\n|Threshold|Min event distance|CV Score|Public Score|Private Score|\n|---|---|---|\n|0.001|60|0.7871|0.740|0.782|\n\n# Sources\n- https://www.kaggle.com/code/tubotubo/cmi-submit\n- https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940\n- https://github.com/tubo213/kaggle-child-mind-institute-detect-sleep-states/tree/main\n\n# Ending note - trust your CV or not\nI started with my own pipeline, using handcrafted features and decision tree models and later moved to NNs. Even though the models were getting really good CV score (over 0.8) the leaderboard score was always about 0.15 lower. I tried multiple CV strategies, random split, split based on length, number of nights without labels etc. and could not get a good CV strategy. I checked my code multiple times for leaks and was not able to find out what could be the cause. Getting discouraged by the low leaderboard score I abandoned my pipeline and switched to 213tubo's amazing solution about a week before the competition end. After the private leaderboard was revealed I found out that my pipeline was working just as well and only the public score was low. Lesson learned to trust your CV even though it's hard sometimes.",
      "votes": null
    },
    {
      "id": "2566951",
      "postDate": "12/19/2023 09:28:41",
      "content": "<p>Congrats! I guess the one thing to takeaway from this competition, is that the train dataset is very robust (probably one of the best I have seen) that you can trust the CV score easily.</p>\n<p>For my team, when the private LB was released, we realised our CV scores are always around 0.005-0.008 higher than the private LB, with little deviation. For once, the right submissions were chosen.</p>",
      "rawMarkdown": "Congrats! I guess the one thing to takeaway from this competition, is that the train dataset is very robust (probably one of the best I have seen) that you can trust the CV score easily.\n\nFor my team, when the private LB was released, we realised our CV scores are always around 0.005-0.008 higher than the private LB, with little deviation. For once, the right submissions were chosen.",
      "votes": null
    }
  ],
  "comments": [
    {
      "id": 2566951,
      "author_name": "yeoyunsianggeremie",
      "author_url": "",
      "post_date": "12/19/2023 09:28:41",
      "content": "<p>Congrats! I guess the one thing to takeaway from this competition, is that the train dataset is very robust (probably one of the best I have seen) that you can trust the CV score easily.</p>\n<p>For my team, when the private LB was released, we realised our CV scores are always around 0.005-0.008 higher than the private LB, with little deviation. For once, the right submissions were chosen.</p>",
      "votes": null,
      "replies": []
    }
  ],
  "raw_markdown_by_id": {
    "2566523": "First, big thanks to the organizers for preparing such an interesting competition. I really enjoyed working on it and learned a lot. I was quite surprised how well the NN models worked in this competition and how easily they surpassed the handcrafted features and decision tree models.\n\nAlso big thanks to all the people who shared their knowledge in discussions and code. Especially to:\n- [213tubo](https://www.kaggle.com/tubotubo) for [CMI-submit](https://www.kaggle.com/code/tubotubo/cmi-submit)\n- [Daniel Phalen](https://www.kaggle.com/danielphalen) for [CMSS-GRUNET](https://www.kaggle.com/code/danielphalen/cmss-grunet-infer)\n- [werus23](https://www.kaggle.com/werus23) for [Sleep - Critical point](https://www.kaggle.com/code/werus23/sleep-critical-point-infer)\n- [Lucas Burke](https://www.kaggle.com/lccburk) for [Feature Engineering and Random Forest Prediction](https://www.kaggle.com/code/lccburk/feature-engineering-and-random-forest-prediction)\n\nEven though my best solution was based on code published by [213tubo](https://www.kaggle.com/tubotubo) in [Detecting sleep using UNet(2D) post](https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940), I decided to publish the write up to show some simple ways how to improve on it and that it's possible to get good results using only limited resources. All training was done on Google Colab using T4 GPU instance.\n\n#Context\nBusiness Context - https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/overview\nData Context - https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/data\n\n#Overview of the Approach\nMy best scoring pipeline is based on code published by [213tubo](https://www.kaggle.com/tubotubo) in [Detecting sleep using UNet(2D) post](https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940).\n\n## Model\nAs described by in the original post the model consist of feature extractor, encoder and decoder.\n- **Feature extractor** takes raw input and transforms it into wavegram of image like shape. 3 Conv1d layers with different kernel sizes were used as feature extractors.\n- **Encoder** takes the wavegram and creates a two dimensional heatmap. Unet and FPN models with resnet34 as backbone from [PyTorch Segmentation models library](https://github.com/qubvel/segmentation_models.pytorch) were used as encoders.\n- **Decoder** creates probabilities of sleep, onset and wakeup from the heatmap generated by encoder. 1d Unet and TransformerEncoder were used as decoders. Only onset and wakeup predictions are used during inference.\n\nModel input: 8 hour window (5760 samples) with 4 features: normalized anglez, normalized enmo, hour sin and hour cos\n\nUsed around 20% of data for validation and trained on remaining 80%.\n\n## Additional improvements that boosted the score:\n1) Train more models\n2) Ensemble using average output of multiple models\n3) Use timeframes with overlap during inference, average out predictions from overlapping segments\n4) Tune threshold and peak gap - lower threshold with 5 minute gap between events produced the best results\n\n#Details of the submission\nModel published in [CMI-submit](https://www.kaggle.com/code/tubotubo/cmi-submit)\n|Base model|CV Score|Public Score|Private Score|\n|---|---|---|---|\n|Base model|0.7305|0.707|0.728|\n\n## Experiment with different models, architectures, settings\nI trained around 30 models with different encoders, feature extractors, downsampling rate, losses, architectures and sizes. Most of the models were trained with downsample rate 4 which sped up the training and did not decrease performance. From those models I selected best 6 for the final ensemble based on their score.\n\n|Feature extractor base filters|Encoder architecture|Decoder|downsample rate|CV Score|\n|---|---|---|---|---|\n|256|Unet|UNet1DDecoder|4|0.7545|\n|64|Unet|TransformerCNNDecoder|4|0.7526|\n|128|Unet|UNet1DDecoder|4|0.7525|\n|128|Unet|UNet1DDecoder|2|0.7519|\n|128|Unet|UNet1DDecoder|3|0.7510|\n|128| FPN|UNet1DDecoder|4|0.7486|\n\n## Ensemble\nAverage output of models was used as prediction. I tested mean, max, min and some simple weighted average based on CV score but simple average gave best results.\n|Ensemble|Ensemble method|CV Score|\n|---|---|---|\n|6 model ensemble|mean|0.7717|\n\n## Timeframe with overlaps during inference\nInference was made on 8 hour windows with 4 hour step, overlapping segments were avereged out. Bigger overlap gave better results but unfortunatelly finished few minutes after competition ended. Taking average from overlapping segments was better than taking minimum prediction, using maximum was giving the worst results.\n|Overlap during inference|CV Score|\n|---|---|\n|4 hours|0.7818|\n\n## Threshold and peak gap tuning\nLower threshold with bigger gap between events produced better score. Missing on event seems to be punished more than extra prediction but too many predicted events led to submission scoring error. Best score I was able to get was using 0.001 threshold and 60 sample distance (5 minutes) between predicted events.\n|Threshold|Min event distance|CV Score|Public Score|Private Score|\n|---|---|---|\n|0.001|60|0.7871|0.740|0.782|\n\n# Sources\n- https://www.kaggle.com/code/tubotubo/cmi-submit\n- https://www.kaggle.com/competitions/child-mind-institute-detect-sleep-states/discussion/452940\n- https://github.com/tubo213/kaggle-child-mind-institute-detect-sleep-states/tree/main\n\n# Ending note - trust your CV or not\nI started with my own pipeline, using handcrafted features and decision tree models and later moved to NNs. Even though the models were getting really good CV score (over 0.8) the leaderboard score was always about 0.15 lower. I tried multiple CV strategies, random split, split based on length, number of nights without labels etc. and could not get a good CV strategy. I checked my code multiple times for leaks and was not able to find out what could be the cause. Getting discouraged by the low leaderboard score I abandoned my pipeline and switched to 213tubo's amazing solution about a week before the competition end. After the private leaderboard was revealed I found out that my pipeline was working just as well and only the public score was low. Lesson learned to trust your CV even though it's hard sometimes.",
    "2566951": "Congrats! I guess the one thing to takeaway from this competition, is that the train dataset is very robust (probably one of the best I have seen) that you can trust the CV score easily.\n\nFor my team, when the private LB was released, we realised our CV scores are always around 0.005-0.008 higher than the private LB, with little deviation. For once, the right submissions were chosen."
  },
  "source": "existing_discussion",
  "complete": true,
  "competition_id": 53666,
  "competition_slug": "child-mind-institute-detect-sleep-states",
  "team_id": 11031115,
  "solution_url": "/competitions/child-mind-institute-detect-sleep-states/writeups/michal-86th-place-solution-for-the-child-mind-inst"
}