{
  "id": 603659,
  "title": "48th Place Solution",
  "url": "/competitions/cmi-detect-behavior-with-sensor-data/discussion/603659",
  "author_name": "HayatoFujihara",
  "post_date": "2025-09-03T14:17:07.436000",
  "votes": 10,
  "comment_count": 0,
  "views": 0,
  "content": "<p>First of all, thank you for organizing such a fantastic competition! Kudos to the organizers and all the participants for their hard work!</p>\n<p>Here’s a brief overview of my approach.</p>\n<h2>Models Used</h2>\n<p>I used an ensemble of transformer, GRU, and squeezeformer models. The models were divided into two categories: IMU only and Full data. For Full data, I used only GRU models. Below is the structure of the GRU model (other models are quite similar).</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F20605011%2F7c556f5732790869f2d634d4cd8b1c55%2Fcmi2025_1.drawio.png?generation=1756909002205113&amp;alt=media\" alt=\"GRU_model\"></p>\n<p>A key feature was the addition of a 1D CNN layer after the input, rather than using GRU alone. This significantly boosted the cross-validation (CV) score.</p>\n<h3>Best Submission (Overview)</h3>\n<p>IMU only: 2 transformer models + 2 GRU models + 1 squeezeformer model</p>\n<p>Full data: 10 GRU models</p>\n<h2>Feature Engineering</h2>\n<p>Since this is time-series data, I mainly added features that incorporate information beyond the current row. For example, applying a rolling standard deviation to acc_x improved CV. I also included centered rolling statistics:</p>\n<pre><code>train_df[] = train_df.groupby()[].transform( x: x.rolling(window=, min_periods=).std())\ntrain_df[] = train_df.groupby()[].transform( x: x.rolling(window=, center=, min_periods=).std())\n</code></pre>\n<p>Interestingly, something unexpected happened: I accidentally added a feature from thm twice, and for some reason, CV improved by 0.01. Although the reason is unclear, I kept it since it enhanced performance.</p>\n<h2>EMA</h2>\n<p>Incorporating EMA helped improve CV while also enhancing generalization.</p>\n<h2>Null Handling</h2>\n<p>The tof data contained a fair amount of missing values, so proper null handling was essential. I used linear interpolation and median imputation to fill in missing values smoothly. Here’s the code used for null handling during training:</p>\n<pre><code> group_prefix  tof_groups:\n    group_cols = [col  col  train_df.columns  col.startswith(group_prefix)]\n    train_df[group_cols] = train_df[group_cols].replace(-, np.nan)\n    train_df[group_cols] = train_df[group_cols].apply(\n         row: row.interpolate(method=, limit_direction=, limit=), axis=\n    )\n    train_df[group_cols] = train_df[group_cols].apply(\n         row: row.fillna(row.median()), axis=\n    )\n    train_df[group_cols] = train_df[group_cols].fillna(-)\n</code></pre>\n<h2>Mixup</h2>\n<p>I set the mixup alpha to 1.0. This was intended to improve generalization, and it did lead to better CV.</p>\n<h2>Best Models and Final Models</h2>\n<p>Models with early stopping were treated as “best models,” while those without were considered “final models.” Both types were used in the ensemble. Here’s the breakdown:</p>\n<p>IMU only:</p>\n<p>2 transformer models (best &amp; final)</p>\n<p>2 GRU models (best &amp; final)</p>\n<p>1 squeezeformer model (final only)</p>\n<p>Full data:</p>\n<p>10 GRU models (2 versions × 5 seeds: best &amp; final)</p>\n<h2>Ensemble Method</h2>\n<p>I used simple averaging for the ensemble.</p>\n<h2>What Didn’t Work</h2>\n<p>Auxiliary Loss for orientation: It slightly improved CV but worsened the leaderboard (LB) score, so I didn’t adopt it.</p>\n<p>Thanks for reading!</p>",
  "messages": [
    {
      "id": 3281048,
      "postDate": "2025-09-03T14:17:07.437Z",
      "content": "<p>First of all, thank you for organizing such a fantastic competition! Kudos to the organizers and all the participants for their hard work!</p>\n<p>Here’s a brief overview of my approach.</p>\n<h2>Models Used</h2>\n<p>I used an ensemble of transformer, GRU, and squeezeformer models. The models were divided into two categories: IMU only and Full data. For Full data, I used only GRU models. Below is the structure of the GRU model (other models are quite similar).</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F20605011%2F7c556f5732790869f2d634d4cd8b1c55%2Fcmi2025_1.drawio.png?generation=1756909002205113&amp;alt=media\" alt=\"GRU_model\"></p>\n<p>A key feature was the addition of a 1D CNN layer after the input, rather than using GRU alone. This significantly boosted the cross-validation (CV) score.</p>\n<h3>Best Submission (Overview)</h3>\n<p>IMU only: 2 transformer models + 2 GRU models + 1 squeezeformer model</p>\n<p>Full data: 10 GRU models</p>\n<h2>Feature Engineering</h2>\n<p>Since this is time-series data, I mainly added features that incorporate information beyond the current row. For example, applying a rolling standard deviation to acc_x improved CV. I also included centered rolling statistics:</p>\n<pre><code>train_df[] = train_df.groupby()[].transform( x: x.rolling(window=, min_periods=).std())\ntrain_df[] = train_df.groupby()[].transform( x: x.rolling(window=, center=, min_periods=).std())\n</code></pre>\n<p>Interestingly, something unexpected happened: I accidentally added a feature from thm twice, and for some reason, CV improved by 0.01. Although the reason is unclear, I kept it since it enhanced performance.</p>\n<h2>EMA</h2>\n<p>Incorporating EMA helped improve CV while also enhancing generalization.</p>\n<h2>Null Handling</h2>\n<p>The tof data contained a fair amount of missing values, so proper null handling was essential. I used linear interpolation and median imputation to fill in missing values smoothly. Here’s the code used for null handling during training:</p>\n<pre><code> group_prefix  tof_groups:\n    group_cols = [col  col  train_df.columns  col.startswith(group_prefix)]\n    train_df[group_cols] = train_df[group_cols].replace(-, np.nan)\n    train_df[group_cols] = train_df[group_cols].apply(\n         row: row.interpolate(method=, limit_direction=, limit=), axis=\n    )\n    train_df[group_cols] = train_df[group_cols].apply(\n         row: row.fillna(row.median()), axis=\n    )\n    train_df[group_cols] = train_df[group_cols].fillna(-)\n</code></pre>\n<h2>Mixup</h2>\n<p>I set the mixup alpha to 1.0. This was intended to improve generalization, and it did lead to better CV.</p>\n<h2>Best Models and Final Models</h2>\n<p>Models with early stopping were treated as “best models,” while those without were considered “final models.” Both types were used in the ensemble. Here’s the breakdown:</p>\n<p>IMU only:</p>\n<p>2 transformer models (best &amp; final)</p>\n<p>2 GRU models (best &amp; final)</p>\n<p>1 squeezeformer model (final only)</p>\n<p>Full data:</p>\n<p>10 GRU models (2 versions × 5 seeds: best &amp; final)</p>\n<h2>Ensemble Method</h2>\n<p>I used simple averaging for the ensemble.</p>\n<h2>What Didn’t Work</h2>\n<p>Auxiliary Loss for orientation: It slightly improved CV but worsened the leaderboard (LB) score, so I didn’t adopt it.</p>\n<p>Thanks for reading!</p>",
      "rawMarkdown": "First of all, thank you for organizing such a fantastic competition! Kudos to the organizers and all the participants for their hard work!\n\nHere’s a brief overview of my approach.\n\n## Models Used\nI used an ensemble of transformer, GRU, and squeezeformer models. The models were divided into two categories: IMU only and Full data. For Full data, I used only GRU models. Below is the structure of the GRU model (other models are quite similar).\n\n![GRU_model](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F20605011%2F7c556f5732790869f2d634d4cd8b1c55%2Fcmi2025_1.drawio.png?generation=1756909002205113&alt=media)\n\nA key feature was the addition of a 1D CNN layer after the input, rather than using GRU alone. This significantly boosted the cross-validation (CV) score.\n\n### Best Submission (Overview)\nIMU only: 2 transformer models + 2 GRU models + 1 squeezeformer model\n\nFull data: 10 GRU models\n\n## Feature Engineering\nSince this is time-series data, I mainly added features that incorporate information beyond the current row. For example, applying a rolling standard deviation to acc_x improved CV. I also included centered rolling statistics:\n\n```python\ntrain_df['acc_x_std'] = train_df.groupby('sequence_id')['acc_x'].transform(lambda x: x.rolling(window=5, min_periods=1).std())\ntrain_df['acc_x_center_std'] = train_df.groupby('sequence_id')['acc_x'].transform(lambda x: x.rolling(window=5, center=True, min_periods=1).std())\n```\n\nInterestingly, something unexpected happened: I accidentally added a feature from thm twice, and for some reason, CV improved by 0.01. Although the reason is unclear, I kept it since it enhanced performance.\n\n## EMA\nIncorporating EMA helped improve CV while also enhancing generalization.\n\n## Null Handling\nThe tof data contained a fair amount of missing values, so proper null handling was essential. I used linear interpolation and median imputation to fill in missing values smoothly. Here’s the code used for null handling during training:\n\n```python\nfor group_prefix in tof_groups:\n    group_cols = [col for col in train_df.columns if col.startswith(group_prefix)]\n    train_df[group_cols] = train_df[group_cols].replace(-1, np.nan)\n    train_df[group_cols] = train_df[group_cols].apply(\n        lambda row: row.interpolate(method='linear', limit_direction='both', limit=5), axis=1\n    )\n    train_df[group_cols] = train_df[group_cols].apply(\n        lambda row: row.fillna(row.median()), axis=1\n    )\n    train_df[group_cols] = train_df[group_cols].fillna(-1)\n```\n\n## Mixup\nI set the mixup alpha to 1.0. This was intended to improve generalization, and it did lead to better CV.\n\n## Best Models and Final Models\nModels with early stopping were treated as “best models,” while those without were considered “final models.” Both types were used in the ensemble. Here’s the breakdown:\n\nIMU only:\n\n2 transformer models (best & final)\n\n2 GRU models (best & final)\n\n1 squeezeformer model (final only)\n\nFull data:\n\n10 GRU models (2 versions × 5 seeds: best & final)\n\n## Ensemble Method\nI used simple averaging for the ensemble.\n\n## What Didn’t Work\nAuxiliary Loss for orientation: It slightly improved CV but worsened the leaderboard (LB) score, so I didn’t adopt it.\n\n\n\nThanks for reading!\n",
      "votes": 10
    }
  ],
  "comments": [],
  "raw_markdown_by_id": {
    "3281048": "First of all, thank you for organizing such a fantastic competition! Kudos to the organizers and all the participants for their hard work!\n\nHere’s a brief overview of my approach.\n\n## Models Used\nI used an ensemble of transformer, GRU, and squeezeformer models. The models were divided into two categories: IMU only and Full data. For Full data, I used only GRU models. Below is the structure of the GRU model (other models are quite similar).\n\n![GRU_model](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F20605011%2F7c556f5732790869f2d634d4cd8b1c55%2Fcmi2025_1.drawio.png?generation=1756909002205113&alt=media)\n\nA key feature was the addition of a 1D CNN layer after the input, rather than using GRU alone. This significantly boosted the cross-validation (CV) score.\n\n### Best Submission (Overview)\nIMU only: 2 transformer models + 2 GRU models + 1 squeezeformer model\n\nFull data: 10 GRU models\n\n## Feature Engineering\nSince this is time-series data, I mainly added features that incorporate information beyond the current row. For example, applying a rolling standard deviation to acc_x improved CV. I also included centered rolling statistics:\n\n```python\ntrain_df['acc_x_std'] = train_df.groupby('sequence_id')['acc_x'].transform(lambda x: x.rolling(window=5, min_periods=1).std())\ntrain_df['acc_x_center_std'] = train_df.groupby('sequence_id')['acc_x'].transform(lambda x: x.rolling(window=5, center=True, min_periods=1).std())\n```\n\nInterestingly, something unexpected happened: I accidentally added a feature from thm twice, and for some reason, CV improved by 0.01. Although the reason is unclear, I kept it since it enhanced performance.\n\n## EMA\nIncorporating EMA helped improve CV while also enhancing generalization.\n\n## Null Handling\nThe tof data contained a fair amount of missing values, so proper null handling was essential. I used linear interpolation and median imputation to fill in missing values smoothly. Here’s the code used for null handling during training:\n\n```python\nfor group_prefix in tof_groups:\n    group_cols = [col for col in train_df.columns if col.startswith(group_prefix)]\n    train_df[group_cols] = train_df[group_cols].replace(-1, np.nan)\n    train_df[group_cols] = train_df[group_cols].apply(\n        lambda row: row.interpolate(method='linear', limit_direction='both', limit=5), axis=1\n    )\n    train_df[group_cols] = train_df[group_cols].apply(\n        lambda row: row.fillna(row.median()), axis=1\n    )\n    train_df[group_cols] = train_df[group_cols].fillna(-1)\n```\n\n## Mixup\nI set the mixup alpha to 1.0. This was intended to improve generalization, and it did lead to better CV.\n\n## Best Models and Final Models\nModels with early stopping were treated as “best models,” while those without were considered “final models.” Both types were used in the ensemble. Here’s the breakdown:\n\nIMU only:\n\n2 transformer models (best & final)\n\n2 GRU models (best & final)\n\n1 squeezeformer model (final only)\n\nFull data:\n\n10 GRU models (2 versions × 5 seeds: best & final)\n\n## Ensemble Method\nI used simple averaging for the ensemble.\n\n## What Didn’t Work\nAuxiliary Loss for orientation: It slightly improved CV but worsened the leaderboard (LB) score, so I didn’t adopt it.\n\n\n\nThanks for reading!\n"
  }
}